def display(self): if len(self.folderRefernece) == 0: print(FC.w("当前还没有正在备份的文件夹", 'g') + "\n") else: print(FC.w("当前备份文件夹信息:", 'c') + "\n") for idx, ref in enumerate(self.folderRefernece): print( FC.g("[{}] {}\n".format(str(idx).zfill(2), ref['name'])) + " Src: {}".format(ref['src'])) for destIdx, dest in enumerate(ref['dest']): if dest['lastSyncTime'] is None: print(' {} Dest: {}{}'.format( FC.g("[{}]".format(str(destIdx).zfill(2))), dest['destPath'], FC.y("[尚未备份]"))) else: print(' {} Dest: {}{}'.format( FC.g("[{}]".format(str(destIdx).zfill(2))), dest['destPath'], FC.y("[{}]".format(dest['lastSyncTime'])))) for remote in dest['remote']: text = remote['username'] + '@' + remote[ 'host'] + ':' + remote['rootPath'] print(' Remote: {}'.format(text)) print()
def _syncFile(self, srcFile, destFile, destRootPath, remote, trashbin): """比较并同步单个文件""" # 大小不同直接复制 if os.stat(srcFile).st_size != os.stat(destFile).st_size: print(FC.r("[Diff] ") + srcFile.split('/')[-1]) trashbin.moveToTrashbin(destFile, destRootPath) shutil.copy(srcFile, destFile) for r in remote: r.put(srcFile, destFile[len(destRootPath) + 1:]) return with open(srcFile, 'rb') as f1, open(destFile, 'rb') as f2: while True: data1 = f1.read(4096) data2 = f2.read(4096) if data1 != data2: trashbin.moveToTrashbin(destFile, destRootPath) shutil.copy(srcFile, destFile) for r in remote: r.put(srcFile, destFile[len(destRootPath) + 1:]) print(FC.r("[Diff] ") + srcFile.split('/')[-1]) return elif data1 == b'': print(FC.g("[Same] ") + srcFile.split('/')[-1]) return
def execAttachCommand(config): remoteLogFile = "/tmp/" + '_'.join(config['rootPath'].split('/')) fp = open(remoteLogFile, 'w') fp.write(json.dumps(config['transLog'])) fp.close() remote = ServerTransporter(config) remote.put(remoteLogFile, remoteLogFile) if remote.attachCommand is not None: print(FC.g("[Do] ") + remote.username + '@' + remote.host + ':' + remote.rootPath + ' => ' + remote.attachCommand) stdin, stdout, stderr = remote._ssh.exec_command(remote.attachCommand) # 阻塞等待 err = stderr.read()
def _desktopNotify(self, syncedPaths, failedConnectPath): title = "备份完成,同步了{}个文件夹".format(len(syncedPaths)) syncedText = '\n'.join(syncedPaths) + '\n' failedConnectText = '' for destPath, remotePath in failedConnectRemoteDict.items(): failedConnectText += remotePath + '\n' text = syncedText + ('\n备份失败,remote未连接:\n' if failedConnectText != '' else '') + failedConnectText os.system('notify-cron -t 5000 "{}" "{}"'.format(title, text)) print(FC.g(title) + '\n' + text) with open(self.syncHistoryPath, 'a') as f: f.write("[{}]\n{}-------------------------------\n".format( getStrfTime(self.dateTimePattern), text))
def _checkAndDeleteFormerPath(self, path): """删除修改前(被弃用)的文件夹路径""" if not os.path.isdir(path): return while True: print(("是否删除被弃置的文件夹[" + FC.g("{}") + "]?[y/N]").format(path)) check = input().strip() if check == '' or check.upper() == 'N': return elif check.upper() == 'Y': shutil.rmtree(path) print(FC.r("文件夹已删除")) return
def createAndTest(): config = {} config['host'] = input("域名:") config['port'] = int(input("端口:")) config['username'] = input("用户名:") config['pwd'] = input("密码(留空则用秘钥文件登陆):").strip() if config['pwd'] == '': config['pwd'] = None config['privateKeyPath'] = input("请输入秘钥文件路径:") else: config['privateKeyPath'] = None config['rootPath'] = input("远端备份路径:") try: server = ServerTransporter(config) print(FC.g("测试连接成功")) while not server.mkdir(''): config['rootPath'] = input("请重新输入远程路径:") return config except: return False
def modifyRenference(self): self.display() idx = input(FC.r("请输入要修改的序号:")) try: _idx = int(idx) assert _idx < len(self.folderRefernece), '序号超出范围' print("\n" + FC.g("[1]") + "修改原路径") print(FC.g("[2]") + "修改目标路径") modifyField = int(input(FC.c("请输入修改项:"))) # modifyField = int(input()) if modifyField == 1: # 修改原路径 print("请输入备份原文件夹路径:") srcPath = input() while not os.path.isdir(srcPath): print("\n该地址不是有效的文件夹,请输入备份源文件夹路径:") srcPath = input() if srcPath == self.folderRefernece[_idx]['src']: print("文件夹未变动") assert self._checkSameInReference(srcPath, 'src') < 0, "路径已存在在当前备份中" assert self._checkSameInReference( srcPath, 'dest') < 0, "还不支持映射中存在相同的原路径和目标路径" self._checkAndDeleteFormerPath( self.folderRefernece[_idx]['src']) self.folderRefernece[_idx]['src'] = srcPath self._updateFolderReference() print("更新完毕") else: for idx, dest in enumerate(self.folderRefernece[_idx]['dest']): print("\n" + FC.g("[{}]".format(idx)) + dest['destPath']) for remote in dest['remote']: print(" " + remote['username'] + '@' + remote['host'] + ':' + remote['rootPath']) _destIdx = int(input("请输入要修改的目标路径编号:")) assert 0 <= _destIdx <= idx, "编号有误" idx = 0 dest = self.folderRefernece[_idx]['dest'][_destIdx] print("\n" + FC.g("[{}]".format(idx)) + dest['destPath']) for remote in dest['remote']: idx += 1 print( FC.g("[{}]".format(idx)) + remote['username'] + '@' + remote['host'] + ':' + remote['rootPath']) _destIdx = int(input("请输入要修改的目标路径编号:")) assert 0 <= _destIdx <= idx, "编号有误" if _destIdx == 0: # 修改目标路径 _t = True while _t: print("\n请输入备份目标文件夹绝对路径:") destPath = input() if not os.path.isdir(destPath): os.makedirs(destPath) _t = False else: _i = input("该文件夹非空,使用则清空文件夹,确认使用吗?[y/N]") if _i.strip() == '' or _i.strip().upper() == 'N': continue elif _i.upper() == 'Y': _t = False else: print("输入有误") if destPath != dest['destPath']: assert self._checkSameInReference( destPath, 'dest') < 0, "路径已存在在当前备份中" assert self._checkSameInReference( destPath, 'src') < 0, "还不支持映射中存在相同的原路径和目标路径" trashPath = TrashManager.getDestRootPathInTrash( self.trashPath, dest['destPath']) self._checkAndDeleteFormerPath(dest['destPath']) self._checkAndDeleteFormerPath(trashPath) dest['destPath'] = destPath self._updateFolderReference() print("更新完毕") else: print("文件夹未变动") return else: # remote print("修改远程配置尚未完成") except AssertionError as e: print(e) except: print('序号不合规范')
def addNewReference(self): print("请输入备份源文件夹路径:") srcPath = input() while not os.path.isdir(srcPath): print("\n该地址不是有效的文件夹,请输入备份源文件夹路径:") srcPath = input() # 若源文件夹已被关联, 则直接并入 idx = self._checkSameInReference(srcPath, 'src') if idx >= 0: print("该文件夹已被映射, 只添加目标文件夹配置") destrefs = [] while True: print(("\n请输入备份目标文件夹根路径\n(留空则用默认路径)[" + FC.y("{}") + "]:").format( self.defaultDestPath)) destPath = input() if destPath.strip() == '': destPath = self.defaultDestPath if not os.path.isdir(destPath): os.makedirs(destPath) srcFolderName = os.path.realpath(srcPath).split('/')[-1] print(("\n请输入备份目标文件夹名\n(留空则用源文件夹名)[" + FC.y("{}") + "]:").format(srcFolderName)) destFolderName = input() if destFolderName.strip() == '': destFolderName = srcFolderName while os.path.isdir(os.path.join(destPath, destFolderName)): print("\n目标文件夹下已有同名文件夹,换个名字:") destFolderName = input() destPath = os.path.join(destPath, destFolderName) remotes = [] ipt = input("是否要挂载远端文件夹(和备份文件夹相同修改,但不做检查)?[y/N]").strip().upper() while True: if ipt == 'Y': remote = ServerTransporter.createAndTest() if remote is not False: remotes.append(remote) else: print("测试连接失败") ipt = input("是否继续或重新添加远程地址?[y/N]").strip().upper() else: break while True: try: expiredPeriod = input(("\n请输入回收站清理过期天数, 留空则默认[" + FC.y("7天") + "]:")).strip() if expiredPeriod == '': expiredPeriod = 7 break else: expiredPeriod = int(expiredPeriod) break except: continue os.makedirs(destPath) destrefs.append({ "destPath": destPath, "expiredPeriod": expiredPeriod * 24 * 3600, "lastSyncTime": None, "remote": remotes }) i = input("是否继续添加目标地址?[y/N]").strip().upper() if i != 'Y': break if idx < 0: refname = input("再给这个备份关联起个名字, 留空默认[" + FC.y(srcFolderName) + "]:").strip() if refname == '': refname = srcFolderName while self._checkSameInReference(refname, "name") >= 0: refname = input("名字已被占用重新输入, 留空默认[" + FC.y(srcFolderName) + "]:").strip() if refname == '': refname = srcFolderName reference = {"name": refname, "src": srcPath, "dest": destrefs} self.folderRefernece.append(reference) else: self.folderRefernece[idx]['dest'].extend(destrefs) reference = self.folderRefernece[idx] print(FC.g("添加成功")) self.showReference(reference) self._updateFolderReference()