mgmt.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. # -*- encoding: utf8 -*-
  2. #
  3. # The Qubes OS Project, http://www.qubes-os.org
  4. #
  5. # Copyright (C) 2017 Marek Marczykowski-Górecki
  6. # <marmarek@invisiblethingslab.com>
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License along
  19. # with this program; if not, see <http://www.gnu.org/licenses/>.
  20. ''' Tests for management calls endpoints '''
  21. import asyncio
  22. import libvirt
  23. import unittest.mock
  24. import qubes
  25. import qubes.tests
  26. import qubes.mgmt
  27. class MgmtTestCase(qubes.tests.QubesTestCase):
  28. def setUp(self):
  29. super().setUp()
  30. app = qubes.Qubes('/tmp/qubes-test.xml', load=False)
  31. app.vmm = unittest.mock.Mock(spec=qubes.app.VMMConnection)
  32. app.load_initial_values()
  33. app.default_kernel = '1.0'
  34. app.default_netvm = None
  35. app.add_new_vm('TemplateVM', label='black', name='test-template')
  36. app.default_template = 'test-template'
  37. app.save = unittest.mock.Mock()
  38. self.vm = app.add_new_vm('AppVM', label='red', name='test-vm1',
  39. template='test-template')
  40. self.app = app
  41. libvirt_attrs = {
  42. 'libvirt_conn.lookupByUUID.return_value.isActive.return_value':
  43. False,
  44. 'libvirt_conn.lookupByUUID.return_value.state.return_value':
  45. [libvirt.VIR_DOMAIN_SHUTOFF],
  46. }
  47. app.vmm.configure_mock(**libvirt_attrs)
  48. self.emitter = qubes.tests.TestEmitter()
  49. self.app.domains[0].fire_event = self.emitter.fire_event
  50. self.app.domains[0].fire_event_pre = self.emitter.fire_event_pre
  51. def call_mgmt_func(self, method, dest, arg=b'', payload=b''):
  52. mgmt_obj = qubes.mgmt.QubesMgmt(self.app, b'dom0', method, dest, arg)
  53. loop = asyncio.get_event_loop()
  54. response = loop.run_until_complete(
  55. mgmt_obj.execute(untrusted_payload=payload))
  56. self.assertEventFired(self.emitter,
  57. 'mgmt-permission:' + method.decode('ascii'))
  58. return response
  59. class TC_00_VMs(MgmtTestCase):
  60. def test_000_vm_list(self):
  61. value = self.call_mgmt_func(b'mgmt.vm.List', b'dom0')
  62. self.assertEqual(value,
  63. 'dom0 class=AdminVM state=Running\n'
  64. 'test-template class=TemplateVM state=Halted\n'
  65. 'test-vm1 class=AppVM state=Halted\n')
  66. def test_001_vm_list_single(self):
  67. value = self.call_mgmt_func(b'mgmt.vm.List', b'test-vm1')
  68. self.assertEqual(value,
  69. 'test-vm1 class=AppVM state=Halted\n')
  70. def test_002_vm_list_unexpected_arg(self):
  71. with self.assertRaises(AssertionError):
  72. self.call_mgmt_func(b'mgmt.vm.List', b'dom0', b'test-vm1', b'')
  73. def test_003_vm_list_unexpected_payload(self):
  74. with self.assertRaises(AssertionError):
  75. self.call_mgmt_func(b'mgmt.vm.List', b'dom0', b'', b'test-vm1')
  76. def test_010_vm_property_list(self):
  77. # this test is kind of stupid, but at least check if appropriate
  78. # mgmt-permission event is fired
  79. value = self.call_mgmt_func(b'mgmt.vm.property.List', b'test-vm1')
  80. properties = self.app.domains['test-vm1'].property_list()
  81. self.assertEqual(value,
  82. ''.join('{}\n'.format(prop.__name__) for prop in properties))
  83. def test_020_vm_property_get_str(self):
  84. value = self.call_mgmt_func(b'mgmt.vm.property.Get', b'test-vm1',
  85. b'name')
  86. self.assertEqual(value, 'default=False type=str test-vm1')
  87. def test_021_vm_property_get_int(self):
  88. value = self.call_mgmt_func(b'mgmt.vm.property.Get', b'test-vm1',
  89. b'vcpus')
  90. self.assertEqual(value, 'default=True type=int 42')
  91. def test_022_vm_property_get_bool(self):
  92. value = self.call_mgmt_func(b'mgmt.vm.property.Get', b'test-vm1',
  93. b'provides_network')
  94. self.assertEqual(value, 'default=True type=bool False')
  95. def test_023_vm_property_get_label(self):
  96. value = self.call_mgmt_func(b'mgmt.vm.property.Get', b'test-vm1',
  97. b'label')
  98. self.assertEqual(value, 'default=False type=label red')
  99. def test_024_vm_property_get_vm(self):
  100. value = self.call_mgmt_func(b'mgmt.vm.property.Get', b'test-vm1',
  101. b'template')
  102. self.assertEqual(value, 'default=False type=vm test-template')
  103. def test_025_vm_property_get_vm_none(self):
  104. value = self.call_mgmt_func(b'mgmt.vm.property.Get', b'test-vm1',
  105. b'netvm')
  106. self.assertEqual(value, 'default=True type=vm ')
  107. def test_030_vm_property_set_vm(self):
  108. netvm = self.app.add_new_vm('AppVM', label='red', name='test-net',
  109. template='test-template', provides_network=True)
  110. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  111. value = self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  112. b'netvm', b'test-net')
  113. self.assertIsNone(value)
  114. mock.assert_called_once_with(self.vm, netvm)
  115. self.app.save.assert_called_once_with()
  116. def test_031_vm_property_set_vm_invalid1(self):
  117. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  118. with self.assertRaises(qubes.exc.QubesValueError):
  119. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  120. b'netvm', b'no-such-vm')
  121. self.assertFalse(mock.called)
  122. self.assertFalse(self.app.save.called)
  123. def test_032_vm_property_set_vm_invalid2(self):
  124. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  125. with self.assertRaises(qubes.exc.QubesValueError):
  126. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  127. b'netvm', b'forbidden-chars/../!')
  128. self.assertFalse(mock.called)
  129. self.assertFalse(self.app.save.called)
  130. def test_033_vm_property_set_vm_invalid3(self):
  131. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  132. with self.assertRaises(qubes.exc.QubesValueError):
  133. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  134. b'netvm', b'\x80\x90\xa0')
  135. self.assertFalse(mock.called)
  136. self.assertFalse(self.app.save.called)
  137. def test_034_vm_propert_set_bool_true(self):
  138. with unittest.mock.patch('qubes.property.__set__') as mock:
  139. value = self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  140. b'autostart', b'True')
  141. self.assertIsNone(value)
  142. mock.assert_called_once_with(self.vm, True)
  143. self.app.save.assert_called_once_with()
  144. def test_035_vm_propert_set_bool_false(self):
  145. with unittest.mock.patch('qubes.property.__set__') as mock:
  146. value = self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  147. b'autostart', b'False')
  148. self.assertIsNone(value)
  149. mock.assert_called_once_with(self.vm, False)
  150. self.app.save.assert_called_once_with()
  151. def test_036_vm_propert_set_bool_invalid1(self):
  152. with unittest.mock.patch('qubes.property.__set__') as mock:
  153. with self.assertRaises(qubes.exc.QubesValueError):
  154. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  155. b'autostart', b'some string')
  156. self.assertFalse(mock.called)
  157. self.assertFalse(self.app.save.called)
  158. def test_037_vm_propert_set_bool_invalid2(self):
  159. with unittest.mock.patch('qubes.property.__set__') as mock:
  160. with self.assertRaises(qubes.exc.QubesValueError):
  161. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  162. b'autostart', b'\x80\x90@#$%^&*(')
  163. self.assertFalse(mock.called)
  164. self.assertFalse(self.app.save.called)
  165. def test_038_vm_propert_set_str(self):
  166. with unittest.mock.patch('qubes.property.__set__') as mock:
  167. value = self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  168. b'kernel', b'1.0')
  169. self.assertIsNone(value)
  170. mock.assert_called_once_with(self.vm, '1.0')
  171. self.app.save.assert_called_once_with()
  172. def test_039_vm_propert_set_str_invalid1(self):
  173. with unittest.mock.patch('qubes.property.__set__') as mock:
  174. with self.assertRaises(qubes.exc.QubesValueError):
  175. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  176. b'kernel', b'some, non-ASCII: \x80\xd2')
  177. self.assertFalse(mock.called)
  178. self.assertFalse(self.app.save.called)
  179. def test_040_vm_propert_set_int(self):
  180. with unittest.mock.patch('qubes.property.__set__') as mock:
  181. value = self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  182. b'maxmem', b'1024000')
  183. self.assertIsNone(value)
  184. mock.assert_called_once_with(self.vm, 1024000)
  185. self.app.save.assert_called_once_with()
  186. def test_041_vm_propert_set_int_invalid1(self):
  187. with unittest.mock.patch('qubes.property.__set__') as mock:
  188. with self.assertRaises(qubes.exc.QubesValueError):
  189. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  190. b'maxmem', b'fourty two')
  191. self.assertFalse(mock.called)
  192. self.assertFalse(self.app.save.called)
  193. def test_042_vm_propert_set_label(self):
  194. with unittest.mock.patch('qubes.property.__set__') as mock:
  195. value = self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  196. b'label', b'green')
  197. self.assertIsNone(value)
  198. mock.assert_called_once_with(self.vm, 'green')
  199. self.app.save.assert_called_once_with()
  200. def test_043_vm_propert_set_label_invalid1(self):
  201. with unittest.mock.patch('qubes.property.__set__') as mock:
  202. with self.assertRaises(qubes.exc.QubesValueError):
  203. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  204. b'maxmem', b'some, non-ASCII: \x80\xd2')
  205. self.assertFalse(mock.called)
  206. self.assertFalse(self.app.save.called)
  207. @unittest.skip('label existence not checked before actual setter yet')
  208. def test_044_vm_propert_set_label_invalid2(self):
  209. with unittest.mock.patch('qubes.property.__set__') as mock:
  210. with self.assertRaises(qubes.exc.QubesValueError):
  211. self.call_mgmt_func(b'mgmt.vm.property.Set', b'test-vm1',
  212. b'maxmem', b'non-existing-color')
  213. self.assertFalse(mock.called)
  214. self.assertFalse(self.app.save.called)
  215. def test_050_vm_property_help(self):
  216. value = self.call_mgmt_func(b'mgmt.vm.property.Help', b'test-vm1',
  217. b'label')
  218. self.assertEqual(value,
  219. 'Colourful label assigned to VM. This is where the colour of the '
  220. 'padlock is set.')
  221. self.assertFalse(self.app.save.called)
  222. def test_051_vm_property_help_unexpected_payload(self):
  223. with self.assertRaises(AssertionError):
  224. self.call_mgmt_func(b'mgmt.vm.property.Help', b'test-vm1',
  225. b'label', b'asdasd')
  226. self.assertFalse(self.app.save.called)
  227. def test_052_vm_property_help_invalid_property(self):
  228. with self.assertRaises(AssertionError):
  229. self.call_mgmt_func(b'mgmt.vm.property.Help', b'test-vm1',
  230. b'no-such-property')
  231. self.assertFalse(self.app.save.called)
  232. def test_060_vm_property_reset(self):
  233. with unittest.mock.patch('qubes.property.__delete__') as mock:
  234. value = self.call_mgmt_func(b'mgmt.vm.property.Reset', b'test-vm1',
  235. b'default_user')
  236. mock.assert_called_with(self.vm)
  237. self.assertIsNone(value)
  238. self.app.save.assert_called_once_with()
  239. def test_061_vm_property_reset_unexpected_payload(self):
  240. with unittest.mock.patch('qubes.property.__delete__') as mock:
  241. with self.assertRaises(AssertionError):
  242. self.call_mgmt_func(b'mgmt.vm.property.Help', b'test-vm1',
  243. b'label', b'asdasd')
  244. self.assertFalse(mock.called)
  245. self.assertFalse(self.app.save.called)
  246. def test_062_vm_property_reset_invalid_property(self):
  247. with unittest.mock.patch('qubes.property.__delete__') as mock:
  248. with self.assertRaises(AssertionError):
  249. self.call_mgmt_func(b'mgmt.vm.property.Help', b'test-vm1',
  250. b'no-such-property')
  251. self.assertFalse(mock.called)
  252. self.assertFalse(self.app.save.called)