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 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()

View File

@ -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')

View File

@ -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

View File

@ -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')

View File

@ -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:

View File

@ -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

View File

@ -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: