Permalink
Cannot retrieve contributors at this time
#!/usr/bin/env python | |
# Interesting: | |
# - https://github.com/laurent22/rsync-time-backup | |
import argparse | |
import datetime as dt | |
import os | |
import re | |
import socket | |
import subprocess | |
import sys | |
HOSTNAME = socket.gethostname() | |
GATE = '192.168.1.1' | |
BDIR = '/arch/backup/' | |
RSYNC = 'sudo rsync -aAXHvh --stats --delete' | |
FILTERS = [ | |
'+ /boot/', | |
'+ /etc/', | |
'+ /root/', | |
'+ /home/', | |
'+ /home/*', | |
'+ /home/naspeh/v/', | |
'- /home/naspeh/v/*', | |
'- /home/*/.thumbnails/', | |
'- /home/*', | |
'+ /arch/', | |
'+ /arch/naspeh/', | |
'+ /arch/nayavu/', | |
'+ /arch/photo/', | |
'+ /arch/temp/', | |
'- /arch/*', | |
'- /**/.cache/', | |
'- /*' | |
] | |
def star(diskname, devpath=None): | |
devpath = devpath if devpath else '/dev/%s/arch' % diskname | |
return ( | |
( | |
'[ -d /media/{diskname}/backups ] || (' | |
' mkdir -p /media/{diskname} &&' | |
' mount {devpath} /media/{diskname}' | |
')'.format(diskname=diskname, devpath=devpath) | |
), | |
'backup copy /media/{diskname}/backups/{hostname} --nolog --extra="' | |
' --backup --backup-dir=/media/{diskname}/backups/_delta/{hostname}' | |
'"'.format(diskname=diskname, hostname=HOSTNAME) | |
) | |
TARGETS = { | |
'*_to_trans': star('trans', '/dev/disk/by-label/t-arch'), | |
'*_to_sea': star('sea'), | |
'*_to_box': star('box'), | |
} | |
def main(): | |
parser = argparse.ArgumentParser() | |
cmds = parser.add_subparsers(title='commands') | |
def cmd(name, **kw): | |
s = cmds.add_parser(name, **kw) | |
s.set_defaults(cmd=name) | |
s.arg = lambda *a, **kw: s.add_argument(*a, **kw) and s | |
s.arg('--log', action='store_true', help='log to file') | |
s.arg('--nolog', action='store_true', help='don\'t log to file') | |
s.arg('--bdir', default=BDIR, help='backup directory') | |
s.arg_fake = lambda: ( | |
s.arg('-f', '--fake', action='store_true', help='dry run') | |
) | |
return s | |
cmd('run', help='backup important data').arg_fake() | |
cmd('tar', help='archive delta directory')\ | |
.arg('-n', '--name', help='name for archive')\ | |
.arg('-d', '--delete', action='store_true', help='delete in the end') | |
cmd('copy', help='copy backup directory').arg_fake()\ | |
.arg('src', default=BDIR, nargs='?', help='source path')\ | |
.arg('dest', help='destination path')\ | |
.arg('-e', '--extra', default='', help='extra options for rsync') | |
cmd('full', help='backup whole root partition').arg_fake() | |
cmd('call', help='call fixed action')\ | |
.arg('target', choices=TARGETS.keys(), help='choice fixed action')\ | |
.arg('-s', '--show', action='store_true', help='only print command') | |
args = parser.parse_args(sys.argv[1:]) | |
if not hasattr(args, 'cmd'): | |
parser.print_usage() | |
exit(2) | |
today = dt.date.today().isoformat() | |
now = dt.datetime.now().strftime("%Y-%m-%d--%X") | |
base_cmd = re.sub(r'[^\w]+', '-', ' '.join(sys.argv[1:])) | |
if hasattr(args, 'fake'): | |
rsync = RSYNC + ' --dry-run' if args.fake else RSYNC | |
bdir = args.bdir.rstrip('/') + '/' | |
dlog, dmnt, dnamed = bdir + 'log/', bdir + 'mnt/', bdir + 'named/' | |
for f in [dlog, dmnt]: | |
if not os.path.exists(f): | |
os.mkdir(f) | |
def sh(cmd, **kw): | |
cmd = 'echo "{}" && time ({})'.format(cmd.replace('"', '\"'), cmd) | |
if args.log or os.environ.get('BACKUP_LOG', False) and not args.nolog: | |
file = '%s%s--%s.log' % (dlog, now, base_cmd) | |
cmd = '({}; echo "{}") &>> {}'.format(cmd, '-' * 20, file) | |
subprocess.call(cmd, shell=True, **kw) | |
if args.cmd == 'run': | |
sh('pkglist -P') | |
filters = ' '.join(['--filter="%s"' % v for v in FILTERS]) | |
sh( | |
' '.join([rsync, filters, '--delete-excluded']) + | |
' --backup --backup-dir={dest}delta/' | |
' / {dest}latest/' | |
.format(dest=bdir) | |
) | |
elif args.cmd == 'tar': | |
name = today | |
if args.name: | |
name += '-' + args.name | |
name = dnamed + name | |
sh( | |
'cd {dest}' | |
' && tar -cvf {name}.tar --directory=delta .' | |
' && find *.tar -mtime +14 -delete' | |
.format(dest=bdir, name=name) | |
) | |
if args.delete: | |
sh('rm -r {}delta'.format(bdir)) | |
elif args.cmd == 'copy': | |
sh( | |
rsync + ' -x {extra} {src} {dest}/' | |
.format(src=args.src, dest=args.dest.rstrip('/'), extra=args.extra) | |
) | |
elif args.cmd == 'full': | |
snap = '/dev/%s/snap' % HOSTNAME | |
root = '/dev/%s/root' % HOSTNAME | |
rsync += ' -x --exclude=/var/lib/docker/devicemapper --exclude=' + ( | |
'{/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/mnt/*,/media/*,/lost+found}' | |
) | |
try: | |
sh( | |
'lvcreate --size 10G --snapshot --name snap {root}' | |
' && mount {snap} {mnt}' | |
' && {rsync} {mnt} {dest}full/' | |
.format( | |
dest=bdir, rsync=rsync, | |
root=root, snap=snap, mnt=dmnt | |
) | |
) | |
finally: | |
sh( | |
'umount {mnt}; lvremove -f {snap}' | |
.format(mnt=dmnt, snap=snap) | |
) | |
elif args.cmd == 'call': | |
check, cmd_ = TARGETS.get(args.target) | |
if args.show: | |
print(' && '.join([c for c in [check, cmd_] if c])) | |
elif args.target.startswith(HOSTNAME) or args.target.startswith('*'): | |
if check and 0 != subprocess.call(check, shell=True): | |
raise SystemExit('Check failed: %s' % check) | |
sh(cmd_) | |
else: | |
print(cmd_) | |
raise SystemExit('...Wrong hostname for this target.') | |
if __name__ == '__main__': | |
try: | |
if os.geteuid() != 0: | |
subprocess.call(['sudo'] + sys.argv) | |
exit() | |
main() | |
except KeyboardInterrupt: | |
exit('Closed.') |