Browse Source

qvm-ls: add filtering by domain power state

Patrik Hagara 5 years ago
parent
commit
4cd513757b
3 changed files with 64 additions and 1 deletions
  1. 6 1
      doc/manpages/qvm-ls.rst
  2. 36 0
      qubesadmin/tests/tools/qvm_ls.py
  3. 22 0
      qubesadmin/tools/qvm_ls.py

+ 6 - 1
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

+ 36 - 0
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[

+ 22 - 0
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)