本篇博客为了实现一个文件同步的脚本,其实在前面已经有铺垫,专门有两遍博客介绍了watchdogparamiko模块,分别用于文件监控和SSH连接。如果对这两个模块不太熟悉,请先了解一下。

需求

1、将服务器B上指定目录同步到服务器A的指定目录 2、实时监控服务器A的此目录,并将更新同步到服务器B 3、更新信息打印到日志文件 4、脚本服务化

创建一个SSH功能的类

关键代码:

class ParamikoClient:
    def __init__(self, host, port, username, key):
        self.host = host
        self.port = port
        self.username = username
        self.pkey = paramiko.RSAKey.from_private_key_file(key)

    @property
    def sftp(self):
        transport = paramiko.Transport((self.host, self.port))
        transport.connect(username=self.username, pkey=self.pkey)
        sftp = paramiko.SFTPClient.from_transport(transport)
        return sftp

这里返回一个sftp对象,用来操作远程主机的文件。

创建一个监控类

关键代码:

class MyHandler(FileSystemEventHandler):

    # 初始化传入一个sftp对象
    def __init__(self, sftp):
        self.sftp = sftp

    # 文件变化
    def on_modified(self, event):
        if not_hidden(event.src_path):
            if not event.is_directory:
                self.on_created(event)

    # 文件改名
    def on_moved(self, event):
        rename(self.sftp, event.src_path, event.dest_path)

    # 创建文件
    def on_created(self, event):
        if not_hidden(event.src_path):
            if not event.is_directory:
                put(self.sftp, event.src_path)
            else:
                mkdir(self.sftp, event.src_path)

    # 删除文件
    def on_deleted(self, event):
        if not event.is_directory:
            delete(self.sftp, event.src_path)
        else:
            delete_dir(self.sftp, event.src_path)

监控本地文件的修改,然后调用不同的方法。

创建一个打印日志的类

关键代码:

class Logger():

    def __init__(self, name):
        # 创建一个logger实例
        self._logger = logging.getLogger(name)
        # 指定最低输出级别
        self._logger.setLevel(logging.DEBUG)
        # 创建一个StreamHandler
        ch = logging.StreamHandler()
        ch.setLevel(logging.WARN)
        # 创建一个FileHandler
        # 指定日志文件目录
        log_path = './logging.log'
        ch2 = logging.FileHandler(log_path)
        ch2.setLevel(logging.INFO)
        # 设置日志输出格式
        formatter = logging.Formatter(
                    '%(asctime)s - %(name)s - %(levelname)s - %(message)s')

        ch.setFormatter(formatter)
        ch2.setFormatter(formatter)

        self._logger.addHandler(ch)
        self._logger.addHandler(ch2)

logging模块的使用方法,还有一点复杂的,建议先了解一下。

递归搜索

由于paramiko的sftp不支持递归搜索,所以需要自己实现,代码如下:

def walk(sftp, path):
    all_files = []
    all_dirs = []
    files = sftp.listdir_attr(path)
    for f in files:
        filename = path + '/' + f.filename
        if stat.S_ISDIR(f.st_mode):
            file_list, dir_list = walk(sftp, filename)
            all_files.extend(file_list)
            all_dirs.extend(dir_list)
            all_dirs.append(filename)
        else:
            all_files.append(filename)
    return all_files, all_dirs

还有很多其他的操作方法,这里就不一一列举了。完整代码,已更新到github. https://github.com/pangerl/python/tree/master/inotify

服务化

python脚本服务化这里用的supervisor。配置比较简单,就不详细说了。 下次再写一遍专门介绍supervisor的文章。