From 55e4ddcd3dae8a16d884290770a455d4467c152a Mon Sep 17 00:00:00 2001 From: Wojtek Porczyk Date: Mon, 5 Oct 2015 13:46:06 +0200 Subject: [PATCH] qubes/tools: custom QubesArgumentParser Common functions that revolve around running the tool (like `dont_run_as_root`) will be methods of this class. --- qubes/tools/__init__.py | 120 ++++++++++++++++++++---------------- qubes/tools/qmemmand.py | 4 +- qubes/tools/qubes_create.py | 5 +- qubes/tools/qvm_create.py | 6 +- qubes/tools/qvm_ls.py | 2 +- qubes/tools/qvm_prefs.py | 27 ++++---- qubes/tools/qvm_start.py | 9 ++- 7 files changed, 92 insertions(+), 81 deletions(-) diff --git a/qubes/tools/__init__.py b/qubes/tools/__init__.py index 316b455d..2695e231 100644 --- a/qubes/tools/__init__.py +++ b/qubes/tools/__init__.py @@ -27,6 +27,7 @@ import argparse import importlib +import logging import os import qubes.log @@ -85,37 +86,82 @@ class SinglePropertyAction(argparse.Action): if self.const is None else self.const -# TODO --verbose, logger setup -def get_parser_base(want_force_root=False, **kwargs): - '''Get base parser with options common to all Qubes OS tools. +class QubesArgumentParser(argparse.ArgumentParser): + '''Parser preconfigured for use in most of the Qubes command-line tools. :param bool want_force_root: add ``--force-root`` option + :param bool want_vmname: add ``VMNAME`` as first positional argument *kwargs* are passed to :py:class:`argparser.ArgumentParser`. - Currently supported options: ``--force-root`` (optional), ``--xml``. + Currenty supported options: + ``--force-root`` (optional) + ``--xml`` location of :file:`qubes.xml` (help is suppressed) + ``--verbose`` and ``--quiet`` ''' - parser = argparse.ArgumentParser(**kwargs) - parser.add_argument('--xml', metavar='XMLFILE', - action='store', - help=argparse.SUPPRESS) + def __init__(self, + want_force_root=False, + want_vmname=False, + **kwargs): + self.add_argument('--xml', metavar='XMLFILE', + action='store', + help=argparse.SUPPRESS) - parser.add_argument('--verbose', '-v', - action='count', - help='increase verbosity') + self.add_argument('--verbose', '-v', + action='count', + help='increase verbosity') - parser.add_argument('--quiet', '-q', - action='count', - help='decrease verbosity') + self.add_argument('--quiet', '-q', + action='count', + help='decrease verbosity') - if want_force_root: - parser.add_argument('--force-root', - action='store_true', default=False, - help='Force to run as root.') + if want_force_root: + self.add_argument('--force-root', + action='store_true', default=False, + help='force to run as root') - parser.set_defaults(verbose=1, quiet=0) + if want_vmname: + self.add_argument('vmname', metavar='VMNAME', + action='store', + help='name of the domain') - return parser + self.set_defaults(verbose=1, quiet=0) + + + def dont_run_as_root(self, args): + '''Prevent running as root. + + :param argparse.Namespace args: if there is ``.force_root`` attribute \ + set to true, run anyway + ''' + try: + euid = os.geteuid() + except AttributeError: # no geteuid(), probably NT + return + + if euid == 0 and not args.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 + + + @staticmethod + def set_qubes_verbosity(args): + '''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 + + if verbose >= 2: + qubes.log.enable_debug() + elif verbose >= 1: + qubes.log.enable() def get_parser_for_command(command): @@ -139,37 +185,3 @@ def get_parser_for_command(command): raise AttributeError('cannot find parser in module') return parser - - -def dont_run_as_root(parser, args): - '''Prevent running as root. - - :param argparse.ArgumentParser parser: parser on which we invoke error - :param argparse.Namespace args: if there is ``.force_root`` attribute set \ - to true, run anyway - :return: If we should back off - :rtype bool: - ''' - try: - euid = os.geteuid() - except AttributeError: # no geteuid(), probably NT - return - - if euid == 0 and not args.force_root: - parser.error('refusing to run as root; add --force-root to override') - - -def set_verbosity(parser, args): - '''Apply a verbosity setting. - - This is done by configuring global logging. - :param argparse.ArgumentParser parser: command parser - :param argparse.Namespace args: args as parsed by parser - ''' - - verbose = args.verbose - args.quiet - - if verbose >= 2: - qubes.log.enable_debug() - elif verbose >= 1: - qubes.log.enable() diff --git a/qubes/tools/qmemmand.py b/qubes/tools/qmemmand.py index b96ab94f..12983c24 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.get_parser_base() +parser = qubes.tools.QubesArgumentParser() parser.add_argument('--config', '-c', metavar='FILE', action='store', default='/etc/qubes/qmemman.conf', @@ -208,7 +208,7 @@ def main(): sys.stdin.close() - logging.root.setLevel((args.quiet - args.verbose) * 10 + logging.WARNING) + logging.root.setLevel(parser.get_loglevel_from_verbosity(args)) log = logging.getLogger('qmemman.daemon') diff --git a/qubes/tools/qubes_create.py b/qubes/tools/qubes_create.py index 47572772..978ab2cd 100644 --- a/qubes/tools/qubes_create.py +++ b/qubes/tools/qubes_create.py @@ -30,7 +30,8 @@ import argparse import qubes import qubes.tools -parser = qubes.tools.get_parser_base(description='Create new Qubes OS store.') +parser = qubes.tools.QubesArgumentParser( + description='Create new Qubes OS store.') parser.add_argument('--property', '--prop', '-p', action=qubes.tools.PropertyAction, @@ -45,7 +46,7 @@ def main(args=None): ''' args = parser.parse_args(args) - qubes.tools.set_verbosity(parser, args) + parser.set_qubes_verbosity(args) app = qubes.Qubes.create_empty_store(args.xml, **args.properties) return True diff --git a/qubes/tools/qvm_create.py b/qubes/tools/qvm_create.py index bc46e872..4ab16a8a 100644 --- a/qubes/tools/qvm_create.py +++ b/qubes/tools/qvm_create.py @@ -36,7 +36,7 @@ import qubes import qubes.tools -parser = qubes.tools.get_parser_base(want_force_root=True) +parser = qubes.tools.QubesArgumentParser(want_force_root=True) parser.add_argument('--class', '-C', dest='cls', default='AppVM', @@ -75,8 +75,8 @@ parser.add_argument('name', metavar='VMNAME', def main(): args = parser.parse_args() - qubes.tools.set_verbosity(parser, args) - qubes.tools.dont_run_as_root(parser, args) + parser.set_verbosity(args) + parser.dont_run_as_root(args) if 'label' not in args.properties: parser.error('--label option is mandatory') diff --git a/qubes/tools/qvm_ls.py b/qubes/tools/qvm_ls.py index f5f6257e..86cd8c96 100644 --- a/qubes/tools/qvm_ls.py +++ b/qubes/tools/qvm_ls.py @@ -595,7 +595,7 @@ def main(args=None): parser = get_parser() args = parser.parse_args(args) - qubes.tools.set_verbosity(parser, args) + parser.set_qubes_verbosity(args) app = qubes.Qubes(args.xml) if args.fields: diff --git a/qubes/tools/qvm_prefs.py b/qubes/tools/qvm_prefs.py index 96252887..81c17f83 100644 --- a/qubes/tools/qvm_prefs.py +++ b/qubes/tools/qvm_prefs.py @@ -70,13 +70,12 @@ class _HelpPropertiesAction(argparse.Action): + '\n\nThere may be more properties in specific domain classes.\n') -parser = qubes.tools.get_parser_base(want_force_root=True) +parser = qubes.tools.QubesArgumentParser( + want_force_root=True, + want_vmname=True) parser.add_argument('--help-properties', action=_HelpPropertiesAction) -parser.add_argument('name', metavar='VMNAME', - help='name of the domain to change') - parser.add_argument('property', metavar='PROPERTY', nargs='?', help='name of the property to show or change') @@ -96,28 +95,28 @@ parser.add_argument('--unset', '--default', '--delete', '-D', def main(): args = parser.parse_args() - qubes.tools.set_verbosity(parser, args) - qubes.tools.dont_run_as_root(parser, args) + parser.set_verbosity(args) + parser.dont_run_as_root(args) app = qubes.Qubes(args.xml) try: - domain = app.domains[args.name] + vm = app.domains[args.vmname] except KeyError: - parser.error('no such domain: {!r}'.format(args.name)) + parser.error('no such domain: {!r}'.format(args.vmname)) if args.property is None: - properties = domain.property_list() + properties = vm.property_list() width = max(len(prop.__name__) for prop in properties) for prop in sorted(properties): try: - value = getattr(domain, prop.__name__) + value = getattr(vm, prop.__name__) except AttributeError: print('{name:{width}s} U'.format( name=prop.__name__, width=width)) continue - if domain.property_is_default(prop): + if vm.property_is_default(prop): print('{name:{width}s} D {value!r}'.format( name=prop.__name__, width=width, value=value)) else: @@ -128,17 +127,17 @@ def main(): if args.value is not None: - setattr(domain, args.property, args.value) + setattr(vm, args.property, args.value) app.save() return True if args.delete: - delattr(domain, args.property) + delattr(vm, args.property) app.save() return True - print(str(getattr(domain, args.property))) + print(str(getattr(vm, args.property))) return True diff --git a/qubes/tools/qvm_start.py b/qubes/tools/qvm_start.py index 92a63fdd..4b40a2a4 100644 --- a/qubes/tools/qvm_start.py +++ b/qubes/tools/qvm_start.py @@ -47,7 +47,9 @@ class DriveAction(argparse.Action): setattr(namespace, self.dest, prefix + value) -parser = qubes.tools.get_parser_base(description='start a domain') +parser = qubes.tools.QubesArgumentParser( + want_vmname=True, + description='start a domain') parser_drive = parser.add_mutually_exclusive_group() @@ -90,9 +92,6 @@ parser.add_argument('--no-start-guid', #parser.add_option ("--tray", action="store_true", dest="tray", default=False, # help="Use tray notifications instead of stdout" ) -parser.add_argument('name', metavar='VMNAME', - help='domain to start') - parser.set_defaults(drive=None) def main(args=None): @@ -103,7 +102,7 @@ def main(args=None): ''' args = parser.parse_args(args) - qubes.tools.set_verbosity(parser, args) + parser.set_qubes_verbosity(parser, args) app = qubes.Qubes(args.xml) # if options.tray: