diff --git a/virt-back b/virt-back
index 4d60822..a288c06 100755
--- a/virt-back
+++ b/virt-back
@@ -34,248 +34,271 @@ from shutil import move, copy2
try:
from operator import methodcaller
except ImportError:
+
def methodcaller(name, *args, **kwargs):
def caller(obj):
return getattr(obj, name)(*args, **kwargs)
+
return caller
+
class Domfetcher(object):
"""Abstract libvirt API, supply methods to return dom object lists"""
- def __init__(self, uri = None):
+
+ def __init__(self, uri=None):
"""Connect to hypervisor uri with read write access"""
# register logit as the error handler
- libvirt.registerErrorHandler( logit, 'libvirt error' )
-
+ libvirt.registerErrorHandler(logit, "libvirt error")
+
try:
- self.c = libvirt.open( uri )
+ self.c = libvirt.open(uri)
except:
self.c = None
- logit( "libvirt error", "Failed to open connection to the hypervisor" )
+ logit("libvirt error", "Failed to open connection to the hypervisor")
def get_running_doms(self):
"""Return a list of running dom objects"""
doms = []
- for id in self.c.listDomainsID(): # loop over the running ids
- dom = self.c.lookupByID( id ) # fetch dom object by id
- if 'Domain-' not in dom.name(): # prevent actions on the Xen hypervisor
- doms.append( dom ) # append dom object to doms
+ for id in self.c.listDomainsID(): # loop over the running ids
+ dom = self.c.lookupByID(id) # fetch dom object by id
+ if "Domain-" not in dom.name(): # prevent actions on the Xen hypervisor
+ doms.append(dom) # append dom object to doms
return doms
-
+
def get_shutoff_doms(self):
"""Return a list of all shutoff but defined dom objects"""
- return [self.c.lookupByName( name ) for name in self.c.listDefinedDomains()]
-
+ return [self.c.lookupByName(name) for name in self.c.listDefinedDomains()]
+
def get_doms_by_names(self, guest_names):
"""Accept a list of guest_names, return a list of related dom objects"""
doms = []
for name in guest_names:
try:
- dom = self.c.lookupByName( name )
- doms.append( dom )
- except libvirt.libvirtError: pass # logit reg'd as libvirt error handler
+ dom = self.c.lookupByName(name)
+ doms.append(dom)
+ except libvirt.libvirtError:
+ pass # logit reg'd as libvirt error handler
return doms
def get_all_doms(self):
"""Return a list of all dom objects"""
return self.get_running_doms() + self.get_shutoff_doms()
-
-def invoke( doms, method ):
+
+def invoke(doms, method):
"""Pattern to invoke shutdown, destroy, and start on a list of doms"""
- f = methodcaller( method )
+ f = methodcaller(method)
for dom in doms:
try:
- logit( method, 'invoking %s on %s' % (method, dom.name()) )
+ logit(method, "invoking %s on %s" % (method, dom.name()))
retcode = f(dom)
- if retcode: # log retcode
- logit(
- method,
- '{0} returned {1} on {2}'.format( method, retcode, dom.name() )
- )
+ if retcode: # log retcode
+ logit(
+ method,
+ "{0} returned {1} on {2}".format(method, retcode, dom.name()),
+ )
except libvirt.libvirtError:
pass
-def backup( doms ):
+
+def backup(doms):
"""Accept a list of dom objects, run backup proceedure on each"""
for dom in doms:
recreate = dom.isActive()
- if dom.isActive(): # if dom is active, shutdown
+ if dom.isActive(): # if dom is active, shutdown
shutdown([dom])
-
- if dom.isActive(): # if dom is active, error
- logit(
- 'error',
- 'unable to shutdown or destroy %s and BACKUP FAILED!' % dom.name()
- )
- continue # skip to the next dom
- xml = dom.XMLDesc( 0 )
- xmlfile = path.join( options.backpath, dom.name() + '.xml' )
- f = open( xmlfile, 'w')
- f.write( xml )
+ if dom.isActive(): # if dom is active, error
+ logit(
+ "error",
+ "unable to shutdown or destroy %s and BACKUP FAILED!" % dom.name(),
+ )
+ continue # skip to the next dom
+
+ xml = dom.XMLDesc(0)
+ xmlfile = path.join(options.backpath, dom.name() + ".xml")
+ f = open(xmlfile, "w")
+ f.write(xml)
f.close()
- disklist = findall( "\n", xml )
+ disklist = findall("\n", xml)
- logit( 'backup', 'invoking backup for ' + dom.name() )
-
- for disk_source in disklist:
- disk_file = disk_source.split('/')[-1]
- disk_dest = path.join( options.backpath, disk_file )
-
- logit(
- 'backup',
- 'copying %s to %s for %s' % (disk_source, disk_dest, dom.name())
- )
- copy2( disk_source, disk_dest )
-
- if recreate: # if true, start guest after backup
- create([dom]) # start dom
-
- ext, tarmode = '.tar.gz', 'w:gz'
- if options.nogzip:
- ext, tarmode = '.tar', 'w'
-
- tarfilename = dom.name() + ext
- if options.tardate:
- tarfilename = dom.name() + '-' + TODAY + ext
-
- tarpath = path.join( options.backpath, tarfilename )
-
- if path.isfile( tarpath ): # if file exists, run rotate
- logit( 'backup', 'rotating backup files for ' + dom.name() )
- rotate( tarpath, options.retention )
-
- logit(
- 'backup',
- 'archiving files for %s to %s' % (dom.name(), tarpath)
- )
- tar = tarfile.open( tarpath, tarmode )
-
- logit(
- 'backup',
- 'archiving %s for %s' % (xmlfile, dom.name())
- )
- tar.add( xmlfile ) # add xml to tar
- remove( xmlfile ) # cleanup tmp files
+ logit("backup", "invoking backup for " + dom.name())
for disk_source in disklist:
- disk_file = disk_source.split('/')[-1]
- disk_dest = path.join( options.backpath, disk_file )
+ disk_file = disk_source.split("/")[-1]
+ disk_dest = path.join(options.backpath, disk_file)
+
logit(
- 'backup',
- 'archiving %s for %s' % (disk_dest, dom.name())
+ "backup",
+ "copying %s to %s for %s" % (disk_source, disk_dest, dom.name()),
)
- tar.add( disk_dest ) # add img to tar
- remove( disk_dest ) # cleanup tmp files
-
+ copy2(disk_source, disk_dest)
+
+ if recreate: # if true, start guest after backup
+ create([dom]) # start dom
+
+ ext, tarmode = ".tar.gz", "w:gz"
+ if options.nogzip:
+ ext, tarmode = ".tar", "w"
+
+ tarfilename = dom.name() + ext
+ if options.tardate:
+ tarfilename = dom.name() + "-" + TODAY + ext
+
+ tarpath = path.join(options.backpath, tarfilename)
+
+ if path.isfile(tarpath): # if file exists, run rotate
+ logit("backup", "rotating backup files for " + dom.name())
+ rotate(tarpath, options.retention)
+
+ logit("backup", "archiving files for %s to %s" % (dom.name(), tarpath))
+ tar = tarfile.open(tarpath, tarmode)
+
+ logit("backup", "archiving %s for %s" % (xmlfile, dom.name()))
+ tar.add(xmlfile) # add xml to tar
+ remove(xmlfile) # cleanup tmp files
+
+ for disk_source in disklist:
+ disk_file = disk_source.split("/")[-1]
+ disk_dest = path.join(options.backpath, disk_file)
+ logit("backup", "archiving %s for %s" % (disk_dest, dom.name()))
+ tar.add(disk_dest) # add img to tar
+ remove(disk_dest) # cleanup tmp files
+
tar.close()
- logit( 'backup', 'finished backup for ' + dom.name() )
-
-def shutdown( doms, wait=180 ):
+ logit("backup", "finished backup for " + dom.name())
+
+
+def shutdown(doms, wait=180):
"""Accept a list of dom objects, attempt to shutdown the active ones"""
- # get all running guests from list and invoke shutdown
- invoke( get_all_running( doms ), 'shutdown' )
+ # get all running guests from list and invoke shutdown
+ invoke(get_all_running(doms), "shutdown")
"""loop until all guests are shut off or destroy
all active guests if wait timer is reached."""
secs = 10
- wait /= secs # divide wait by secs
+ wait /= secs # divide wait by secs
- for i in range( 0, wait + 1 ):
+ for i in range(0, wait + 1):
# if all doms are shut off, leave loop
- if check_all_shutoff( doms ):
+ if check_all_shutoff(doms):
break
else:
logit(
- 'shutdown',
- 'waited ' + str( i * secs ) + ' seconds for ' +
- ', '.join( dom.name() for dom in get_all_running( doms ) ) +
- ' to shut off'
+ "shutdown",
+ "waited "
+ + str(i * secs)
+ + " seconds for "
+ + ", ".join(dom.name() for dom in get_all_running(doms))
+ + " to shut off",
)
# if the wait time is reached, destroy all active doms
- if i == wait:
- invoke( get_all_running( doms ) ,'destroy' )
-
- sleep( secs )
+ if i == wait:
+ invoke(get_all_running(doms), "destroy")
-def create( doms ):
+ sleep(secs)
+
+
+def create(doms):
"""Accept a list of dom objects, attempt to start the inactive ones"""
- # get all shutoff guests from list and invoke create
- invoke( get_all_shutoff( doms ), 'create' )
+ # get all shutoff guests from list and invoke create
+ invoke(get_all_shutoff(doms), "create")
-def reboot( doms ):
+
+def reboot(doms):
"""Accept a list of dom objects, attempt to shutdown then start"""
- shutdown( doms )
- create( doms )
-
-def info( doms ):
+ shutdown(doms)
+ create(doms)
+
+
+def info(doms):
"""Accept a list of dom objects, attempt to display info for all"""
- #invoke( doms, 'name' )
- #invoke( doms, 'info')
- if check_all_running( doms ): print "NOTE: All guests are running"
- if check_all_shutoff( doms ): print "NOTE: All guests are shut off"
-
- print ''
- print 'running guests: ' + ', '.join( [ dom.name() for dom in get_all_running( doms ) ] )
- print 'shutoff guests: ' + ', '.join( [ dom.name() for dom in get_all_shutoff( doms ) ] )
- print ''
- print 'DomName'.ljust(16) + 'Memory MB'.rjust(12) + 'vCPUs'.rjust(8) + 'CPUtime ms'.rjust(18)
- print '======================================================'
+ # invoke( doms, 'name' )
+ # invoke( doms, 'info')
+ if check_all_running(doms):
+ print("NOTE: All guests are running")
+ if check_all_shutoff(doms):
+ print("NOTE: All guests are shut off")
+
+ print("")
+ print("running guests: " + ", ".join([dom.name() for dom in get_all_running(doms)]))
+ print("shutoff guests: " + ", ".join([dom.name() for dom in get_all_shutoff(doms)]))
+ print("")
+ print(
+ "DomName".ljust(16)
+ + "Memory MB".rjust(12)
+ + "vCPUs".rjust(8)
+ + "CPUtime ms".rjust(18)
+ )
+ print("======================================================")
for dom in doms:
name = dom.name()
- rams = str(dom.info()[2]/1024) + '/' + str(dom.info()[1]/1024)
+ rams = str(dom.info()[2] / 1024) + "/" + str(dom.info()[1] / 1024)
cpus = str(dom.info()[3])
- time = str(dom.info()[4]/1000000)
- print name.ljust(16) + rams.rjust(12) + cpus.rjust(8) + time.rjust(18)
+ time = str(dom.info()[4] / 1000000)
+ print(name.ljust(16) + rams.rjust(12) + cpus.rjust(8) + time.rjust(18))
-def check_all_running( doms ):
+
+def check_all_running(doms):
"""Accept a list of dom objects, check if all guest dom are active"""
- if sum( [dom.isActive() for dom in doms] ) == len( doms ):
+ if sum([dom.isActive() for dom in doms]) == len(doms):
return True
return False
-def check_all_shutoff( doms ):
+
+def check_all_shutoff(doms):
"""Accept a list of dom objects, check if all guest dom are shut off"""
- if sum( [dom.isActive() for dom in doms] ):
+ if sum([dom.isActive() for dom in doms]):
return False
return True
-def get_all_running( doms ):
+
+def get_all_running(doms):
"""Accept a list of dom objects, return a list of running dom objects"""
- return [ dom for dom in doms if dom.isActive() ]
+ return [dom for dom in doms if dom.isActive()]
-def get_all_shutoff( doms ):
+
+def get_all_shutoff(doms):
"""Accept a list of dom objects, return a list of shutoff dom objects"""
- return [ dom for dom in doms if not dom.isActive() ]
+ return [dom for dom in doms if not dom.isActive()]
-def logit( context, message, quiet = False ):
+
+def logit(context, message, quiet=False):
"""syslog and error handler"""
- if type( message ) is tuple:
- message = message[2] # libvirt message is a tuple
+ if type(message) is tuple:
+ message = message[2] # libvirt message is a tuple
- try: quiet = options.quiet
- except NameError: pass
+ try:
+ quiet = options.quiet
+ except NameError:
+ pass
- if quiet: pass
- else: print context + ': ' + message
+ if quiet:
+ pass
+ else:
+ print(context + ": " + message)
- syslog.openlog( 'virt-back', 0, syslog.LOG_LOCAL3 )
- syslog.syslog( message )
+ syslog.openlog("virt-back", 0, syslog.LOG_LOCAL3)
+ syslog.syslog(message)
syslog.closelog()
-def rotate( target, retention = 3 ):
+
+def rotate(target, retention=3):
"""file rotation routine"""
- for i in range( retention-2, 0, -1 ): # count backwards
- old_name = "%s.%s" % ( target, i )
- new_name = "%s.%s" % ( target, i + 1 )
- try: move( old_name, new_name )
- except IOError: pass
- move( target, target + '.1' )
+ for i in range(retention - 2, 0, -1): # count backwards
+ old_name = "%s.%s" % (target, i)
+ new_name = "%s.%s" % (target, i + 1)
+ try:
+ move(old_name, new_name)
+ except IOError:
+ pass
+ move(target, target + ".1")
+
def getoptions():
"""Fetch cli args, parse and map to python, test sanity"""
@@ -283,101 +306,219 @@ def getoptions():
# create an option parcer object
p = OptionParser()
- p.set_description( DESCRIPTION )
-
- p.add_option( '-q', '--quiet', dest='quiet', action='store_true', default=False,
- help='prevent output to stdout' )
+ p.set_description(DESCRIPTION)
- p.add_option( '-d', '--date', dest='tardate', action='store_true', default=False,
- help='append date to tar filename [default: no date]' )
+ p.add_option(
+ "-q",
+ "--quiet",
+ dest="quiet",
+ action="store_true",
+ default=False,
+ help="prevent output to stdout",
+ )
- p.add_option( '-g', '--no-gzip', dest='nogzip', action='store_true', default=False,
- help='do not gzip the resulting tar file' )
+ p.add_option(
+ "-d",
+ "--date",
+ dest="tardate",
+ action="store_true",
+ default=False,
+ help="append date to tar filename [default: no date]",
+ )
- p.add_option( '-a', '--retention', dest='retention', metavar="amount", default=3,
- type='int', help="backups to retain [default: 3]" )
+ p.add_option(
+ "-g",
+ "--no-gzip",
+ dest="nogzip",
+ action="store_true",
+ default=False,
+ help="do not gzip the resulting tar file",
+ )
- p.add_option( '-p', '--path', dest='backpath', metavar="'PATH'", default='/KVMBACK',
- help="backup path [default: '/KVMBACK']" )
-
- p.add_option( '-u', '--uri', dest='uri', metavar="'URI'",
- help="optional hypervisor uri" )
+ p.add_option(
+ "-a",
+ "--retention",
+ dest="retention",
+ metavar="amount",
+ default=3,
+ type="int",
+ help="backups to retain [default: 3]",
+ )
+
+ p.add_option(
+ "-p",
+ "--path",
+ dest="backpath",
+ metavar="'PATH'",
+ default="/KVMBACK",
+ help="backup path [default: '/KVMBACK']",
+ )
+
+ p.add_option(
+ "-u", "--uri", dest="uri", metavar="'URI'", help="optional hypervisor uri"
+ )
# Actions for info testing: These options display info/ test a list of guests only.
-
- g0 = OptionGroup( p, "Actions for info testing", "These options display info or test a list of guests." )
-
- g0.add_option( '-i', '--info', dest='info', action='store_true', default=False,
- help='info/test a list of guests (space delimited dom names)' )
-
- g0.add_option( '--info-all', dest='infoall', action='store_true', default=False,
- help='attempt to show info on ALL guests' )
+
+ g0 = OptionGroup(
+ p,
+ "Actions for info testing",
+ "These options display info or test a list of guests.",
+ )
+
+ g0.add_option(
+ "-i",
+ "--info",
+ dest="info",
+ action="store_true",
+ default=False,
+ help="info/test a list of guests (space delimited dom names)",
+ )
+
+ g0.add_option(
+ "--info-all",
+ dest="infoall",
+ action="store_true",
+ default=False,
+ help="attempt to show info on ALL guests",
+ )
# WARNING: Dangerous options below, option grouping for scary actions
-
- g1 = OptionGroup( p, "Actions for a list of dom names", "WARNING: These options WILL bring down guests!" )
-
- g1.add_option( '-b', '--backup', dest='backup', action='store_true', default=False,
- help='backup a list of guests (space delimited dom names)' )
-
- g1.add_option( '-r', '--reboot', dest='reboot', action='store_true', default=False,
- help='reboot a list of guests (space delimited dom names)' )
-
- g1.add_option( '-s', '--shutdown', dest='shutdown', action='store_true', default=False,
- help='shutdown a list of guests (space delimited dom names)' )
-
- g1.add_option( '-c', '--create', dest='create', action='store_true', default=False,
- help='start a list of guests (space delimited dom names)' )
-
- g2 = OptionGroup( p, "Actions for all doms", "WARNING: These options WILL bring down ALL guests!" )
- g2.add_option( '--backup-all', dest='backupall', action='store_true', default=False,
- help='attempt to shutdown, backup, and start ALL guests' )
+ g1 = OptionGroup(
+ p,
+ "Actions for a list of dom names",
+ "WARNING: These options WILL bring down guests!",
+ )
- g2.add_option( '--reboot-all', dest='rebootall', action='store_true', default=False,
- help='attempt to shutdown and then start ALL guests' )
-
- g2.add_option( '--shutdown-all', dest='shutdownall', action='store_true', default=False,
- help='attempt to shutdown ALL guests' )
+ g1.add_option(
+ "-b",
+ "--backup",
+ dest="backup",
+ action="store_true",
+ default=False,
+ help="backup a list of guests (space delimited dom names)",
+ )
- g2.add_option( '--create-all', dest='createall', action='store_true', default=False,
- help='attempt to start ALL guests' )
+ g1.add_option(
+ "-r",
+ "--reboot",
+ dest="reboot",
+ action="store_true",
+ default=False,
+ help="reboot a list of guests (space delimited dom names)",
+ )
+
+ g1.add_option(
+ "-s",
+ "--shutdown",
+ dest="shutdown",
+ action="store_true",
+ default=False,
+ help="shutdown a list of guests (space delimited dom names)",
+ )
+
+ g1.add_option(
+ "-c",
+ "--create",
+ dest="create",
+ action="store_true",
+ default=False,
+ help="start a list of guests (space delimited dom names)",
+ )
+
+ g2 = OptionGroup(
+ p, "Actions for all doms", "WARNING: These options WILL bring down ALL guests!"
+ )
+
+ g2.add_option(
+ "--backup-all",
+ dest="backupall",
+ action="store_true",
+ default=False,
+ help="attempt to shutdown, backup, and start ALL guests",
+ )
+
+ g2.add_option(
+ "--reboot-all",
+ dest="rebootall",
+ action="store_true",
+ default=False,
+ help="attempt to shutdown and then start ALL guests",
+ )
+
+ g2.add_option(
+ "--shutdown-all",
+ dest="shutdownall",
+ action="store_true",
+ default=False,
+ help="attempt to shutdown ALL guests",
+ )
+
+ g2.add_option(
+ "--create-all",
+ dest="createall",
+ action="store_true",
+ default=False,
+ help="attempt to start ALL guests",
+ )
# attach groups g1 and g2 to the parser p object
- p.add_option_group( g0 )
- p.add_option_group( g1 )
- p.add_option_group( g2 )
+ p.add_option_group(g0)
+ p.add_option_group(g1)
+ p.add_option_group(g2)
# parse options and args
options, guest_names = p.parse_args()
# the actionsum should be 1 to continue, bool math ftw
- actions = [ options.backup, options.reboot, options.shutdown,
- options.create, options.info, options.backupall, options.rebootall,
- options.shutdownall, options.createall, options.infoall, ]
- actionsum = sum( actions )
+ actions = [
+ options.backup,
+ options.reboot,
+ options.shutdown,
+ options.create,
+ options.info,
+ options.backupall,
+ options.rebootall,
+ options.shutdownall,
+ options.createall,
+ options.infoall,
+ ]
+ actionsum = sum(actions)
if actionsum == 1:
return options, guest_names
else:
- exit("\nYou must have 1 action, no more, no less.\n\nRun 'virt-back --help' for help.\n")
+ exit(
+ "\nYou must have 1 action, no more, no less.\n\nRun 'virt-back --help' for help.\n"
+ )
-if __name__ == '__main__':
- TODAY = str( date.today() )
+
+if __name__ == "__main__":
+ TODAY = str(date.today())
# Get the list of options and list of guest_names from cli
options, guest_names = getoptions()
# connect to hypervisor with Domfetcher (read-only)
- domfetcher = Domfetcher( options.uri )
-
- if options.backup or options.reboot or options.shutdown or options.create or options.info:
- doms = domfetcher.get_doms_by_names( guest_names )
- else:
+ domfetcher = Domfetcher(options.uri)
+
+ if (
+ options.backup
+ or options.reboot
+ or options.shutdown
+ or options.create
+ or options.info
+ ):
+ doms = domfetcher.get_doms_by_names(guest_names)
+ else:
doms = domfetcher.get_all_doms()
-
- if options.backup or options.backupall: backup( doms )
- if options.reboot or options.rebootall: reboot( doms )
- if options.shutdown or options.shutdownall: shutdown( doms )
- if options.create or options.createall: create( doms )
- if options.info or options.infoall: info( doms )
-
+ if options.backup or options.backupall:
+ backup(doms)
+ if options.reboot or options.rebootall:
+ reboot(doms)
+ if options.shutdown or options.shutdownall:
+ shutdown(doms)
+ if options.create or options.createall:
+ create(doms)
+ if options.info or options.infoall:
+ info(doms)