Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 188 lines (162 sloc) 5.77 KB
#!/usr/bin/env python
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('[^\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.')