From 4cd513757b1c4b18b2251b945711474f88282ac8 Mon Sep 17 00:00:00 2001 From: Patrik Hagara Date: Wed, 10 Oct 2018 19:58:32 +0200 Subject: [PATCH] qvm-ls: add filtering by domain power state --- doc/manpages/qvm-ls.rst | 7 ++++++- qubesadmin/tests/tools/qvm_ls.py | 36 ++++++++++++++++++++++++++++++++ qubesadmin/tools/qvm_ls.py | 22 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/doc/manpages/qvm-ls.rst b/doc/manpages/qvm-ls.rst index 5ca0650..2decf37 100644 --- a/doc/manpages/qvm-ls.rst +++ b/doc/manpages/qvm-ls.rst @@ -6,7 +6,7 @@ Synopsis -------- -:command:`qvm-ls` [-h] [--verbose] [--quiet] [--help-columns] [--help-formats] [--format *FORMAT* | --fields *FIELD*,...] [--tags *TAG* [*TAG* ...]] +:command:`qvm-ls` [-h] [--verbose] [--quiet] [--help-columns] [--help-formats] [--format *FORMAT* | --fields *FIELD*,...] [--tags *TAG* [*TAG* ...]] [--running] [--paused] [--halted] Options ------- @@ -47,6 +47,11 @@ Options Shows only VMs having specific tag(s). +.. option:: --running, --paused, --halted + + Shows only VMs matching the specified power state(s). When none of these + options is used (default), all VMs are shown. + .. option:: --raw-data Output data in easy to parse format. Table header is skipped and columns are diff --git a/qubesadmin/tests/tools/qvm_ls.py b/qubesadmin/tests/tools/qvm_ls.py index 7bc3c53..a229e3e 100644 --- a/qubesadmin/tests/tools/qvm_ls.py +++ b/qubesadmin/tests/tools/qvm_ls.py @@ -175,6 +175,42 @@ class TC_70_Tags(qubesadmin.tests.QubesTestCase): 'NAME STATE CLASS LABEL TEMPLATE NETVM\n') +class TC_80_Power_state_filters(qubesadmin.tests.QubesTestCase): + def setUp(self): + self.app = TestApp() + self.app.domains = TestVMCollection( + [ + ('a', TestVM('a', power_state='Halted')), + ('b', TestVM('b', power_state='Transient')), + ('c', TestVM('c', power_state='Running')) + ] + ) + + def test_100_nofilter(self): + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + qubesadmin.tools.qvm_ls.main([], app=self.app) + self.assertEqual(stdout.getvalue(), + 'NAME STATE CLASS LABEL TEMPLATE NETVM\n' + 'a Halted TestVM - - -\n' + 'b Transient TestVM - - -\n' + 'c Running TestVM - - -\n') + + def test_100_running(self): + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + qubesadmin.tools.qvm_ls.main(['--running'], app=self.app) + self.assertEqual(stdout.getvalue(), + 'NAME STATE CLASS LABEL TEMPLATE NETVM\n' + 'c Running TestVM - - -\n') + + def test_100_running_or_halted(self): + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + qubesadmin.tools.qvm_ls.main(['--running', '--halted'], app=self.app) + self.assertEqual(stdout.getvalue(), + 'NAME STATE CLASS LABEL TEMPLATE NETVM\n' + 'a Halted TestVM - - -\n' + 'c Running TestVM - - -\n') + + class TC_90_List_with_qubesd_calls(qubesadmin.tests.QubesTestCase): def test_100_list_with_status(self): self.app.expected_calls[ diff --git a/qubesadmin/tools/qvm_ls.py b/qubesadmin/tools/qvm_ls.py index 5b52657..f739fa7 100644 --- a/qubesadmin/tools/qvm_ls.py +++ b/qubesadmin/tools/qvm_ls.py @@ -494,6 +494,21 @@ class _HelpFormatsAction(argparse.Action): parser.exit(message=text) +# common VM power states for easy command-line filtering +DOMAIN_POWER_STATES = ['running', 'paused', 'halted'] + + +def matches_power_states(domain, **states): + '''Filter domains by their power state''' + # if all values are False (default) => return match on every VM + if not states or set(states.values()) == {False}: + return True + + # otherwise => only VMs matching True states + requested_states = [state for state, active in states.items() if active] + return domain.get_power_state().lower() in requested_states + + def get_parser(): '''Create :py:class:`argparse.ArgumentParser` suitable for :program:`qvm-ls`. @@ -531,6 +546,10 @@ def get_parser(): parser.add_argument('--tags', nargs='+', metavar='TAG', help='show only VMs having specific tag(s)') + for pwrstate in DOMAIN_POWER_STATES: + parser.add_argument('--{}'.format(pwrstate), action='store_true', + help='show {} VMs'.format(pwrstate)) + parser.add_argument('--raw-data', action='store_true', help='Display specify data of specified VMs. Intended for ' 'bash-parsing.') @@ -614,6 +633,9 @@ def main(args=None, app=None): domains = [dom for dom in domains if set(dom.tags).intersection(set(args.tags))] + pwrstates = {state: getattr(args, state) for state in DOMAIN_POWER_STATES} + domains = filter(lambda x: matches_power_states(x, **pwrstates), domains) + table = Table(domains, columns, spinner, args.raw_data) table.write_table(sys.stdout)