qubes/tools: unify looking up the domain

This is common operation that is repeatable.
This commit is contained in:
Wojtek Porczyk 2015-10-05 17:34:13 +02:00
parent 55e4ddcd3d
commit 015b01fe46
7 changed files with 71 additions and 55 deletions

View File

@ -89,23 +89,37 @@ class SinglePropertyAction(argparse.Action):
class QubesArgumentParser(argparse.ArgumentParser): class QubesArgumentParser(argparse.ArgumentParser):
'''Parser preconfigured for use in most of the Qubes command-line tools. '''Parser preconfigured for use in most of the Qubes command-line tools.
:param bool want_app: instantiate :py:class:`qubes.Qubes` object
:param bool want_app_no_instance: don't actually instantiate \
:py:class:`qubes.Qubes` object, just add argument for custom xml file
:param bool want_force_root: add ``--force-root`` option :param bool want_force_root: add ``--force-root`` option
:param bool want_vmname: add ``VMNAME`` as first positional argument :param bool want_vm: add ``VMNAME`` as first positional argument
*kwargs* are passed to :py:class:`argparser.ArgumentParser`. *kwargs* are passed to :py:class:`argparser.ArgumentParser`.
Currenty supported options: Currenty supported options:
``--force-root`` (optional) ``--force-root`` (optional)
``--xml`` location of :file:`qubes.xml` (help is suppressed) ``--qubesxml`` location of :file:`qubes.xml` (help is suppressed)
``--verbose`` and ``--quiet`` ``--verbose`` and ``--quiet``
''' '''
def __init__(self, def __init__(self,
want_app=True,
want_app_no_instance=False,
want_force_root=False, want_force_root=False,
want_vmname=False, want_vm=False,
**kwargs): **kwargs):
self.add_argument('--xml', metavar='XMLFILE',
action='store', super(QubesArgumentParser, self).__init__(**kwargs)
help=argparse.SUPPRESS)
self._want_app = want_app
self._want_app_no_instance = want_app_no_instance
self._want_force_root = want_force_root
self._want_vm = want_vm
if self._want_app:
self.add_argument('--qubesxml', metavar='FILE',
action='store', dest='app',
help=argparse.SUPPRESS)
self.add_argument('--verbose', '-v', self.add_argument('--verbose', '-v',
action='count', action='count',
@ -115,20 +129,39 @@ class QubesArgumentParser(argparse.ArgumentParser):
action='count', action='count',
help='decrease verbosity') help='decrease verbosity')
if want_force_root: if self._want_force_root:
self.add_argument('--force-root', self.add_argument('--force-root',
action='store_true', default=False, action='store_true', default=False,
help='force to run as root') help='force to run as root')
if want_vmname: if self._want_vm:
self.add_argument('vmname', metavar='VMNAME', self.add_argument('vm', metavar='VMNAME',
action='store', action='store',
help='name of the domain') help='name of the domain')
self.set_defaults(verbose=1, quiet=0) self.set_defaults(verbose=1, quiet=0)
def dont_run_as_root(self, args): def parse_args(self, *args, **kwargs):
namespace = super(QubesArgumentParser, self).parse_args(*args, **kwargs)
if self._want_app and not self._want_app_no_instance:
self.set_qubes_verbosity(namespace)
namespace.app = qubes.Qubes(namespace.app)
if self._want_vm:
try:
namespace.vm = namespace.app.domains[namespace.vm]
except KeyError:
self.error('no such domain: {!r}'.format(namespace.vm))
if self._want_force_root:
self.dont_run_as_root(namespace)
return namespace
def dont_run_as_root(self, namespace):
'''Prevent running as root. '''Prevent running as root.
:param argparse.Namespace args: if there is ``.force_root`` attribute \ :param argparse.Namespace args: if there is ``.force_root`` attribute \
@ -139,24 +172,24 @@ class QubesArgumentParser(argparse.ArgumentParser):
except AttributeError: # no geteuid(), probably NT except AttributeError: # no geteuid(), probably NT
return return
if euid == 0 and not args.force_root: if euid == 0 and not namespace.force_root:
self.error('refusing to run as root; add --force-root to override') self.error('refusing to run as root; add --force-root to override')
@staticmethod @staticmethod
def get_loglevel_from_verbosity(args): def get_loglevel_from_verbosity(namespace):
return (args.quiet - args.verbose) * 10 + logging.WARNING return (namespace.quiet - namespace.verbose) * 10 + logging.WARNING
@staticmethod @staticmethod
def set_qubes_verbosity(args): def set_qubes_verbosity(namespace):
'''Apply a verbosity setting. '''Apply a verbosity setting.
This is done by configuring global logging. This is done by configuring global logging.
:param argparse.Namespace args: args as parsed by parser :param argparse.Namespace args: args as parsed by parser
''' '''
verbose = args.verbose - args.quiet verbose = namespace.verbose - namespace.quiet
if verbose >= 2: if verbose >= 2:
qubes.log.enable_debug() qubes.log.enable_debug()

View File

@ -170,7 +170,7 @@ class QMemmanReqHandler(SocketServer.BaseRequestHandler):
# XXX no release of lock? # XXX no release of lock?
parser = qubes.tools.QubesArgumentParser() parser = qubes.tools.QubesArgumentParser(want_app=False)
parser.add_argument('--config', '-c', metavar='FILE', parser.add_argument('--config', '-c', metavar='FILE',
action='store', default='/etc/qubes/qmemman.conf', action='store', default='/etc/qubes/qmemman.conf',

View File

@ -31,7 +31,9 @@ import qubes
import qubes.tools import qubes.tools
parser = qubes.tools.QubesArgumentParser( parser = qubes.tools.QubesArgumentParser(
description='Create new Qubes OS store.') description='Create new Qubes OS store.',
want_app=True,
want_app_no_instance=True)
parser.add_argument('--property', '--prop', '-p', parser.add_argument('--property', '--prop', '-p',
action=qubes.tools.PropertyAction, action=qubes.tools.PropertyAction,
@ -46,8 +48,7 @@ def main(args=None):
''' '''
args = parser.parse_args(args) args = parser.parse_args(args)
parser.set_qubes_verbosity(args) app = qubes.Qubes.create_empty_store(args.app, **args.properties)
app = qubes.Qubes.create_empty_store(args.xml, **args.properties)
return True return True

View File

@ -73,10 +73,8 @@ parser.add_argument('name', metavar='VMNAME',
#parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) #parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
def main(): def main(args=None):
args = parser.parse_args() args = parser.parse_args(args)
parser.set_verbosity(args)
parser.dont_run_as_root(args)
if 'label' not in args.properties: if 'label' not in args.properties:
parser.error('--label option is mandatory') parser.error('--label option is mandatory')
@ -84,12 +82,11 @@ def main():
if 'name' not in args.properties: if 'name' not in args.properties:
parser.error('VMNAME is mandatory') parser.error('VMNAME is mandatory')
app = qubes.Qubes(args.xml)
try: try:
label = app.get_label(args.properties['label']) label = args.app.get_label(args.properties['label'])
except KeyError: except KeyError:
parser.error('no such label: {!r}; available: {}'.format(args.label, parser.error('no such label: {!r}; available: {}'.format(args.label,
', '.join(repr(l.name) for l in app.labels))) ', '.join(repr(l.name) for l in args.app.labels)))
try: try:
cls = qubes.vm.BaseVM.register[args.cls] cls = qubes.vm.BaseVM.register[args.cls]
@ -100,7 +97,7 @@ def main():
'template' not in (prop.__name__ for prop in cls.property_list()): 'template' not in (prop.__name__ for prop in cls.property_list()):
parser.error('this domain class does not support template') parser.error('this domain class does not support template')
vm = app.add_new_vm(cls, **args.properties) vm = args.app.add_new_vm(cls, **args.properties)
# if not options.standalone and any([options.root_copy_from, options.root_move_from]): # if not options.standalone and any([options.root_copy_from, options.root_move_from]):
# print >> sys.stderr, "root.img can be specified only for standalone VMs" # print >> sys.stderr, "root.img can be specified only for standalone VMs"
@ -146,7 +143,7 @@ def main():
except (IOError, OSError) as err: except (IOError, OSError) as err:
parser.error(str(err)) parser.error(str(err))
app.save() args.app.save()
return True return True

View File

@ -595,8 +595,6 @@ def main(args=None):
parser = get_parser() parser = get_parser()
args = parser.parse_args(args) args = parser.parse_args(args)
parser.set_qubes_verbosity(args)
app = qubes.Qubes(args.xml)
if args.fields: if args.fields:
columns = [col.strip() for col in args.fields.split(',')] columns = [col.strip() for col in args.fields.split(',')]
@ -606,7 +604,7 @@ def main(args=None):
else: else:
columns = formats[args.format] columns = formats[args.format]
table = Table(app, columns) table = Table(args.app, columns)
table.write_table(sys.stdout) table.write_table(sys.stdout)
return True return True

View File

@ -72,7 +72,7 @@ class _HelpPropertiesAction(argparse.Action):
parser = qubes.tools.QubesArgumentParser( parser = qubes.tools.QubesArgumentParser(
want_force_root=True, want_force_root=True,
want_vmname=True) want_vm=True)
parser.add_argument('--help-properties', action=_HelpPropertiesAction) parser.add_argument('--help-properties', action=_HelpPropertiesAction)
@ -95,28 +95,20 @@ parser.add_argument('--unset', '--default', '--delete', '-D',
def main(): def main():
args = parser.parse_args() args = parser.parse_args()
parser.set_verbosity(args)
parser.dont_run_as_root(args)
app = qubes.Qubes(args.xml)
try:
vm = app.domains[args.vmname]
except KeyError:
parser.error('no such domain: {!r}'.format(args.vmname))
if args.property is None: if args.property is None:
properties = vm.property_list() properties = args.vm.property_list()
width = max(len(prop.__name__) for prop in properties) width = max(len(prop.__name__) for prop in properties)
for prop in sorted(properties): for prop in sorted(properties):
try: try:
value = getattr(vm, prop.__name__) value = getattr(args.vm, prop.__name__)
except AttributeError: except AttributeError:
print('{name:{width}s} U'.format( print('{name:{width}s} U'.format(
name=prop.__name__, width=width)) name=prop.__name__, width=width))
continue continue
if vm.property_is_default(prop): if args.vm.property_is_default(prop):
print('{name:{width}s} D {value!r}'.format( print('{name:{width}s} D {value!r}'.format(
name=prop.__name__, width=width, value=value)) name=prop.__name__, width=width, value=value))
else: else:
@ -127,17 +119,17 @@ def main():
if args.value is not None: if args.value is not None:
setattr(vm, args.property, args.value) setattr(args.vm, args.property, args.value)
app.save() args.app.save()
return True return True
if args.delete: if args.delete:
delattr(vm, args.property) delattr(args.vm, args.property)
app.save() args.app.save()
return True return True
print(str(getattr(vm, args.property))) print(str(getattr(args.vm, args.property)))
return True return True

View File

@ -48,7 +48,7 @@ class DriveAction(argparse.Action):
parser = qubes.tools.QubesArgumentParser( parser = qubes.tools.QubesArgumentParser(
want_vmname=True, want_vm=True,
description='start a domain') description='start a domain')
@ -102,16 +102,11 @@ def main(args=None):
''' '''
args = parser.parse_args(args) args = parser.parse_args(args)
parser.set_qubes_verbosity(parser, args)
app = qubes.Qubes(args.xml)
# if options.tray: # if options.tray:
# tray_notify_init() # tray_notify_init()
try: vm = args.vm
vm = app.domains[args.name]
except KeyError:
parser.error('no such domain: {!r}'.format(args.name))
if args.drive is not None: if args.drive is not None:
if 'drive' not in (prop.__name__ for prop in vm.property_list()): if 'drive' not in (prop.__name__ for prop in vm.property_list()):