运行环境:
本地:Window7 64位,Python 2.7.6,paramiko 1.12.1,watchdog 0.8.1
远端:Ubuntu 14.04,Openssh-server
from auto_ssh_upload_paramiko import SSHFileUpload
auto_ssh_upload_paramiko 模块参看[这里]
dirs_handlers.py:
#!/usr/bin/python # coding:utf8 import re import logging from watchdog.events import RegexMatchingEventHandler, PatternMatchingEventHandler from pathtools.patterns import match_any_paths, match_path class BaseRsyncEventHandler(): """文件监听事件基础类 """ def __init__(self): logging.info("Rsync service started") def __del__(self): if self._ssh: self._ssh.close() logging.debug("ssh connection closed") def on_moved(self, event): """文件发生移动事件 """ remote_src_path = event.src_path.replace(self._local_path, self._remote_path).replace('\\', '/').replace('//', '/') remote_dest_path = event.dest_path.replace(self._local_path, self._remote_path).replace('\\', '/').replace('//', '/') self._ssh.exec_command("mv %s %s" % (remote_src_path, remote_dest_path)) what = 'directory' if event.is_directory else 'file' logging.info("Moved %s: from %s to %s", what, event.src_path.replace('\\', '/').replace('//', '/'), event.dest_path.replace('\\', '/').replace('//', '/')) def on_created(self, event): """文件新建事件处理 """ local_path = event.src_path.replace('\\', '/').replace('//', '/') remote_path = event.src_path.replace(self._local_path, self._remote_path).replace('\\', '/').replace('//', '/') if event.is_directory: # 新建文件夹 self._ssh.exec_command("mkdir -p %s" % remote_path) else: self._ssh.copyFile(local_path, remote_path) what = 'directory' if event.is_directory else 'file' logging.info("Created %s: %s", what, event.src_path) def on_deleted(self, event): """文件删除事件处理 """ local_path = event.src_path.replace('\\', '/').replace('//', '/') remote_path = event.src_path.replace(self._local_path, self._remote_path).replace('\\', '/').replace('//', '/') self._ssh.exec_command("rm -f %s" % remote_path) what = 'directory' if event.is_directory else 'file' logging.info("Deleted %s: %s", what, event.src_path.replace('\\', '/').replace('//', '/')) def on_modified(self, event): """文件修改事件处理 """ if not event.is_directory: local_path = event.src_path.replace('\\', '/').replace('//', '/') remote_path = event.src_path.replace(self._local_path, self._remote_path).replace('\\', '/').replace('//', '/') self._ssh.copyFile(local_path, remote_path) what = 'directory' if event.is_directory else 'file' logging.info("Modified %s: %s", what, event.src_path.replace('\\', '/').replace('//', '/')) def ignoreRegexesFilter(ignore_regexes, case_sensitive=False): """正则表达式匹配, 忽略监听特定文件 """ _ignore_regexes = [] if case_sensitive: _ignore_regexes = [re.compile(r) for r in ignore_regexes] else: _ignore_regexes = [re.compile(r, re.I) for r in ignore_regexes] def wapper(file_path): if any(r.match(file_path) for r in _ignore_regexes): return False return True return wapper class RsyncRegexMatchingEventHandler(BaseRsyncEventHandler, RegexMatchingEventHandler): """正则表达式匹配, 文件同步处理类 """ def __init__(self, ssh, local_path, remote_path, regexes=[r".*"], ignore_regexes=[], ignore_directories=False, case_sensitive=False): RegexMatchingEventHandler.__init__(self, regexes, ignore_regexes, ignore_directories, case_sensitive) if not ssh: raise ValueError('ssh argument should NOT be null') self._ssh = ssh self._ssh.connect() self._local_path = local_path self._remote_path = remote_path self._ssh.copyDir(local_path, remote_path, filters=[ignoreRegexesFilter(ignore_regexes)]) BaseRsyncEventHandler.__init__(self) def ignorePatternsFilter(ignore_patterns, case_sensitive=False): def wapper(file_path): return match_any_paths(file_path,included_patterns=None, excluded_patterns=set(ignore_patterns), case_sensitive=case_sensitive) return wapper class RsyncPatternMatchingEventHandler(BaseRsyncEventHandler, PatternMatchingEventHandler): def __init__(self, ssh, local_path, remote_path, patterns=None, ignore_patterns=None, ignore_directories=False, case_sensitive=False): if not ssh: raise ValueError('ssh argument should NOT be null') self._ssh = ssh self._ssh.connect() self._local_path = local_path self._remote_path = remote_path # self._ssh.copyDir(local_path, remote_path, filters=[ignorePatternsFilter(ignore_patterns)]) PatternMatchingEventHandler.__init__(self, patterns, ignore_patterns, ignore_directories, case_sensitive) BaseRsyncEventHandler.__init__(self)
#!/usr/bin/python # coding:utf8 import os import os.path import re import sys import time import logging from logging.handlers import TimedRotatingFileHandler from ConfigParser import SafeConfigParser from pathtools.patterns import match_any_paths from watchdog.observers import Observer from auto_ssh_upload_paramiko import SSHFileUpload from dirs_handlers import RsyncRegexMatchingEventHandler, RsyncPatternMatchingEventHandler def getSSHConfig(section_name='env', conf_file='ssh-config.ini'): config = SafeConfigParser() config.readfp(open(conf_file)) return dict(config.items(section_name)) CRITICAL = 50 FATAL = CRITICAL ERROR = 40 WARNING = 30 WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0 _logLevelNames = { CRITICAL : 'CRITICAL', ERROR : 'ERROR', WARNING : 'WARNING', INFO : 'INFO', DEBUG : 'DEBUG', NOTSET : 'NOTSET', 'CRITICAL' : CRITICAL, 'ERROR' : ERROR, 'WARN' : WARNING, 'WARNING' : WARNING, 'INFO' : INFO, 'DEBUG' : DEBUG, 'NOTSET' : NOTSET, } if __name__ == "__main__": config = getSSHConfig("env") log_path = config.get('log_path').replace('\\', '/').replace('//', '/') log_level = config.get('log_level') if not log_level: log_level = _logLevelNames['INFO'] formatter='%(asctime)s - %(message)s' datefmt='%Y-%m-%d %H:%M:%S' logging.basicConfig( level=_logLevelNames[log_level] ,format=formatter ,datefmt=datefmt # ,filename=log_path # ,filemode='a' ) fmt = logging.Formatter(formatter) log_handler = TimedRotatingFileHandler(log_path, when='D', interval=1, backupCount=40) log_handler.setLevel(logging.INFO) log_handler.suffix = r"%Y%m%d-%H%M.log" log_handler.setFormatter(fmt) logging.getLogger().addHandler(log_handler) host = config.get('host') port = int(config.get('port')) username = config.get('username') password = config.get('password') local_path = config.get('local_path') remote_path = config.get('remote_path') ssh = SSHFileUpload(host, port, username, password) with open("./ignoreregexes") as f: ignoreregexes = [unicode(line.strip('\n'), 'cp936').encode('utf8') for line in f.readlines() if line.strip('\n')] event_handler = RsyncRegexMatchingEventHandler(ssh, local_path, remote_path, ignore_regexes=ignoreregexes #[r".*\.tmp$", r".*\.git.*", r".*\.settings.*", r".*\.project.*", r".*\.buildpath.*", r".*\.idea.*", r".*___jb_.*"] ) observer = Observer() observer.schedule(event_handler, local_path, recursive=True) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join()
配置文件(ssh-config.ini):
[env] host=192.168.88.128 port=22 username=root password=your_passwd local_path=E:/app remote_path=/home/app log_path=./dirs-rsync.log
ignoreregexes文件文本内容:
.*\.tmp$ .*\.git.* .*\.settings.* .*\.project.* .*\.buildpath.* .*\.idea.* .*___jb_.*