Initial mysync implementation
This commit is contained in:
parent
727495386e
commit
81f7cf4b2e
14
example/backup_config.py
Normal file
14
example/backup_config.py
Normal file
@ -0,0 +1,14 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
backup = True
|
||||
backup_basepath = '/data/backup/ahorn'
|
||||
|
||||
installations = ['mint', 'kubuntu', 'openSuSE']
|
||||
for installation in installations:
|
||||
setattr(sys.modules[__name__], 'home_%s_remotepath' % installation, 'dirk@ahorn:/home')
|
||||
setattr(sys.modules[__name__], 'home_%s_localpath' % installation, os.path.join(installation, 'home'))
|
||||
|
||||
user_data_remotepath = 'dirk@ahorn:/user_data'
|
||||
user_data_localpath = 'user_data'
|
||||
user_data_skip = ['static_data']
|
25
example/sync_config.py
Normal file
25
example/sync_config.py
Normal file
@ -0,0 +1,25 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
backup = False
|
||||
|
||||
hosts = ['ahorn', 'erle', 'linde']
|
||||
__basepath__ = '/user_data'
|
||||
entries = (
|
||||
('dirk@%s:/user_data/bin', os.path.join(__basepath__, 'bin'), None),
|
||||
('dirk@%s:/user_data/data', os.path.join(__basepath__, 'data'), None),
|
||||
('dirk@%s:/user_data/static_data', os.path.join(__basepath__, 'static_data'), ['Audio', 'timeshift', 'lost+found']),
|
||||
('dirk@%s:/home', '/home', None),
|
||||
)
|
||||
|
||||
|
||||
for host in hosts:
|
||||
for remote, local, skip in entries:
|
||||
setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_remotepath', remote % host)
|
||||
setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_localpath', local)
|
||||
if skip is not None:
|
||||
setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_skip', skip)
|
||||
|
||||
|
||||
mount_mockery_audio_remotepath = 'root@mount-mockery.de:/data/audio/items'
|
||||
mount_mockery_audio_localpath = os.path.join(__basepath__, 'static_data', 'dirk', 'Audio')
|
137
mysync
Executable file
137
mysync
Executable file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
import config
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
DEBUG = False
|
||||
|
||||
RSYNC_REMOTE_COMMAND = 'sudo rsync'
|
||||
TIME_FORMAT = "%Y-%m-%d_%H-%M-%S"
|
||||
try:
|
||||
basepath = config.backup_basepath
|
||||
except AttributeError:
|
||||
basepath = os.path.dirname(os.path.abspath(__file__))
|
||||
timestamp = time.strftime(TIME_FORMAT)
|
||||
output_lines = 5
|
||||
|
||||
PROP_REMOTE = 'remotepath'
|
||||
PROP_LOCAL = 'localpath'
|
||||
PROP_SKIP = 'skip'
|
||||
|
||||
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
CLEAR_REST_OF_LINE = '\033[K'
|
||||
|
||||
|
||||
def status_line(line):
|
||||
sys.stdout.write('\r .....' + line.replace('\n', '')[-60:] + CLEAR_REST_OF_LINE)
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def parse_config_to_dict():
|
||||
d = {}
|
||||
config_dict = dir(config)
|
||||
for key in config_dict:
|
||||
if key.endswith('_' + PROP_REMOTE):
|
||||
entry_name = key[:-len(PROP_REMOTE) - 1]
|
||||
if entry_name + '_' + PROP_LOCAL in config_dict:
|
||||
d[entry_name] = {}
|
||||
for key in config_dict:
|
||||
if key.startswith(entry_name):
|
||||
d[entry_name][key[key.rfind('_') + 1:]] = getattr(config, key)
|
||||
return d
|
||||
|
||||
|
||||
def rsync_command(**kwargs):
|
||||
def path_filter(path):
|
||||
return path if path.endswith('/') else path + '/'
|
||||
#
|
||||
if config.backup:
|
||||
localpath = path_filter(os.path.join(basepath, timestamp, kwargs[PROP_LOCAL]))
|
||||
if timestamp not in localpath:
|
||||
sys.stderr.write('Unable to backup to %s. Please give a relative path for a backup!\n' % localpath)
|
||||
localpath = None
|
||||
else:
|
||||
localpath = path_filter(os.path.join(basepath, kwargs.get('localpath', '')))
|
||||
if localpath is not None:
|
||||
remotepath = path_filter(kwargs.get(PROP_REMOTE))
|
||||
skip = kwargs.get(PROP_SKIP)
|
||||
cmd_l = ['rsync', '-avn' if DEBUG else '-av', '--delete', '--rsync-path="%s"' % RSYNC_REMOTE_COMMAND, remotepath, localpath]
|
||||
if skip is not None:
|
||||
cmd_l.append('--exclude=%s' % ','.join(skip))
|
||||
if not os.path.exists(localpath):
|
||||
return 'mkdir -p %s && ' % localpath + ' '.join(cmd_l)
|
||||
return ' '.join(cmd_l)
|
||||
|
||||
|
||||
def link_copy_command():
|
||||
newest_backup = None
|
||||
if not os.path.exists(basepath):
|
||||
os.system('mkdir -p ' + basepath)
|
||||
for path in os.listdir(basepath):
|
||||
try:
|
||||
t = time.strptime(path, TIME_FORMAT)
|
||||
if newest_backup is None or t > newest_backup:
|
||||
newest_backup = t
|
||||
except ValueError:
|
||||
pass # seems to be not one of my backups
|
||||
if newest_backup is not None:
|
||||
newest_backup = os.path.join(basepath, time.strftime(TIME_FORMAT, newest_backup))
|
||||
return 'cp -alv ' + newest_backup + ' ' + os.path.join(basepath, timestamp)
|
||||
|
||||
|
||||
def help_msg():
|
||||
backup_dict = parse_config_to_dict()
|
||||
print("Possible arguments are:")
|
||||
for entry in backup_dict:
|
||||
print(" * %s (%s -> %s)" % (entry + (20 - len(entry)) * ' ', backup_dict[entry][PROP_REMOTE] + (40 - len(backup_dict[entry][PROP_REMOTE])) * ' ', backup_dict[entry][PROP_LOCAL] + (40 - len(backup_dict[entry][PROP_LOCAL])) * ' '))
|
||||
|
||||
def make_link_copy():
|
||||
cmd = link_copy_command()
|
||||
if cmd is not None:
|
||||
sys.stdout.write(BOLD + HEADER + UNDERLINE + 'Creating Link copy of last backup.' + ENDC + '\n\n')
|
||||
with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
|
||||
err = b""
|
||||
for line in p.stdout:
|
||||
status_line(line.decode('utf-8'))
|
||||
err += p.stderr.read()
|
||||
sys.stdout.write('\n' + FAIL + BOLD + err.decode('utf-8') + ENDC)
|
||||
sys.stdout.write('\n' + 80 * '-' + '\n')
|
||||
|
||||
#
|
||||
if __name__ == '__main__':
|
||||
backup_dict = parse_config_to_dict()
|
||||
#
|
||||
backup_dict = parse_config_to_dict()
|
||||
did_nothing = True
|
||||
for entry in backup_dict:
|
||||
if entry in sys.argv:
|
||||
cmd = rsync_command(**backup_dict[entry])
|
||||
if cmd is not None:
|
||||
if config.backup and did_nothing:
|
||||
make_link_copy()
|
||||
did_nothing = False
|
||||
sys.stdout.write(BOLD + HEADER + UNDERLINE + 'Doing %s for %s:' % ('backup' if config.backup else 'sync', repr(entry)) + ENDC + '\n\n')
|
||||
with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
|
||||
err = b""
|
||||
for line in p.stdout:
|
||||
status_line(line.decode('utf-8'))
|
||||
err += p.stderr.read()
|
||||
sys.stdout.write('\n' + FAIL + BOLD + err.decode('utf-8') + ENDC)
|
||||
sys.stdout.write('\n' + 80 * '-' + '\n')
|
||||
if did_nothing:
|
||||
help_msg()
|
||||
else:
|
||||
help_msg()
|
Loading…
x
Reference in New Issue
Block a user