qubes/tools: custom QubesArgumentParser

Common functions that revolve around running the tool (like
`dont_run_as_root`) will be methods of this class.
This commit is contained in:
Wojtek Porczyk 2015-10-05 13:46:06 +02:00
parent c9cbf8ffe2
commit 55e4ddcd3d
7 changed files with 92 additions and 81 deletions

View File

@ -27,6 +27,7 @@
import argparse import argparse
import importlib import importlib
import logging
import os import os
import qubes.log import qubes.log
@ -85,37 +86,82 @@ class SinglePropertyAction(argparse.Action):
if self.const is None else self.const if self.const is None else self.const
# TODO --verbose, logger setup class QubesArgumentParser(argparse.ArgumentParser):
def get_parser_base(want_force_root=False, **kwargs): '''Parser preconfigured for use in most of the Qubes command-line tools.
'''Get base parser with options common to all Qubes OS tools.
: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
*kwargs* are passed to :py:class:`argparser.ArgumentParser`. *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', def __init__(self,
action='store', want_force_root=False,
help=argparse.SUPPRESS) want_vmname=False,
**kwargs):
self.add_argument('--xml', metavar='XMLFILE',
action='store',
help=argparse.SUPPRESS)
parser.add_argument('--verbose', '-v', self.add_argument('--verbose', '-v',
action='count', action='count',
help='increase verbosity') help='increase verbosity')
parser.add_argument('--quiet', '-q', self.add_argument('--quiet', '-q',
action='count', action='count',
help='decrease verbosity') help='decrease verbosity')
if want_force_root: if want_force_root:
parser.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')
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): def get_parser_for_command(command):
@ -139,37 +185,3 @@ def get_parser_for_command(command):
raise AttributeError('cannot find parser in module') raise AttributeError('cannot find parser in module')
return parser 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()

View File

@ -170,7 +170,7 @@ class QMemmanReqHandler(SocketServer.BaseRequestHandler):
# XXX no release of lock? # XXX no release of lock?
parser = qubes.tools.get_parser_base() parser = qubes.tools.QubesArgumentParser()
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',
@ -208,7 +208,7 @@ def main():
sys.stdin.close() 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') log = logging.getLogger('qmemman.daemon')

View File

@ -30,7 +30,8 @@ import argparse
import qubes import qubes
import qubes.tools 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', parser.add_argument('--property', '--prop', '-p',
action=qubes.tools.PropertyAction, action=qubes.tools.PropertyAction,
@ -45,7 +46,7 @@ def main(args=None):
''' '''
args = parser.parse_args(args) 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) app = qubes.Qubes.create_empty_store(args.xml, **args.properties)
return True return True

View File

@ -36,7 +36,7 @@ import qubes
import qubes.tools 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', parser.add_argument('--class', '-C', dest='cls',
default='AppVM', default='AppVM',
@ -75,8 +75,8 @@ parser.add_argument('name', metavar='VMNAME',
def main(): def main():
args = parser.parse_args() args = parser.parse_args()
qubes.tools.set_verbosity(parser, args) parser.set_verbosity(args)
qubes.tools.dont_run_as_root(parser, 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')

View File

@ -595,7 +595,7 @@ def main(args=None):
parser = get_parser() parser = get_parser()
args = parser.parse_args(args) args = parser.parse_args(args)
qubes.tools.set_verbosity(parser, args) parser.set_qubes_verbosity(args)
app = qubes.Qubes(args.xml) app = qubes.Qubes(args.xml)
if args.fields: if args.fields:

View File

@ -70,13 +70,12 @@ class _HelpPropertiesAction(argparse.Action):
+ '\n\nThere may be more properties in specific domain classes.\n') + '\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('--help-properties', action=_HelpPropertiesAction)
parser.add_argument('name', metavar='VMNAME',
help='name of the domain to change')
parser.add_argument('property', metavar='PROPERTY', parser.add_argument('property', metavar='PROPERTY',
nargs='?', nargs='?',
help='name of the property to show or change') help='name of the property to show or change')
@ -96,28 +95,28 @@ parser.add_argument('--unset', '--default', '--delete', '-D',
def main(): def main():
args = parser.parse_args() args = parser.parse_args()
qubes.tools.set_verbosity(parser, args) parser.set_verbosity(args)
qubes.tools.dont_run_as_root(parser, args) parser.dont_run_as_root(args)
app = qubes.Qubes(args.xml) app = qubes.Qubes(args.xml)
try: try:
domain = app.domains[args.name] vm = app.domains[args.vmname]
except KeyError: 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: if args.property is None:
properties = domain.property_list() properties = 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(domain, prop.__name__) value = getattr(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 domain.property_is_default(prop): if 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:
@ -128,17 +127,17 @@ def main():
if args.value is not None: if args.value is not None:
setattr(domain, args.property, args.value) setattr(vm, args.property, args.value)
app.save() app.save()
return True return True
if args.delete: if args.delete:
delattr(domain, args.property) delattr(vm, args.property)
app.save() app.save()
return True return True
print(str(getattr(domain, args.property))) print(str(getattr(vm, args.property)))
return True return True

View File

@ -47,7 +47,9 @@ class DriveAction(argparse.Action):
setattr(namespace, self.dest, prefix + value) 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() 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, #parser.add_option ("--tray", action="store_true", dest="tray", default=False,
# help="Use tray notifications instead of stdout" ) # help="Use tray notifications instead of stdout" )
parser.add_argument('name', metavar='VMNAME',
help='domain to start')
parser.set_defaults(drive=None) parser.set_defaults(drive=None)
def main(args=None): def main(args=None):
@ -103,7 +102,7 @@ def main(args=None):
''' '''
args = parser.parse_args(args) args = parser.parse_args(args)
qubes.tools.set_verbosity(parser, args) parser.set_qubes_verbosity(parser, args)
app = qubes.Qubes(args.xml) app = qubes.Qubes(args.xml)
# if options.tray: # if options.tray: