Improvements to qvm-pool argument parser
This change introduces a complete rewrite of the argument parser for the `qvm-pool` tool. As suggested by @marmarek in qubes-issues#5407, the goal is to be consistent with other tools such as `qvm-device`, `qvm-volume` etc. (resolves QubesOS/qubes-issues#5407)
This commit is contained in:
		
							parent
							
								
									81b3152fa8
								
							
						
					
					
						commit
						434d8c60bd
					
				| @ -31,177 +31,168 @@ import qubesadmin.storage | |||||||
| import qubesadmin.tools | import qubesadmin.tools | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class _Info(qubesadmin.tools.PoolsAction): | def list_drivers(args): | ||||||
|     ''' Action for argument parser that displays pool info and exits. ''' |     ''' Lists all drivers with their options ''' | ||||||
| 
 |  | ||||||
|     def __init__(self, option_strings, help='print pool info and exit', |  | ||||||
|                  **kwargs): |  | ||||||
|         # pylint: disable=redefined-builtin |  | ||||||
|         super(_Info, self).__init__(option_strings, help=help, **kwargs) |  | ||||||
| 
 |  | ||||||
|     def __call__(self, parser, namespace, values, option_string=None): |  | ||||||
|         setattr(namespace, 'command', 'info') |  | ||||||
|         super(_Info, self).__call__(parser, namespace, values, option_string) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def pool_info(pool): |  | ||||||
|     ''' Prints out pool name and config ''' |  | ||||||
|     data = [("name", pool.name)] |  | ||||||
|     data += [i for i in sorted(pool.config.items()) if i[0] != 'name'] |  | ||||||
|     qubesadmin.tools.print_table(data) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def list_pools(app): |  | ||||||
|     ''' Prints out all known pools and their drivers ''' |  | ||||||
|     result = [('NAME', 'DRIVER')] |  | ||||||
|     for pool in app.pools.values(): |  | ||||||
|         result += [(pool.name, pool.driver)] |  | ||||||
|     qubesadmin.tools.print_table(result) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class _Remove(argparse.Action): |  | ||||||
|     ''' Action for argument parser that removes a pool ''' |  | ||||||
| 
 |  | ||||||
|     def __init__(self, option_strings, dest=None, default=None, metavar=None): |  | ||||||
|         super(_Remove, self).__init__(option_strings=option_strings, |  | ||||||
|                                       dest=dest, |  | ||||||
|                                       metavar=metavar, |  | ||||||
|                                       default=default, |  | ||||||
|                                       help='remove pool') |  | ||||||
| 
 |  | ||||||
|     def __call__(self, parser, namespace, name, option_string=None): |  | ||||||
|         setattr(namespace, 'command', 'remove') |  | ||||||
|         setattr(namespace, 'name', name) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class _Add(argparse.Action): |  | ||||||
|     ''' Action for argument parser that adds a pool. ''' |  | ||||||
| 
 |  | ||||||
|     def __init__(self, option_strings, dest=None, default=None, metavar=None): |  | ||||||
|         super(_Add, self).__init__(option_strings=option_strings, |  | ||||||
|                                    dest=dest, |  | ||||||
|                                    metavar=metavar, |  | ||||||
|                                    default=default, |  | ||||||
|                                    nargs=2, |  | ||||||
|                                    help='add pool') |  | ||||||
| 
 |  | ||||||
|     def __call__(self, parser, namespace, values, option_string=None): |  | ||||||
|         name, driver = values |  | ||||||
|         setattr(namespace, 'command', 'add') |  | ||||||
|         setattr(namespace, 'name', name) |  | ||||||
|         setattr(namespace, 'driver', driver) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class _Set(qubesadmin.tools.PoolsAction): |  | ||||||
|     ''' Action for argument parser that sets pool options. ''' |  | ||||||
| 
 |  | ||||||
|     def __init__(self, option_strings, dest=None, default=None, metavar=None): |  | ||||||
|         super(_Set, self).__init__(option_strings=option_strings, |  | ||||||
|                                    dest=dest, |  | ||||||
|                                    metavar=metavar, |  | ||||||
|                                    default=default, |  | ||||||
|                                    help='modify pool (use -o to specify ' |  | ||||||
|                                         'modifications)') |  | ||||||
| 
 |  | ||||||
|     def __call__(self, parser, namespace, name, option_string=None): |  | ||||||
|         setattr(namespace, 'command', 'set') |  | ||||||
|         super(_Set, self).__call__(parser, namespace, name, option_string) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class _Options(argparse.Action): |  | ||||||
|     ''' Action for argument parser that parsers options. ''' |  | ||||||
| 
 |  | ||||||
|     def __init__(self, option_strings, dest, default, metavar='options'): |  | ||||||
|         super(_Options, self).__init__( |  | ||||||
|             option_strings=option_strings, |  | ||||||
|             dest=dest, |  | ||||||
|             metavar=metavar, |  | ||||||
|             default=default, |  | ||||||
|             help='comma-separated list of driver options') |  | ||||||
| 
 |  | ||||||
|     def __call__(self, parser, namespace, options, option_string=None): |  | ||||||
|         setattr(namespace, 'options', |  | ||||||
|                 dict([option.split('=', 1) for option in options.split(',')])) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def get_parser(): |  | ||||||
|     ''' Parses the provided args ''' |  | ||||||
|     parser = qubesadmin.tools.QubesArgumentParser(description=__doc__) |  | ||||||
|     parser.add_argument('-o', action=_Options, dest='options', default={}) |  | ||||||
|     group = parser.add_mutually_exclusive_group() |  | ||||||
|     group.add_argument('-l', |  | ||||||
|                        '--list', |  | ||||||
|                        dest='command', |  | ||||||
|                        const='list', |  | ||||||
|                        action='store_const', |  | ||||||
|                        help='list all pools and exit (default action)') |  | ||||||
|     group.add_argument('-i', '--info', metavar='POOLNAME', dest='pools', |  | ||||||
|                        action=_Info, default=[]) |  | ||||||
|     group.add_argument('-a', |  | ||||||
|                        '--add', |  | ||||||
|                        action=_Add, |  | ||||||
|                        dest='command', |  | ||||||
|                        metavar=('NAME', 'DRIVER')) |  | ||||||
|     group.add_argument('-r', '--remove', metavar='NAME', action=_Remove) |  | ||||||
|     group.add_argument('-s', '--set', metavar='POOLNAME', dest='pool', |  | ||||||
|                        action=_Set, default=[]) |  | ||||||
|     group.add_argument('--help-drivers', |  | ||||||
|                        dest='command', |  | ||||||
|                        const='list-drivers', |  | ||||||
|                        action='store_const', |  | ||||||
|                        help='list all drivers with their options and exit') |  | ||||||
|     return parser |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def main(args=None, app=None): |  | ||||||
|     '''Main routine of :program:`qvm-pools`. |  | ||||||
| 
 |  | ||||||
|     :param list args: Optional arguments to override those delivered from \ |  | ||||||
|         command line. |  | ||||||
|     ''' |  | ||||||
|     parser = get_parser() |  | ||||||
|     try: |  | ||||||
|         args = parser.parse_args(args, app=app) |  | ||||||
|     except qubesadmin.exc.QubesException as e: |  | ||||||
|         parser.print_error(str(e)) |  | ||||||
|         return 1 |  | ||||||
| 
 |  | ||||||
|     if args.command is None or args.command == 'list': |  | ||||||
|         list_pools(args.app) |  | ||||||
|     elif args.command == 'list-drivers': |  | ||||||
|     result = [('DRIVER', 'OPTIONS')] |     result = [('DRIVER', 'OPTIONS')] | ||||||
|     for driver in sorted(args.app.pool_drivers): |     for driver in sorted(args.app.pool_drivers): | ||||||
|         params = args.app.pool_driver_parameters(driver) |         params = args.app.pool_driver_parameters(driver) | ||||||
|         driver_options = ', '.join(params) |         driver_options = ', '.join(params) | ||||||
|         result += [(driver, driver_options)] |         result += [(driver, driver_options)] | ||||||
|     qubesadmin.tools.print_table(result) |     qubesadmin.tools.print_table(result) | ||||||
|     elif args.command == 'add': | 
 | ||||||
|  | 
 | ||||||
|  | def list_pools(args): | ||||||
|  |     ''' Lists all available pools ''' | ||||||
|  |     result = [('NAME', 'DRIVER')] | ||||||
|  |     for pool in args.app.pools.values(): | ||||||
|  |         result += [(pool.name, pool.driver)] | ||||||
|  |     qubesadmin.tools.print_table(result) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def info_pools(args): | ||||||
|  |     ''' Prints info about the specified pools ''' | ||||||
|  |     data = [] | ||||||
|  |     for idx, pool in enumerate(args.pools): | ||||||
|  |         data += [("", "")] if idx > 0 else [] | ||||||
|  |         data += [("name", pool.name)] | ||||||
|  |         data += [i for i in sorted(pool.config.items()) if i[0] != 'name'] | ||||||
|  |     qubesadmin.tools.print_table(data) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def add_pool(args): | ||||||
|  |     ''' Adds a new pool ''' | ||||||
|  |     options = dict(opt.split('=', 1) for opt in args.option or []) | ||||||
|     try: |     try: | ||||||
|             args.app.add_pool(name=args.name, driver=args.driver, |         args.app.add_pool(name=args.pool_name, driver=args.driver, **options) | ||||||
|                 **args.options) |  | ||||||
|     except qubesadmin.exc.QubesException as e: |     except qubesadmin.exc.QubesException as e: | ||||||
|             parser.error('failed to add pool %s: %s\n' % (args.name, str(e))) |         raise qubesadmin.exc.QubesException('Failed to add pool %s: %s\n', | ||||||
|     elif args.command == 'remove': |                                             args.pool_name, str(e)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def remove_pools(args): | ||||||
|  |     ''' Removes the specified pools ''' | ||||||
|  |     errors = [] | ||||||
|  |     for pool_name in args.pool_names: | ||||||
|         try: |         try: | ||||||
|             args.app.remove_pool(args.name) |             args.app.remove_pool(pool_name) | ||||||
|         except KeyError: |         except KeyError: | ||||||
|             parser.print_error('no such pool %s\n' % args.name) |             errors.append('No such pool %s\n' % pool_name) | ||||||
|         except qubesadmin.exc.QubesException as e: |         except qubesadmin.exc.QubesException as e: | ||||||
|             parser.error('failed to remove pool %s: %s\n' % (args.name, str(e))) |             errors.append( | ||||||
|     elif args.command == 'info': |                 'Failed to remove pool %s: %s\n' % (pool_name, str(e))) | ||||||
|         for pool in args.pools: |     if errors: | ||||||
|             pool_info(pool) |         raise qubesadmin.exc.QubesException('\n'.join(errors)) | ||||||
|     elif args.command == 'set': | 
 | ||||||
|         pool = args.pool[0] | 
 | ||||||
|         for opt, value in args.options.items(): | def set_pool(args): | ||||||
|  |     ''' Modifies driver options for a pool ''' | ||||||
|  |     options = (opt.split('=', 1) for opt in args.option or []) | ||||||
|  |     pool = args.app.pools[args.pool_name] | ||||||
|  |     errors = [] | ||||||
|  |     for opt, value in options: | ||||||
|         if not hasattr(type(pool), opt): |         if not hasattr(type(pool), opt): | ||||||
|                 parser.error('setting pool option %s is not supported' % ( |             errors.append( | ||||||
|                     pool.name)) |                 'Setting option %s is not supported for pool %s\n' % ( | ||||||
|  |                     opt, pool.name)) | ||||||
|         try: |         try: | ||||||
|             setattr(pool, opt, value) |             setattr(pool, opt, value) | ||||||
|         except qubesadmin.exc.QubesException as e: |         except qubesadmin.exc.QubesException as e: | ||||||
|                 parser.error('failed to set pool %s option %s: %s\n' % ( |             errors.append('Failed to set option %s for pool %s: %s\n' % ( | ||||||
|                     pool.name, opt, str(e))) |                 opt, pool.name, str(e))) | ||||||
|  |     if errors: | ||||||
|  |         raise qubesadmin.exc.QubesException('\n'.join(errors)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def init_list_parser(sub_parsers): | ||||||
|  |     ''' Add 'list' action related options ''' | ||||||
|  |     l_parser = sub_parsers.add_parser( | ||||||
|  |         'list', aliases=('l', 'ls'), help='List all available pools') | ||||||
|  |     l_parser.set_defaults(func=list_pools) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def init_info_parser(sub_parsers): | ||||||
|  |     ''' Add 'info' action related options ''' | ||||||
|  |     i_parser = sub_parsers.add_parser( | ||||||
|  |         'info', aliases=('i',), help='Print info about the specified pools') | ||||||
|  |     i_parser.add_argument(metavar='POOL_NAME', dest='pools', nargs='+', | ||||||
|  |                              action=qubesadmin.tools.PoolsAction) | ||||||
|  |     i_parser.set_defaults(func=info_pools) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def init_add_parser(sub_parsers): | ||||||
|  |     ''' Add 'add' action related options ''' | ||||||
|  |     a_parser = sub_parsers.add_parser( | ||||||
|  |         'add', aliases=('a',), help='Add a new pool') | ||||||
|  |     a_parser.add_argument(metavar='POOL_NAME', dest='pool_name') | ||||||
|  |     a_parser.add_argument(metavar='DRIVER', dest='driver') | ||||||
|  |     a_parser.add_argument('--option', '-o', action='append', | ||||||
|  |                           help="Set option for the driver in opt=value form" | ||||||
|  |                                "(can be specified multiple times) --" | ||||||
|  |                                "see `man qvm-pool` for details") | ||||||
|  |     a_parser.set_defaults(func=add_pool) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def init_remove_parser(sub_parsers): | ||||||
|  |     ''' Add 'remove' action related options ''' | ||||||
|  |     r_parser = sub_parsers.add_parser( | ||||||
|  |         'remove', aliases=('r', 'rm'), help='Remove the specified pools') | ||||||
|  |     r_parser.add_argument(metavar='POOL_NAME', dest='pool_names', nargs='+') | ||||||
|  |     r_parser.set_defaults(func=remove_pools) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def init_set_parser(sub_parsers): | ||||||
|  |     ''' Add 'set' action related options ''' | ||||||
|  |     s_parser = sub_parsers.add_parser( | ||||||
|  |         'set', aliases=('s',), help='Modify driver options for a pool') | ||||||
|  |     s_parser.add_argument(metavar='POOL_NAME', dest='pool_name') | ||||||
|  |     s_parser.add_argument('--option', '-o', action='append', | ||||||
|  |                           help="Set option for the driver in opt=value form" | ||||||
|  |                                "(can be specified multiple times) --" | ||||||
|  |                                "see `man qvm-pool` for details") | ||||||
|  |     s_parser.set_defaults(func=set_pool) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_parser(): | ||||||
|  |     '''Create :py:class:`argparse.ArgumentParser` suitable for | ||||||
|  |     :program:`qvm-pool`. | ||||||
|  |     ''' | ||||||
|  |     parser = qubesadmin.tools.QubesArgumentParser(description=__doc__, | ||||||
|  |                                                   want_app=True) | ||||||
|  |     parser.register('action', 'parsers', | ||||||
|  |                     qubesadmin.tools.AliasedSubParsersAction) | ||||||
|  | 
 | ||||||
|  |     sub_parsers = parser.add_subparsers( | ||||||
|  |         title='commands', dest='command', | ||||||
|  |         description="For more information see qvm-pool command -h") | ||||||
|  | 
 | ||||||
|  |     d_parser = sub_parsers.add_parser( | ||||||
|  |         'drivers', aliases=('d',), help='List all drivers with their options') | ||||||
|  |     d_parser.set_defaults(func=list_drivers) | ||||||
|  | 
 | ||||||
|  |     init_list_parser(sub_parsers) | ||||||
|  |     init_info_parser(sub_parsers) | ||||||
|  |     init_add_parser(sub_parsers) | ||||||
|  |     init_remove_parser(sub_parsers) | ||||||
|  |     init_set_parser(sub_parsers) | ||||||
|  | 
 | ||||||
|  |     # default action | ||||||
|  |     parser.set_defaults(func=list_pools) | ||||||
|  | 
 | ||||||
|  |     return parser | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main(args=None, app=None): | ||||||
|  |     '''Main routine of :program:`qvm-pool`.''' | ||||||
|  | 
 | ||||||
|  |     parser = get_parser() | ||||||
|  |     args = parser.parse_args(args, app=app) | ||||||
|  | 
 | ||||||
|  |     try: | ||||||
|  |         args.func(args) | ||||||
|  |     except qubesadmin.exc.QubesException as e: | ||||||
|  |         parser.error_runtime(str(e)) | ||||||
|  |         return 1 | ||||||
|  | 
 | ||||||
|     return 0 |     return 0 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Saswat Padhi
						Saswat Padhi