Browse Source

Merge remote-tracking branch 'origin/pr/381'

* origin/pr/381:
  api/admin: add 'wait' parameter to admin.vm.Shutdown
Marek Marczykowski-Górecki 3 years ago
parent
commit
a1931057b6
2 changed files with 55 additions and 5 deletions
  1. 9 3
      qubes/api/admin.py
  2. 46 2
      qubes/tests/api_admin.py

+ 9 - 3
qubes/api/admin.py

@@ -899,9 +899,15 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
         scope='local', execute=True)
     @asyncio.coroutine
     def vm_shutdown(self):
-        force = (self.arg == 'force')
-        self.fire_event_for_permission(force=force)
-        yield from self.dest.shutdown(force=force)
+        if self.arg:
+            args = self.arg.split('+')
+        else:
+            args = []
+        self.enforce(all(arg in ('force', 'wait') for arg in args))
+        force = ('force' in args)
+        wait = ('wait' in args)
+        self.fire_event_for_permission(force=force, wait=wait)
+        yield from self.dest.shutdown(force=force, wait=wait)
 
     @qubes.api.method('admin.vm.Pause', no_payload=True,
         scope='local', execute=True)

+ 46 - 2
qubes/tests/api_admin.py

@@ -1030,7 +1030,7 @@ netvm default=True type=vm \n'''
         self.vm.shutdown = coroutine_mock
         value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1')
         self.assertIsNone(value)
-        func_mock.assert_called_once_with(force=False)
+        func_mock.assert_called_once_with(force=False, wait=False)
 
     def test_231_shutdown_force(self):
         func_mock = unittest.mock.Mock()
@@ -1041,7 +1041,51 @@ netvm default=True type=vm \n'''
         self.vm.shutdown = coroutine_mock
         value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'force')
         self.assertIsNone(value)
-        func_mock.assert_called_once_with(force=True)
+        func_mock.assert_called_once_with(force=True, wait=False)
+
+    def test_232_shutdown_wait(self):
+        func_mock = unittest.mock.Mock()
+
+        @asyncio.coroutine
+        def coroutine_mock(*args, **kwargs):
+            return func_mock(*args, **kwargs)
+        self.vm.shutdown = coroutine_mock
+        value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'wait')
+        self.assertIsNone(value)
+        func_mock.assert_called_once_with(force=False, wait=True)
+
+    def test_233_shutdown_wait_force(self):
+        func_mock = unittest.mock.Mock()
+
+        @asyncio.coroutine
+        def coroutine_mock(*args, **kwargs):
+            return func_mock(*args, **kwargs)
+        self.vm.shutdown = coroutine_mock
+        value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'wait+force')
+        self.assertIsNone(value)
+        func_mock.assert_called_once_with(force=True, wait=True)
+
+    def test_234_shutdown_force_wait(self):
+        func_mock = unittest.mock.Mock()
+
+        @asyncio.coroutine
+        def coroutine_mock(*args, **kwargs):
+            return func_mock(*args, **kwargs)
+        self.vm.shutdown = coroutine_mock
+        value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'force+wait')
+        self.assertIsNone(value)
+        func_mock.assert_called_once_with(force=True, wait=True)
+
+    def test_234_shutdown_force_wait_invalid(self):
+        func_mock = unittest.mock.Mock()
+
+        @asyncio.coroutine
+        def coroutine_mock(*args, **kwargs):
+            return func_mock(*args, **kwargs)
+        self.vm.shutdown = coroutine_mock
+        with self.assertRaises(qubes.api.PermissionDenied):
+            self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'forcewait')
+        func_mock.assert_not_called()
 
     def test_240_pause(self):
         func_mock = unittest.mock.Mock()