api_admin.py 84 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936
  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 os
  23. import shutil
  24. import unittest.mock
  25. import libvirt
  26. import qubes
  27. import qubes.devices
  28. import qubes.api.admin
  29. import qubes.tests
  30. # properties defined in API
  31. volume_properties = [
  32. 'pool', 'vid', 'size', 'usage', 'rw', 'internal', 'source',
  33. 'save_on_stop', 'snap_on_start']
  34. class AdminAPITestCase(qubes.tests.QubesTestCase):
  35. def setUp(self):
  36. super().setUp()
  37. self.test_base_dir = '/tmp/qubes-test-dir'
  38. self.base_dir_patch = unittest.mock.patch.dict(qubes.config.system_path,
  39. {'qubes_base_dir': self.test_base_dir})
  40. self.base_dir_patch2 = unittest.mock.patch(
  41. 'qubes.config.qubes_base_dir', self.test_base_dir)
  42. self.base_dir_patch.start()
  43. self.base_dir_patch2.start()
  44. app = qubes.Qubes('/tmp/qubes-test.xml', load=False)
  45. app.vmm = unittest.mock.Mock(spec=qubes.app.VMMConnection)
  46. app.load_initial_values()
  47. app.default_kernel = '1.0'
  48. app.default_netvm = None
  49. self.template = app.add_new_vm('TemplateVM', label='black',
  50. name='test-template')
  51. app.default_template = 'test-template'
  52. with qubes.tests.substitute_entry_points('qubes.storage',
  53. 'qubes.tests.storage'):
  54. app.add_pool('test', driver='test')
  55. app.save = unittest.mock.Mock()
  56. self.vm = app.add_new_vm('AppVM', label='red', name='test-vm1',
  57. template='test-template')
  58. self.app = app
  59. libvirt_attrs = {
  60. 'libvirt_conn.lookupByUUID.return_value.isActive.return_value':
  61. False,
  62. 'libvirt_conn.lookupByUUID.return_value.state.return_value':
  63. [libvirt.VIR_DOMAIN_SHUTOFF],
  64. }
  65. app.vmm.configure_mock(**libvirt_attrs)
  66. self.emitter = qubes.tests.TestEmitter()
  67. self.app.domains[0].fire_event = self.emitter.fire_event
  68. self.app.domains[0].fire_event_pre = self.emitter.fire_event_pre
  69. def tearDown(self):
  70. self.base_dir_patch2.stop()
  71. self.base_dir_patch.stop()
  72. if os.path.exists(self.test_base_dir):
  73. shutil.rmtree(self.test_base_dir)
  74. super(AdminAPITestCase, self).tearDown()
  75. def call_mgmt_func(self, method, dest, arg=b'', payload=b''):
  76. mgmt_obj = qubes.api.admin.QubesAdminAPI(self.app, b'dom0', method, dest, arg)
  77. loop = asyncio.get_event_loop()
  78. response = loop.run_until_complete(
  79. mgmt_obj.execute(untrusted_payload=payload))
  80. self.assertEventFired(self.emitter,
  81. 'mgmt-permission:' + method.decode('ascii'))
  82. return response
  83. class TC_00_VMs(AdminAPITestCase):
  84. def test_000_vm_list(self):
  85. value = self.call_mgmt_func(b'admin.vm.List', b'dom0')
  86. self.assertEqual(value,
  87. 'dom0 class=AdminVM state=Running\n'
  88. 'test-template class=TemplateVM state=Halted\n'
  89. 'test-vm1 class=AppVM state=Halted\n')
  90. def test_001_vm_list_single(self):
  91. value = self.call_mgmt_func(b'admin.vm.List', b'test-vm1')
  92. self.assertEqual(value,
  93. 'test-vm1 class=AppVM state=Halted\n')
  94. def test_010_vm_property_list(self):
  95. # this test is kind of stupid, but at least check if appropriate
  96. # mgmt-permission event is fired
  97. value = self.call_mgmt_func(b'admin.vm.property.List', b'test-vm1')
  98. properties = self.app.domains['test-vm1'].property_list()
  99. self.assertEqual(value,
  100. ''.join('{}\n'.format(prop.__name__) for prop in properties))
  101. def test_020_vm_property_get_str(self):
  102. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  103. b'name')
  104. self.assertEqual(value, 'default=False type=str test-vm1')
  105. def test_021_vm_property_get_int(self):
  106. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  107. b'vcpus')
  108. self.assertEqual(value, 'default=True type=int 42')
  109. def test_022_vm_property_get_bool(self):
  110. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  111. b'provides_network')
  112. self.assertEqual(value, 'default=True type=bool False')
  113. def test_023_vm_property_get_label(self):
  114. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  115. b'label')
  116. self.assertEqual(value, 'default=False type=label red')
  117. def test_024_vm_property_get_vm(self):
  118. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  119. b'template')
  120. self.assertEqual(value, 'default=False type=vm test-template')
  121. def test_025_vm_property_get_vm_none(self):
  122. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  123. b'netvm')
  124. self.assertEqual(value, 'default=True type=vm ')
  125. def test_030_vm_property_set_vm(self):
  126. netvm = self.app.add_new_vm('AppVM', label='red', name='test-net',
  127. template='test-template', provides_network=True)
  128. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  129. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  130. b'netvm', b'test-net')
  131. self.assertIsNone(value)
  132. mock.assert_called_once_with(self.vm, 'test-net')
  133. self.app.save.assert_called_once_with()
  134. def test_0301_vm_property_set_vm_none(self):
  135. netvm = self.app.add_new_vm('AppVM', label='red', name='test-net',
  136. template='test-template', provides_network=True)
  137. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  138. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  139. b'netvm', b'')
  140. self.assertIsNone(value)
  141. mock.assert_called_once_with(self.vm, '')
  142. self.app.save.assert_called_once_with()
  143. def test_032_vm_property_set_vm_invalid1(self):
  144. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  145. with self.assertRaises(qubes.exc.QubesValueError):
  146. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  147. b'netvm', b'forbidden-chars/../!')
  148. self.assertFalse(mock.called)
  149. self.assertFalse(self.app.save.called)
  150. def test_033_vm_property_set_vm_invalid2(self):
  151. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  152. with self.assertRaises(qubes.exc.QubesValueError):
  153. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  154. b'netvm', b'\x80\x90\xa0')
  155. self.assertFalse(mock.called)
  156. self.assertFalse(self.app.save.called)
  157. def test_034_vm_propert_set_bool_true(self):
  158. with unittest.mock.patch('qubes.property.__set__') as mock:
  159. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  160. b'autostart', b'True')
  161. self.assertIsNone(value)
  162. mock.assert_called_once_with(self.vm, True)
  163. self.app.save.assert_called_once_with()
  164. def test_035_vm_propert_set_bool_false(self):
  165. with unittest.mock.patch('qubes.property.__set__') as mock:
  166. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  167. b'autostart', b'False')
  168. self.assertIsNone(value)
  169. mock.assert_called_once_with(self.vm, False)
  170. self.app.save.assert_called_once_with()
  171. def test_036_vm_propert_set_bool_invalid1(self):
  172. with unittest.mock.patch('qubes.property.__set__') as mock:
  173. with self.assertRaises(qubes.exc.QubesValueError):
  174. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  175. b'autostart', b'some string')
  176. self.assertFalse(mock.called)
  177. self.assertFalse(self.app.save.called)
  178. def test_037_vm_propert_set_bool_invalid2(self):
  179. with unittest.mock.patch('qubes.property.__set__') as mock:
  180. with self.assertRaises(qubes.exc.QubesValueError):
  181. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  182. b'autostart', b'\x80\x90@#$%^&*(')
  183. self.assertFalse(mock.called)
  184. self.assertFalse(self.app.save.called)
  185. def test_038_vm_propert_set_str(self):
  186. with unittest.mock.patch('qubes.property.__set__') as mock:
  187. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  188. b'kernel', b'1.0')
  189. self.assertIsNone(value)
  190. mock.assert_called_once_with(self.vm, '1.0')
  191. self.app.save.assert_called_once_with()
  192. def test_039_vm_propert_set_str_invalid1(self):
  193. with unittest.mock.patch('qubes.property.__set__') as mock:
  194. with self.assertRaises(qubes.exc.QubesValueError):
  195. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  196. b'kernel', b'some, non-ASCII: \x80\xd2')
  197. self.assertFalse(mock.called)
  198. self.assertFalse(self.app.save.called)
  199. def test_040_vm_propert_set_int(self):
  200. with unittest.mock.patch('qubes.property.__set__') as mock:
  201. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  202. b'maxmem', b'1024000')
  203. self.assertIsNone(value)
  204. mock.assert_called_once_with(self.vm, 1024000)
  205. self.app.save.assert_called_once_with()
  206. def test_041_vm_propert_set_int_invalid1(self):
  207. with unittest.mock.patch('qubes.property.__set__') as mock:
  208. with self.assertRaises(qubes.exc.QubesValueError):
  209. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  210. b'maxmem', b'fourty two')
  211. self.assertFalse(mock.called)
  212. self.assertFalse(self.app.save.called)
  213. def test_042_vm_propert_set_label(self):
  214. with unittest.mock.patch('qubes.property.__set__') as mock:
  215. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  216. b'label', b'green')
  217. self.assertIsNone(value)
  218. mock.assert_called_once_with(self.vm, 'green')
  219. self.app.save.assert_called_once_with()
  220. def test_043_vm_propert_set_label_invalid1(self):
  221. with unittest.mock.patch('qubes.property.__set__') as mock:
  222. with self.assertRaises(qubes.exc.QubesValueError):
  223. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  224. b'maxmem', b'some, non-ASCII: \x80\xd2')
  225. self.assertFalse(mock.called)
  226. self.assertFalse(self.app.save.called)
  227. @unittest.skip('label existence not checked before actual setter yet')
  228. def test_044_vm_propert_set_label_invalid2(self):
  229. with unittest.mock.patch('qubes.property.__set__') as mock:
  230. with self.assertRaises(qubes.exc.QubesValueError):
  231. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  232. b'maxmem', b'non-existing-color')
  233. self.assertFalse(mock.called)
  234. self.assertFalse(self.app.save.called)
  235. def test_050_vm_property_help(self):
  236. value = self.call_mgmt_func(b'admin.vm.property.Help', b'test-vm1',
  237. b'label')
  238. self.assertEqual(value,
  239. 'Colourful label assigned to VM. This is where the colour of the '
  240. 'padlock is set.')
  241. self.assertFalse(self.app.save.called)
  242. def test_052_vm_property_help_invalid_property(self):
  243. with self.assertRaises(qubes.exc.QubesNoSuchPropertyError):
  244. self.call_mgmt_func(b'admin.vm.property.Help', b'test-vm1',
  245. b'no-such-property')
  246. self.assertFalse(self.app.save.called)
  247. def test_060_vm_property_reset(self):
  248. with unittest.mock.patch('qubes.property.__delete__') as mock:
  249. value = self.call_mgmt_func(b'admin.vm.property.Reset', b'test-vm1',
  250. b'default_user')
  251. mock.assert_called_with(self.vm)
  252. self.assertIsNone(value)
  253. self.app.save.assert_called_once_with()
  254. def test_062_vm_property_reset_invalid_property(self):
  255. with unittest.mock.patch('qubes.property.__delete__') as mock:
  256. with self.assertRaises(qubes.exc.QubesNoSuchPropertyError):
  257. self.call_mgmt_func(b'admin.vm.property.Help', b'test-vm1',
  258. b'no-such-property')
  259. self.assertFalse(mock.called)
  260. self.assertFalse(self.app.save.called)
  261. def test_070_vm_volume_list(self):
  262. self.vm.volumes = unittest.mock.Mock()
  263. volumes_conf = {
  264. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  265. }
  266. self.vm.volumes.configure_mock(**volumes_conf)
  267. value = self.call_mgmt_func(b'admin.vm.volume.List', b'test-vm1')
  268. self.assertEqual(value, 'root\nprivate\nvolatile\nkernel\n')
  269. # check if _only_ keys were accessed
  270. self.assertEqual(self.vm.volumes.mock_calls,
  271. [unittest.mock.call.keys()])
  272. def test_080_vm_volume_info(self):
  273. self.vm.volumes = unittest.mock.MagicMock()
  274. volumes_conf = {
  275. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  276. }
  277. for prop in volume_properties:
  278. volumes_conf[
  279. '__getitem__.return_value.{}'.format(prop)] = prop +'-value'
  280. self.vm.volumes.configure_mock(**volumes_conf)
  281. value = self.call_mgmt_func(b'admin.vm.volume.Info', b'test-vm1',
  282. b'private')
  283. self.assertEqual(value,
  284. ''.join('{p}={p}-value\n'.format(p=p) for p in volume_properties))
  285. self.assertEqual(self.vm.volumes.mock_calls,
  286. [unittest.mock.call.keys(),
  287. unittest.mock.call.__getattr__('__getitem__')('private')])
  288. def test_080_vm_volume_info_invalid_volume(self):
  289. self.vm.volumes = unittest.mock.MagicMock()
  290. volumes_conf = {
  291. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  292. }
  293. self.vm.volumes.configure_mock(**volumes_conf)
  294. with self.assertRaises(AssertionError):
  295. self.call_mgmt_func(b'admin.vm.volume.Info', b'test-vm1',
  296. b'no-such-volume')
  297. self.assertEqual(self.vm.volumes.mock_calls,
  298. [unittest.mock.call.keys()])
  299. def test_090_vm_volume_listsnapshots(self):
  300. self.vm.volumes = unittest.mock.MagicMock()
  301. volumes_conf = {
  302. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  303. '__getitem__.return_value.revisions': ['rev1', 'rev2'],
  304. }
  305. self.vm.volumes.configure_mock(**volumes_conf)
  306. value = self.call_mgmt_func(b'admin.vm.volume.ListSnapshots',
  307. b'test-vm1', b'private')
  308. self.assertEqual(value,
  309. 'rev1\nrev2\n')
  310. self.assertEqual(self.vm.volumes.mock_calls,
  311. [unittest.mock.call.keys(),
  312. unittest.mock.call.__getattr__('__getitem__')('private')])
  313. def test_090_vm_volume_listsnapshots_invalid_volume(self):
  314. self.vm.volumes = unittest.mock.MagicMock()
  315. volumes_conf = {
  316. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  317. }
  318. self.vm.volumes.configure_mock(**volumes_conf)
  319. with self.assertRaises(AssertionError):
  320. self.call_mgmt_func(b'admin.vm.volume.ListSnapshots', b'test-vm1',
  321. b'no-such-volume')
  322. self.assertEqual(self.vm.volumes.mock_calls,
  323. [unittest.mock.call.keys()])
  324. @unittest.skip('method not implemented yet')
  325. def test_100_vm_volume_snapshot(self):
  326. pass
  327. @unittest.skip('method not implemented yet')
  328. def test_100_vm_volume_snapshot_invlid_volume(self):
  329. self.vm.volumes = unittest.mock.MagicMock()
  330. volumes_conf = {
  331. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  332. '__getitem__.return_value.revisions': ['rev1', 'rev2'],
  333. }
  334. self.vm.volumes.configure_mock(**volumes_conf)
  335. with self.assertRaises(AssertionError):
  336. self.call_mgmt_func(b'admin.vm.volume.Snapshots',
  337. b'test-vm1', b'no-such-volume')
  338. self.assertEqual(self.vm.volumes.mock_calls,
  339. [unittest.mock.call.keys()])
  340. @unittest.skip('method not implemented yet')
  341. def test_100_vm_volume_snapshot_invalid_revision(self):
  342. self.vm.volumes = unittest.mock.MagicMock()
  343. volumes_conf = {
  344. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  345. }
  346. self.vm.volumes.configure_mock(**volumes_conf)
  347. with self.assertRaises(AssertionError):
  348. self.call_mgmt_func(b'admin.vm.volume.Snapshots',
  349. b'test-vm1', b'private', b'no-such-rev')
  350. self.assertEqual(self.vm.volumes.mock_calls,
  351. [unittest.mock.call.keys(),
  352. unittest.mock.call.__getattr__('__getitem__')('private')])
  353. def test_110_vm_volume_revert(self):
  354. self.vm.volumes = unittest.mock.MagicMock()
  355. volumes_conf = {
  356. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  357. '__getitem__.return_value.revisions': ['rev1', 'rev2'],
  358. }
  359. self.vm.volumes.configure_mock(**volumes_conf)
  360. self.vm.storage = unittest.mock.Mock()
  361. value = self.call_mgmt_func(b'admin.vm.volume.Revert',
  362. b'test-vm1', b'private', b'rev1')
  363. self.assertIsNone(value)
  364. self.assertEqual(self.vm.volumes.mock_calls,
  365. [unittest.mock.call.keys(),
  366. unittest.mock.call.__getattr__('__getitem__')('private')])
  367. self.assertEqual(self.vm.storage.mock_calls,
  368. [unittest.mock.call.get_pool(self.vm.volumes['private']),
  369. unittest.mock.call.get_pool().revert('rev1')])
  370. def test_110_vm_volume_revert_invalid_rev(self):
  371. self.vm.volumes = unittest.mock.MagicMock()
  372. volumes_conf = {
  373. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  374. '__getitem__.return_value.revisions': ['rev1', 'rev2'],
  375. }
  376. self.vm.volumes.configure_mock(**volumes_conf)
  377. self.vm.storage = unittest.mock.Mock()
  378. with self.assertRaises(AssertionError):
  379. self.call_mgmt_func(b'admin.vm.volume.Revert',
  380. b'test-vm1', b'private', b'no-such-rev')
  381. self.assertEqual(self.vm.volumes.mock_calls,
  382. [unittest.mock.call.keys(),
  383. unittest.mock.call.__getattr__('__getitem__')('private')])
  384. self.assertFalse(self.vm.storage.called)
  385. def test_120_vm_volume_resize(self):
  386. self.vm.volumes = unittest.mock.MagicMock()
  387. volumes_conf = {
  388. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  389. }
  390. self.vm.volumes.configure_mock(**volumes_conf)
  391. self.vm.storage = unittest.mock.Mock()
  392. value = self.call_mgmt_func(b'admin.vm.volume.Resize',
  393. b'test-vm1', b'private', b'1024000000')
  394. self.assertIsNone(value)
  395. self.assertEqual(self.vm.volumes.mock_calls,
  396. [unittest.mock.call.keys()])
  397. self.assertEqual(self.vm.storage.mock_calls,
  398. [unittest.mock.call.resize('private', 1024000000)])
  399. def test_120_vm_volume_resize_invalid_size1(self):
  400. self.vm.volumes = unittest.mock.MagicMock()
  401. volumes_conf = {
  402. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  403. }
  404. self.vm.volumes.configure_mock(**volumes_conf)
  405. self.vm.storage = unittest.mock.Mock()
  406. with self.assertRaises(AssertionError):
  407. self.call_mgmt_func(b'admin.vm.volume.Resize',
  408. b'test-vm1', b'private', b'no-int-size')
  409. self.assertEqual(self.vm.volumes.mock_calls,
  410. [unittest.mock.call.keys()])
  411. self.assertFalse(self.vm.storage.called)
  412. def test_120_vm_volume_resize_invalid_size2(self):
  413. self.vm.volumes = unittest.mock.MagicMock()
  414. volumes_conf = {
  415. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  416. }
  417. self.vm.volumes.configure_mock(**volumes_conf)
  418. self.vm.storage = unittest.mock.Mock()
  419. with self.assertRaises(AssertionError):
  420. self.call_mgmt_func(b'admin.vm.volume.Resize',
  421. b'test-vm1', b'private', b'-1')
  422. self.assertEqual(self.vm.volumes.mock_calls,
  423. [unittest.mock.call.keys()])
  424. self.assertFalse(self.vm.storage.called)
  425. def test_130_pool_list(self):
  426. self.app.pools = ['file', 'lvm']
  427. value = self.call_mgmt_func(b'admin.pool.List', b'dom0')
  428. self.assertEqual(value, 'file\nlvm\n')
  429. self.assertFalse(self.app.save.called)
  430. @unittest.mock.patch('qubes.storage.pool_drivers')
  431. @unittest.mock.patch('qubes.storage.driver_parameters')
  432. def test_140_pool_listdrivers(self, mock_parameters, mock_drivers):
  433. self.app.pools = ['file', 'lvm']
  434. mock_drivers.return_value = ['driver1', 'driver2']
  435. mock_parameters.side_effect = \
  436. lambda driver: {
  437. 'driver1': ['param1', 'param2'],
  438. 'driver2': ['param3', 'param4']
  439. }[driver]
  440. value = self.call_mgmt_func(b'admin.pool.ListDrivers', b'dom0')
  441. self.assertEqual(value,
  442. 'driver1 param1 param2\ndriver2 param3 param4\n')
  443. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  444. self.assertEqual(mock_parameters.mock_calls,
  445. [unittest.mock.call('driver1'), unittest.mock.call('driver2')])
  446. self.assertFalse(self.app.save.called)
  447. def test_150_pool_info(self):
  448. self.app.pools = {
  449. 'pool1': unittest.mock.Mock(config={
  450. 'param1': 'value1', 'param2': 'value2'})
  451. }
  452. value = self.call_mgmt_func(b'admin.pool.Info', b'dom0', b'pool1')
  453. self.assertEqual(value, 'param1=value1\nparam2=value2\n')
  454. self.assertFalse(self.app.save.called)
  455. @unittest.mock.patch('qubes.storage.pool_drivers')
  456. @unittest.mock.patch('qubes.storage.driver_parameters')
  457. def test_160_pool_add(self, mock_parameters, mock_drivers):
  458. self.app.pools = {
  459. 'file': unittest.mock.Mock(),
  460. 'lvm': unittest.mock.Mock()
  461. }
  462. mock_drivers.return_value = ['driver1', 'driver2']
  463. mock_parameters.side_effect = \
  464. lambda driver: {
  465. 'driver1': ['param1', 'param2'],
  466. 'driver2': ['param3', 'param4']
  467. }[driver]
  468. self.app.add_pool = unittest.mock.Mock()
  469. value = self.call_mgmt_func(b'admin.pool.Add', b'dom0', b'driver1',
  470. b'name=test-pool\nparam1=some-value\n')
  471. self.assertIsNone(value)
  472. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  473. self.assertEqual(mock_parameters.mock_calls,
  474. [unittest.mock.call('driver1')])
  475. self.assertEqual(self.app.add_pool.mock_calls,
  476. [unittest.mock.call(name='test-pool', driver='driver1',
  477. param1='some-value')])
  478. self.assertTrue(self.app.save.called)
  479. @unittest.mock.patch('qubes.storage.pool_drivers')
  480. @unittest.mock.patch('qubes.storage.driver_parameters')
  481. def test_160_pool_add_invalid_driver(self, mock_parameters, mock_drivers):
  482. self.app.pools = {
  483. 'file': unittest.mock.Mock(),
  484. 'lvm': unittest.mock.Mock()
  485. }
  486. mock_drivers.return_value = ['driver1', 'driver2']
  487. mock_parameters.side_effect = \
  488. lambda driver: {
  489. 'driver1': ['param1', 'param2'],
  490. 'driver2': ['param3', 'param4']
  491. }[driver]
  492. self.app.add_pool = unittest.mock.Mock()
  493. with self.assertRaises(AssertionError):
  494. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  495. b'no-such-driver', b'name=test-pool\nparam1=some-value\n')
  496. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  497. self.assertEqual(mock_parameters.mock_calls, [])
  498. self.assertEqual(self.app.add_pool.mock_calls, [])
  499. self.assertFalse(self.app.save.called)
  500. @unittest.mock.patch('qubes.storage.pool_drivers')
  501. @unittest.mock.patch('qubes.storage.driver_parameters')
  502. def test_160_pool_add_invalid_param(self, mock_parameters, mock_drivers):
  503. self.app.pools = {
  504. 'file': unittest.mock.Mock(),
  505. 'lvm': unittest.mock.Mock()
  506. }
  507. mock_drivers.return_value = ['driver1', 'driver2']
  508. mock_parameters.side_effect = \
  509. lambda driver: {
  510. 'driver1': ['param1', 'param2'],
  511. 'driver2': ['param3', 'param4']
  512. }[driver]
  513. self.app.add_pool = unittest.mock.Mock()
  514. with self.assertRaises(AssertionError):
  515. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  516. b'driver1', b'name=test-pool\nparam3=some-value\n')
  517. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  518. self.assertEqual(mock_parameters.mock_calls,
  519. [unittest.mock.call('driver1')])
  520. self.assertEqual(self.app.add_pool.mock_calls, [])
  521. self.assertFalse(self.app.save.called)
  522. @unittest.mock.patch('qubes.storage.pool_drivers')
  523. @unittest.mock.patch('qubes.storage.driver_parameters')
  524. def test_160_pool_add_missing_name(self, mock_parameters, mock_drivers):
  525. self.app.pools = {
  526. 'file': unittest.mock.Mock(),
  527. 'lvm': unittest.mock.Mock()
  528. }
  529. mock_drivers.return_value = ['driver1', 'driver2']
  530. mock_parameters.side_effect = \
  531. lambda driver: {
  532. 'driver1': ['param1', 'param2'],
  533. 'driver2': ['param3', 'param4']
  534. }[driver]
  535. self.app.add_pool = unittest.mock.Mock()
  536. with self.assertRaises(AssertionError):
  537. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  538. b'driver1', b'param1=value\nparam2=some-value\n')
  539. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  540. self.assertEqual(mock_parameters.mock_calls, [])
  541. self.assertEqual(self.app.add_pool.mock_calls, [])
  542. self.assertFalse(self.app.save.called)
  543. @unittest.mock.patch('qubes.storage.pool_drivers')
  544. @unittest.mock.patch('qubes.storage.driver_parameters')
  545. def test_160_pool_add_existing_pool(self, mock_parameters, mock_drivers):
  546. self.app.pools = {
  547. 'file': unittest.mock.Mock(),
  548. 'lvm': unittest.mock.Mock()
  549. }
  550. mock_drivers.return_value = ['driver1', 'driver2']
  551. mock_parameters.side_effect = \
  552. lambda driver: {
  553. 'driver1': ['param1', 'param2'],
  554. 'driver2': ['param3', 'param4']
  555. }[driver]
  556. self.app.add_pool = unittest.mock.Mock()
  557. with self.assertRaises(AssertionError):
  558. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  559. b'driver1', b'name=file\nparam1=value\nparam2=some-value\n')
  560. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  561. self.assertEqual(mock_parameters.mock_calls, [])
  562. self.assertEqual(self.app.add_pool.mock_calls, [])
  563. self.assertFalse(self.app.save.called)
  564. @unittest.mock.patch('qubes.storage.pool_drivers')
  565. @unittest.mock.patch('qubes.storage.driver_parameters')
  566. def test_160_pool_add_invalid_config_format(self, mock_parameters,
  567. mock_drivers):
  568. self.app.pools = {
  569. 'file': unittest.mock.Mock(),
  570. 'lvm': unittest.mock.Mock()
  571. }
  572. mock_drivers.return_value = ['driver1', 'driver2']
  573. mock_parameters.side_effect = \
  574. lambda driver: {
  575. 'driver1': ['param1', 'param2'],
  576. 'driver2': ['param3', 'param4']
  577. }[driver]
  578. self.app.add_pool = unittest.mock.Mock()
  579. with self.assertRaises(AssertionError):
  580. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  581. b'driver1', b'name=test-pool\nparam 1=value\n_param2\n')
  582. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  583. self.assertEqual(mock_parameters.mock_calls, [])
  584. self.assertEqual(self.app.add_pool.mock_calls, [])
  585. self.assertFalse(self.app.save.called)
  586. def test_170_pool_remove(self):
  587. self.app.pools = {
  588. 'file': unittest.mock.Mock(),
  589. 'lvm': unittest.mock.Mock(),
  590. 'test-pool': unittest.mock.Mock(),
  591. }
  592. self.app.remove_pool = unittest.mock.Mock()
  593. value = self.call_mgmt_func(b'admin.pool.Remove', b'dom0', b'test-pool')
  594. self.assertIsNone(value)
  595. self.assertEqual(self.app.remove_pool.mock_calls,
  596. [unittest.mock.call('test-pool')])
  597. self.assertTrue(self.app.save.called)
  598. def test_170_pool_remove_invalid_pool(self):
  599. self.app.pools = {
  600. 'file': unittest.mock.Mock(),
  601. 'lvm': unittest.mock.Mock(),
  602. 'test-pool': unittest.mock.Mock(),
  603. }
  604. self.app.remove_pool = unittest.mock.Mock()
  605. with self.assertRaises(AssertionError):
  606. self.call_mgmt_func(b'admin.pool.Remove', b'dom0',
  607. b'no-such-pool')
  608. self.assertEqual(self.app.remove_pool.mock_calls, [])
  609. self.assertFalse(self.app.save.called)
  610. def test_180_label_list(self):
  611. value = self.call_mgmt_func(b'admin.label.List', b'dom0')
  612. self.assertEqual(value,
  613. ''.join('{}\n'.format(l.name) for l in self.app.labels.values()))
  614. self.assertFalse(self.app.save.called)
  615. def test_190_label_get(self):
  616. self.app.get_label = unittest.mock.Mock()
  617. self.app.get_label.configure_mock(**{'return_value.color': '0xff0000'})
  618. value = self.call_mgmt_func(b'admin.label.Get', b'dom0', b'red')
  619. self.assertEqual(value, '0xff0000')
  620. self.assertEqual(self.app.get_label.mock_calls,
  621. [unittest.mock.call('red')])
  622. self.assertFalse(self.app.save.called)
  623. def test_195_label_index(self):
  624. self.app.get_label = unittest.mock.Mock()
  625. self.app.get_label.configure_mock(**{'return_value.index': 1})
  626. value = self.call_mgmt_func(b'admin.label.Index', b'dom0', b'red')
  627. self.assertEqual(value, '1')
  628. self.assertEqual(self.app.get_label.mock_calls,
  629. [unittest.mock.call('red')])
  630. self.assertFalse(self.app.save.called)
  631. def test_200_label_create(self):
  632. self.app.get_label = unittest.mock.Mock()
  633. self.app.get_label.side_effect=KeyError
  634. self.app.labels = unittest.mock.MagicMock()
  635. labels_config = {
  636. 'keys.return_value': range(1, 9),
  637. }
  638. self.app.labels.configure_mock(**labels_config)
  639. value = self.call_mgmt_func(b'admin.label.Create', b'dom0', b'cyan',
  640. b'0x00ffff')
  641. self.assertIsNone(value)
  642. self.assertEqual(self.app.get_label.mock_calls,
  643. [unittest.mock.call('cyan')])
  644. self.assertEqual(self.app.labels.mock_calls,
  645. [unittest.mock.call.keys(),
  646. unittest.mock.call.__getattr__('__setitem__')(9,
  647. qubes.Label(9, '0x00ffff', 'cyan'))])
  648. self.assertTrue(self.app.save.called)
  649. def test_200_label_create_invalid_color(self):
  650. self.app.get_label = unittest.mock.Mock()
  651. self.app.get_label.side_effect=KeyError
  652. self.app.labels = unittest.mock.MagicMock()
  653. labels_config = {
  654. 'keys.return_value': range(1, 9),
  655. }
  656. self.app.labels.configure_mock(**labels_config)
  657. with self.assertRaises(AssertionError):
  658. self.call_mgmt_func(b'admin.label.Create', b'dom0', b'cyan',
  659. b'abcd')
  660. self.assertEqual(self.app.get_label.mock_calls,
  661. [unittest.mock.call('cyan')])
  662. self.assertEqual(self.app.labels.mock_calls, [])
  663. self.assertFalse(self.app.save.called)
  664. def test_200_label_create_invalid_name(self):
  665. self.app.get_label = unittest.mock.Mock()
  666. self.app.get_label.side_effect=KeyError
  667. self.app.labels = unittest.mock.MagicMock()
  668. labels_config = {
  669. 'keys.return_value': range(1, 9),
  670. }
  671. self.app.labels.configure_mock(**labels_config)
  672. with self.assertRaises(AssertionError):
  673. self.call_mgmt_func(b'admin.label.Create', b'dom0', b'01',
  674. b'0xff0000')
  675. with self.assertRaises(AssertionError):
  676. self.call_mgmt_func(b'admin.label.Create', b'dom0', b'../xxx',
  677. b'0xff0000')
  678. with self.assertRaises(AssertionError):
  679. self.call_mgmt_func(b'admin.label.Create', b'dom0',
  680. b'strange-name!@#$',
  681. b'0xff0000')
  682. self.assertEqual(self.app.get_label.mock_calls, [])
  683. self.assertEqual(self.app.labels.mock_calls, [])
  684. self.assertFalse(self.app.save.called)
  685. def test_200_label_create_already_exists(self):
  686. self.app.get_label = unittest.mock.Mock(wraps=self.app.get_label)
  687. with self.assertRaises(qubes.exc.QubesValueError):
  688. self.call_mgmt_func(b'admin.label.Create', b'dom0', b'red',
  689. b'abcd')
  690. self.assertEqual(self.app.get_label.mock_calls,
  691. [unittest.mock.call('red')])
  692. self.assertFalse(self.app.save.called)
  693. def test_210_label_remove(self):
  694. label = qubes.Label(9, '0x00ffff', 'cyan')
  695. self.app.labels[9] = label
  696. self.app.get_label = unittest.mock.Mock(wraps=self.app.get_label,
  697. **{'return_value.index': 9})
  698. self.app.labels = unittest.mock.MagicMock(wraps=self.app.labels)
  699. value = self.call_mgmt_func(b'admin.label.Remove', b'dom0', b'cyan')
  700. self.assertIsNone(value)
  701. self.assertEqual(self.app.get_label.mock_calls,
  702. [unittest.mock.call('cyan')])
  703. self.assertEqual(self.app.labels.mock_calls,
  704. [unittest.mock.call.__delitem__(9)])
  705. self.assertTrue(self.app.save.called)
  706. def test_210_label_remove_invalid_label(self):
  707. with self.assertRaises(qubes.exc.QubesValueError):
  708. self.call_mgmt_func(b'admin.label.Remove', b'dom0',
  709. b'no-such-label')
  710. self.assertFalse(self.app.save.called)
  711. def test_210_label_remove_default_label(self):
  712. self.app.labels = unittest.mock.MagicMock(wraps=self.app.labels)
  713. self.app.get_label = unittest.mock.Mock(wraps=self.app.get_label,
  714. **{'return_value.index': 6})
  715. with self.assertRaises(AssertionError):
  716. self.call_mgmt_func(b'admin.label.Remove', b'dom0',
  717. b'blue')
  718. self.assertEqual(self.app.labels.mock_calls, [])
  719. self.assertFalse(self.app.save.called)
  720. def test_210_label_remove_in_use(self):
  721. self.app.labels = unittest.mock.MagicMock(wraps=self.app.labels)
  722. self.app.get_label = unittest.mock.Mock(wraps=self.app.get_label,
  723. **{'return_value.index': 1})
  724. with self.assertRaises(AssertionError):
  725. self.call_mgmt_func(b'admin.label.Remove', b'dom0',
  726. b'red')
  727. self.assertEqual(self.app.labels.mock_calls, [])
  728. self.assertFalse(self.app.save.called)
  729. def test_220_start(self):
  730. func_mock = unittest.mock.Mock()
  731. @asyncio.coroutine
  732. def coroutine_mock(*args, **kwargs):
  733. return func_mock(*args, **kwargs)
  734. self.vm.start = coroutine_mock
  735. value = self.call_mgmt_func(b'admin.vm.Start', b'test-vm1')
  736. self.assertIsNone(value)
  737. func_mock.assert_called_once_with()
  738. def test_230_shutdown(self):
  739. func_mock = unittest.mock.Mock()
  740. @asyncio.coroutine
  741. def coroutine_mock(*args, **kwargs):
  742. return func_mock(*args, **kwargs)
  743. self.vm.shutdown = coroutine_mock
  744. value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1')
  745. self.assertIsNone(value)
  746. func_mock.assert_called_once_with()
  747. def test_240_pause(self):
  748. func_mock = unittest.mock.Mock()
  749. @asyncio.coroutine
  750. def coroutine_mock(*args, **kwargs):
  751. return func_mock(*args, **kwargs)
  752. self.vm.pause = coroutine_mock
  753. value = self.call_mgmt_func(b'admin.vm.Pause', b'test-vm1')
  754. self.assertIsNone(value)
  755. func_mock.assert_called_once_with()
  756. def test_250_unpause(self):
  757. func_mock = unittest.mock.Mock()
  758. @asyncio.coroutine
  759. def coroutine_mock(*args, **kwargs):
  760. return func_mock(*args, **kwargs)
  761. self.vm.unpause = coroutine_mock
  762. value = self.call_mgmt_func(b'admin.vm.Unpause', b'test-vm1')
  763. self.assertIsNone(value)
  764. func_mock.assert_called_once_with()
  765. def test_260_kill(self):
  766. func_mock = unittest.mock.Mock()
  767. @asyncio.coroutine
  768. def coroutine_mock(*args, **kwargs):
  769. return func_mock(*args, **kwargs)
  770. self.vm.kill = coroutine_mock
  771. value = self.call_mgmt_func(b'admin.vm.Kill', b'test-vm1')
  772. self.assertIsNone(value)
  773. func_mock.assert_called_once_with()
  774. def test_270_events(self):
  775. send_event = unittest.mock.Mock(spec=[])
  776. mgmt_obj = qubes.api.admin.QubesAdminAPI(self.app, b'dom0', b'admin.Events',
  777. b'dom0', b'', send_event=send_event)
  778. @asyncio.coroutine
  779. def fire_event():
  780. self.vm.fire_event('test-event', arg1='abc')
  781. mgmt_obj.cancel()
  782. loop = asyncio.get_event_loop()
  783. execute_task = asyncio.ensure_future(
  784. mgmt_obj.execute(untrusted_payload=b''))
  785. asyncio.ensure_future(fire_event())
  786. loop.run_until_complete(execute_task)
  787. self.assertIsNone(execute_task.result())
  788. self.assertEventFired(self.emitter,
  789. 'mgmt-permission:' + 'admin.Events')
  790. self.assertEqual(send_event.mock_calls,
  791. [
  792. unittest.mock.call(self.app, 'connection-established'),
  793. unittest.mock.call(self.vm, 'test-event', arg1='abc')
  794. ])
  795. def test_271_events_add_vm(self):
  796. send_event = unittest.mock.Mock(spec=[])
  797. mgmt_obj = qubes.api.admin.QubesAdminAPI(self.app, b'dom0', b'admin.Events',
  798. b'dom0', b'', send_event=send_event)
  799. @asyncio.coroutine
  800. def fire_event():
  801. self.vm.fire_event('test-event', arg1='abc')
  802. # add VM _after_ starting admin.Events call
  803. vm = self.app.add_new_vm('AppVM', label='red', name='test-vm2',
  804. template='test-template')
  805. vm.fire_event('test-event2', arg1='abc')
  806. mgmt_obj.cancel()
  807. return vm
  808. loop = asyncio.get_event_loop()
  809. execute_task = asyncio.ensure_future(
  810. mgmt_obj.execute(untrusted_payload=b''))
  811. event_task = asyncio.ensure_future(fire_event())
  812. loop.run_until_complete(execute_task)
  813. vm2 = event_task.result()
  814. self.assertIsNone(execute_task.result())
  815. self.assertEventFired(self.emitter,
  816. 'mgmt-permission:' + 'admin.Events')
  817. self.assertEqual(send_event.mock_calls,
  818. [
  819. unittest.mock.call(self.app, 'connection-established'),
  820. unittest.mock.call(self.vm, 'test-event', arg1='abc'),
  821. unittest.mock.call(self.app, 'domain-add', vm=vm2),
  822. unittest.mock.call(vm2, 'test-event2', arg1='abc'),
  823. ])
  824. def test_280_feature_list(self):
  825. self.vm.features['test-feature'] = 'some-value'
  826. value = self.call_mgmt_func(b'admin.vm.feature.List', b'test-vm1')
  827. self.assertEqual(value, 'test-feature\n')
  828. self.assertFalse(self.app.save.called)
  829. def test_290_feature_get(self):
  830. self.vm.features['test-feature'] = 'some-value'
  831. value = self.call_mgmt_func(b'admin.vm.feature.Get', b'test-vm1',
  832. b'test-feature')
  833. self.assertEqual(value, 'some-value')
  834. self.assertFalse(self.app.save.called)
  835. def test_291_feature_get_none(self):
  836. with self.assertRaises(qubes.exc.QubesFeatureNotFoundError):
  837. self.call_mgmt_func(b'admin.vm.feature.Get',
  838. b'test-vm1', b'test-feature')
  839. self.assertFalse(self.app.save.called)
  840. def test_300_feature_remove(self):
  841. self.vm.features['test-feature'] = 'some-value'
  842. value = self.call_mgmt_func(b'admin.vm.feature.Remove', b'test-vm1',
  843. b'test-feature')
  844. self.assertIsNone(value, None)
  845. self.assertNotIn('test-feature', self.vm.features)
  846. self.assertTrue(self.app.save.called)
  847. def test_301_feature_remove_none(self):
  848. with self.assertRaises(qubes.exc.QubesFeatureNotFoundError):
  849. self.call_mgmt_func(b'admin.vm.feature.Remove',
  850. b'test-vm1', b'test-feature')
  851. self.assertFalse(self.app.save.called)
  852. def test_310_feature_checkwithtemplate(self):
  853. self.vm.features['test-feature'] = 'some-value'
  854. value = self.call_mgmt_func(b'admin.vm.feature.CheckWithTemplate',
  855. b'test-vm1', b'test-feature')
  856. self.assertEqual(value, 'some-value')
  857. self.assertFalse(self.app.save.called)
  858. def test_311_feature_checkwithtemplate_tpl(self):
  859. self.template.features['test-feature'] = 'some-value'
  860. value = self.call_mgmt_func(b'admin.vm.feature.CheckWithTemplate',
  861. b'test-vm1', b'test-feature')
  862. self.assertEqual(value, 'some-value')
  863. self.assertFalse(self.app.save.called)
  864. def test_312_feature_checkwithtemplate_none(self):
  865. with self.assertRaises(qubes.exc.QubesFeatureNotFoundError):
  866. self.call_mgmt_func(b'admin.vm.feature.CheckWithTemplate',
  867. b'test-vm1', b'test-feature')
  868. self.assertFalse(self.app.save.called)
  869. def test_320_feature_set(self):
  870. value = self.call_mgmt_func(b'admin.vm.feature.Set',
  871. b'test-vm1', b'test-feature', b'some-value')
  872. self.assertIsNone(value)
  873. self.assertEqual(self.vm.features['test-feature'], 'some-value')
  874. self.assertTrue(self.app.save.called)
  875. def test_321_feature_set_empty(self):
  876. value = self.call_mgmt_func(b'admin.vm.feature.Set',
  877. b'test-vm1', b'test-feature', b'')
  878. self.assertIsNone(value)
  879. self.assertEqual(self.vm.features['test-feature'], '')
  880. self.assertTrue(self.app.save.called)
  881. def test_320_feature_set_invalid(self):
  882. with self.assertRaises(UnicodeDecodeError):
  883. self.call_mgmt_func(b'admin.vm.feature.Set',
  884. b'test-vm1', b'test-feature', b'\x02\x03\xffsome-value')
  885. self.assertNotIn('test-feature', self.vm.features)
  886. self.assertFalse(self.app.save.called)
  887. @asyncio.coroutine
  888. def dummy_coro(self, *args, **kwargs):
  889. pass
  890. @unittest.mock.patch('qubes.storage.Storage.create')
  891. def test_330_vm_create_standalone(self, storage_mock):
  892. storage_mock.side_effect = self.dummy_coro
  893. self.call_mgmt_func(b'admin.vm.Create.StandaloneVM',
  894. b'dom0', b'', b'name=test-vm2 label=red')
  895. self.assertIn('test-vm2', self.app.domains)
  896. vm = self.app.domains['test-vm2']
  897. self.assertIsInstance(vm, qubes.vm.standalonevm.StandaloneVM)
  898. self.assertEqual(vm.label, self.app.get_label('red'))
  899. self.assertEqual(storage_mock.mock_calls,
  900. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  901. self.assertTrue(os.path.exists(os.path.join(
  902. self.test_base_dir, 'appvms', 'test-vm2')))
  903. self.assertTrue(self.app.save.called)
  904. @unittest.mock.patch('qubes.storage.Storage.create')
  905. def test_331_vm_create_standalone_spurious_template(self, storage_mock):
  906. storage_mock.side_effect = self.dummy_coro
  907. with self.assertRaises(AssertionError):
  908. self.call_mgmt_func(b'admin.vm.Create.StandaloneVM',
  909. b'dom0', b'test-template', b'name=test-vm2 label=red')
  910. self.assertNotIn('test-vm2', self.app.domains)
  911. self.assertEqual(storage_mock.mock_calls, [])
  912. self.assertFalse(os.path.exists(os.path.join(
  913. self.test_base_dir, 'appvms', 'test-vm2')))
  914. self.assertNotIn('test-vm2', self.app.domains)
  915. self.assertFalse(self.app.save.called)
  916. @unittest.mock.patch('qubes.storage.Storage.create')
  917. def test_332_vm_create_app(self, storage_mock):
  918. storage_mock.side_effect = self.dummy_coro
  919. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  920. b'dom0', b'test-template', b'name=test-vm2 label=red')
  921. self.assertIn('test-vm2', self.app.domains)
  922. vm = self.app.domains['test-vm2']
  923. self.assertEqual(vm.label, self.app.get_label('red'))
  924. self.assertEqual(vm.template, self.app.domains['test-template'])
  925. self.assertEqual(storage_mock.mock_calls,
  926. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  927. self.assertTrue(os.path.exists(os.path.join(
  928. self.test_base_dir, 'appvms', 'test-vm2')))
  929. self.assertTrue(self.app.save.called)
  930. @unittest.mock.patch('qubes.storage.Storage.create')
  931. def test_333_vm_create_app_default_template(self, storage_mock):
  932. storage_mock.side_effect = self.dummy_coro
  933. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  934. b'dom0', b'', b'name=test-vm2 label=red')
  935. self.assertEqual(storage_mock.mock_calls,
  936. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  937. self.assertIn('test-vm2', self.app.domains)
  938. self.assertEqual(self.app.domains['test-vm2'].template,
  939. self.app.default_template)
  940. self.assertTrue(self.app.save.called)
  941. @unittest.mock.patch('qubes.storage.Storage.create')
  942. def test_334_vm_create_invalid_name(self, storage_mock):
  943. storage_mock.side_effect = self.dummy_coro
  944. with self.assertRaises(qubes.exc.QubesValueError):
  945. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  946. b'dom0', b'test-template', b'name=test-###')
  947. self.assertNotIn('test-###', self.app.domains)
  948. self.assertFalse(self.app.save.called)
  949. @unittest.mock.patch('qubes.storage.Storage.create')
  950. def test_335_vm_create_missing_name(self, storage_mock):
  951. storage_mock.side_effect = self.dummy_coro
  952. with self.assertRaises(AssertionError):
  953. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  954. b'dom0', b'test-template', b'label=red')
  955. self.assertFalse(self.app.save.called)
  956. @unittest.mock.patch('qubes.storage.Storage.create')
  957. def test_336_vm_create_spurious_pool(self, storage_mock):
  958. storage_mock.side_effect = self.dummy_coro
  959. with self.assertRaises(AssertionError):
  960. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  961. b'dom0', b'test-template',
  962. b'name=test-vm2 label=red pool=default')
  963. self.assertNotIn('test-vm2', self.app.domains)
  964. self.assertFalse(self.app.save.called)
  965. @unittest.mock.patch('qubes.storage.Storage.create')
  966. def test_337_vm_create_duplicate_name(self, storage_mock):
  967. storage_mock.side_effect = self.dummy_coro
  968. with self.assertRaises(qubes.exc.QubesException):
  969. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  970. b'dom0', b'test-template',
  971. b'name=test-vm1 label=red')
  972. self.assertFalse(self.app.save.called)
  973. @unittest.mock.patch('qubes.storage.Storage.create')
  974. def test_338_vm_create_name_twice(self, storage_mock):
  975. storage_mock.side_effect = self.dummy_coro
  976. with self.assertRaises(AssertionError):
  977. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  978. b'dom0', b'test-template',
  979. b'name=test-vm2 name=test-vm3 label=red')
  980. self.assertNotIn('test-vm2', self.app.domains)
  981. self.assertNotIn('test-vm3', self.app.domains)
  982. self.assertFalse(self.app.save.called)
  983. @unittest.mock.patch('qubes.storage.Storage.create')
  984. def test_340_vm_create_in_pool_app(self, storage_mock):
  985. storage_mock.side_effect = self.dummy_coro
  986. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  987. b'dom0', b'test-template', b'name=test-vm2 label=red '
  988. b'pool=test')
  989. self.assertIn('test-vm2', self.app.domains)
  990. vm = self.app.domains['test-vm2']
  991. self.assertEqual(vm.label, self.app.get_label('red'))
  992. self.assertEqual(vm.template, self.app.domains['test-template'])
  993. # setting pool= affect only volumes actually created for this VM,
  994. # not used from a template or so
  995. self.assertEqual(vm.volume_config['root']['pool'], 'default')
  996. self.assertEqual(vm.volume_config['private']['pool'], 'test')
  997. self.assertEqual(vm.volume_config['volatile']['pool'], 'test')
  998. self.assertEqual(vm.volume_config['kernel']['pool'], 'linux-kernel')
  999. self.assertEqual(storage_mock.mock_calls,
  1000. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  1001. self.assertTrue(os.path.exists(os.path.join(
  1002. self.test_base_dir, 'appvms', 'test-vm2')))
  1003. self.assertTrue(self.app.save.called)
  1004. @unittest.mock.patch('qubes.storage.Storage.create')
  1005. def test_341_vm_create_in_pool_private(self, storage_mock):
  1006. storage_mock.side_effect = self.dummy_coro
  1007. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1008. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1009. b'pool:private=test')
  1010. self.assertIn('test-vm2', self.app.domains)
  1011. vm = self.app.domains['test-vm2']
  1012. self.assertEqual(vm.label, self.app.get_label('red'))
  1013. self.assertEqual(vm.template, self.app.domains['test-template'])
  1014. self.assertEqual(vm.volume_config['root']['pool'], 'default')
  1015. self.assertEqual(vm.volume_config['private']['pool'], 'test')
  1016. self.assertEqual(vm.volume_config['volatile']['pool'], 'default')
  1017. self.assertEqual(vm.volume_config['kernel']['pool'], 'linux-kernel')
  1018. self.assertEqual(storage_mock.mock_calls,
  1019. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  1020. self.assertTrue(os.path.exists(os.path.join(
  1021. self.test_base_dir, 'appvms', 'test-vm2')))
  1022. self.assertTrue(self.app.save.called)
  1023. @unittest.mock.patch('qubes.storage.Storage.create')
  1024. def test_342_vm_create_in_pool_invalid_pool(self, storage_mock):
  1025. storage_mock.side_effect = self.dummy_coro
  1026. with self.assertRaises(qubes.exc.QubesException):
  1027. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1028. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1029. b'pool=no-such-pool')
  1030. self.assertFalse(self.app.save.called)
  1031. @unittest.mock.patch('qubes.storage.Storage.create')
  1032. def test_343_vm_create_in_pool_invalid_pool2(self, storage_mock):
  1033. storage_mock.side_effect = self.dummy_coro
  1034. with self.assertRaises(qubes.exc.QubesException):
  1035. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1036. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1037. b'pool:private=no-such-pool')
  1038. self.assertNotIn('test-vm2', self.app.domains)
  1039. self.assertFalse(self.app.save.called)
  1040. @unittest.mock.patch('qubes.storage.Storage.create')
  1041. def test_344_vm_create_in_pool_invalid_volume(self, storage_mock):
  1042. storage_mock.side_effect = self.dummy_coro
  1043. with self.assertRaises(AssertionError):
  1044. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1045. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1046. b'pool:invalid=test')
  1047. self.assertNotIn('test-vm2', self.app.domains)
  1048. self.assertFalse(self.app.save.called)
  1049. @unittest.mock.patch('qubes.storage.Storage.create')
  1050. def test_345_vm_create_in_pool_app_root(self, storage_mock):
  1051. # setting custom pool for 'root' volume of AppVM should not be
  1052. # allowed - this volume belongs to the template
  1053. storage_mock.side_effect = self.dummy_coro
  1054. with self.assertRaises(qubes.exc.QubesException):
  1055. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1056. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1057. b'pool:root=test')
  1058. self.assertNotIn('test-vm2', self.app.domains)
  1059. self.assertFalse(self.app.save.called)
  1060. @unittest.mock.patch('qubes.storage.Storage.create')
  1061. def test_346_vm_create_in_pool_duplicate_pool(self, storage_mock):
  1062. # setting custom pool for 'root' volume of AppVM should not be
  1063. # allowed - this volume belongs to the template
  1064. storage_mock.side_effect = self.dummy_coro
  1065. with self.assertRaises(AssertionError):
  1066. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1067. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1068. b'pool=test pool:root=test')
  1069. self.assertNotIn('test-vm2', self.app.domains)
  1070. self.assertFalse(self.app.save.called)
  1071. @unittest.mock.patch('qubes.storage.Storage.clone')
  1072. @unittest.mock.patch('qubes.storage.Storage.verify')
  1073. def test_350_vm_clone(self, mock_verify, mock_clone):
  1074. mock_clone.side_effect = self.dummy_coro
  1075. mock_verify.side_effect = self.dummy_coro
  1076. self.call_mgmt_func(b'admin.vm.Clone',
  1077. b'test-vm1', b'', b'name=test-vm2')
  1078. self.assertIn('test-vm2', self.app.domains)
  1079. vm = self.app.domains['test-vm2']
  1080. self.assertEqual(vm.label, self.app.get_label('red'))
  1081. self.assertEqual(vm.template, self.app.domains['test-template'])
  1082. self.assertEqual(vm.tags, self.vm.tags)
  1083. self.assertEqual(vm.features, self.vm.features)
  1084. self.assertEqual(vm.firewall, self.vm.firewall)
  1085. self.assertEqual(mock_clone.mock_calls,
  1086. [unittest.mock.call(self.app.domains['test-vm2']).clone(
  1087. self.app.domains['test-vm1'])])
  1088. self.assertTrue(os.path.exists(os.path.join(
  1089. self.test_base_dir, 'appvms', 'test-vm2')))
  1090. self.assertTrue(self.app.save.called)
  1091. @unittest.mock.patch('qubes.storage.Storage.clone')
  1092. @unittest.mock.patch('qubes.storage.Storage.verify')
  1093. def test_351_vm_clone_extra_params(self, mock_verify, mock_clone):
  1094. mock_clone.side_effect = self.dummy_coro
  1095. mock_verify.side_effect = self.dummy_coro
  1096. with self.assertRaises(qubes.exc.QubesException):
  1097. self.call_mgmt_func(b'admin.vm.Clone',
  1098. b'test-vm1', b'', b'name=test-vm2 label=red')
  1099. self.assertNotIn('test-vm2', self.app.domains)
  1100. self.assertEqual(mock_clone.mock_calls, [])
  1101. self.assertFalse(os.path.exists(os.path.join(
  1102. self.test_base_dir, 'appvms', 'test-vm2')))
  1103. self.assertFalse(self.app.save.called)
  1104. @unittest.mock.patch('qubes.storage.Storage.clone')
  1105. @unittest.mock.patch('qubes.storage.Storage.verify')
  1106. def test_352_vm_clone_duplicate_name(self, mock_verify, mock_clone):
  1107. mock_clone.side_effect = self.dummy_coro
  1108. mock_verify.side_effect = self.dummy_coro
  1109. with self.assertRaises(qubes.exc.QubesException):
  1110. self.call_mgmt_func(b'admin.vm.Clone',
  1111. b'test-vm1', b'', b'name=test-vm1')
  1112. self.assertFalse(self.app.save.called)
  1113. @unittest.mock.patch('qubes.storage.Storage.clone')
  1114. @unittest.mock.patch('qubes.storage.Storage.verify')
  1115. def test_353_vm_clone_invalid_name(self, mock_verify, mock_clone):
  1116. mock_clone.side_effect = self.dummy_coro
  1117. mock_verify.side_effect = self.dummy_coro
  1118. with self.assertRaises(qubes.exc.QubesException):
  1119. self.call_mgmt_func(b'admin.vm.Clone',
  1120. b'test-vm1', b'', b'name=test-vm2/..')
  1121. self.assertNotIn('test-vm2/..', self.app.domains)
  1122. self.assertEqual(mock_clone.mock_calls, [])
  1123. self.assertFalse(os.path.exists(os.path.join(
  1124. self.test_base_dir, 'appvms', 'test-vm2/..')))
  1125. self.assertFalse(self.app.save.called)
  1126. def test_400_property_list(self):
  1127. # actual function tested for admin.vm.property.* already
  1128. # this test is kind of stupid, but at least check if appropriate
  1129. # mgmt-permission event is fired
  1130. value = self.call_mgmt_func(b'admin.property.List', b'dom0')
  1131. properties = self.app.property_list()
  1132. self.assertEqual(value,
  1133. ''.join('{}\n'.format(prop.__name__) for prop in properties))
  1134. def test_410_property_get_str(self):
  1135. # actual function tested for admin.vm.property.* already
  1136. value = self.call_mgmt_func(b'admin.property.Get', b'dom0',
  1137. b'default_kernel')
  1138. self.assertEqual(value, 'default=False type=str 1.0')
  1139. def test_420_propert_set_str(self):
  1140. # actual function tested for admin.vm.property.* already
  1141. with unittest.mock.patch('qubes.property.__set__') as mock:
  1142. value = self.call_mgmt_func(b'admin.property.Set', b'dom0',
  1143. b'default_kernel', b'1.0')
  1144. self.assertIsNone(value)
  1145. mock.assert_called_once_with(self.app, '1.0')
  1146. self.app.save.assert_called_once_with()
  1147. def test_440_property_help(self):
  1148. # actual function tested for admin.vm.property.* already
  1149. value = self.call_mgmt_func(b'admin.property.Help', b'dom0',
  1150. b'clockvm')
  1151. self.assertEqual(value,
  1152. 'Which VM to use as NTP proxy for updating AdminVM')
  1153. self.assertFalse(self.app.save.called)
  1154. def test_450_property_reset(self):
  1155. # actual function tested for admin.vm.property.* already
  1156. with unittest.mock.patch('qubes.property.__delete__') as mock:
  1157. value = self.call_mgmt_func(b'admin.property.Reset', b'dom0',
  1158. b'clockvm')
  1159. mock.assert_called_with(self.app)
  1160. self.assertIsNone(value)
  1161. self.app.save.assert_called_once_with()
  1162. def device_list_testclass(self, vm, event):
  1163. if vm is not self.vm:
  1164. return
  1165. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  1166. dev.description = 'Some device'
  1167. dev.data = {'other_property': 'property-value'}
  1168. dev.extra_prop = 'xx'
  1169. yield dev
  1170. dev = qubes.devices.DeviceInfo(self.vm, '4321')
  1171. dev.description = 'Some other device'
  1172. yield dev
  1173. def test_460_vm_device_available(self):
  1174. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1175. value = self.call_mgmt_func(b'admin.vm.device.testclass.Available',
  1176. b'test-vm1')
  1177. self.assertEqual(value,
  1178. '1234 extra_prop=xx other_property=property-value description=Some '
  1179. 'device\n'
  1180. '4321 description=Some other device\n')
  1181. self.assertFalse(self.app.save.called)
  1182. def test_461_vm_device_available_specific(self):
  1183. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1184. value = self.call_mgmt_func(b'admin.vm.device.testclass.Available',
  1185. b'test-vm1', b'4321')
  1186. self.assertEqual(value,
  1187. '4321 description=Some other device\n')
  1188. self.assertFalse(self.app.save.called)
  1189. def test_462_vm_device_available_invalid(self):
  1190. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1191. value = self.call_mgmt_func(b'admin.vm.device.testclass.Available',
  1192. b'test-vm1', b'no-such-device')
  1193. self.assertEqual(value, '')
  1194. self.assertFalse(self.app.save.called)
  1195. def test_470_vm_device_list_persistent(self):
  1196. assignment = qubes.devices.DeviceAssignment(self.vm, '1234',
  1197. persistent=True)
  1198. self.vm.devices['testclass'].attach(assignment)
  1199. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1200. b'test-vm1')
  1201. self.assertEqual(value,
  1202. 'test-vm1+1234 persistent=yes\n')
  1203. self.assertFalse(self.app.save.called)
  1204. def test_471_vm_device_list_persistent_options(self):
  1205. assignment = qubes.devices.DeviceAssignment(self.vm, '1234',
  1206. persistent=True, options={'opt1': 'value'})
  1207. self.vm.devices['testclass'].attach(assignment)
  1208. assignment = qubes.devices.DeviceAssignment(self.vm, '4321',
  1209. persistent=True)
  1210. self.vm.devices['testclass'].attach(assignment)
  1211. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1212. b'test-vm1')
  1213. self.assertEqual(value,
  1214. 'test-vm1+1234 opt1=value persistent=yes\n'
  1215. 'test-vm1+4321 persistent=yes\n')
  1216. self.assertFalse(self.app.save.called)
  1217. def device_list_attached_testclass(self, vm, event, **kwargs):
  1218. if vm is not self.vm:
  1219. return
  1220. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  1221. yield (dev, {'attach_opt': 'value'})
  1222. def test_472_vm_device_list_temporary(self):
  1223. self.vm.add_handler('device-list-attached:testclass',
  1224. self.device_list_attached_testclass)
  1225. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1226. b'test-vm1')
  1227. self.assertEqual(value,
  1228. 'test-vm1+1234 attach_opt=value persistent=no\n')
  1229. self.assertFalse(self.app.save.called)
  1230. def test_473_vm_device_list_mixed(self):
  1231. self.vm.add_handler('device-list-attached:testclass',
  1232. self.device_list_attached_testclass)
  1233. assignment = qubes.devices.DeviceAssignment(self.vm, '4321',
  1234. persistent=True)
  1235. self.vm.devices['testclass'].attach(assignment)
  1236. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1237. b'test-vm1')
  1238. self.assertEqual(value,
  1239. 'test-vm1+1234 attach_opt=value persistent=no\n'
  1240. 'test-vm1+4321 persistent=yes\n')
  1241. self.assertFalse(self.app.save.called)
  1242. def test_474_vm_device_list_specific(self):
  1243. self.vm.add_handler('device-list-attached:testclass',
  1244. self.device_list_attached_testclass)
  1245. assignment = qubes.devices.DeviceAssignment(self.vm, '4321',
  1246. persistent=True)
  1247. self.vm.devices['testclass'].attach(assignment)
  1248. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1249. b'test-vm1', b'test-vm1+1234')
  1250. self.assertEqual(value,
  1251. 'test-vm1+1234 attach_opt=value persistent=no\n')
  1252. self.assertFalse(self.app.save.called)
  1253. def test_480_vm_device_attach(self):
  1254. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1255. mock_attach = unittest.mock.Mock()
  1256. mock_attach.return_value = None
  1257. self.vm.add_handler('device-attach:testclass', mock_attach)
  1258. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1259. 'is_halted', lambda _: False):
  1260. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1261. b'test-vm1', b'test-vm1+1234')
  1262. self.assertIsNone(value)
  1263. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1264. device=self.vm.devices['testclass']['1234'],
  1265. options={})
  1266. self.assertEqual(len(self.vm.devices['testclass'].persistent()), 0)
  1267. self.app.save.assert_called_once_with()
  1268. def test_481_vm_device_attach(self):
  1269. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1270. mock_attach = unittest.mock.Mock()
  1271. mock_attach.return_value = None
  1272. self.vm.add_handler('device-attach:testclass', mock_attach)
  1273. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1274. 'is_halted', lambda _: False):
  1275. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1276. b'test-vm1', b'test-vm1+1234', b'persistent=no')
  1277. self.assertIsNone(value)
  1278. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1279. device=self.vm.devices['testclass']['1234'],
  1280. options={})
  1281. self.assertEqual(len(self.vm.devices['testclass'].persistent()), 0)
  1282. self.app.save.assert_called_once_with()
  1283. def test_482_vm_device_attach_not_running(self):
  1284. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1285. mock_attach = unittest.mock.Mock()
  1286. self.vm.add_handler('device-attach:testclass', mock_attach)
  1287. with self.assertRaises(qubes.exc.QubesVMNotRunningError):
  1288. self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1289. b'test-vm1', b'test-vm1+1234')
  1290. self.assertFalse(mock_attach.called)
  1291. self.assertEqual(len(self.vm.devices['testclass'].persistent()), 0)
  1292. self.assertFalse(self.app.save.called)
  1293. def test_483_vm_device_attach_persistent(self):
  1294. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1295. mock_attach = unittest.mock.Mock()
  1296. mock_attach.return_value = None
  1297. self.vm.add_handler('device-attach:testclass', mock_attach)
  1298. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1299. 'is_halted', lambda _: False):
  1300. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1301. b'test-vm1', b'test-vm1+1234', b'persistent=yes')
  1302. self.assertIsNone(value)
  1303. dev = self.vm.devices['testclass']['1234']
  1304. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1305. device=dev,
  1306. options={})
  1307. self.assertIn(dev, self.vm.devices['testclass'].persistent())
  1308. self.app.save.assert_called_once_with()
  1309. def test_484_vm_device_attach_persistent_not_running(self):
  1310. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1311. mock_attach = unittest.mock.Mock()
  1312. mock_attach.return_value = None
  1313. self.vm.add_handler('device-attach:testclass', mock_attach)
  1314. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1315. b'test-vm1', b'test-vm1+1234', b'persistent=yes')
  1316. self.assertIsNone(value)
  1317. dev = self.vm.devices['testclass']['1234']
  1318. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1319. device=dev,
  1320. options={})
  1321. self.assertIn(dev, self.vm.devices['testclass'].persistent())
  1322. self.app.save.assert_called_once_with()
  1323. def test_485_vm_device_attach_options(self):
  1324. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1325. mock_attach = unittest.mock.Mock()
  1326. mock_attach.return_value = None
  1327. self.vm.add_handler('device-attach:testclass', mock_attach)
  1328. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1329. 'is_halted', lambda _: False):
  1330. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1331. b'test-vm1', b'test-vm1+1234', b'option1=value2')
  1332. self.assertIsNone(value)
  1333. dev = self.vm.devices['testclass']['1234']
  1334. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1335. device=dev,
  1336. options={'option1': 'value2'})
  1337. self.app.save.assert_called_once_with()
  1338. def test_490_vm_device_detach(self):
  1339. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1340. self.vm.add_handler('device-list-attached:testclass',
  1341. self.device_list_attached_testclass)
  1342. mock_detach = unittest.mock.Mock()
  1343. mock_detach.return_value = None
  1344. self.vm.add_handler('device-detach:testclass', mock_detach)
  1345. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1346. 'is_halted', lambda _: False):
  1347. value = self.call_mgmt_func(b'admin.vm.device.testclass.Detach',
  1348. b'test-vm1', b'test-vm1+1234')
  1349. self.assertIsNone(value)
  1350. mock_detach.assert_called_once_with(self.vm, 'device-detach:testclass',
  1351. device=self.vm.devices['testclass']['1234'])
  1352. self.app.save.assert_called_once_with()
  1353. def test_491_vm_device_detach_not_attached(self):
  1354. mock_detach = unittest.mock.Mock()
  1355. mock_detach.return_value = None
  1356. self.vm.add_handler('device-detach:testclass', mock_detach)
  1357. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1358. 'is_halted', lambda _: False):
  1359. with self.assertRaises(qubes.devices.DeviceNotAttached):
  1360. self.call_mgmt_func(b'admin.vm.device.testclass.Detach',
  1361. b'test-vm1', b'test-vm1+1234')
  1362. self.assertFalse(mock_detach.called)
  1363. self.assertFalse(self.app.save.called)
  1364. @unittest.mock.patch('qubes.storage.Storage.remove')
  1365. @unittest.mock.patch('shutil.rmtree')
  1366. def test_500_vm_remove(self, mock_rmtree, mock_remove):
  1367. value = self.call_mgmt_func(b'admin.vm.Remove', b'test-vm1')
  1368. self.assertIsNone(value)
  1369. mock_rmtree.assert_called_once_with(
  1370. '/tmp/qubes-test-dir/appvms/test-vm1')
  1371. mock_remove.assert_called_once_with()
  1372. self.app.save.assert_called_once_with()
  1373. @unittest.mock.patch('qubes.storage.Storage.remove')
  1374. @unittest.mock.patch('shutil.rmtree')
  1375. def test_501_vm_remove_running(self, mock_rmtree, mock_remove):
  1376. with unittest.mock.patch.object(
  1377. self.vm, 'get_power_state', lambda: 'Running'):
  1378. with self.assertRaises(qubes.exc.QubesVMNotHaltedError):
  1379. self.call_mgmt_func(b'admin.vm.Remove', b'test-vm1')
  1380. self.assertFalse(mock_rmtree.called)
  1381. self.assertFalse(mock_remove.called)
  1382. self.assertFalse(self.app.save.called)
  1383. def test_510_vm_volume_import(self):
  1384. value = self.call_mgmt_func(b'admin.vm.volume.Import', b'test-vm1',
  1385. b'private')
  1386. self.assertEqual(value, '{} {}'.format(
  1387. 2*2**30, '/tmp/qubes-test-dir/appvms/test-vm1/private.img'))
  1388. self.assertFalse(self.app.save.called)
  1389. def test_511_vm_volume_import_running(self):
  1390. with unittest.mock.patch.object(
  1391. self.vm, 'get_power_state', lambda: 'Running'):
  1392. with self.assertRaises(qubes.exc.QubesVMNotHaltedError):
  1393. self.call_mgmt_func(b'admin.vm.volume.Import', b'test-vm1',
  1394. b'private')
  1395. def test_990_vm_unexpected_payload(self):
  1396. methods_with_no_payload = [
  1397. b'admin.vm.List',
  1398. b'admin.vm.Remove',
  1399. b'admin.vm.property.List',
  1400. b'admin.vm.property.Get',
  1401. b'admin.vm.property.Help',
  1402. b'admin.vm.property.HelpRst',
  1403. b'admin.vm.property.Reset',
  1404. b'admin.vm.feature.List',
  1405. b'admin.vm.feature.Get',
  1406. b'admin.vm.feature.CheckWithTemplate',
  1407. b'admin.vm.feature.Remove',
  1408. b'admin.vm.tag.List',
  1409. b'admin.vm.tag.Get',
  1410. b'admin.vm.tag.Remove',
  1411. b'admin.vm.tag.Set',
  1412. b'admin.vm.firewall.Get',
  1413. b'admin.vm.firewall.RemoveRule',
  1414. b'admin.vm.firewall.Flush',
  1415. b'admin.vm.device.pci.Attach',
  1416. b'admin.vm.device.pci.Detach',
  1417. b'admin.vm.device.pci.List',
  1418. b'admin.vm.device.pci.Available',
  1419. b'admin.vm.microphone.Attach',
  1420. b'admin.vm.microphone.Detach',
  1421. b'admin.vm.microphone.Status',
  1422. b'admin.vm.volume.ListSnapshots',
  1423. b'admin.vm.volume.List',
  1424. b'admin.vm.volume.Info',
  1425. b'admin.vm.Start',
  1426. b'admin.vm.Shutdown',
  1427. b'admin.vm.Pause',
  1428. b'admin.vm.Unpause',
  1429. b'admin.vm.Kill',
  1430. b'admin.Events',
  1431. b'admin.vm.feature.List',
  1432. b'admin.vm.feature.Get',
  1433. b'admin.vm.feature.Remove',
  1434. b'admin.vm.feature.CheckWithTemplate',
  1435. ]
  1436. # make sure also no methods on actual VM gets called
  1437. vm_mock = unittest.mock.MagicMock()
  1438. vm_mock.name = self.vm.name
  1439. vm_mock.qid = self.vm.qid
  1440. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  1441. self.app.domains._dict[self.vm.qid] = vm_mock
  1442. for method in methods_with_no_payload:
  1443. # should reject payload regardless of having argument or not
  1444. with self.subTest(method.decode('ascii')):
  1445. with self.assertRaises(AssertionError):
  1446. self.call_mgmt_func(method, b'test-vm1', b'',
  1447. b'unexpected-payload')
  1448. self.assertFalse(vm_mock.called)
  1449. self.assertFalse(self.app.save.called)
  1450. with self.subTest(method.decode('ascii') + '+arg'):
  1451. with self.assertRaises(AssertionError):
  1452. self.call_mgmt_func(method, b'test-vm1', b'some-arg',
  1453. b'unexpected-payload')
  1454. self.assertFalse(vm_mock.called)
  1455. self.assertFalse(self.app.save.called)
  1456. def test_991_vm_unexpected_argument(self):
  1457. methods_with_no_argument = [
  1458. b'admin.vm.List',
  1459. b'admin.vm.Clone',
  1460. b'admin.vm.Remove',
  1461. b'admin.vm.property.List',
  1462. b'admin.vm.feature.List',
  1463. b'admin.vm.tag.List',
  1464. b'admin.vm.firewall.List',
  1465. b'admin.vm.firewall.Flush',
  1466. b'admin.vm.microphone.Attach',
  1467. b'admin.vm.microphone.Detach',
  1468. b'admin.vm.microphone.Status',
  1469. b'admin.vm.volume.List',
  1470. b'admin.vm.Start',
  1471. b'admin.vm.Shutdown',
  1472. b'admin.vm.Pause',
  1473. b'admin.vm.Unpause',
  1474. b'admin.vm.Kill',
  1475. b'admin.Events',
  1476. b'admin.vm.feature.List',
  1477. ]
  1478. # make sure also no methods on actual VM gets called
  1479. vm_mock = unittest.mock.MagicMock()
  1480. vm_mock.name = self.vm.name
  1481. vm_mock.qid = self.vm.qid
  1482. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  1483. self.app.domains._dict[self.vm.qid] = vm_mock
  1484. for method in methods_with_no_argument:
  1485. # should reject argument regardless of having payload or not
  1486. with self.subTest(method.decode('ascii')):
  1487. with self.assertRaises(AssertionError):
  1488. self.call_mgmt_func(method, b'test-vm1', b'some-arg',
  1489. b'')
  1490. self.assertFalse(vm_mock.called)
  1491. self.assertFalse(self.app.save.called)
  1492. with self.subTest(method.decode('ascii') + '+payload'):
  1493. with self.assertRaises(AssertionError):
  1494. self.call_mgmt_func(method, b'test-vm1', b'unexpected-arg',
  1495. b'some-payload')
  1496. self.assertFalse(vm_mock.called)
  1497. self.assertFalse(self.app.save.called)
  1498. def test_992_dom0_unexpected_payload(self):
  1499. methods_with_no_payload = [
  1500. b'admin.vmclass.List',
  1501. b'admin.vm.List',
  1502. b'admin.label.List',
  1503. b'admin.label.Get',
  1504. b'admin.label.Remove',
  1505. b'admin.property.List',
  1506. b'admin.property.Get',
  1507. b'admin.property.Help',
  1508. b'admin.property.HelpRst',
  1509. b'admin.property.Reset',
  1510. b'admin.pool.List',
  1511. b'admin.pool.ListDrivers',
  1512. b'admin.pool.Info',
  1513. b'admin.pool.Remove',
  1514. b'admin.backup.Execute',
  1515. b'admin.Events',
  1516. ]
  1517. # make sure also no methods on actual VM gets called
  1518. vm_mock = unittest.mock.MagicMock()
  1519. vm_mock.name = self.vm.name
  1520. vm_mock.qid = self.vm.qid
  1521. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  1522. self.app.domains._dict[self.vm.qid] = vm_mock
  1523. for method in methods_with_no_payload:
  1524. # should reject payload regardless of having argument or not
  1525. with self.subTest(method.decode('ascii')):
  1526. with self.assertRaises(AssertionError):
  1527. self.call_mgmt_func(method, b'dom0', b'',
  1528. b'unexpected-payload')
  1529. self.assertFalse(vm_mock.called)
  1530. self.assertFalse(self.app.save.called)
  1531. with self.subTest(method.decode('ascii') + '+arg'):
  1532. with self.assertRaises(AssertionError):
  1533. self.call_mgmt_func(method, b'dom0', b'some-arg',
  1534. b'unexpected-payload')
  1535. self.assertFalse(vm_mock.called)
  1536. self.assertFalse(self.app.save.called)
  1537. def test_993_dom0_unexpected_argument(self):
  1538. methods_with_no_argument = [
  1539. b'admin.vmclass.List',
  1540. b'admin.vm.List',
  1541. b'admin.label.List',
  1542. b'admin.property.List',
  1543. b'admin.pool.List',
  1544. b'admin.pool.ListDrivers',
  1545. b'admin.Events',
  1546. ]
  1547. # make sure also no methods on actual VM gets called
  1548. vm_mock = unittest.mock.MagicMock()
  1549. vm_mock.name = self.vm.name
  1550. vm_mock.qid = self.vm.qid
  1551. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  1552. self.app.domains._dict[self.vm.qid] = vm_mock
  1553. for method in methods_with_no_argument:
  1554. # should reject argument regardless of having payload or not
  1555. with self.subTest(method.decode('ascii')):
  1556. with self.assertRaises(AssertionError):
  1557. self.call_mgmt_func(method, b'dom0', b'some-arg',
  1558. b'')
  1559. self.assertFalse(vm_mock.called)
  1560. self.assertFalse(self.app.save.called)
  1561. with self.subTest(method.decode('ascii') + '+payload'):
  1562. with self.assertRaises(AssertionError):
  1563. self.call_mgmt_func(method, b'dom0', b'unexpected-arg',
  1564. b'some-payload')
  1565. self.assertFalse(vm_mock.called)
  1566. self.assertFalse(self.app.save.called)
  1567. def test_994_dom0_only_calls(self):
  1568. # TODO set some better arguments, to make sure the call was rejected
  1569. # because of invalid destination, not invalid arguments
  1570. methods_for_dom0_only = [
  1571. b'admin.vmclass.List',
  1572. b'admin.vm.Create.AppVM',
  1573. b'admin.vm.CreateInPool.AppVM',
  1574. b'admin.vm.CreateTemplate',
  1575. b'admin.label.List',
  1576. b'admin.label.Create',
  1577. b'admin.label.Get',
  1578. b'admin.label.Remove',
  1579. b'admin.property.List',
  1580. b'admin.property.Get',
  1581. b'admin.property.Set',
  1582. b'admin.property.Help',
  1583. b'admin.property.HelpRst',
  1584. b'admin.property.Reset',
  1585. b'admin.pool.List',
  1586. b'admin.pool.ListDrivers',
  1587. b'admin.pool.Info',
  1588. b'admin.pool.Add',
  1589. b'admin.pool.Remove',
  1590. b'admin.pool.volume.List',
  1591. b'admin.pool.volume.Info',
  1592. b'admin.pool.volume.ListSnapshots',
  1593. b'admin.pool.volume.Snapshot',
  1594. b'admin.pool.volume.Revert',
  1595. b'admin.pool.volume.Resize',
  1596. b'admin.backup.Execute',
  1597. b'admin.backup.Info',
  1598. b'admin.backup.Restore',
  1599. ]
  1600. # make sure also no methods on actual VM gets called
  1601. vm_mock = unittest.mock.MagicMock()
  1602. vm_mock.name = self.vm.name
  1603. vm_mock.qid = self.vm.qid
  1604. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  1605. self.app.domains._dict[self.vm.qid] = vm_mock
  1606. for method in methods_for_dom0_only:
  1607. # should reject call regardless of having payload or not
  1608. with self.subTest(method.decode('ascii')):
  1609. with self.assertRaises(AssertionError):
  1610. self.call_mgmt_func(method, b'test-vm1', b'',
  1611. b'')
  1612. self.assertFalse(vm_mock.called)
  1613. self.assertFalse(self.app.save.called)
  1614. with self.subTest(method.decode('ascii') + '+arg'):
  1615. with self.assertRaises(AssertionError):
  1616. self.call_mgmt_func(method, b'test-vm1', b'some-arg',
  1617. b'')
  1618. self.assertFalse(vm_mock.called)
  1619. self.assertFalse(self.app.save.called)
  1620. with self.subTest(method.decode('ascii') + '+payload'):
  1621. with self.assertRaises(AssertionError):
  1622. self.call_mgmt_func(method, b'test-vm1', b'',
  1623. b'payload')
  1624. self.assertFalse(vm_mock.called)
  1625. self.assertFalse(self.app.save.called)
  1626. with self.subTest(method.decode('ascii') + '+arg+payload'):
  1627. with self.assertRaises(AssertionError):
  1628. self.call_mgmt_func(method, b'test-vm1', b'some-arg',
  1629. b'some-payload')
  1630. self.assertFalse(vm_mock.called)
  1631. self.assertFalse(self.app.save.called)
  1632. @unittest.skip('undecided')
  1633. def test_995_vm_only_calls(self):
  1634. # XXX is it really a good idea to prevent those calls this early?
  1635. # TODO set some better arguments, to make sure the call was rejected
  1636. # because of invalid destination, not invalid arguments
  1637. methods_for_vm_only = [
  1638. b'admin.vm.Clone',
  1639. b'admin.vm.Remove',
  1640. b'admin.vm.property.List',
  1641. b'admin.vm.property.Get',
  1642. b'admin.vm.property.Set',
  1643. b'admin.vm.property.Help',
  1644. b'admin.vm.property.HelpRst',
  1645. b'admin.vm.property.Reset',
  1646. b'admin.vm.feature.List',
  1647. b'admin.vm.feature.Get',
  1648. b'admin.vm.feature.Set',
  1649. b'admin.vm.feature.CheckWithTemplate',
  1650. b'admin.vm.feature.Remove',
  1651. b'admin.vm.tag.List',
  1652. b'admin.vm.tag.Get',
  1653. b'admin.vm.tag.Remove',
  1654. b'admin.vm.tag.Set',
  1655. b'admin.vm.firewall.Get',
  1656. b'admin.vm.firewall.RemoveRule',
  1657. b'admin.vm.firewall.InsertRule',
  1658. b'admin.vm.firewall.Flush',
  1659. b'admin.vm.device.pci.Attach',
  1660. b'admin.vm.device.pci.Detach',
  1661. b'admin.vm.device.pci.List',
  1662. b'admin.vm.device.pci.Available',
  1663. b'admin.vm.microphone.Attach',
  1664. b'admin.vm.microphone.Detach',
  1665. b'admin.vm.microphone.Status',
  1666. b'admin.vm.volume.ListSnapshots',
  1667. b'admin.vm.volume.List',
  1668. b'admin.vm.volume.Info',
  1669. b'admin.vm.volume.Revert',
  1670. b'admin.vm.volume.Resize',
  1671. b'admin.vm.Start',
  1672. b'admin.vm.Shutdown',
  1673. b'admin.vm.Pause',
  1674. b'admin.vm.Unpause',
  1675. b'admin.vm.Kill',
  1676. b'admin.vm.feature.List',
  1677. b'admin.vm.feature.Get',
  1678. b'admin.vm.feature.Set',
  1679. b'admin.vm.feature.Remove',
  1680. b'admin.vm.feature.CheckWithTemplate',
  1681. ]
  1682. # make sure also no methods on actual VM gets called
  1683. vm_mock = unittest.mock.MagicMock()
  1684. vm_mock.name = self.vm.name
  1685. vm_mock.qid = self.vm.qid
  1686. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  1687. self.app.domains._dict[self.vm.qid] = vm_mock
  1688. for method in methods_for_vm_only:
  1689. # should reject payload regardless of having argument or not
  1690. # should reject call regardless of having payload or not
  1691. with self.subTest(method.decode('ascii')):
  1692. with self.assertRaises(AssertionError):
  1693. self.call_mgmt_func(method, b'dom0', b'',
  1694. b'')
  1695. self.assertFalse(vm_mock.called)
  1696. self.assertFalse(self.app.save.called)
  1697. with self.subTest(method.decode('ascii') + '+arg'):
  1698. with self.assertRaises(AssertionError):
  1699. self.call_mgmt_func(method, b'dom0', b'some-arg',
  1700. b'')
  1701. self.assertFalse(vm_mock.called)
  1702. self.assertFalse(self.app.save.called)
  1703. with self.subTest(method.decode('ascii') + '+payload'):
  1704. with self.assertRaises(AssertionError):
  1705. self.call_mgmt_func(method, b'dom0', b'',
  1706. b'payload')
  1707. self.assertFalse(vm_mock.called)
  1708. self.assertFalse(self.app.save.called)
  1709. with self.subTest(method.decode('ascii') + '+arg+payload'):
  1710. with self.assertRaises(AssertionError):
  1711. self.call_mgmt_func(method, b'dom0', b'some-arg',
  1712. b'some-payload')
  1713. self.assertFalse(vm_mock.called)
  1714. self.assertFalse(self.app.save.called)