diff --git a/Makefile b/Makefile index cbaa5c92..29e3d8ce 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,7 @@ ADMIN_API_METHODS_SIMPLE = \ admin.vm.device.mic.Detach \ admin.vm.device.mic.List \ admin.vm.device.mic.Set.persistent \ + admin.vm.feature.CheckWithNetvm \ admin.vm.feature.CheckWithTemplate \ admin.vm.feature.Get \ admin.vm.feature.List \ diff --git a/qubes/api/admin.py b/qubes/api/admin.py index 6e45cdbe..d5a0060a 100644 --- a/qubes/api/admin.py +++ b/qubes/api/admin.py @@ -883,6 +883,19 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI): raise qubes.exc.QubesFeatureNotFoundError(self.dest, self.arg) return value + @qubes.api.method('admin.vm.feature.CheckWithNetvm', no_payload=True, + scope='local', read=True) + @asyncio.coroutine + def vm_feature_checkwithnetvm(self): + # validation of self.arg done by qrexec-policy is enough + + self.fire_event_for_permission() + try: + value = self.dest.features.check_with_netvm(self.arg) + except KeyError: + raise qubes.exc.QubesFeatureNotFoundError(self.dest, self.arg) + return value + @qubes.api.method('admin.vm.feature.Remove', no_payload=True, scope='local', write=True) @asyncio.coroutine diff --git a/qubes/tests/api_admin.py b/qubes/tests/api_admin.py index 0bee0dff..501e4154 100644 --- a/qubes/tests/api_admin.py +++ b/qubes/tests/api_admin.py @@ -90,6 +90,10 @@ class AdminAPITestCase(qubes.tests.QubesTestCase): self.base_dir_patch.stop() if os.path.exists(self.test_base_dir): shutil.rmtree(self.test_base_dir) + try: + del self.netvm + except AttributeError: + pass del self.vm del self.template self.app.close() @@ -1047,6 +1051,31 @@ class TC_00_VMs(AdminAPITestCase): b'test-vm1', b'test-feature') self.assertFalse(self.app.save.called) + def test_315_feature_checkwithnetvm(self): + self.vm.features['test-feature'] = 'some-value' + value = self.call_mgmt_func(b'admin.vm.feature.CheckWithNetvm', + b'test-vm1', b'test-feature') + self.assertEqual(value, 'some-value') + self.assertFalse(self.app.save.called) + + def test_316_feature_checkwithnetvm_netvm(self): + self.netvm = self.app.add_new_vm('AppVM', label='red', + name='test-netvm1', + template='test-template', + provides_network=True) + self.vm.netvm = self.netvm + self.netvm.features['test-feature'] = 'some-value' + value = self.call_mgmt_func(b'admin.vm.feature.CheckWithNetvm', + b'test-vm1', b'test-feature') + self.assertEqual(value, 'some-value') + self.assertFalse(self.app.save.called) + + def test_317_feature_checkwithnetvm_none(self): + with self.assertRaises(qubes.exc.QubesFeatureNotFoundError): + self.call_mgmt_func(b'admin.vm.feature.CheckWithNetvm', + b'test-vm1', b'test-feature') + self.assertFalse(self.app.save.called) + def test_320_feature_set(self): value = self.call_mgmt_func(b'admin.vm.feature.Set', b'test-vm1', b'test-feature', b'some-value') diff --git a/qubes/vm/__init__.py b/qubes/vm/__init__.py index 70d77c35..0a16392b 100644 --- a/qubes/vm/__init__.py +++ b/qubes/vm/__init__.py @@ -186,6 +186,20 @@ class Features(dict): return default + def check_with_netvm(self, feature, default=_NO_DEFAULT): + ''' Check if the vm's netvm has the specified feature. ''' + if feature in self: + return self[feature] + + if hasattr(self.vm, 'netvm') and self.vm.netvm is not None: + return self.vm.netvm.features.check_with_netvm(feature, + default) + + if default is self._NO_DEFAULT: + raise KeyError(feature) + + return default + class Tags(set): '''Manager of the tags.