backup-manager now has list, delete, and script options
This commit is contained in:
parent
def1f8bc4d
commit
27a0be4b86
1 changed files with 139 additions and 13 deletions
|
@ -2,16 +2,11 @@
|
||||||
|
|
||||||
# Script to manage S3-stored backups
|
# Script to manage S3-stored backups
|
||||||
|
|
||||||
import base64
|
import optparse
|
||||||
import glob
|
|
||||||
import md5
|
|
||||||
import os
|
|
||||||
import secrets
|
import secrets
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from subprocess import *
|
|
||||||
|
|
||||||
from boto.s3.connection import S3Connection
|
from boto.s3.connection import S3Connection
|
||||||
from boto.s3.key import Key
|
from boto.s3.key import Key
|
||||||
import boto.exception
|
import boto.exception
|
||||||
|
@ -137,21 +132,152 @@ def make_restore_script(backup, expire=86400):
|
||||||
output.append('\nEOF\n')
|
output.append('\nEOF\n')
|
||||||
|
|
||||||
output.append('\n# join and untar files\n')
|
output.append('\n# join and untar files\n')
|
||||||
output.append('\ncat .restorescript-scratch/*.tar.?? | tar -xf -\n\n')
|
output.append('cat .restorescript-scratch/*.tar.?? | tar -xf -\n\n')
|
||||||
|
|
||||||
output.append('\necho "DONE! Have a nice day."\n')
|
output.append('echo "DONE! Have a nice day."\n##\n')
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# check command line options
|
||||||
|
parser = optparse.OptionParser(usage="usage: %prog [options] list/delete/script")
|
||||||
|
parser.add_option("-H", "--host", dest="host",
|
||||||
|
help="Name of backed-up host")
|
||||||
|
parser.add_option("-b", "--backup-number", dest="backupnum",
|
||||||
|
help="Backup number")
|
||||||
|
parser.add_option("-a", "--age", dest="age",
|
||||||
|
help="Delete backups older than AGE days")
|
||||||
|
parser.add_option("-f", "--filename", dest="filename",
|
||||||
|
help="Output filename for script")
|
||||||
|
parser.add_option("-x", "--expire", dest="expire",
|
||||||
|
help="Maximum age of script, default 86400 seconds")
|
||||||
|
parser.add_option("-t", "--test", dest="test", action="store_true",
|
||||||
|
help="Test mode; don't actually delete")
|
||||||
|
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
conn = open_s3(secrets.accesskey, secrets.sharedkey)
|
conn = open_s3(secrets.accesskey, secrets.sharedkey)
|
||||||
|
|
||||||
for bucket in iter_backup_buckets(conn, name='olpc'):
|
if options.backupnum and not options.host:
|
||||||
backups = list_backups(bucket)
|
parser.error('Must specify --host when specifying --backup-number')
|
||||||
|
|
||||||
for backup in backups['olpc'].keys():
|
if options.backupnum:
|
||||||
print make_restore_script(backups['olpc'][backup])
|
options.backupnum = int(options.backupnum)
|
||||||
sys.exit(0)
|
|
||||||
|
if len(args) == 0:
|
||||||
|
args.append('list')
|
||||||
|
|
||||||
|
if len(args) > 1:
|
||||||
|
parser.error('Too many arguments.')
|
||||||
|
|
||||||
|
if args[0] != 'delete' and options.age:
|
||||||
|
parser.error('--age only makes sense with delete')
|
||||||
|
|
||||||
|
if args[0] != 'script' and (options.expire or options.filename):
|
||||||
|
parser.error('--expire and --filename only make sense with script')
|
||||||
|
|
||||||
|
if args[0] in ['list', 'script', 'delete']:
|
||||||
|
if options.host:
|
||||||
|
buckets = iter_backup_buckets(conn, name=options.host)
|
||||||
|
if not buckets:
|
||||||
|
parser.error('No buckets found for host "%s"' % options.host)
|
||||||
|
else:
|
||||||
|
buckets = iter_backup_buckets(conn)
|
||||||
|
if not buckets:
|
||||||
|
parser.error('No buckets found!')
|
||||||
|
else:
|
||||||
|
parser.error('Invalid option: %s' + args[0])
|
||||||
|
|
||||||
|
if args[0] == 'script':
|
||||||
|
if not options.host:
|
||||||
|
parser.error('Must specify --host to generate a script for')
|
||||||
|
|
||||||
|
backups = list_backups(buckets.next())
|
||||||
|
|
||||||
|
if not options.backupnum:
|
||||||
|
# assuming highest number
|
||||||
|
options.backupnum = max(backups[options.host].keys())
|
||||||
|
|
||||||
|
backup = backups[options.host][options.backupnum]
|
||||||
|
|
||||||
|
if not options.expire:
|
||||||
|
options.expire = "86400"
|
||||||
|
|
||||||
|
if options.filename:
|
||||||
|
fd = open(options.filename, 'w')
|
||||||
|
fd.writelines(make_restore_script(backup, expire=int(options.expire)))
|
||||||
|
else:
|
||||||
|
sys.stdout.writelines(make_restore_script(backup, expire=int(options.expire)))
|
||||||
|
elif args[0] == 'list':
|
||||||
|
sys.stdout.write('%25s | %5s | %20s | %5s\n' % ("Hostname", "Bkup#", "Age", "Files"))
|
||||||
|
sys.stdout.write('-'*72 + '\n')
|
||||||
|
for bucket in buckets:
|
||||||
|
hostnames = list_backups(bucket)
|
||||||
|
for hostname in hostnames.keys():
|
||||||
|
backups = hostnames[hostname]
|
||||||
|
for backupnum in backups.keys():
|
||||||
|
filecount = len(backups[backupnum]['keys'])
|
||||||
|
datestruct = backups[backupnum]['date']
|
||||||
|
timestamp = time.mktime(datestruct)
|
||||||
|
delta = int(time.time() - timestamp)
|
||||||
|
if delta < 3600:
|
||||||
|
prettydelta = '%i min ago' % (delta/60)
|
||||||
|
elif delta < 86400:
|
||||||
|
prettydelta = '%i hr ago' % (delta/3600)
|
||||||
|
else:
|
||||||
|
days = int(delta/60/60/24)
|
||||||
|
if days == 1:
|
||||||
|
s = ''
|
||||||
|
else:
|
||||||
|
s = 's'
|
||||||
|
prettydelta = '%i day%s ago' % (days, s)
|
||||||
|
|
||||||
|
sys.stdout.write('%25s | %5i | %20s | %5i\n' % (hostname, backupnum, prettydelta, filecount))
|
||||||
|
elif args[0] == 'delete':
|
||||||
|
if options.age:
|
||||||
|
maxage = int(options.age)*86400
|
||||||
|
for bucket in buckets:
|
||||||
|
hostnames = list_backups(bucket)
|
||||||
|
for hostname in hostnames.keys():
|
||||||
|
backups = hostnames[hostname]
|
||||||
|
for backupnum in backups.keys():
|
||||||
|
filecount = len(backups[backupnum]['keys'])
|
||||||
|
datestruct = backups[backupnum]['date']
|
||||||
|
timestamp = time.mktime(datestruct)
|
||||||
|
delta = int(time.time() - timestamp)
|
||||||
|
if delta > maxage:
|
||||||
|
sys.stdout.write('Deleting %s #%i (%i files, age %.2f days)...' % (hostname, backupnum, filecount, delta/86400.0))
|
||||||
|
for key in backups[backupnum]['keys']:
|
||||||
|
if options.test:
|
||||||
|
sys.stdout.write('*')
|
||||||
|
else:
|
||||||
|
key.delete()
|
||||||
|
sys.stdout.write('.')
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
elif options.host and options.backupnum:
|
||||||
|
for bucket in buckets:
|
||||||
|
hostnames = list_backups(bucket)
|
||||||
|
if options.host in hostnames.keys():
|
||||||
|
if options.backupnum not in hostnames[options.host].keys():
|
||||||
|
parser.error('Backup number %i not found' % options.backupnum)
|
||||||
|
toast = hostnames[options.host][options.backupnum]
|
||||||
|
filecount = len(toast['keys'])
|
||||||
|
datestruct = toast['date']
|
||||||
|
timestamp = time.mktime(datestruct)
|
||||||
|
delta = int(time.time() - timestamp)
|
||||||
|
|
||||||
|
sys.stdout.write('Deleting %s #%i (%i files, age %.2f days)...' % (options.host, options.backupnum, filecount, delta/86400.0))
|
||||||
|
for key in toast['keys']:
|
||||||
|
if options.test:
|
||||||
|
sys.stdout.write('*')
|
||||||
|
else:
|
||||||
|
key.delete()
|
||||||
|
sys.stdout.write('.')
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
else:
|
||||||
|
parser.error('Host %s not found' % options.host)
|
||||||
|
else:
|
||||||
|
parser.error('Need either an age or a host AND backup number.')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in a new issue