正文如下:
目录结构:
- config
- run.yaml 用来配置远程信息
- core
- connect.py 用来远程连接
- read_yaml.py 用来解析yaml文件
- sample_log.py 用来记录日志
- ssh_run.py 用来运行备份程序
-
requirements的支持:
asn1crypto==0.24.0
bcrypt==3.1.7
cffi==1.12.3
Click==7.0
cryptography==2.7
Flask==1.1.1
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
paramiko==2.6.0
pycparser==2.19
PyNaCl==1.3.0
PyYAML==5.1.1
six==1.12.0
Werkzeug==0.15.4
# 安装方法:pip install -r requirements.txt
-
run.yaml结构:
---
# 一个连接的记录
-
#任务的名称,方便日志记录
task_name: test1
#远程连接的ip地址
host: 0.0.0.0
#远程连接的端口号
port: 22
#远程连接的用户名
user: root
#远程连接的密码(和私钥二选一)
passwd:
#远程连接的私钥路径(和密码二选一)
key: xxx\.ssh\id.pem
#私钥的密码(选填)
key_passwd:
get:
#本地到本地的文件路径${%Y%m%d_%H%M}使用时间格式转换
local_file: d:/tmp/${%Y%m%d_%H%M}/test1.zip
#远程需要备份的路径
remote_file: /opt/xxx
#远程存放和生成临时文件的路径
remote_tmp_file: /tmp/a/zz.zip
-
connect.py的代码:
import paramiko
import os
import time
class kong:
def add(self, s):
pass
#远程连接方法
class SSH:
def __init__(self, host, port, user, passwd=None, key=None, key_passwd=None, log=kong()):
self.log = log
transport = paramiko.Transport((str(host), int(port)))
if key is None and passwd is None:
raise Exception('passwd or key must input one~')
elif key is None:
transport.connect(username=str(user), password=str(passwd))
else:
if key_passwd is None:
private_key = paramiko.RSAKey.from_private_key_file(str(key))
else:
private_key = paramiko.RSAKey.from_private_key_file(str(key), str(key_passwd))
transport.connect(username='root', pkey=private_key)
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh._transport = transport
sftp = ssh.open_sftp()
self.ssh_value = ssh
self.sftp_value = sftp
self.transport = transport
def ssh(self, cmd, inp=None):
stdin, stdout, stderr = self.ssh_value.exec_command(str(cmd))
if inp is not None:
stdin.write(str(inp))
if len(stderr.read().decode()) > 0:
raise Exception(stderr.read().decode())
return stderr.read().decode()
def sftp(self):
return self.sftp_value
def put(self, local_file, remote_file):
if not os.path.exists(local_file):
raise Exception(local_file + ',文件不存在')
if not self.is_exist(os.path.dirname(remote_file)):
self.mkdirs(os.path.dirname(remote_file))
self.sftp_value.put(local_file, remote_file)
def get(self, local_file, remote_file):
if os.path.exists(local_file):
t = time.strftime('%Y%m%d_%H%M%S', time.localtime())
os.rename(local_file, local_file + '_backup' + t)
else:
if not os.path.exists(os.path.dirname(local_file)):
raise Exception(os.path.dirname(local_file) + ',目录不存在')
if not self.is_exist(remote_file):
raise Exception(remote_file + '文件不存在')
self.sftp_value.get(remote_file, local_file)
def is_exist(self, path):
try:
self.ssh('ls ' + str(path))
return True
except:
return False
def mkdirs(self, path):
if self.is_exist(path):
pass
else:
if path == '/':
raise Exception('创建文件夹失败')
else:
try:
self.sftp_value.mkdir(path)
self.log.add('创建文件夹成功:' + path)
print('创建文件夹成功:', path)
except:
self.mkdirs(os.path.dirname(path))
self.mkdirs(path)
def close(self):
self.sftp_value.close()
self.ssh_value.close()
self.transport.close()
-
read_yaml.py的代码:
import yaml
import re
import time
import os
class kong:
def add(self, s):
pass
class Yaml:
def __init__(self, path='../config/run.yaml', log=kong()):
if not os.path.exists(path):
if path.startswith('../'):
path = path[3:]
self.path = path
self.da = []
self.time = time.localtime()
self.log = log
def read(self):
with open(self.path, encoding='utf-8') as f:
self.da = yaml.full_load(f)
def mkdirs(self, path, is_chird=False):
if not is_chird:
path = os.path.dirname(path)
if os.path.exists(path):
pass
# if not is_chird:
# print('存在文件夹:', path)
else:
if path == '/':
raise Exception('创建文件夹失败,路径:' + path)
else:
try:
os.mkdir(path)
self.log.add('创建文件夹成功:' + path)
print('创建文件夹成功:', path)
except:
self.mkdirs(os.path.dirname(path), is_chird=True)
self.mkdirs(path, is_chird=True)
def check(self):
temp_list = []
for inf in self.da:
if inf.get('host') is None or inf.get('port') is None or inf.get('user') is None or (
inf.get('passwd') is None and inf.get('key') is None or inf.get('get') is None):
self.log.add(inf['task_name'] + ',信息不全,无法执行' + str(inf))
print(inf['task_name'] + ',信息不全,无法执行' + str(inf))
continue
else:
if inf.get('get').get('local_file') is None:
print(inf['task_name'] + '本地备份路径为空,请检查:' + str(inf))
self.log.add(inf['task_name'] + '本地备份路径为空,请检查:' + str(inf))
continue
elif inf.get('get').get('remote_file') is None:
print(inf['task_name'] + '远程文件为空,请检查:' + str(inf))
self.log.add(inf['task_name'] + '远程文件为空,请检查:' + str(inf))
continue
elif inf.get('get').get('remote_tmp_file') is None:
print(inf['task_name'] + '远程临时文件为空,请检查;' + str(inf))
self.log.add(inf['task_name'] + '远程临时文件为空,请检查;' + str(inf))
continue
else:
local_path = inf['get']['local_file']
pas = re.findall(r"\$\{(.+?)\}", local_path)
for time1 in pas:
local_path = local_path.replace('${' + time1 + '}', time.strftime(time1, self.time))
inf['get']['local_file'] = local_path
self.mkdirs(local_path)
tmp_path = inf['get']['remote_tmp_file']
pas = re.findall(r"\$\{(.+?)\}", tmp_path)
for time1 in pas:
tmp_path = tmp_path.replace('${' + time1 + '}', time.strftime(time1, self.time))
inf['get']['remote_tmp_file'] = tmp_path
temp_list.append(inf)
print('校验数据,有{}条数据填写正确'.format(str(len(temp_list))))
self.log.add('校验数据,有{}条数据填写正确'.format(str(len(temp_list))))
return temp_list
-
sample_log.py的代码:
import time
import os
class SampleLog:
def __init__(self):
t = time.strftime('%Y%m%d', time.localtime())
self.t = t
if not os.path.exists(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'log', self.t, 'log.txt')):
if not os.path.exists(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'log')):
os.mkdir(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'log'))
with open(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'log', self.t + '.log'), 'w', encoding='utf8') as f:
f.write('#create time :' + time.strftime('%Y%m%d %H:%M:%S', time.localtime()) + '\n')
def add(self, s):
with open(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'log', self.t + '.log'), 'a', encoding='utf8') as f:
f.write(time.strftime('%Y%m%d %H:%M:%S', time.localtime()) + '>>\t' + str(s) + '\n')
-
ssh_run.py的代码:
import os, sys
sys.path.append(os.path.dirname(os.path.abspath('')))
sys.path.append(os.path.abspath(''))
from core.connect import SSH
from core.read_yaml import Yaml
from core.sample_log import SampleLog
s = SampleLog()
try:
y = Yaml(log=s)
y.read()
da = y.check()
print('_' * 30)
except Exception as e:
print(e)
raise Exception('读取文件错误')
for inf in da:
try:
ssh = SSH(inf['host'], inf['port'], inf['user'], inf['passwd'], inf['key'], inf['key_passwd'], s)
print(inf['task_name'] + '连接成功,正在远程生成压缩包')
s.add(inf['task_name'] + '连接成功')
ssh.ssh('rm -f ' + inf['get']['remote_tmp_file'])
ssh.ssh('cd {};tar cPf {} *'.format(inf['get']['remote_file'], inf['get']['remote_tmp_file']))
print(inf['task_name'] + '生成压缩包在' + inf['get']['remote_tmp_file'] + ',正在复制文件到本地')
s.add(inf['task_name'] + '生成压缩包在' + inf['get']['remote_tmp_file'])
ssh.get(inf['get']['local_file'], inf['get']['remote_tmp_file'])
print(inf['task_name'] + '复制到本地:' + inf['get']['local_file'])
s.add(inf['task_name'] + '复制到本地:' + inf['get']['local_file'])
ssh.close()
except Exception as e:
print(e)
s.add(str(e))