From bee55a3bcec1c0a90e58b46195405fec7304d315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 7 Dec 2018 21:50:55 +0100 Subject: [PATCH] tools/qvm-device: allow detaching all devices QubesOS/qubes-issues#4530 --- doc/manpages/qvm-device.rst | 5 +++-- qubesadmin/tests/tools/qvm_device.py | 13 +++++++++++++ qubesadmin/tools/qvm_device.py | 11 ++++++++--- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/doc/manpages/qvm-device.rst b/doc/manpages/qvm-device.rst index bdd949f..23f8c0d 100644 --- a/doc/manpages/qvm-device.rst +++ b/doc/manpages/qvm-device.rst @@ -8,7 +8,7 @@ Synopsis ======== | :command:`qvm-device` [*options*] *DEVICE_CLASS* {list,ls,l} <*vm-name*> | :command:`qvm-device` [*options*] *DEVICE_CLASS* {attach,at,a} <*vm-name*> <*device*> -| :command:`qvm-device` [*options*] *DEVICE_CLASS* {detach,dt,d} <*vm-name*> <*device*> +| :command:`qvm-device` [*options*] *DEVICE_CLASS* {detach,dt,d} <*vm-name*> [<*device*>] | :command:`qvm-*DEVICE_CLASS*` [*options*] {list,ls,l,attach,at,a,detach,dt,d} <*vmname*> ... Tool can be called either as `qvm-device *DEVICE_CLASS* ...`, or @@ -79,7 +79,8 @@ detach | :command:`qvm-device` *DEVICE_CLASS* detach [-h] [--verbose] [--quiet] *VMNAME* *BACKEND_DOMAIN:DEVICE_ID* -Detach the device with *BACKEND_DOMAIN:DEVICE_ID* from domain *VMNAME* +Detach the device with *BACKEND_DOMAIN:DEVICE_ID* from domain *VMNAME*. +If no device is given, detach all *DEVICE_CLASS* devices. aliases: d, dt diff --git a/qubesadmin/tests/tools/qvm_device.py b/qubesadmin/tests/tools/qvm_device.py index d0cdace..0bf4f77 100644 --- a/qubesadmin/tests/tools/qvm_device.py +++ b/qubesadmin/tests/tools/qvm_device.py @@ -199,3 +199,16 @@ class TC_00_qvm_device(qubesadmin.tests.QubesTestCase): ['test', 'detach', 'test-vm2', 'test-vm1:dev7'], app=self.app) self.assertAllCalled() + def test_022_detach_all(self): + ''' Test detach action ''' + self.app.expected_calls[('test-vm2', 'admin.vm.device.test.List', + None, None)] = \ + b'0\0test-vm1+dev1\ntest-vm1+dev2\n' + self.app.expected_calls[('test-vm2', 'admin.vm.device.test.Detach', + 'test-vm1+dev1', None)] = b'0\0' + self.app.expected_calls[('test-vm2', 'admin.vm.device.test.Detach', + 'test-vm1+dev2', None)] = b'0\0' + qubesadmin.tools.qvm_device.main( + ['test', 'detach', 'test-vm2'], app=self.app) + self.assertAllCalled() + diff --git a/qubesadmin/tools/qvm_device.py b/qubesadmin/tools/qvm_device.py index 7172b9d..d399514 100644 --- a/qubesadmin/tools/qvm_device.py +++ b/qubesadmin/tools/qvm_device.py @@ -130,9 +130,12 @@ def detach_device(args): ''' Called by the parser to execute the :program:`qvm-devices detach` subcommand. ''' - device_assignment = args.device_assignment vm = args.domains[0] - vm.devices[args.devclass].detach(device_assignment) + if args.device_assignment: + vm.devices[args.devclass].detach(args.device_assignment) + else: + for device_assignment in vm.devices[args.devclass].assignments(): + vm.devices[args.devclass].detach(device_assignment) def init_list_parser(sub_parsers): @@ -169,6 +172,8 @@ class DeviceAction(qubesadmin.tools.QubesAction): app = namespace.app backend_device_id = getattr(namespace, self.dest) devclass = namespace.devclass + if backend_device_id is None: + return try: vmname, device_id = backend_device_id.split(':', 1) @@ -232,7 +237,7 @@ def get_parser(device_class=None): dest='device_assignment', action=DeviceAction) detach_parser.add_argument(metavar='BACKEND:DEVICE_ID', - dest='device_assignment', + dest='device_assignment', nargs=argparse.OPTIONAL, action=DeviceAction, allow_unknown=True) attach_parser.add_argument('--option', '-o', action='append',