diff --git a/doc/manpages/qvm-clone.rst b/doc/manpages/qvm-clone.rst index f77e4ce..eae0218 100644 --- a/doc/manpages/qvm-clone.rst +++ b/doc/manpages/qvm-clone.rst @@ -38,6 +38,11 @@ Options Specify the pool to use for the specific volume +.. option:: --ignore-errors + + Log errors encountered when creating metadata, but continue with clone + operation. Useful if qvm-appmenus call fails from an AdminVM during clone. + .. option:: --quiet, -q Be quiet diff --git a/qubesadmin/tests/tools/qvm_clone.py b/qubesadmin/tests/tools/qvm_clone.py index f8ca372..7f31543 100644 --- a/qubesadmin/tests/tools/qvm_clone.py +++ b/qubesadmin/tests/tools/qvm_clone.py @@ -31,7 +31,7 @@ class TC_00_qvm_clone(qubesadmin.tests.QubesTestCase): b'test-vm class=AppVM state=Halted\n' qubesadmin.tools.qvm_clone.main(['test-vm', 'new-vm'], app=self.app) self.app.clone_vm.assert_called_with(self.app.domains['test-vm'], - 'new-vm', new_cls=None, pool=None, pools={}) + 'new-vm', new_cls=None, pool=None, pools={}, ignore_errors=False) self.assertAllCalled() def test_001_missing_vm(self): @@ -43,6 +43,19 @@ class TC_00_qvm_clone(qubesadmin.tests.QubesTestCase): self.assertFalse(self.app.clone_vm.called) self.assertAllCalled() + def test_002_ignore_errors(self): + self.app.clone_vm = mock.Mock() + self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \ + b'0\x00new-vm class=AppVM state=Halted\n' \ + b'test-vm class=AppVM state=Halted\n' + + test_args = ['test-vm', 'new-vm', '--ignore-errors'] + qubesadmin.tools.qvm_clone.main(test_args, app=self.app) + self.app.clone_vm.assert_called_with(self.app.domains['test-vm'], + 'new-vm', new_cls=None, pool=None, pools={}, + ignore_errors=True) + self.assertAllCalled() + def test_004_pool(self): self.app.clone_vm = mock.Mock() self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \ @@ -51,7 +64,7 @@ class TC_00_qvm_clone(qubesadmin.tests.QubesTestCase): qubesadmin.tools.qvm_clone.main(['-P', 'some-pool', 'test-vm', 'new-vm'], app=self.app) self.app.clone_vm.assert_called_with(self.app.domains['test-vm'], - 'new-vm', new_cls=None, pool='some-pool', pools={}) + 'new-vm', new_cls=None, pool='some-pool', pools={}, ignore_errors=False) self.assertAllCalled() def test_005_pools(self): @@ -64,7 +77,7 @@ class TC_00_qvm_clone(qubesadmin.tests.QubesTestCase): app=self.app) self.app.clone_vm.assert_called_with(self.app.domains['test-vm'], 'new-vm', new_cls=None, pool=None, pools={'private': 'some-pool', - 'volatile': 'other-pool'}) + 'volatile': 'other-pool'}, ignore_errors=False) self.assertAllCalled() def test_006_new_cls(self): @@ -76,5 +89,6 @@ class TC_00_qvm_clone(qubesadmin.tests.QubesTestCase): 'test-vm', 'new-vm'], app=self.app) self.app.clone_vm.assert_called_with(self.app.domains['test-vm'], - 'new-vm', new_cls='StandaloneVM', pool=None, pools={}) + 'new-vm', new_cls='StandaloneVM', pool=None, pools={}, + ignore_errors=False) self.assertAllCalled() diff --git a/qubesadmin/tools/qvm_clone.py b/qubesadmin/tools/qvm_clone.py index 0ae78be..e6236d0 100644 --- a/qubesadmin/tools/qvm_clone.py +++ b/qubesadmin/tools/qvm_clone.py @@ -39,7 +39,8 @@ parser.add_argument('--class', '-C', dest='cls', parser.add_argument('--ignore-errors', action='store_true', default=False, - help='log errors encountered during setting metadata, but continue clone operation') + help='log errors encountered during setting metadata' + 'but continue clone operation') group = parser.add_mutually_exclusive_group() group.add_argument('-P', @@ -76,7 +77,8 @@ def main(args=None, app=None): 'Pool argument must be of form: -P volume_name=pool_name') try: - app.clone_vm(src_vm, new_name, new_cls=args.cls, pool=pool, pools=pools, ignore_errors=args.ignore_errors) + app.clone_vm(src_vm, new_name, new_cls=args.cls, pool=pool, pools=pools, + ignore_errors=args.ignore_errors) except qubesadmin.exc.QubesException as e: parser.error_runtime(e)