From ad2a6e34086a717fa5aec14207a2f59012b4758b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marta=20Marczykowska-G=C3=B3recka?= Date: Fri, 20 Jul 2018 00:21:47 +0200 Subject: [PATCH 1/2] Better information on error in qvm-remove If qvm-remove fails because the VM is in use, it will display information about where it is used. fixes QubesOS/qubes-issues#3193 --- qubesadmin/exc.py | 4 ++++ qubesadmin/tools/qvm_remove.py | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/qubesadmin/exc.py b/qubesadmin/exc.py index 169caa7..9fd024e 100644 --- a/qubesadmin/exc.py +++ b/qubesadmin/exc.py @@ -85,6 +85,10 @@ class QubesNoTemplateError(QubesVMError): '''Cannot start domain, because there is no template''' +class QubesVMInUseError(QubesVMError): + '''VM is in use, cannot remove.''' + + class QubesValueError(QubesException, ValueError): '''Cannot set some value, because it is invalid, out of bounds, etc.''' pass diff --git a/qubesadmin/tools/qvm_remove.py b/qubesadmin/tools/qvm_remove.py index ee08c55..b60f66c 100644 --- a/qubesadmin/tools/qvm_remove.py +++ b/qubesadmin/tools/qvm_remove.py @@ -25,6 +25,7 @@ import sys import qubesadmin.exc from qubesadmin.tools import QubesArgumentParser +import qubesadmin.utils parser = QubesArgumentParser(description=__doc__, want_app=True, @@ -47,6 +48,15 @@ def main(args=None, app=None): # pylint: disable=missing-docstring for vm in args.domains: try: del args.app.domains[vm.name] + except qubesadmin.exc.QubesVMInUseError: + dependencies = qubesadmin.utils.vm_dependencies(vm.app, vm) + print("VM {} cannot be removed. It is in use as:".format( + vm.name)) + for (holder, prop) in dependencies: + if holder: + print(" - {} for {}".format(prop, holder.name)) + else: + print(" - global property {}".format(prop)) except qubesadmin.exc.QubesException as e: parser.error_runtime(e) retcode = 0 From 47b4e8673601fa95f13a0b60de58de265a1e67ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marta=20Marczykowska-G=C3=B3recka?= Date: Fri, 20 Jul 2018 00:34:21 +0200 Subject: [PATCH 2/2] Added test for qvm-remove dependency reporting --- qubesadmin/tests/tools/qvm_remove.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/qubesadmin/tests/tools/qvm_remove.py b/qubesadmin/tests/tools/qvm_remove.py index 38995b3..3e50e95 100644 --- a/qubesadmin/tests/tools/qvm_remove.py +++ b/qubesadmin/tests/tools/qvm_remove.py @@ -21,6 +21,7 @@ import qubesadmin.tests import qubesadmin.tests.tools import qubesadmin.tools.qvm_remove +import unittest.mock class TC_00_qvm_remove(qubesadmin.tests.QubesTestCase): @@ -33,3 +34,20 @@ class TC_00_qvm_remove(qubesadmin.tests.QubesTestCase): b'0\x00\n' qubesadmin.tools.qvm_remove.main(['-f', 'some-vm'], app=self.app) self.assertAllCalled() + + @unittest.mock.patch('qubesadmin.utils.vm_dependencies') + def test_100_dependencies(self, mock_dependencies): + self.app.expected_calls[ + ('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00some-vm class=AppVM state=Running\n' + self.app.expected_calls[ + ('some-vm', 'admin.vm.Remove', None, None)] = \ + b'2\x00QubesVMInUseError\x00\x00An error occurred\x00' + + mock_dependencies.return_value = \ + [(None, 'default_template'), (self.app.domains['some-vm'], 'netvm')] + + qubesadmin.tools.qvm_remove.main(['-f', 'some-vm'], app=self.app) + + self.assertTrue(mock_dependencies.called, + "Dependencies check not called.")