From 015b01fe465fce667b9302bcf933dc41d712d607 Mon Sep 17 00:00:00 2001 From: Wojtek Porczyk Date: Mon, 5 Oct 2015 17:34:13 +0200 Subject: [PATCH] qubes/tools: unify looking up the domain This is common operation that is repeatable. --- qubes/tools/__init__.py | 63 ++++++++++++++++++++++++++++--------- qubes/tools/qmemmand.py | 2 +- qubes/tools/qubes_create.py | 7 +++-- qubes/tools/qvm_create.py | 15 ++++----- qubes/tools/qvm_ls.py | 4 +-- qubes/tools/qvm_prefs.py | 26 ++++++--------- qubes/tools/qvm_start.py | 9 ++---- 7 files changed, 71 insertions(+), 55 deletions(-) diff --git a/qubes/tools/__init__.py b/qubes/tools/__init__.py index 2695e231..dd2e09a7 100644 --- a/qubes/tools/__init__.py +++ b/qubes/tools/__init__.py @@ -89,23 +89,37 @@ class SinglePropertyAction(argparse.Action): class QubesArgumentParser(argparse.ArgumentParser): '''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_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`. Currenty supported options: ``--force-root`` (optional) - ``--xml`` location of :file:`qubes.xml` (help is suppressed) + ``--qubesxml`` location of :file:`qubes.xml` (help is suppressed) ``--verbose`` and ``--quiet`` ''' def __init__(self, + want_app=True, + want_app_no_instance=False, want_force_root=False, - want_vmname=False, + want_vm=False, **kwargs): - self.add_argument('--xml', metavar='XMLFILE', - action='store', - help=argparse.SUPPRESS) + + super(QubesArgumentParser, self).__init__(**kwargs) + + 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', action='count', @@ -115,20 +129,39 @@ class QubesArgumentParser(argparse.ArgumentParser): action='count', help='decrease verbosity') - if want_force_root: + if self._want_force_root: self.add_argument('--force-root', action='store_true', default=False, help='force to run as root') - if want_vmname: - self.add_argument('vmname', metavar='VMNAME', + if self._want_vm: + self.add_argument('vm', metavar='VMNAME', action='store', help='name of the domain') 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. :param argparse.Namespace args: if there is ``.force_root`` attribute \ @@ -139,24 +172,24 @@ class QubesArgumentParser(argparse.ArgumentParser): except AttributeError: # no geteuid(), probably NT 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') @staticmethod - def get_loglevel_from_verbosity(args): - return (args.quiet - args.verbose) * 10 + logging.WARNING + def get_loglevel_from_verbosity(namespace): + return (namespace.quiet - namespace.verbose) * 10 + logging.WARNING @staticmethod - def set_qubes_verbosity(args): + def set_qubes_verbosity(namespace): '''Apply a verbosity setting. This is done by configuring global logging. :param argparse.Namespace args: args as parsed by parser ''' - verbose = args.verbose - args.quiet + verbose = namespace.verbose - namespace.quiet if verbose >= 2: qubes.log.enable_debug() diff --git a/qubes/tools/qmemmand.py b/qubes/tools/qmemmand.py index 12983c24..40392171 100644 --- a/qubes/tools/qmemmand.py +++ b/qubes/tools/qmemmand.py @@ -170,7 +170,7 @@ class QMemmanReqHandler(SocketServer.BaseRequestHandler): # XXX no release of lock? -parser = qubes.tools.QubesArgumentParser() +parser = qubes.tools.QubesArgumentParser(want_app=False) parser.add_argument('--config', '-c', metavar='FILE', action='store', default='/etc/qubes/qmemman.conf', diff --git a/qubes/tools/qubes_create.py b/qubes/tools/qubes_create.py index 978ab2cd..b52278d6 100644 --- a/qubes/tools/qubes_create.py +++ b/qubes/tools/qubes_create.py @@ -31,7 +31,9 @@ import qubes import qubes.tools 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', action=qubes.tools.PropertyAction, @@ -46,8 +48,7 @@ def main(args=None): ''' args = parser.parse_args(args) - parser.set_qubes_verbosity(args) - app = qubes.Qubes.create_empty_store(args.xml, **args.properties) + app = qubes.Qubes.create_empty_store(args.app, **args.properties) return True diff --git a/qubes/tools/qvm_create.py b/qubes/tools/qvm_create.py index 4ab16a8a..82cd8e4f 100644 --- a/qubes/tools/qvm_create.py +++ b/qubes/tools/qvm_create.py @@ -73,10 +73,8 @@ parser.add_argument('name', metavar='VMNAME', #parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) -def main(): - args = parser.parse_args() - parser.set_verbosity(args) - parser.dont_run_as_root(args) +def main(args=None): + args = parser.parse_args(args) if 'label' not in args.properties: parser.error('--label option is mandatory') @@ -84,12 +82,11 @@ def main(): if 'name' not in args.properties: parser.error('VMNAME is mandatory') - app = qubes.Qubes(args.xml) try: - label = app.get_label(args.properties['label']) + label = args.app.get_label(args.properties['label']) except KeyError: 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: cls = qubes.vm.BaseVM.register[args.cls] @@ -100,7 +97,7 @@ def main(): 'template' not in (prop.__name__ for prop in cls.property_list()): 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]): # print >> sys.stderr, "root.img can be specified only for standalone VMs" @@ -146,7 +143,7 @@ def main(): except (IOError, OSError) as err: parser.error(str(err)) - app.save() + args.app.save() return True diff --git a/qubes/tools/qvm_ls.py b/qubes/tools/qvm_ls.py index 86cd8c96..121bb568 100644 --- a/qubes/tools/qvm_ls.py +++ b/qubes/tools/qvm_ls.py @@ -595,8 +595,6 @@ def main(args=None): parser = get_parser() args = parser.parse_args(args) - parser.set_qubes_verbosity(args) - app = qubes.Qubes(args.xml) if args.fields: columns = [col.strip() for col in args.fields.split(',')] @@ -606,7 +604,7 @@ def main(args=None): else: columns = formats[args.format] - table = Table(app, columns) + table = Table(args.app, columns) table.write_table(sys.stdout) return True diff --git a/qubes/tools/qvm_prefs.py b/qubes/tools/qvm_prefs.py index 81c17f83..27c9175e 100644 --- a/qubes/tools/qvm_prefs.py +++ b/qubes/tools/qvm_prefs.py @@ -72,7 +72,7 @@ class _HelpPropertiesAction(argparse.Action): parser = qubes.tools.QubesArgumentParser( want_force_root=True, - want_vmname=True) + want_vm=True) parser.add_argument('--help-properties', action=_HelpPropertiesAction) @@ -95,28 +95,20 @@ parser.add_argument('--unset', '--default', '--delete', '-D', def main(): 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: - properties = vm.property_list() + properties = args.vm.property_list() width = max(len(prop.__name__) for prop in properties) for prop in sorted(properties): try: - value = getattr(vm, prop.__name__) + value = getattr(args.vm, prop.__name__) except AttributeError: print('{name:{width}s} U'.format( name=prop.__name__, width=width)) continue - if vm.property_is_default(prop): + if args.vm.property_is_default(prop): print('{name:{width}s} D {value!r}'.format( name=prop.__name__, width=width, value=value)) else: @@ -127,17 +119,17 @@ def main(): if args.value is not None: - setattr(vm, args.property, args.value) - app.save() + setattr(args.vm, args.property, args.value) + args.app.save() return True if args.delete: - delattr(vm, args.property) - app.save() + delattr(args.vm, args.property) + args.app.save() return True - print(str(getattr(vm, args.property))) + print(str(getattr(args.vm, args.property))) return True diff --git a/qubes/tools/qvm_start.py b/qubes/tools/qvm_start.py index 4b40a2a4..41951895 100644 --- a/qubes/tools/qvm_start.py +++ b/qubes/tools/qvm_start.py @@ -48,7 +48,7 @@ class DriveAction(argparse.Action): parser = qubes.tools.QubesArgumentParser( - want_vmname=True, + want_vm=True, description='start a domain') @@ -102,16 +102,11 @@ def main(args=None): ''' args = parser.parse_args(args) - parser.set_qubes_verbosity(parser, args) - app = qubes.Qubes(args.xml) # if options.tray: # tray_notify_init() - try: - vm = app.domains[args.name] - except KeyError: - parser.error('no such domain: {!r}'.format(args.name)) + vm = args.vm if args.drive is not None: if 'drive' not in (prop.__name__ for prop in vm.property_list()):