Bladeren bron

Merge branch 'improved-tools-messages'

* improved-tools-messages:
  tools: suppress full traceback in console tools
  tools: add SubParsersHelpAction, which include subcommands details in --help
Marek Marczykowski-Górecki 6 jaren geleden
bovenliggende
commit
a99acc68da

+ 35 - 1
qubesadmin/tools/__init__.py

@@ -339,7 +339,7 @@ class QubesArgumentParser(argparse.ArgumentParser):
     def __init__(self, want_app=True, want_app_no_instance=False,
                  vmname_nargs=None, **kwargs):
 
-        super(QubesArgumentParser, self).__init__(**kwargs)
+        super(QubesArgumentParser, self).__init__(add_help=False, **kwargs)
 
         self._want_app = want_app
         self._want_app_no_instance = want_app_no_instance
@@ -360,6 +360,9 @@ class QubesArgumentParser(argparse.ArgumentParser):
         self.add_argument('--force-root', action='store_true',
                           default=False, help=argparse.SUPPRESS)
 
+        self.add_argument('--help', '-h', action=SubParsersHelpAction,
+                          help='show this help message and exit')
+
         if self._vmname_nargs in [argparse.ZERO_OR_MORE, argparse.ONE_OR_MORE]:
             vm_name_group = VmNameGroup(self,
                 required=(self._vmname_nargs
@@ -436,6 +439,37 @@ class QubesArgumentParser(argparse.ArgumentParser):
         print(*args, file=sys.stderr, **kwargs)
 
 
+class SubParsersHelpAction(argparse._HelpAction):
+    ''' Print help for all options _and all subparsers_ '''
+    # source https://stackoverflow.com/a/24122778
+    # pylint: disable=protected-access,too-few-public-methods
+
+    @staticmethod
+    def _indent(indent, text):
+        '''Indent *text* by *indent* spaces'''
+        return '\n'.join((' ' * indent) + l for l in text.splitlines())
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        parser.print_help()
+
+        # retrieve subparsers from parser
+        subparsers_actions = [
+            action for action in parser._actions
+            if isinstance(action, argparse._SubParsersAction)]
+        # there will probably only be one subparser_action,
+        # but better save than sorry
+        for subparsers_action in subparsers_actions:
+            # get all subparsers and print help
+            for pseudo_action in subparsers_action._choices_actions:
+                choice = pseudo_action.dest.split(' ', 1)[0]
+                subparser = subparsers_action.choices[choice]
+                print("\nCommand '{}':".format(choice))
+                choice_help = subparser.format_usage()
+                choice_help = self._indent(2, choice_help)
+                print(choice_help)
+        parser.exit()
+
+
 class AliasedSubParsersAction(argparse._SubParsersAction):
     '''SubParser with support for action aliases'''
     # source https://gist.github.com/sampsyo/471779

+ 7 - 3
qubesadmin/tools/qvm_backup.py

@@ -157,9 +157,13 @@ def main(args=None, app=None):
     else:
         profile_name = args.profile
 
-    backup_summary = args.app.qubesd_call(
-        'dom0', 'admin.backup.Info', profile_name)
-    print(backup_summary.decode())
+    try:
+        backup_summary = args.app.qubesd_call(
+            'dom0', 'admin.backup.Info', profile_name)
+        print(backup_summary.decode())
+    except QubesException as err:
+        print('\nBackup preparation error: {}'.format(err), file=sys.stderr)
+        return 1
 
     if not args.yes:
         if input("Do you want to proceed? [y/N] ").upper() != "Y":

+ 5 - 1
qubesadmin/tools/qvm_clone.py

@@ -24,6 +24,7 @@
 
 import sys
 
+import qubesadmin.exc
 from qubesadmin.tools import QubesArgumentParser
 
 parser = QubesArgumentParser(description=__doc__, vmname_nargs=1)
@@ -70,7 +71,10 @@ def main(args=None, app=None):
                 parser.error(
                     'Pool argument must be of form: -P volume_name=pool_name')
 
-    app.clone_vm(src_vm, new_name, new_cls=args.cls, pool=pool, pools=pools)
+    try:
+        app.clone_vm(src_vm, new_name, new_cls=args.cls, pool=pool, pools=pools)
+    except qubesadmin.exc.QubesException as e:
+        parser.error_runtime(e)
 
 if __name__ == '__main__':
     sys.exit(main())

+ 13 - 3
qubesadmin/tools/qvm_features.py

@@ -63,8 +63,11 @@ def main(args=None, app=None):
         if args.delete:
             parser.error('--unset requires a feature')
 
-        features = [(feat, vm.features[feat]) for feat in vm.features]
-        qubesadmin.tools.print_table(features)
+        try:
+            features = [(feat, vm.features[feat]) for feat in vm.features]
+            qubesadmin.tools.print_table(features)
+        except qubesadmin.exc.QubesException as e:
+            parser.error_runtime(e)
 
     elif args.delete:
         if args.value is not None:
@@ -73,6 +76,8 @@ def main(args=None, app=None):
             del vm.features[args.feature]
         except KeyError:
             pass
+        except qubesadmin.exc.QubesException as e:
+            parser.error_runtime(e)
 
     elif args.value is None:
         try:
@@ -80,8 +85,13 @@ def main(args=None, app=None):
             return 0
         except KeyError:
             return 1
+        except qubesadmin.exc.QubesException as e:
+            parser.error_runtime(e)
     else:
-        vm.features[args.feature] = args.value
+        try:
+            vm.features[args.feature] = args.value
+        except qubesadmin.exc.QubesException as e:
+            parser.error_runtime(e)
 
     return 0
 

+ 2 - 0
qubesadmin/tools/qvm_pool.py

@@ -168,6 +168,8 @@ def main(args=None, app=None):
             args.app.remove_pool(args.name)
         except KeyError:
             parser.print_error('no such pool %s\n' % args.name)
+        except qubesadmin.exc.QubesException as e:
+            parser.error('failed to remove pool %s: %s\n' % (args.name, str(e)))
     elif args.command == 'info':
         for pool in args.pools:
             pool_info(pool)

+ 6 - 0
qubesadmin/tools/qvm_prefs.py

@@ -116,6 +116,8 @@ def process_actions(parser, args, target):
             setattr(target, args.property, args.value)
         except AttributeError:
             parser.error('no such property: {!r}'.format(args.property))
+        except qubesadmin.exc.QubesException as e:
+            parser.error_runtime(e)
         return 0
 
     if args.delete:
@@ -123,6 +125,8 @@ def process_actions(parser, args, target):
             delattr(target, args.property)
         except AttributeError:
             parser.error('no such property: {!r}'.format(args.property))
+        except qubesadmin.exc.QubesException as e:
+            parser.error_runtime(e)
         return 0
 
     try:
@@ -131,6 +135,8 @@ def process_actions(parser, args, target):
             print(str(value))
     except AttributeError:
         parser.error('no such property: {!r}'.format(args.property))
+    except qubesadmin.exc.QubesException as e:
+        parser.error_runtime(e)
 
     return 0
 

+ 5 - 1
qubesadmin/tools/qvm_remove.py

@@ -23,6 +23,7 @@
 
 import sys
 
+import qubesadmin.exc
 from qubesadmin.tools import QubesArgumentParser
 
 parser = QubesArgumentParser(description=__doc__,
@@ -44,7 +45,10 @@ def main(args=None, app=None):  # pylint: disable=missing-docstring
 
     if args.no_confirm or go_ahead == "Y":
         for vm in args.domains:
-            del args.app.domains[vm.name]
+            try:
+                del args.app.domains[vm.name]
+            except qubesadmin.exc.QubesException as e:
+                parser.error_runtime(e)
         retcode = 0
     else:
         print("Remove cancelled.")

+ 4 - 0
qubesadmin/tools/qvm_shutdown.py

@@ -92,6 +92,8 @@ def main(args=None, app=None):  # pylint: disable=missing-docstring
                     except qubesadmin.exc.QubesVMNotStartedError:
                         # already shut down
                         pass
+                    except qubesadmin.exc.QubesException as e:
+                        parser.error_runtime(e)
         else:
             timeout = args.timeout
             current_vms = list(sorted(this_round_domains))
@@ -114,6 +116,8 @@ def main(args=None, app=None):  # pylint: disable=missing-docstring
                 except qubesadmin.exc.QubesVMNotStartedError:
                     # already shut down
                     pass
+                except qubesadmin.exc.QubesException as e:
+                    parser.error_runtime(e)
 
     if args.wait:
         if have_events:

+ 5 - 1
qubesadmin/tools/qvm_tags.py

@@ -26,6 +26,7 @@ from __future__ import print_function
 import sys
 
 import qubesadmin
+import qubesadmin.exc
 import qubesadmin.tools
 
 def mode_query(args):
@@ -101,7 +102,10 @@ def main(args=None, app=None):
 
     parser = get_parser()
     args = parser.parse_args(args, app=app)
-    return args.func(args)
+    try:
+        return args.func(args)
+    except qubesadmin.exc.QubesException as e:
+        parser.error_runtime(e)
 
 
 if __name__ == '__main__':