api_admin.py 138 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217
  1. # -*- encoding: utf-8 -*-
  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 library is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU Lesser General Public
  10. # License as published by the Free Software Foundation; either
  11. # version 2.1 of the License, or (at your option) any later version.
  12. #
  13. # This library 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 GNU
  16. # Lesser General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public
  19. # License along with this library; if not, see <https://www.gnu.org/licenses/>.
  20. ''' Tests for management calls endpoints '''
  21. import asyncio
  22. import operator
  23. import os
  24. import shutil
  25. import tempfile
  26. import unittest.mock
  27. import libvirt
  28. import copy
  29. import pathlib
  30. import qubes
  31. import qubes.devices
  32. import qubes.firewall
  33. import qubes.api.admin
  34. import qubes.api.internal
  35. import qubes.tests
  36. import qubes.storage
  37. # properties defined in API
  38. volume_properties = [
  39. 'pool', 'vid', 'size', 'usage', 'rw', 'source', 'path',
  40. 'save_on_stop', 'snap_on_start', 'revisions_to_keep']
  41. class AdminAPITestCase(qubes.tests.QubesTestCase):
  42. def setUp(self):
  43. super().setUp()
  44. self.test_base_dir = '/tmp/qubes-test-dir'
  45. self.base_dir_patch = unittest.mock.patch.dict(qubes.config.system_path,
  46. {'qubes_base_dir': self.test_base_dir})
  47. self.base_dir_patch2 = unittest.mock.patch(
  48. 'qubes.config.qubes_base_dir', self.test_base_dir)
  49. self.base_dir_patch3 = unittest.mock.patch.dict(
  50. qubes.config.defaults['pool_configs']['varlibqubes'],
  51. {'dir_path': self.test_base_dir})
  52. self.base_dir_patch.start()
  53. self.base_dir_patch2.start()
  54. self.base_dir_patch3.start()
  55. app = qubes.Qubes('/tmp/qubes-test.xml', load=False)
  56. app.vmm = unittest.mock.Mock(spec=qubes.app.VMMConnection)
  57. app.load_initial_values()
  58. self.loop.run_until_complete(app.setup_pools())
  59. app.default_kernel = '1.0'
  60. app.default_netvm = None
  61. self.template = app.add_new_vm('TemplateVM', label='black',
  62. name='test-template')
  63. app.default_template = 'test-template'
  64. with qubes.tests.substitute_entry_points('qubes.storage',
  65. 'qubes.tests.storage'):
  66. self.loop.run_until_complete(
  67. app.add_pool('test', driver='test'))
  68. app.default_pool = 'varlibqubes'
  69. app.save = unittest.mock.Mock()
  70. self.vm = app.add_new_vm('AppVM', label='red', name='test-vm1',
  71. template='test-template')
  72. self.app = app
  73. libvirt_attrs = {
  74. 'libvirt_conn.lookupByUUID.return_value.isActive.return_value':
  75. False,
  76. 'libvirt_conn.lookupByUUID.return_value.state.return_value':
  77. [libvirt.VIR_DOMAIN_SHUTOFF],
  78. }
  79. app.vmm.configure_mock(**libvirt_attrs)
  80. self.emitter = qubes.tests.TestEmitter()
  81. self.app.domains[0].fire_event = self.emitter.fire_event
  82. def tearDown(self):
  83. self.base_dir_patch3.stop()
  84. self.base_dir_patch2.stop()
  85. self.base_dir_patch.stop()
  86. if os.path.exists(self.test_base_dir):
  87. shutil.rmtree(self.test_base_dir)
  88. try:
  89. del self.netvm
  90. except AttributeError:
  91. pass
  92. del self.vm
  93. del self.template
  94. self.app.close()
  95. del self.app
  96. del self.emitter
  97. super(AdminAPITestCase, self).tearDown()
  98. def call_mgmt_func(self, method, dest, arg=b'', payload=b''):
  99. mgmt_obj = qubes.api.admin.QubesAdminAPI(self.app, b'dom0', method, dest, arg)
  100. loop = asyncio.get_event_loop()
  101. response = loop.run_until_complete(
  102. mgmt_obj.execute(untrusted_payload=payload))
  103. self.assertEventFired(self.emitter,
  104. 'admin-permission:' + method.decode('ascii'))
  105. return response
  106. def call_internal_mgmt_func(self, method, dest, arg=b'', payload=b''):
  107. mgmt_obj = qubes.api.internal.QubesInternalAPI(self.app, b'dom0', method, dest, arg)
  108. loop = asyncio.get_event_loop()
  109. response = loop.run_until_complete(
  110. mgmt_obj.execute(untrusted_payload=payload))
  111. return response
  112. class TC_00_VMs(AdminAPITestCase):
  113. def test_000_vm_list(self):
  114. value = self.call_mgmt_func(b'admin.vm.List', b'dom0')
  115. self.assertEqual(value,
  116. 'dom0 class=AdminVM state=Running\n'
  117. 'test-template class=TemplateVM state=Halted\n'
  118. 'test-vm1 class=AppVM state=Halted\n')
  119. def test_001_vm_list_single(self):
  120. value = self.call_mgmt_func(b'admin.vm.List', b'test-vm1')
  121. self.assertEqual(value,
  122. 'test-vm1 class=AppVM state=Halted\n')
  123. def test_002_vm_list_filter(self):
  124. with tempfile.TemporaryDirectory() as tmpdir:
  125. tmpdir = pathlib.Path(tmpdir)
  126. with unittest.mock.patch(
  127. 'qubes.ext.admin.AdminExtension._instance.policy_cache.path',
  128. pathlib.Path(tmpdir)):
  129. with (tmpdir / 'admin.policy').open('w') as f:
  130. f.write('admin.vm.List * @anyvm @adminvm allow\n')
  131. f.write('admin.vm.List * @anyvm test-vm1 allow')
  132. mgmt_obj = qubes.api.admin.QubesAdminAPI(self.app, b'test-vm1',
  133. b'admin.vm.List', b'dom0', b'')
  134. loop = asyncio.get_event_loop()
  135. value = loop.run_until_complete(
  136. mgmt_obj.execute(untrusted_payload=b''))
  137. self.assertEqual(value,
  138. 'dom0 class=AdminVM state=Running\n'
  139. 'test-vm1 class=AppVM state=Halted\n')
  140. def test_010_vm_property_list(self):
  141. # this test is kind of stupid, but at least check if appropriate
  142. # admin-permission event is fired
  143. value = self.call_mgmt_func(b'admin.vm.property.List', b'test-vm1')
  144. properties = self.app.domains['test-vm1'].property_list()
  145. self.assertEqual(value,
  146. ''.join('{}\n'.format(prop.__name__) for prop in properties))
  147. def test_020_vm_property_get_str(self):
  148. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  149. b'name')
  150. self.assertEqual(value, 'default=False type=str test-vm1')
  151. def test_021_vm_property_get_int(self):
  152. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  153. b'vcpus')
  154. self.assertEqual(value, 'default=True type=int 2')
  155. def test_022_vm_property_get_bool(self):
  156. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  157. b'provides_network')
  158. self.assertEqual(value, 'default=True type=bool False')
  159. def test_023_vm_property_get_label(self):
  160. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  161. b'label')
  162. self.assertEqual(value, 'default=False type=label red')
  163. def test_024_vm_property_get_vm(self):
  164. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  165. b'template')
  166. self.assertEqual(value, 'default=False type=vm test-template')
  167. def test_025_vm_property_get_vm_none(self):
  168. value = self.call_mgmt_func(b'admin.vm.property.Get', b'test-vm1',
  169. b'netvm')
  170. self.assertEqual(value, 'default=True type=vm ')
  171. def test_025_vm_property_get_default_vm_none(self):
  172. value = self.call_mgmt_func(
  173. b'admin.vm.property.GetDefault',
  174. b'test-vm1',
  175. b'template')
  176. self.assertEqual(value, None)
  177. def test_026_vm_property_get_default_bool(self):
  178. self.vm.provides_network = True
  179. value = self.call_mgmt_func(
  180. b'admin.vm.property.GetDefault',
  181. b'test-vm1',
  182. b'provides_network')
  183. self.assertEqual(value, 'type=bool False')
  184. def test_027_vm_property_get_all(self):
  185. # any string property, test \n encoding
  186. self.vm.kernelopts = 'opt1\nopt2\nopt3\\opt4'
  187. # let it have 'dns' property
  188. self.vm.provides_network = True
  189. with unittest.mock.patch.object(self.vm, 'property_list') as list_mock:
  190. list_mock.return_value = [
  191. self.vm.property_get_def('name'),
  192. self.vm.property_get_def('default_user'),
  193. self.vm.property_get_def('netvm'),
  194. self.vm.property_get_def('klass'),
  195. self.vm.property_get_def('debug'),
  196. self.vm.property_get_def('label'),
  197. self.vm.property_get_def('kernelopts'),
  198. self.vm.property_get_def('qrexec_timeout'),
  199. self.vm.property_get_def('qid'),
  200. self.vm.property_get_def('updateable'),
  201. self.vm.property_get_def('dns'),
  202. ]
  203. value = self.call_mgmt_func(b'admin.vm.property.GetAll', b'test-vm1')
  204. self.maxDiff = None
  205. expected = '''debug default=True type=bool False
  206. default_user default=True type=str user
  207. dns default=True type=str 10.139.1.1 10.139.1.2
  208. klass default=True type=str AppVM
  209. label default=False type=label red
  210. name default=False type=str test-vm1
  211. qid default=False type=int 2
  212. qrexec_timeout default=True type=int 60
  213. updateable default=True type=bool False
  214. kernelopts default=False type=str opt1\\nopt2\\nopt3\\\\opt4
  215. netvm default=True type=vm \n'''
  216. self.assertEqual(value, expected)
  217. def test_028_vm_property_get_list(self):
  218. self.vm.provides_network = True
  219. value = self.call_mgmt_func(
  220. b'admin.vm.property.Get',
  221. b'test-vm1',
  222. b'dns')
  223. self.assertEqual(value, 'default=True type=str 10.139.1.1 10.139.1.2')
  224. def test_029_vm_property_get_list_none(self):
  225. value = self.call_mgmt_func(
  226. b'admin.vm.property.Get',
  227. b'test-vm1',
  228. b'dns')
  229. self.assertEqual(value, 'default=True type=str ')
  230. def test_029_vm_property_get_list_default(self):
  231. self.vm.provides_network = True
  232. value = self.call_mgmt_func(
  233. b'admin.vm.property.GetDefault',
  234. b'test-vm1',
  235. b'dns')
  236. self.assertEqual(value, 'type=str 10.139.1.1 10.139.1.2')
  237. def test_030_vm_property_set_vm(self):
  238. netvm = self.app.add_new_vm('AppVM', label='red', name='test-net',
  239. template='test-template', provides_network=True)
  240. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  241. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  242. b'netvm', b'test-net')
  243. self.assertIsNone(value)
  244. mock.assert_called_once_with(self.vm, 'test-net')
  245. self.app.save.assert_called_once_with()
  246. def test_031_vm_property_set_vm_none(self):
  247. netvm = self.app.add_new_vm('AppVM', label='red', name='test-net',
  248. template='test-template', provides_network=True)
  249. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  250. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  251. b'netvm', b'')
  252. self.assertIsNone(value)
  253. mock.assert_called_once_with(self.vm, '')
  254. self.app.save.assert_called_once_with()
  255. def test_032_vm_property_set_vm_invalid1(self):
  256. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  257. with self.assertRaises(qubes.exc.QubesValueError):
  258. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  259. b'netvm', b'forbidden-chars/../!')
  260. self.assertFalse(mock.called)
  261. self.assertFalse(self.app.save.called)
  262. def test_033_vm_property_set_vm_invalid2(self):
  263. with unittest.mock.patch('qubes.vm.VMProperty.__set__') as mock:
  264. with self.assertRaises(qubes.exc.QubesValueError):
  265. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  266. b'netvm', b'\x80\x90\xa0')
  267. self.assertFalse(mock.called)
  268. self.assertFalse(self.app.save.called)
  269. def test_034_vm_propert_set_bool_true(self):
  270. with unittest.mock.patch('qubes.property.__set__') as mock:
  271. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  272. b'autostart', b'True')
  273. self.assertIsNone(value)
  274. mock.assert_called_once_with(self.vm, True)
  275. self.app.save.assert_called_once_with()
  276. def test_035_vm_propert_set_bool_false(self):
  277. with unittest.mock.patch('qubes.property.__set__') as mock:
  278. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  279. b'autostart', b'False')
  280. self.assertIsNone(value)
  281. mock.assert_called_once_with(self.vm, False)
  282. self.app.save.assert_called_once_with()
  283. def test_036_vm_propert_set_bool_invalid1(self):
  284. with unittest.mock.patch('qubes.property.__set__') as mock:
  285. with self.assertRaises(qubes.exc.QubesValueError):
  286. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  287. b'autostart', b'some string')
  288. self.assertFalse(mock.called)
  289. self.assertFalse(self.app.save.called)
  290. def test_037_vm_propert_set_bool_invalid2(self):
  291. with unittest.mock.patch('qubes.property.__set__') as mock:
  292. with self.assertRaises(qubes.exc.QubesValueError):
  293. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  294. b'autostart', b'\x80\x90@#$%^&*(')
  295. self.assertFalse(mock.called)
  296. self.assertFalse(self.app.save.called)
  297. def test_038_vm_propert_set_str(self):
  298. with unittest.mock.patch('qubes.property.__set__') as mock:
  299. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  300. b'kernel', b'1.0')
  301. self.assertIsNone(value)
  302. mock.assert_called_once_with(self.vm, '1.0')
  303. self.app.save.assert_called_once_with()
  304. def test_039_vm_propert_set_str_invalid1(self):
  305. with unittest.mock.patch('qubes.property.__set__') as mock:
  306. with self.assertRaises(qubes.exc.QubesValueError):
  307. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  308. b'kernel', b'some, non-ASCII: \x80\xd2')
  309. self.assertFalse(mock.called)
  310. self.assertFalse(self.app.save.called)
  311. def test_040_vm_propert_set_int(self):
  312. with unittest.mock.patch('qubes.property.__set__') as mock:
  313. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  314. b'maxmem', b'1024000')
  315. self.assertIsNone(value)
  316. mock.assert_called_once_with(self.vm, 1024000)
  317. self.app.save.assert_called_once_with()
  318. def test_041_vm_propert_set_int_invalid1(self):
  319. with unittest.mock.patch('qubes.property.__set__') as mock:
  320. with self.assertRaises(qubes.exc.QubesValueError):
  321. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  322. b'maxmem', b'fourty two')
  323. self.assertFalse(mock.called)
  324. self.assertFalse(self.app.save.called)
  325. def test_042_vm_propert_set_label(self):
  326. with unittest.mock.patch('qubes.property.__set__') as mock:
  327. value = self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  328. b'label', b'green')
  329. self.assertIsNone(value)
  330. mock.assert_called_once_with(self.vm, 'green')
  331. self.app.save.assert_called_once_with()
  332. def test_043_vm_propert_set_label_invalid1(self):
  333. with unittest.mock.patch('qubes.property.__set__') as mock:
  334. with self.assertRaises(qubes.exc.QubesValueError):
  335. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  336. b'maxmem', b'some, non-ASCII: \x80\xd2')
  337. self.assertFalse(mock.called)
  338. self.assertFalse(self.app.save.called)
  339. @unittest.skip('label existence not checked before actual setter yet')
  340. def test_044_vm_propert_set_label_invalid2(self):
  341. with unittest.mock.patch('qubes.property.__set__') as mock:
  342. with self.assertRaises(qubes.exc.QubesValueError):
  343. self.call_mgmt_func(b'admin.vm.property.Set', b'test-vm1',
  344. b'maxmem', b'non-existing-color')
  345. self.assertFalse(mock.called)
  346. self.assertFalse(self.app.save.called)
  347. def test_050_vm_property_help(self):
  348. value = self.call_mgmt_func(b'admin.vm.property.Help', b'test-vm1',
  349. b'label')
  350. self.assertEqual(value,
  351. 'Colourful label assigned to VM. This is where the colour of the '
  352. 'padlock is set.')
  353. self.assertFalse(self.app.save.called)
  354. def test_052_vm_property_help_invalid_property(self):
  355. with self.assertRaises(qubes.exc.QubesNoSuchPropertyError):
  356. self.call_mgmt_func(b'admin.vm.property.Help', b'test-vm1',
  357. b'no-such-property')
  358. self.assertFalse(self.app.save.called)
  359. def test_060_vm_property_reset(self):
  360. with unittest.mock.patch('qubes.property.__delete__') as mock:
  361. value = self.call_mgmt_func(b'admin.vm.property.Reset', b'test-vm1',
  362. b'default_user')
  363. mock.assert_called_with(self.vm)
  364. self.assertIsNone(value)
  365. self.app.save.assert_called_once_with()
  366. def test_062_vm_property_reset_invalid_property(self):
  367. with unittest.mock.patch('qubes.property.__delete__') as mock:
  368. with self.assertRaises(qubes.exc.QubesNoSuchPropertyError):
  369. self.call_mgmt_func(b'admin.vm.property.Help', b'test-vm1',
  370. b'no-such-property')
  371. self.assertFalse(mock.called)
  372. self.assertFalse(self.app.save.called)
  373. def test_070_vm_volume_list(self):
  374. self.vm.volumes = unittest.mock.Mock()
  375. volumes_conf = {
  376. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  377. }
  378. self.vm.volumes.configure_mock(**volumes_conf)
  379. value = self.call_mgmt_func(b'admin.vm.volume.List', b'test-vm1')
  380. self.assertEqual(value, 'root\nprivate\nvolatile\nkernel\n')
  381. # check if _only_ keys were accessed
  382. self.assertEqual(self.vm.volumes.mock_calls,
  383. [unittest.mock.call.keys()])
  384. def test_080_vm_volume_info(self):
  385. self.vm.volumes = unittest.mock.MagicMock()
  386. volumes_conf = {
  387. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  388. }
  389. for prop in volume_properties:
  390. volumes_conf[
  391. '__getitem__.return_value.{}'.format(prop)] = prop + '-value'
  392. volumes_conf[
  393. '__getitem__.return_value.is_outdated.return_value'] = False
  394. self.vm.volumes.configure_mock(**volumes_conf)
  395. value = self.call_mgmt_func(b'admin.vm.volume.Info', b'test-vm1',
  396. b'private')
  397. self.assertEqual(value,
  398. ''.join('{p}={p}-value\n'.format(p=p) for p in volume_properties) +
  399. 'is_outdated=False\n')
  400. self.assertEqual(self.vm.volumes.mock_calls,
  401. [unittest.mock.call.keys(),
  402. unittest.mock.call.__getattr__('__getitem__')('private'),
  403. unittest.mock.call.__getattr__('__getitem__')().is_outdated()])
  404. def test_081_vm_volume_info_unsupported_is_outdated(self):
  405. self.vm.volumes = unittest.mock.MagicMock()
  406. volumes_conf = {
  407. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  408. }
  409. for prop in volume_properties:
  410. volumes_conf[
  411. '__getitem__.return_value.{}'.format(prop)] = prop + '-value'
  412. volumes_conf[
  413. '__getitem__.return_value.is_outdated.side_effect'] = \
  414. NotImplementedError
  415. self.vm.volumes.configure_mock(**volumes_conf)
  416. value = self.call_mgmt_func(b'admin.vm.volume.Info', b'test-vm1',
  417. b'private')
  418. self.assertEqual(value,
  419. ''.join('{p}={p}-value\n'.format(p=p) for p in volume_properties))
  420. self.assertEqual(self.vm.volumes.mock_calls,
  421. [unittest.mock.call.keys(),
  422. unittest.mock.call.__getattr__('__getitem__')('private'),
  423. unittest.mock.call.__getattr__('__getitem__')().is_outdated()])
  424. def test_080_vm_volume_info_invalid_volume(self):
  425. self.vm.volumes = unittest.mock.MagicMock()
  426. volumes_conf = {
  427. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  428. }
  429. self.vm.volumes.configure_mock(**volumes_conf)
  430. with self.assertRaises(qubes.api.PermissionDenied):
  431. self.call_mgmt_func(b'admin.vm.volume.Info', b'test-vm1',
  432. b'no-such-volume')
  433. self.assertEqual(self.vm.volumes.mock_calls,
  434. [unittest.mock.call.keys()])
  435. def test_090_vm_volume_listsnapshots(self):
  436. self.vm.volumes = unittest.mock.MagicMock()
  437. volumes_conf = {
  438. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  439. '__getitem__.return_value.revisions':
  440. {'rev2': '2018-02-22T22:22:22', 'rev1': '2018-01-11T11:11:11'},
  441. }
  442. self.vm.volumes.configure_mock(**volumes_conf)
  443. value = self.call_mgmt_func(b'admin.vm.volume.ListSnapshots',
  444. b'test-vm1', b'private')
  445. self.assertEqual(value,
  446. 'rev1\nrev2\n')
  447. self.assertEqual(self.vm.volumes.mock_calls,
  448. [unittest.mock.call.keys(),
  449. unittest.mock.call.__getattr__('__getitem__')('private')])
  450. def test_090_vm_volume_listsnapshots_invalid_volume(self):
  451. self.vm.volumes = unittest.mock.MagicMock()
  452. volumes_conf = {
  453. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  454. }
  455. self.vm.volumes.configure_mock(**volumes_conf)
  456. with self.assertRaises(qubes.api.PermissionDenied):
  457. self.call_mgmt_func(b'admin.vm.volume.ListSnapshots', b'test-vm1',
  458. b'no-such-volume')
  459. self.assertEqual(self.vm.volumes.mock_calls,
  460. [unittest.mock.call.keys()])
  461. @unittest.skip('method not implemented yet')
  462. def test_100_vm_volume_snapshot(self):
  463. pass
  464. @unittest.skip('method not implemented yet')
  465. def test_100_vm_volume_snapshot_invalid_volume(self):
  466. self.vm.volumes = unittest.mock.MagicMock()
  467. volumes_conf = {
  468. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  469. '__getitem__.return_value.revisions':
  470. {'rev2': '2018-02-22T22:22:22', 'rev1': '2018-01-11T11:11:11'},
  471. }
  472. self.vm.volumes.configure_mock(**volumes_conf)
  473. with self.assertRaises(qubes.api.PermissionDenied):
  474. self.call_mgmt_func(b'admin.vm.volume.Snapshots',
  475. b'test-vm1', b'no-such-volume')
  476. self.assertEqual(self.vm.volumes.mock_calls,
  477. [unittest.mock.call.keys()])
  478. @unittest.skip('method not implemented yet')
  479. def test_100_vm_volume_snapshot_invalid_revision(self):
  480. self.vm.volumes = unittest.mock.MagicMock()
  481. volumes_conf = {
  482. 'keys.return_value': ['root', 'private', 'volatile', 'kernel']
  483. }
  484. self.vm.volumes.configure_mock(**volumes_conf)
  485. with self.assertRaises(qubes.api.PermissionDenied):
  486. self.call_mgmt_func(b'admin.vm.volume.Snapshots',
  487. b'test-vm1', b'private', b'no-such-rev')
  488. self.assertEqual(self.vm.volumes.mock_calls,
  489. [unittest.mock.call.keys(),
  490. unittest.mock.call.__getattr__('__getitem__')('private')])
  491. def test_110_vm_volume_revert(self):
  492. self.vm.volumes = unittest.mock.MagicMock()
  493. volumes_conf = {
  494. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  495. '__getitem__.return_value.revisions':
  496. {'rev2': '2018-02-22T22:22:22', 'rev1': '2018-01-11T11:11:11'},
  497. }
  498. self.vm.volumes.configure_mock(**volumes_conf)
  499. del self.vm.volumes['private'].revert('rev1')._is_coroutine
  500. self.vm.storage = unittest.mock.Mock()
  501. value = self.call_mgmt_func(b'admin.vm.volume.Revert',
  502. b'test-vm1', b'private', b'rev1')
  503. self.assertIsNone(value)
  504. self.assertEqual(self.vm.volumes.mock_calls, [
  505. ('__getitem__', ('private', ), {}),
  506. ('__getitem__().revert', ('rev1', ), {}),
  507. ('keys', (), {}),
  508. ('__getitem__', ('private', ), {}),
  509. ('__getitem__().__hash__', (), {}),
  510. ('__getitem__().revert', ('rev1', ), {}),
  511. ])
  512. self.assertEqual(self.vm.storage.mock_calls, [])
  513. def test_110_vm_volume_revert_invalid_rev(self):
  514. self.vm.volumes = unittest.mock.MagicMock()
  515. volumes_conf = {
  516. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  517. '__getitem__.return_value.revisions':
  518. {'rev2': '2018-02-22T22:22:22', 'rev1': '2018-01-11T11:11:11'},
  519. }
  520. self.vm.volumes.configure_mock(**volumes_conf)
  521. self.vm.storage = unittest.mock.Mock()
  522. with self.assertRaises(qubes.api.PermissionDenied):
  523. self.call_mgmt_func(b'admin.vm.volume.Revert',
  524. b'test-vm1', b'private', b'no-such-rev')
  525. self.assertEqual(self.vm.volumes.mock_calls,
  526. [unittest.mock.call.keys(),
  527. unittest.mock.call.__getattr__('__getitem__')('private')])
  528. self.assertFalse(self.vm.storage.called)
  529. def test_120_vm_volume_resize(self):
  530. self.vm.volumes = unittest.mock.MagicMock()
  531. volumes_conf = {
  532. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  533. }
  534. self.vm.volumes.configure_mock(**volumes_conf)
  535. self.vm.storage = unittest.mock.Mock()
  536. self.vm.storage.resize.side_effect = self.dummy_coro
  537. value = self.call_mgmt_func(b'admin.vm.volume.Resize',
  538. b'test-vm1', b'private', b'1024000000')
  539. self.assertIsNone(value)
  540. self.assertEqual(self.vm.volumes.mock_calls,
  541. [unittest.mock.call.keys()])
  542. self.assertEqual(self.vm.storage.mock_calls,
  543. [unittest.mock.call.resize('private', 1024000000)])
  544. def test_120_vm_volume_resize_invalid_size1(self):
  545. self.vm.volumes = unittest.mock.MagicMock()
  546. volumes_conf = {
  547. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  548. }
  549. self.vm.volumes.configure_mock(**volumes_conf)
  550. self.vm.storage = unittest.mock.Mock()
  551. self.vm.storage.resize.side_effect = self.dummy_coro
  552. with self.assertRaises(qubes.api.PermissionDenied):
  553. self.call_mgmt_func(b'admin.vm.volume.Resize',
  554. b'test-vm1', b'private', b'no-int-size')
  555. self.assertEqual(self.vm.volumes.mock_calls,
  556. [unittest.mock.call.keys()])
  557. self.assertFalse(self.vm.storage.called)
  558. def test_120_vm_volume_resize_invalid_size2(self):
  559. self.vm.volumes = unittest.mock.MagicMock()
  560. volumes_conf = {
  561. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  562. }
  563. self.vm.volumes.configure_mock(**volumes_conf)
  564. self.vm.storage = unittest.mock.Mock()
  565. self.vm.storage.resize.side_effect = self.dummy_coro
  566. with self.assertRaises(qubes.api.PermissionDenied):
  567. self.call_mgmt_func(b'admin.vm.volume.Resize',
  568. b'test-vm1', b'private', b'-1')
  569. self.assertEqual(self.vm.volumes.mock_calls,
  570. [unittest.mock.call.keys()])
  571. self.assertFalse(self.vm.storage.called)
  572. def test_130_pool_list(self):
  573. self.app.pools = ['file', 'lvm']
  574. value = self.call_mgmt_func(b'admin.pool.List', b'dom0')
  575. self.assertEqual(value, 'file\nlvm\n')
  576. self.assertFalse(self.app.save.called)
  577. @unittest.mock.patch('qubes.storage.pool_drivers')
  578. @unittest.mock.patch('qubes.storage.driver_parameters')
  579. def test_140_pool_listdrivers(self, mock_parameters, mock_drivers):
  580. self.app.pools = ['file', 'lvm']
  581. mock_drivers.return_value = ['driver1', 'driver2']
  582. mock_parameters.side_effect = \
  583. lambda driver: {
  584. 'driver1': ['param1', 'param2'],
  585. 'driver2': ['param3', 'param4']
  586. }[driver]
  587. value = self.call_mgmt_func(b'admin.pool.ListDrivers', b'dom0')
  588. self.assertEqual(value,
  589. 'driver1 param1 param2\ndriver2 param3 param4\n')
  590. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  591. self.assertEqual(mock_parameters.mock_calls,
  592. [unittest.mock.call('driver1'), unittest.mock.call('driver2')])
  593. self.assertFalse(self.app.save.called)
  594. def test_150_pool_info(self):
  595. self.app.pools = {
  596. 'pool1': unittest.mock.Mock(config={
  597. 'param1': 'value1', 'param2': 'value2'},
  598. usage=102400,
  599. size=204800)
  600. }
  601. self.app.pools['pool1'].included_in.return_value = None
  602. value = self.call_mgmt_func(b'admin.pool.Info', b'dom0', b'pool1')
  603. self.assertEqual(value,
  604. 'param1=value1\nparam2=value2\nsize=204800\nusage=102400\n')
  605. self.assertFalse(self.app.save.called)
  606. def test_151_pool_info_unsupported_size(self):
  607. self.app.pools = {
  608. 'pool1': unittest.mock.Mock(config={
  609. 'param1': 'value1', 'param2': 'value2'},
  610. size=None, usage=None, usage_details={}),
  611. }
  612. self.app.pools['pool1'].included_in.return_value = None
  613. value = self.call_mgmt_func(b'admin.pool.Info', b'dom0', b'pool1')
  614. self.assertEqual(value,
  615. 'param1=value1\nparam2=value2\n')
  616. self.assertFalse(self.app.save.called)
  617. def test_152_pool_info_included_in(self):
  618. self.app.pools = {
  619. 'pool1': unittest.mock.MagicMock(config={
  620. 'param1': 'value1',
  621. 'param2': 'value2'},
  622. usage=102400,
  623. size=204800)
  624. }
  625. self.app.pools['pool1'].included_in.return_value = \
  626. self.app.pools['pool1']
  627. self.app.pools['pool1'].__str__.return_value = 'pool1'
  628. value = self.call_mgmt_func(b'admin.pool.Info', b'dom0', b'pool1')
  629. self.assertEqual(value,
  630. 'param1=value1\nparam2=value2\nsize=204800\nusage=102400'
  631. '\nincluded_in=pool1\n')
  632. self.assertFalse(self.app.save.called)
  633. def test_153_pool_usage(self):
  634. self.app.pools = {
  635. 'pool1': unittest.mock.Mock(config={
  636. 'param1': 'value1', 'param2': 'value2'},
  637. usage_details={
  638. 'data_usage': 102400,
  639. 'data_size': 204800,
  640. 'metadata_size': 1024,
  641. 'metadata_usage': 50})
  642. }
  643. self.app.pools['pool1'].included_in.return_value = None
  644. value = self.call_mgmt_func(b'admin.pool.UsageDetails', b'dom0', b'pool1')
  645. self.assertEqual(value,
  646. 'data_size=204800\ndata_usage=102400\nmetadata_size=1024\nmetadata_usage=50\n')
  647. self.assertFalse(self.app.save.called)
  648. @unittest.mock.patch('qubes.storage.pool_drivers')
  649. @unittest.mock.patch('qubes.storage.driver_parameters')
  650. def test_160_pool_add(self, mock_parameters, mock_drivers):
  651. self.app.pools = {
  652. 'file': unittest.mock.Mock(),
  653. 'lvm': unittest.mock.Mock()
  654. }
  655. mock_drivers.return_value = ['driver1', 'driver2']
  656. mock_parameters.side_effect = \
  657. lambda driver: {
  658. 'driver1': ['param1', 'param2'],
  659. 'driver2': ['param3', 'param4']
  660. }[driver]
  661. add_pool_mock, self.app.add_pool = self.coroutine_mock()
  662. value = self.call_mgmt_func(b'admin.pool.Add', b'dom0', b'driver1',
  663. b'name=test-pool\nparam1=some-value\n')
  664. self.assertIsNone(value)
  665. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  666. self.assertEqual(mock_parameters.mock_calls,
  667. [unittest.mock.call('driver1')])
  668. self.assertEqual(add_pool_mock.mock_calls,
  669. [unittest.mock.call(name='test-pool', driver='driver1',
  670. param1='some-value')])
  671. self.assertTrue(self.app.save.called)
  672. @unittest.mock.patch('qubes.storage.pool_drivers')
  673. @unittest.mock.patch('qubes.storage.driver_parameters')
  674. def test_160_pool_add_invalid_driver(self, mock_parameters, mock_drivers):
  675. self.app.pools = {
  676. 'file': unittest.mock.Mock(),
  677. 'lvm': unittest.mock.Mock()
  678. }
  679. mock_drivers.return_value = ['driver1', 'driver2']
  680. mock_parameters.side_effect = \
  681. lambda driver: {
  682. 'driver1': ['param1', 'param2'],
  683. 'driver2': ['param3', 'param4']
  684. }[driver]
  685. add_pool_mock, self.app.add_pool = self.coroutine_mock()
  686. with self.assertRaises(qubes.api.PermissionDenied):
  687. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  688. b'no-such-driver', b'name=test-pool\nparam1=some-value\n')
  689. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  690. self.assertEqual(mock_parameters.mock_calls, [])
  691. self.assertEqual(add_pool_mock.mock_calls, [])
  692. self.assertFalse(self.app.save.called)
  693. @unittest.mock.patch('qubes.storage.pool_drivers')
  694. @unittest.mock.patch('qubes.storage.driver_parameters')
  695. def test_160_pool_add_invalid_param(self, mock_parameters, mock_drivers):
  696. self.app.pools = {
  697. 'file': unittest.mock.Mock(),
  698. 'lvm': unittest.mock.Mock()
  699. }
  700. mock_drivers.return_value = ['driver1', 'driver2']
  701. mock_parameters.side_effect = \
  702. lambda driver: {
  703. 'driver1': ['param1', 'param2'],
  704. 'driver2': ['param3', 'param4']
  705. }[driver]
  706. add_pool_mock, self.app.add_pool = self.coroutine_mock()
  707. with self.assertRaises(qubes.exc.QubesException):
  708. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  709. b'driver1', b'name=test-pool\nparam3=some-value\n')
  710. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  711. self.assertEqual(mock_parameters.mock_calls,
  712. [unittest.mock.call('driver1')])
  713. self.assertEqual(add_pool_mock.mock_calls, [])
  714. self.assertFalse(self.app.save.called)
  715. @unittest.mock.patch('qubes.storage.pool_drivers')
  716. @unittest.mock.patch('qubes.storage.driver_parameters')
  717. def test_160_pool_add_missing_name(self, mock_parameters, mock_drivers):
  718. self.app.pools = {
  719. 'file': unittest.mock.Mock(),
  720. 'lvm': unittest.mock.Mock()
  721. }
  722. mock_drivers.return_value = ['driver1', 'driver2']
  723. mock_parameters.side_effect = \
  724. lambda driver: {
  725. 'driver1': ['param1', 'param2'],
  726. 'driver2': ['param3', 'param4']
  727. }[driver]
  728. add_pool_mock, self.app.add_pool = self.coroutine_mock()
  729. with self.assertRaises(qubes.api.PermissionDenied):
  730. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  731. b'driver1', b'param1=value\nparam2=some-value\n')
  732. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  733. self.assertEqual(mock_parameters.mock_calls, [])
  734. self.assertEqual(add_pool_mock.mock_calls, [])
  735. self.assertFalse(self.app.save.called)
  736. @unittest.mock.patch('qubes.storage.pool_drivers')
  737. @unittest.mock.patch('qubes.storage.driver_parameters')
  738. def test_160_pool_add_existing_pool(self, mock_parameters, mock_drivers):
  739. self.app.pools = {
  740. 'file': unittest.mock.Mock(),
  741. 'lvm': unittest.mock.Mock()
  742. }
  743. mock_drivers.return_value = ['driver1', 'driver2']
  744. mock_parameters.side_effect = \
  745. lambda driver: {
  746. 'driver1': ['param1', 'param2'],
  747. 'driver2': ['param3', 'param4']
  748. }[driver]
  749. add_pool_mock, self.app.add_pool = self.coroutine_mock()
  750. with self.assertRaises(qubes.api.PermissionDenied):
  751. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  752. b'driver1', b'name=file\nparam1=value\nparam2=some-value\n')
  753. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  754. self.assertEqual(mock_parameters.mock_calls, [])
  755. self.assertEqual(add_pool_mock.mock_calls, [])
  756. self.assertFalse(self.app.save.called)
  757. @unittest.mock.patch('qubes.storage.pool_drivers')
  758. @unittest.mock.patch('qubes.storage.driver_parameters')
  759. def test_160_pool_add_invalid_config_format(self, mock_parameters,
  760. mock_drivers):
  761. self.app.pools = {
  762. 'file': unittest.mock.Mock(),
  763. 'lvm': unittest.mock.Mock()
  764. }
  765. mock_drivers.return_value = ['driver1', 'driver2']
  766. mock_parameters.side_effect = \
  767. lambda driver: {
  768. 'driver1': ['param1', 'param2'],
  769. 'driver2': ['param3', 'param4']
  770. }[driver]
  771. add_pool_mock, self.app.add_pool = self.coroutine_mock()
  772. with self.assertRaises(qubes.api.PermissionDenied):
  773. self.call_mgmt_func(b'admin.pool.Add', b'dom0',
  774. b'driver1', b'name=test-pool\nparam 1=value\n_param2\n')
  775. self.assertEqual(mock_drivers.mock_calls, [unittest.mock.call()])
  776. self.assertEqual(mock_parameters.mock_calls, [])
  777. self.assertEqual(add_pool_mock.mock_calls, [])
  778. self.assertFalse(self.app.save.called)
  779. def test_170_pool_remove(self):
  780. self.app.pools = {
  781. 'file': unittest.mock.Mock(),
  782. 'lvm': unittest.mock.Mock(),
  783. 'test-pool': unittest.mock.Mock(),
  784. }
  785. remove_pool_mock, self.app.remove_pool = self.coroutine_mock()
  786. value = self.call_mgmt_func(b'admin.pool.Remove', b'dom0', b'test-pool')
  787. self.assertIsNone(value)
  788. self.assertEqual(remove_pool_mock.mock_calls,
  789. [unittest.mock.call('test-pool')])
  790. self.assertTrue(self.app.save.called)
  791. def test_170_pool_remove_invalid_pool(self):
  792. self.app.pools = {
  793. 'file': unittest.mock.Mock(),
  794. 'lvm': unittest.mock.Mock(),
  795. 'test-pool': unittest.mock.Mock(),
  796. }
  797. remove_pool_mock, self.app.remove_pool = self.coroutine_mock()
  798. with self.assertRaises(qubes.api.PermissionDenied):
  799. self.call_mgmt_func(b'admin.pool.Remove', b'dom0',
  800. b'no-such-pool')
  801. self.assertEqual(remove_pool_mock.mock_calls, [])
  802. self.assertFalse(self.app.save.called)
  803. def test_180_label_list(self):
  804. value = self.call_mgmt_func(b'admin.label.List', b'dom0')
  805. self.assertEqual(value,
  806. ''.join('{}\n'.format(l.name) for l in self.app.labels.values()))
  807. self.assertFalse(self.app.save.called)
  808. def test_190_label_get(self):
  809. self.app.get_label = unittest.mock.Mock()
  810. self.app.get_label.configure_mock(**{'return_value.color': '0xff0000'})
  811. value = self.call_mgmt_func(b'admin.label.Get', b'dom0', b'red')
  812. self.assertEqual(value, '0xff0000')
  813. self.assertEqual(self.app.get_label.mock_calls,
  814. [unittest.mock.call('red')])
  815. self.assertFalse(self.app.save.called)
  816. def test_195_label_index(self):
  817. self.app.get_label = unittest.mock.Mock()
  818. self.app.get_label.configure_mock(**{'return_value.index': 1})
  819. value = self.call_mgmt_func(b'admin.label.Index', b'dom0', b'red')
  820. self.assertEqual(value, '1')
  821. self.assertEqual(self.app.get_label.mock_calls,
  822. [unittest.mock.call('red')])
  823. self.assertFalse(self.app.save.called)
  824. def test_200_label_create(self):
  825. self.app.get_label = unittest.mock.Mock()
  826. self.app.get_label.side_effect=KeyError
  827. self.app.labels = unittest.mock.MagicMock()
  828. labels_config = {
  829. 'keys.return_value': range(1, 9),
  830. }
  831. self.app.labels.configure_mock(**labels_config)
  832. value = self.call_mgmt_func(b'admin.label.Create', b'dom0', b'cyan',
  833. b'0x00ffff')
  834. self.assertIsNone(value)
  835. self.assertEqual(self.app.get_label.mock_calls,
  836. [unittest.mock.call('cyan')])
  837. self.assertEqual(self.app.labels.mock_calls,
  838. [unittest.mock.call.keys(),
  839. unittest.mock.call.__getattr__('__setitem__')(9,
  840. qubes.Label(9, '0x00ffff', 'cyan'))])
  841. self.assertTrue(self.app.save.called)
  842. def test_200_label_create_invalid_color(self):
  843. self.app.get_label = unittest.mock.Mock()
  844. self.app.get_label.side_effect=KeyError
  845. self.app.labels = unittest.mock.MagicMock()
  846. labels_config = {
  847. 'keys.return_value': range(1, 9),
  848. }
  849. self.app.labels.configure_mock(**labels_config)
  850. with self.assertRaises(qubes.api.PermissionDenied):
  851. self.call_mgmt_func(b'admin.label.Create', b'dom0', b'cyan',
  852. b'abcd')
  853. self.assertEqual(self.app.get_label.mock_calls,
  854. [unittest.mock.call('cyan')])
  855. self.assertEqual(self.app.labels.mock_calls, [])
  856. self.assertFalse(self.app.save.called)
  857. def test_200_label_create_invalid_name(self):
  858. self.app.get_label = unittest.mock.Mock()
  859. self.app.get_label.side_effect=KeyError
  860. self.app.labels = unittest.mock.MagicMock()
  861. labels_config = {
  862. 'keys.return_value': range(1, 9),
  863. }
  864. self.app.labels.configure_mock(**labels_config)
  865. with self.assertRaises(qubes.api.PermissionDenied):
  866. self.call_mgmt_func(b'admin.label.Create', b'dom0', b'01',
  867. b'0xff0000')
  868. with self.assertRaises(qubes.api.PermissionDenied):
  869. self.call_mgmt_func(b'admin.label.Create', b'dom0', b'../xxx',
  870. b'0xff0000')
  871. with self.assertRaises(qubes.api.PermissionDenied):
  872. self.call_mgmt_func(b'admin.label.Create', b'dom0',
  873. b'strange-name!@#$',
  874. b'0xff0000')
  875. self.assertEqual(self.app.get_label.mock_calls, [])
  876. self.assertEqual(self.app.labels.mock_calls, [])
  877. self.assertFalse(self.app.save.called)
  878. def test_200_label_create_already_exists(self):
  879. self.app.get_label = unittest.mock.Mock(wraps=self.app.get_label)
  880. with self.assertRaises(qubes.exc.QubesValueError):
  881. self.call_mgmt_func(b'admin.label.Create', b'dom0', b'red',
  882. b'abcd')
  883. self.assertEqual(self.app.get_label.mock_calls,
  884. [unittest.mock.call('red')])
  885. self.assertFalse(self.app.save.called)
  886. def test_210_label_remove(self):
  887. label = qubes.Label(9, '0x00ffff', 'cyan')
  888. self.app.labels[9] = label
  889. self.app.get_label = unittest.mock.Mock(wraps=self.app.get_label,
  890. **{'return_value.index': 9})
  891. self.app.labels = unittest.mock.MagicMock(wraps=self.app.labels)
  892. value = self.call_mgmt_func(b'admin.label.Remove', b'dom0', b'cyan')
  893. self.assertIsNone(value)
  894. self.assertEqual(self.app.get_label.mock_calls,
  895. [unittest.mock.call('cyan')])
  896. self.assertEqual(self.app.labels.mock_calls,
  897. [unittest.mock.call.__delitem__(9)])
  898. self.assertTrue(self.app.save.called)
  899. def test_210_label_remove_invalid_label(self):
  900. with self.assertRaises(qubes.exc.QubesValueError):
  901. self.call_mgmt_func(b'admin.label.Remove', b'dom0',
  902. b'no-such-label')
  903. self.assertFalse(self.app.save.called)
  904. def test_210_label_remove_default_label(self):
  905. self.app.labels = unittest.mock.MagicMock(wraps=self.app.labels)
  906. self.app.get_label = unittest.mock.Mock(wraps=self.app.get_label,
  907. **{'return_value.index': 6})
  908. with self.assertRaises(qubes.api.PermissionDenied):
  909. self.call_mgmt_func(b'admin.label.Remove', b'dom0',
  910. b'blue')
  911. self.assertEqual(self.app.labels.mock_calls, [])
  912. self.assertFalse(self.app.save.called)
  913. def test_210_label_remove_in_use(self):
  914. self.app.labels = unittest.mock.MagicMock(wraps=self.app.labels)
  915. self.app.get_label = unittest.mock.Mock(wraps=self.app.get_label,
  916. **{'return_value.index': 1})
  917. with self.assertRaises(qubes.api.PermissionDenied):
  918. self.call_mgmt_func(b'admin.label.Remove', b'dom0',
  919. b'red')
  920. self.assertEqual(self.app.labels.mock_calls, [])
  921. self.assertFalse(self.app.save.called)
  922. def test_220_start(self):
  923. func_mock = unittest.mock.Mock()
  924. @asyncio.coroutine
  925. def coroutine_mock(*args, **kwargs):
  926. return func_mock(*args, **kwargs)
  927. self.vm.start = coroutine_mock
  928. value = self.call_mgmt_func(b'admin.vm.Start', b'test-vm1')
  929. self.assertIsNone(value)
  930. func_mock.assert_called_once_with()
  931. def test_230_shutdown(self):
  932. func_mock = unittest.mock.Mock()
  933. @asyncio.coroutine
  934. def coroutine_mock(*args, **kwargs):
  935. return func_mock(*args, **kwargs)
  936. self.vm.shutdown = coroutine_mock
  937. value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1')
  938. self.assertIsNone(value)
  939. func_mock.assert_called_once_with(force=False, wait=False)
  940. def test_231_shutdown_force(self):
  941. func_mock = unittest.mock.Mock()
  942. @asyncio.coroutine
  943. def coroutine_mock(*args, **kwargs):
  944. return func_mock(*args, **kwargs)
  945. self.vm.shutdown = coroutine_mock
  946. value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'force')
  947. self.assertIsNone(value)
  948. func_mock.assert_called_once_with(force=True, wait=False)
  949. def test_232_shutdown_wait(self):
  950. func_mock = unittest.mock.Mock()
  951. @asyncio.coroutine
  952. def coroutine_mock(*args, **kwargs):
  953. return func_mock(*args, **kwargs)
  954. self.vm.shutdown = coroutine_mock
  955. value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'wait')
  956. self.assertIsNone(value)
  957. func_mock.assert_called_once_with(force=False, wait=True)
  958. def test_233_shutdown_wait_force(self):
  959. func_mock = unittest.mock.Mock()
  960. @asyncio.coroutine
  961. def coroutine_mock(*args, **kwargs):
  962. return func_mock(*args, **kwargs)
  963. self.vm.shutdown = coroutine_mock
  964. value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'wait+force')
  965. self.assertIsNone(value)
  966. func_mock.assert_called_once_with(force=True, wait=True)
  967. def test_234_shutdown_force_wait(self):
  968. func_mock = unittest.mock.Mock()
  969. @asyncio.coroutine
  970. def coroutine_mock(*args, **kwargs):
  971. return func_mock(*args, **kwargs)
  972. self.vm.shutdown = coroutine_mock
  973. value = self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'force+wait')
  974. self.assertIsNone(value)
  975. func_mock.assert_called_once_with(force=True, wait=True)
  976. def test_234_shutdown_force_wait_invalid(self):
  977. func_mock = unittest.mock.Mock()
  978. @asyncio.coroutine
  979. def coroutine_mock(*args, **kwargs):
  980. return func_mock(*args, **kwargs)
  981. self.vm.shutdown = coroutine_mock
  982. with self.assertRaises(qubes.api.PermissionDenied):
  983. self.call_mgmt_func(b'admin.vm.Shutdown', b'test-vm1', b'forcewait')
  984. func_mock.assert_not_called()
  985. def test_240_pause(self):
  986. func_mock = unittest.mock.Mock()
  987. @asyncio.coroutine
  988. def coroutine_mock(*args, **kwargs):
  989. return func_mock(*args, **kwargs)
  990. self.vm.pause = coroutine_mock
  991. value = self.call_mgmt_func(b'admin.vm.Pause', b'test-vm1')
  992. self.assertIsNone(value)
  993. func_mock.assert_called_once_with()
  994. def test_250_unpause(self):
  995. func_mock = unittest.mock.Mock()
  996. @asyncio.coroutine
  997. def coroutine_mock(*args, **kwargs):
  998. return func_mock(*args, **kwargs)
  999. self.vm.unpause = coroutine_mock
  1000. value = self.call_mgmt_func(b'admin.vm.Unpause', b'test-vm1')
  1001. self.assertIsNone(value)
  1002. func_mock.assert_called_once_with()
  1003. def test_260_kill(self):
  1004. func_mock = unittest.mock.Mock()
  1005. @asyncio.coroutine
  1006. def coroutine_mock(*args, **kwargs):
  1007. return func_mock(*args, **kwargs)
  1008. self.vm.kill = coroutine_mock
  1009. value = self.call_mgmt_func(b'admin.vm.Kill', b'test-vm1')
  1010. self.assertIsNone(value)
  1011. func_mock.assert_called_once_with()
  1012. def test_270_events(self):
  1013. send_event = unittest.mock.Mock(spec=[])
  1014. mgmt_obj = qubes.api.admin.QubesAdminAPI(self.app, b'dom0', b'admin.Events',
  1015. b'dom0', b'', send_event=send_event)
  1016. @asyncio.coroutine
  1017. def fire_event():
  1018. self.vm.fire_event('test-event', arg1='abc')
  1019. mgmt_obj.cancel()
  1020. loop = asyncio.get_event_loop()
  1021. execute_task = asyncio.ensure_future(
  1022. mgmt_obj.execute(untrusted_payload=b''))
  1023. asyncio.ensure_future(fire_event())
  1024. loop.run_until_complete(execute_task)
  1025. self.assertIsNone(execute_task.result())
  1026. self.assertEventFired(self.emitter,
  1027. 'admin-permission:' + 'admin.Events')
  1028. self.assertEqual(send_event.mock_calls,
  1029. [
  1030. unittest.mock.call(self.app, 'connection-established'),
  1031. unittest.mock.call(self.vm, 'test-event', arg1='abc')
  1032. ])
  1033. def test_271_events_add_vm(self):
  1034. send_event = unittest.mock.Mock(spec=[])
  1035. mgmt_obj = qubes.api.admin.QubesAdminAPI(self.app, b'dom0', b'admin.Events',
  1036. b'dom0', b'', send_event=send_event)
  1037. @asyncio.coroutine
  1038. def fire_event():
  1039. self.vm.fire_event('test-event', arg1='abc')
  1040. # add VM _after_ starting admin.Events call
  1041. vm = self.app.add_new_vm('AppVM', label='red', name='test-vm2',
  1042. template='test-template')
  1043. vm.fire_event('test-event2', arg1='abc')
  1044. mgmt_obj.cancel()
  1045. return vm
  1046. loop = asyncio.get_event_loop()
  1047. execute_task = asyncio.ensure_future(
  1048. mgmt_obj.execute(untrusted_payload=b''))
  1049. event_task = asyncio.ensure_future(fire_event())
  1050. loop.run_until_complete(execute_task)
  1051. vm2 = event_task.result()
  1052. self.assertIsNone(execute_task.result())
  1053. self.assertEventFired(self.emitter,
  1054. 'admin-permission:' + 'admin.Events')
  1055. self.assertEqual(send_event.mock_calls,
  1056. [
  1057. unittest.mock.call(self.app, 'connection-established'),
  1058. unittest.mock.call(self.vm, 'test-event', arg1='abc'),
  1059. unittest.mock.call(self.app, 'domain-add', vm=vm2),
  1060. unittest.mock.call(vm2, 'test-event2', arg1='abc'),
  1061. ])
  1062. def test_272_events_filter(self):
  1063. with tempfile.TemporaryDirectory() as tmpdir:
  1064. tmpdir = pathlib.Path(tmpdir)
  1065. with unittest.mock.patch(
  1066. 'qubes.ext.admin.AdminExtension._instance.policy_cache.path',
  1067. pathlib.Path(tmpdir)):
  1068. with (tmpdir / 'admin.policy').open('w') as f:
  1069. f.write('admin.Events * @anyvm @adminvm allow\n')
  1070. f.write('admin.Events * @anyvm test-vm1 allow')
  1071. send_event = unittest.mock.Mock(spec=[])
  1072. mgmt_obj = qubes.api.admin.QubesAdminAPI(self.app, b'test-vm1',
  1073. b'admin.Events',
  1074. b'dom0', b'', send_event=send_event)
  1075. @asyncio.coroutine
  1076. def fire_event():
  1077. # add VM _after_ starting admin.Events call
  1078. vm = self.app.add_new_vm('AppVM', label='red',
  1079. name='test-vm2',
  1080. template='test-template')
  1081. vm.fire_event('test-event2', arg1='abc')
  1082. self.vm.fire_event('test-event', arg1='abc')
  1083. mgmt_obj.cancel()
  1084. return vm
  1085. loop = asyncio.get_event_loop()
  1086. execute_task = asyncio.ensure_future(
  1087. mgmt_obj.execute(untrusted_payload=b''))
  1088. event_task = asyncio.ensure_future(fire_event())
  1089. loop.run_until_complete(execute_task)
  1090. vm2 = event_task.result()
  1091. self.assertIsNone(execute_task.result())
  1092. self.assertEqual(send_event.mock_calls,
  1093. [
  1094. unittest.mock.call(self.app, 'connection-established'),
  1095. unittest.mock.call(self.vm, 'test-event', arg1='abc'),
  1096. ])
  1097. def test_280_feature_list(self):
  1098. self.vm.features['test-feature'] = 'some-value'
  1099. value = self.call_mgmt_func(b'admin.vm.feature.List', b'test-vm1')
  1100. self.assertEqual(value, 'test-feature\n')
  1101. self.assertFalse(self.app.save.called)
  1102. def test_290_feature_get(self):
  1103. self.vm.features['test-feature'] = 'some-value'
  1104. value = self.call_mgmt_func(b'admin.vm.feature.Get', b'test-vm1',
  1105. b'test-feature')
  1106. self.assertEqual(value, 'some-value')
  1107. self.assertFalse(self.app.save.called)
  1108. def test_291_feature_get_none(self):
  1109. with self.assertRaises(qubes.exc.QubesFeatureNotFoundError):
  1110. self.call_mgmt_func(b'admin.vm.feature.Get',
  1111. b'test-vm1', b'test-feature')
  1112. self.assertFalse(self.app.save.called)
  1113. def test_300_feature_remove(self):
  1114. self.vm.features['test-feature'] = 'some-value'
  1115. value = self.call_mgmt_func(b'admin.vm.feature.Remove', b'test-vm1',
  1116. b'test-feature')
  1117. self.assertIsNone(value, None)
  1118. self.assertNotIn('test-feature', self.vm.features)
  1119. self.assertTrue(self.app.save.called)
  1120. def test_301_feature_remove_none(self):
  1121. with self.assertRaises(qubes.exc.QubesFeatureNotFoundError):
  1122. self.call_mgmt_func(b'admin.vm.feature.Remove',
  1123. b'test-vm1', b'test-feature')
  1124. self.assertFalse(self.app.save.called)
  1125. def test_310_feature_checkwithtemplate(self):
  1126. self.vm.features['test-feature'] = 'some-value'
  1127. value = self.call_mgmt_func(b'admin.vm.feature.CheckWithTemplate',
  1128. b'test-vm1', b'test-feature')
  1129. self.assertEqual(value, 'some-value')
  1130. self.assertFalse(self.app.save.called)
  1131. def test_311_feature_checkwithtemplate_tpl(self):
  1132. self.template.features['test-feature'] = 'some-value'
  1133. value = self.call_mgmt_func(b'admin.vm.feature.CheckWithTemplate',
  1134. b'test-vm1', b'test-feature')
  1135. self.assertEqual(value, 'some-value')
  1136. self.assertFalse(self.app.save.called)
  1137. def test_312_feature_checkwithtemplate_none(self):
  1138. with self.assertRaises(qubes.exc.QubesFeatureNotFoundError):
  1139. self.call_mgmt_func(b'admin.vm.feature.CheckWithTemplate',
  1140. b'test-vm1', b'test-feature')
  1141. self.assertFalse(self.app.save.called)
  1142. def test_315_feature_checkwithnetvm(self):
  1143. self.vm.features['test-feature'] = 'some-value'
  1144. value = self.call_mgmt_func(b'admin.vm.feature.CheckWithNetvm',
  1145. b'test-vm1', b'test-feature')
  1146. self.assertEqual(value, 'some-value')
  1147. self.assertFalse(self.app.save.called)
  1148. def test_316_feature_checkwithnetvm_netvm(self):
  1149. self.netvm = self.app.add_new_vm('AppVM', label='red',
  1150. name='test-netvm1',
  1151. template='test-template',
  1152. provides_network=True)
  1153. self.vm.netvm = self.netvm
  1154. self.netvm.features['test-feature'] = 'some-value'
  1155. value = self.call_mgmt_func(b'admin.vm.feature.CheckWithNetvm',
  1156. b'test-vm1', b'test-feature')
  1157. self.assertEqual(value, 'some-value')
  1158. self.assertFalse(self.app.save.called)
  1159. def test_317_feature_checkwithnetvm_none(self):
  1160. with self.assertRaises(qubes.exc.QubesFeatureNotFoundError):
  1161. self.call_mgmt_func(b'admin.vm.feature.CheckWithNetvm',
  1162. b'test-vm1', b'test-feature')
  1163. self.assertFalse(self.app.save.called)
  1164. def test_318_feature_checkwithadminvm(self):
  1165. self.app.domains['dom0'].features['test-feature'] = 'some-value'
  1166. value = self.call_mgmt_func(b'admin.vm.feature.CheckWithAdminVM',
  1167. b'test-vm1', b'test-feature')
  1168. self.assertEqual(value, 'some-value')
  1169. self.assertFalse(self.app.save.called)
  1170. def test_319_feature_checkwithtpladminvm(self):
  1171. self.app.domains['dom0'].features['test-feature'] = 'some-value'
  1172. value = self.call_mgmt_func(
  1173. b'admin.vm.feature.CheckWithTemplateAndAdminVM',
  1174. b'test-vm1', b'test-feature')
  1175. self.assertEqual(value, 'some-value')
  1176. self.template.features['test-feature'] = 'some-value2'
  1177. value = self.call_mgmt_func(
  1178. b'admin.vm.feature.CheckWithTemplateAndAdminVM',
  1179. b'test-vm1', b'test-feature')
  1180. self.assertEqual(value, 'some-value2')
  1181. self.assertFalse(self.app.save.called)
  1182. def test_320_feature_set(self):
  1183. value = self.call_mgmt_func(b'admin.vm.feature.Set',
  1184. b'test-vm1', b'test-feature', b'some-value')
  1185. self.assertIsNone(value)
  1186. self.assertEqual(self.vm.features['test-feature'], 'some-value')
  1187. self.assertTrue(self.app.save.called)
  1188. def test_321_feature_set_empty(self):
  1189. value = self.call_mgmt_func(b'admin.vm.feature.Set',
  1190. b'test-vm1', b'test-feature', b'')
  1191. self.assertIsNone(value)
  1192. self.assertEqual(self.vm.features['test-feature'], '')
  1193. self.assertTrue(self.app.save.called)
  1194. def test_320_feature_set_invalid(self):
  1195. with self.assertRaises(UnicodeDecodeError):
  1196. self.call_mgmt_func(b'admin.vm.feature.Set',
  1197. b'test-vm1', b'test-feature', b'\x02\x03\xffsome-value')
  1198. self.assertNotIn('test-feature', self.vm.features)
  1199. self.assertFalse(self.app.save.called)
  1200. @asyncio.coroutine
  1201. def dummy_coro(self, *args, **kwargs):
  1202. pass
  1203. def coroutine_mock(self):
  1204. func_mock = unittest.mock.Mock()
  1205. @asyncio.coroutine
  1206. def coroutine_mock(*args, **kwargs):
  1207. return func_mock(*args, **kwargs)
  1208. return func_mock, coroutine_mock
  1209. @unittest.mock.patch('qubes.storage.Storage.create')
  1210. def test_330_vm_create_standalone(self, storage_mock):
  1211. storage_mock.side_effect = self.dummy_coro
  1212. self.call_mgmt_func(b'admin.vm.Create.StandaloneVM',
  1213. b'dom0', b'', b'name=test-vm2 label=red')
  1214. self.assertIn('test-vm2', self.app.domains)
  1215. vm = self.app.domains['test-vm2']
  1216. self.assertIsInstance(vm, qubes.vm.standalonevm.StandaloneVM)
  1217. self.assertEqual(vm.label, self.app.get_label('red'))
  1218. self.assertEqual(storage_mock.mock_calls,
  1219. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  1220. self.assertTrue(os.path.exists(os.path.join(
  1221. self.test_base_dir, 'appvms', 'test-vm2')))
  1222. self.assertTrue(self.app.save.called)
  1223. @unittest.mock.patch('qubes.storage.Storage.create')
  1224. def test_331_vm_create_standalone_spurious_template(self, storage_mock):
  1225. storage_mock.side_effect = self.dummy_coro
  1226. with self.assertRaises(qubes.exc.QubesValueError):
  1227. self.call_mgmt_func(b'admin.vm.Create.StandaloneVM',
  1228. b'dom0', b'test-template', b'name=test-vm2 label=red')
  1229. self.assertNotIn('test-vm2', self.app.domains)
  1230. self.assertEqual(storage_mock.mock_calls, [])
  1231. self.assertFalse(os.path.exists(os.path.join(
  1232. self.test_base_dir, 'appvms', 'test-vm2')))
  1233. self.assertNotIn('test-vm2', self.app.domains)
  1234. self.assertFalse(self.app.save.called)
  1235. @unittest.mock.patch('qubes.storage.Storage.create')
  1236. def test_332_vm_create_app(self, storage_mock):
  1237. storage_mock.side_effect = self.dummy_coro
  1238. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  1239. b'dom0', b'test-template', b'name=test-vm2 label=red')
  1240. self.assertIn('test-vm2', self.app.domains)
  1241. vm = self.app.domains['test-vm2']
  1242. self.assertEqual(vm.label, self.app.get_label('red'))
  1243. self.assertEqual(vm.template, self.app.domains['test-template'])
  1244. self.assertEqual(storage_mock.mock_calls,
  1245. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  1246. self.assertTrue(os.path.exists(os.path.join(
  1247. self.test_base_dir, 'appvms', 'test-vm2')))
  1248. self.assertTrue(self.app.save.called)
  1249. @unittest.mock.patch('qubes.storage.Storage.create')
  1250. def test_333_vm_create_app_default_template(self, storage_mock):
  1251. storage_mock.side_effect = self.dummy_coro
  1252. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  1253. b'dom0', b'', b'name=test-vm2 label=red')
  1254. self.assertEqual(storage_mock.mock_calls,
  1255. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  1256. self.assertIn('test-vm2', self.app.domains)
  1257. self.assertEqual(self.app.domains['test-vm2'].template,
  1258. self.app.default_template)
  1259. self.assertTrue(self.app.save.called)
  1260. @unittest.mock.patch('qubes.storage.Storage.create')
  1261. def test_334_vm_create_invalid_name(self, storage_mock):
  1262. storage_mock.side_effect = self.dummy_coro
  1263. with self.assertRaises(qubes.exc.QubesValueError):
  1264. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  1265. b'dom0', b'test-template', b'name=test-###')
  1266. self.assertNotIn('test-###', self.app.domains)
  1267. self.assertFalse(self.app.save.called)
  1268. @unittest.mock.patch('qubes.storage.Storage.create')
  1269. def test_335_vm_create_missing_name(self, storage_mock):
  1270. storage_mock.side_effect = self.dummy_coro
  1271. with self.assertRaises(qubes.api.ProtocolError):
  1272. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  1273. b'dom0', b'test-template', b'label=red')
  1274. self.assertFalse(self.app.save.called)
  1275. @unittest.mock.patch('qubes.storage.Storage.create')
  1276. def test_336_vm_create_spurious_pool(self, storage_mock):
  1277. storage_mock.side_effect = self.dummy_coro
  1278. with self.assertRaises(qubes.api.ProtocolError):
  1279. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  1280. b'dom0', b'test-template',
  1281. b'name=test-vm2 label=red pool=default')
  1282. self.assertNotIn('test-vm2', self.app.domains)
  1283. self.assertFalse(self.app.save.called)
  1284. @unittest.mock.patch('qubes.storage.Storage.create')
  1285. def test_337_vm_create_duplicate_name(self, storage_mock):
  1286. storage_mock.side_effect = self.dummy_coro
  1287. with self.assertRaises(qubes.exc.QubesException):
  1288. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  1289. b'dom0', b'test-template',
  1290. b'name=test-vm1 label=red')
  1291. self.assertFalse(self.app.save.called)
  1292. @unittest.mock.patch('qubes.storage.Storage.create')
  1293. def test_338_vm_create_name_twice(self, storage_mock):
  1294. storage_mock.side_effect = self.dummy_coro
  1295. with self.assertRaises(qubes.api.ProtocolError):
  1296. self.call_mgmt_func(b'admin.vm.Create.AppVM',
  1297. b'dom0', b'test-template',
  1298. b'name=test-vm2 name=test-vm3 label=red')
  1299. self.assertNotIn('test-vm2', self.app.domains)
  1300. self.assertNotIn('test-vm3', self.app.domains)
  1301. self.assertFalse(self.app.save.called)
  1302. @unittest.mock.patch('qubes.storage.Storage.create')
  1303. def test_340_vm_create_in_pool_app(self, storage_mock):
  1304. storage_mock.side_effect = self.dummy_coro
  1305. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1306. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1307. b'pool=test')
  1308. self.assertIn('test-vm2', self.app.domains)
  1309. vm = self.app.domains['test-vm2']
  1310. self.assertEqual(vm.label, self.app.get_label('red'))
  1311. self.assertEqual(vm.template, self.app.domains['test-template'])
  1312. # setting pool= affect only volumes actually created for this VM,
  1313. # not used from a template or so
  1314. self.assertEqual(vm.volume_config['root']['pool'],
  1315. self.template.volumes['root'].pool)
  1316. self.assertEqual(vm.volume_config['private']['pool'], 'test')
  1317. self.assertEqual(vm.volume_config['volatile']['pool'], 'test')
  1318. self.assertEqual(vm.volume_config['kernel']['pool'], 'linux-kernel')
  1319. self.assertEqual(storage_mock.mock_calls,
  1320. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  1321. self.assertTrue(os.path.exists(os.path.join(
  1322. self.test_base_dir, 'appvms', 'test-vm2')))
  1323. self.assertTrue(self.app.save.called)
  1324. @unittest.mock.patch('qubes.storage.Storage.create')
  1325. def test_341_vm_create_in_pool_private(self, storage_mock):
  1326. storage_mock.side_effect = self.dummy_coro
  1327. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1328. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1329. b'pool:private=test')
  1330. self.assertIn('test-vm2', self.app.domains)
  1331. vm = self.app.domains['test-vm2']
  1332. self.assertEqual(vm.label, self.app.get_label('red'))
  1333. self.assertEqual(vm.template, self.app.domains['test-template'])
  1334. self.assertEqual(vm.volume_config['root']['pool'],
  1335. self.template.volumes['root'].pool)
  1336. self.assertEqual(vm.volume_config['private']['pool'], 'test')
  1337. self.assertEqual(vm.volume_config['volatile']['pool'],
  1338. self.app.default_pool_volatile)
  1339. self.assertEqual(vm.volume_config['kernel']['pool'], 'linux-kernel')
  1340. self.assertEqual(storage_mock.mock_calls,
  1341. [unittest.mock.call(self.app.domains['test-vm2']).create()])
  1342. self.assertTrue(os.path.exists(os.path.join(
  1343. self.test_base_dir, 'appvms', 'test-vm2')))
  1344. self.assertTrue(self.app.save.called)
  1345. @unittest.mock.patch('qubes.storage.Storage.create')
  1346. def test_342_vm_create_in_pool_invalid_pool(self, storage_mock):
  1347. storage_mock.side_effect = self.dummy_coro
  1348. with self.assertRaises(qubes.exc.QubesException):
  1349. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1350. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1351. b'pool=no-such-pool')
  1352. self.assertFalse(self.app.save.called)
  1353. @unittest.mock.patch('qubes.storage.Storage.create')
  1354. def test_343_vm_create_in_pool_invalid_pool2(self, storage_mock):
  1355. storage_mock.side_effect = self.dummy_coro
  1356. with self.assertRaises(qubes.exc.QubesException):
  1357. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1358. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1359. b'pool:private=no-such-pool')
  1360. self.assertNotIn('test-vm2', self.app.domains)
  1361. self.assertFalse(self.app.save.called)
  1362. @unittest.mock.patch('qubes.storage.Storage.create')
  1363. def test_344_vm_create_in_pool_invalid_volume(self, storage_mock):
  1364. storage_mock.side_effect = self.dummy_coro
  1365. with self.assertRaises(qubes.api.PermissionDenied):
  1366. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1367. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1368. b'pool:invalid=test')
  1369. self.assertNotIn('test-vm2', self.app.domains)
  1370. self.assertFalse(self.app.save.called)
  1371. @unittest.mock.patch('qubes.storage.Storage.create')
  1372. def test_345_vm_create_in_pool_app_root(self, storage_mock):
  1373. # setting custom pool for 'root' volume of AppVM should not be
  1374. # allowed - this volume belongs to the template
  1375. storage_mock.side_effect = self.dummy_coro
  1376. with self.assertRaises(qubes.exc.QubesException):
  1377. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1378. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1379. b'pool:root=test')
  1380. self.assertNotIn('test-vm2', self.app.domains)
  1381. self.assertFalse(self.app.save.called)
  1382. @unittest.mock.patch('qubes.storage.Storage.create')
  1383. def test_346_vm_create_in_pool_duplicate_pool(self, storage_mock):
  1384. # setting custom pool for 'root' volume of AppVM should not be
  1385. # allowed - this volume belongs to the template
  1386. storage_mock.side_effect = self.dummy_coro
  1387. with self.assertRaises(qubes.api.ProtocolError):
  1388. self.call_mgmt_func(b'admin.vm.CreateInPool.AppVM',
  1389. b'dom0', b'test-template', b'name=test-vm2 label=red '
  1390. b'pool=test pool:root=test')
  1391. self.assertNotIn('test-vm2', self.app.domains)
  1392. self.assertFalse(self.app.save.called)
  1393. def test_400_property_list(self):
  1394. # actual function tested for admin.vm.property.* already
  1395. # this test is kind of stupid, but at least check if appropriate
  1396. # admin-permission event is fired
  1397. value = self.call_mgmt_func(b'admin.property.List', b'dom0')
  1398. properties = self.app.property_list()
  1399. self.assertEqual(value,
  1400. ''.join('{}\n'.format(prop.__name__) for prop in properties))
  1401. def test_410_property_get_str(self):
  1402. # actual function tested for admin.vm.property.* already
  1403. value = self.call_mgmt_func(b'admin.property.Get', b'dom0',
  1404. b'default_kernel')
  1405. self.assertEqual(value, 'default=False type=str 1.0')
  1406. def test_420_propert_set_str(self):
  1407. # actual function tested for admin.vm.property.* already
  1408. with unittest.mock.patch('qubes.property.__set__') as mock:
  1409. value = self.call_mgmt_func(b'admin.property.Set', b'dom0',
  1410. b'default_kernel', b'1.0')
  1411. self.assertIsNone(value)
  1412. mock.assert_called_once_with(self.app, '1.0')
  1413. self.app.save.assert_called_once_with()
  1414. def test_440_property_help(self):
  1415. # actual function tested for admin.vm.property.* already
  1416. value = self.call_mgmt_func(b'admin.property.Help', b'dom0',
  1417. b'clockvm')
  1418. self.assertEqual(value,
  1419. 'Which VM to use as NTP proxy for updating AdminVM')
  1420. self.assertFalse(self.app.save.called)
  1421. def test_450_property_reset(self):
  1422. # actual function tested for admin.vm.property.* already
  1423. with unittest.mock.patch('qubes.property.__delete__') as mock:
  1424. value = self.call_mgmt_func(b'admin.property.Reset', b'dom0',
  1425. b'clockvm')
  1426. mock.assert_called_with(self.app)
  1427. self.assertIsNone(value)
  1428. self.app.save.assert_called_once_with()
  1429. def device_list_testclass(self, vm, event):
  1430. if vm is not self.vm:
  1431. return
  1432. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  1433. dev.description = 'Some device'
  1434. dev.extra_prop = 'xx'
  1435. yield dev
  1436. dev = qubes.devices.DeviceInfo(self.vm, '4321')
  1437. dev.description = 'Some other device'
  1438. yield dev
  1439. def test_460_vm_device_available(self):
  1440. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1441. value = self.call_mgmt_func(b'admin.vm.device.testclass.Available',
  1442. b'test-vm1')
  1443. self.assertEqual(value,
  1444. '1234 extra_prop=xx description=Some '
  1445. 'device\n'
  1446. '4321 description=Some other device\n')
  1447. self.assertFalse(self.app.save.called)
  1448. def test_461_vm_device_available_specific(self):
  1449. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1450. value = self.call_mgmt_func(b'admin.vm.device.testclass.Available',
  1451. b'test-vm1', b'4321')
  1452. self.assertEqual(value,
  1453. '4321 description=Some other device\n')
  1454. self.assertFalse(self.app.save.called)
  1455. def test_462_vm_device_available_invalid(self):
  1456. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1457. value = self.call_mgmt_func(b'admin.vm.device.testclass.Available',
  1458. b'test-vm1', b'no-such-device')
  1459. self.assertEqual(value, '')
  1460. self.assertFalse(self.app.save.called)
  1461. def test_470_vm_device_list_persistent(self):
  1462. assignment = qubes.devices.DeviceAssignment(self.vm, '1234',
  1463. persistent=True)
  1464. self.loop.run_until_complete(
  1465. self.vm.devices['testclass'].attach(assignment))
  1466. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1467. b'test-vm1')
  1468. self.assertEqual(value,
  1469. 'test-vm1+1234 persistent=yes\n')
  1470. self.assertFalse(self.app.save.called)
  1471. def test_471_vm_device_list_persistent_options(self):
  1472. assignment = qubes.devices.DeviceAssignment(self.vm, '1234',
  1473. persistent=True, options={'opt1': 'value'})
  1474. self.loop.run_until_complete(
  1475. self.vm.devices['testclass'].attach(assignment))
  1476. assignment = qubes.devices.DeviceAssignment(self.vm, '4321',
  1477. persistent=True)
  1478. self.loop.run_until_complete(
  1479. self.vm.devices['testclass'].attach(assignment))
  1480. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1481. b'test-vm1')
  1482. self.assertEqual(value,
  1483. 'test-vm1+1234 opt1=value persistent=yes\n'
  1484. 'test-vm1+4321 persistent=yes\n')
  1485. self.assertFalse(self.app.save.called)
  1486. def device_list_attached_testclass(self, vm, event, **kwargs):
  1487. if vm is not self.vm:
  1488. return
  1489. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  1490. yield (dev, {'attach_opt': 'value'})
  1491. def test_472_vm_device_list_temporary(self):
  1492. self.vm.add_handler('device-list-attached:testclass',
  1493. self.device_list_attached_testclass)
  1494. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1495. b'test-vm1')
  1496. self.assertEqual(value,
  1497. 'test-vm1+1234 attach_opt=value persistent=no\n')
  1498. self.assertFalse(self.app.save.called)
  1499. def test_473_vm_device_list_mixed(self):
  1500. self.vm.add_handler('device-list-attached:testclass',
  1501. self.device_list_attached_testclass)
  1502. assignment = qubes.devices.DeviceAssignment(self.vm, '4321',
  1503. persistent=True)
  1504. self.loop.run_until_complete(
  1505. self.vm.devices['testclass'].attach(assignment))
  1506. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1507. b'test-vm1')
  1508. self.assertEqual(value,
  1509. 'test-vm1+1234 attach_opt=value persistent=no\n'
  1510. 'test-vm1+4321 persistent=yes\n')
  1511. self.assertFalse(self.app.save.called)
  1512. def test_474_vm_device_list_specific(self):
  1513. self.vm.add_handler('device-list-attached:testclass',
  1514. self.device_list_attached_testclass)
  1515. assignment = qubes.devices.DeviceAssignment(self.vm, '4321',
  1516. persistent=True)
  1517. self.loop.run_until_complete(
  1518. self.vm.devices['testclass'].attach(assignment))
  1519. value = self.call_mgmt_func(b'admin.vm.device.testclass.List',
  1520. b'test-vm1', b'test-vm1+1234')
  1521. self.assertEqual(value,
  1522. 'test-vm1+1234 attach_opt=value persistent=no\n')
  1523. self.assertFalse(self.app.save.called)
  1524. def test_480_vm_device_attach(self):
  1525. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1526. mock_attach = unittest.mock.Mock()
  1527. mock_attach.return_value = None
  1528. del mock_attach._is_coroutine
  1529. self.vm.add_handler('device-attach:testclass', mock_attach)
  1530. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1531. 'is_halted', lambda _: False):
  1532. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1533. b'test-vm1', b'test-vm1+1234')
  1534. self.assertIsNone(value)
  1535. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1536. device=self.vm.devices['testclass']['1234'],
  1537. options={})
  1538. self.assertEqual(len(self.vm.devices['testclass'].persistent()), 0)
  1539. self.app.save.assert_called_once_with()
  1540. def test_481_vm_device_attach(self):
  1541. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1542. mock_attach = unittest.mock.Mock()
  1543. mock_attach.return_value = None
  1544. del mock_attach._is_coroutine
  1545. self.vm.add_handler('device-attach:testclass', mock_attach)
  1546. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1547. 'is_halted', lambda _: False):
  1548. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1549. b'test-vm1', b'test-vm1+1234', b'persistent=no')
  1550. self.assertIsNone(value)
  1551. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1552. device=self.vm.devices['testclass']['1234'],
  1553. options={})
  1554. self.assertEqual(len(self.vm.devices['testclass'].persistent()), 0)
  1555. self.app.save.assert_called_once_with()
  1556. def test_482_vm_device_attach_not_running(self):
  1557. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1558. mock_attach = unittest.mock.Mock()
  1559. del mock_attach._is_coroutine
  1560. self.vm.add_handler('device-attach:testclass', mock_attach)
  1561. with self.assertRaises(qubes.exc.QubesVMNotRunningError):
  1562. self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1563. b'test-vm1', b'test-vm1+1234')
  1564. self.assertFalse(mock_attach.called)
  1565. self.assertEqual(len(self.vm.devices['testclass'].persistent()), 0)
  1566. self.assertFalse(self.app.save.called)
  1567. def test_483_vm_device_attach_persistent(self):
  1568. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1569. mock_attach = unittest.mock.Mock()
  1570. mock_attach.return_value = None
  1571. del mock_attach._is_coroutine
  1572. self.vm.add_handler('device-attach:testclass', mock_attach)
  1573. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1574. 'is_halted', lambda _: False):
  1575. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1576. b'test-vm1', b'test-vm1+1234', b'persistent=yes')
  1577. self.assertIsNone(value)
  1578. dev = self.vm.devices['testclass']['1234']
  1579. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1580. device=dev,
  1581. options={})
  1582. self.assertIn(dev, self.vm.devices['testclass'].persistent())
  1583. self.app.save.assert_called_once_with()
  1584. def test_484_vm_device_attach_persistent_not_running(self):
  1585. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1586. mock_attach = unittest.mock.Mock()
  1587. mock_attach.return_value = None
  1588. del mock_attach._is_coroutine
  1589. self.vm.add_handler('device-attach:testclass', mock_attach)
  1590. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1591. b'test-vm1', b'test-vm1+1234', b'persistent=yes')
  1592. self.assertIsNone(value)
  1593. dev = self.vm.devices['testclass']['1234']
  1594. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1595. device=dev,
  1596. options={})
  1597. self.assertIn(dev, self.vm.devices['testclass'].persistent())
  1598. self.app.save.assert_called_once_with()
  1599. def test_485_vm_device_attach_options(self):
  1600. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1601. mock_attach = unittest.mock.Mock()
  1602. mock_attach.return_value = None
  1603. del mock_attach._is_coroutine
  1604. self.vm.add_handler('device-attach:testclass', mock_attach)
  1605. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1606. 'is_halted', lambda _: False):
  1607. value = self.call_mgmt_func(b'admin.vm.device.testclass.Attach',
  1608. b'test-vm1', b'test-vm1+1234', b'option1=value2')
  1609. self.assertIsNone(value)
  1610. dev = self.vm.devices['testclass']['1234']
  1611. mock_attach.assert_called_once_with(self.vm, 'device-attach:testclass',
  1612. device=dev,
  1613. options={'option1': 'value2'})
  1614. self.app.save.assert_called_once_with()
  1615. def test_490_vm_device_detach(self):
  1616. self.vm.add_handler('device-list:testclass', self.device_list_testclass)
  1617. self.vm.add_handler('device-list-attached:testclass',
  1618. self.device_list_attached_testclass)
  1619. mock_detach = unittest.mock.Mock()
  1620. mock_detach.return_value = None
  1621. del mock_detach._is_coroutine
  1622. self.vm.add_handler('device-detach:testclass', mock_detach)
  1623. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1624. 'is_halted', lambda _: False):
  1625. value = self.call_mgmt_func(b'admin.vm.device.testclass.Detach',
  1626. b'test-vm1', b'test-vm1+1234')
  1627. self.assertIsNone(value)
  1628. mock_detach.assert_called_once_with(self.vm, 'device-detach:testclass',
  1629. device=self.vm.devices['testclass']['1234'])
  1630. self.app.save.assert_called_once_with()
  1631. def test_491_vm_device_detach_not_attached(self):
  1632. mock_detach = unittest.mock.Mock()
  1633. mock_detach.return_value = None
  1634. del mock_detach._is_coroutine
  1635. self.vm.add_handler('device-detach:testclass', mock_detach)
  1636. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  1637. 'is_halted', lambda _: False):
  1638. with self.assertRaises(qubes.devices.DeviceNotAttached):
  1639. self.call_mgmt_func(b'admin.vm.device.testclass.Detach',
  1640. b'test-vm1', b'test-vm1+1234')
  1641. self.assertFalse(mock_detach.called)
  1642. self.assertFalse(self.app.save.called)
  1643. @unittest.mock.patch('qubes.storage.Storage.remove')
  1644. @unittest.mock.patch('shutil.rmtree')
  1645. def test_500_vm_remove(self, mock_rmtree, mock_remove):
  1646. mock_remove.side_effect = self.dummy_coro
  1647. value = self.call_mgmt_func(b'admin.vm.Remove', b'test-vm1')
  1648. self.assertIsNone(value)
  1649. mock_rmtree.assert_called_once_with(
  1650. '/tmp/qubes-test-dir/appvms/test-vm1')
  1651. mock_remove.assert_called_once_with()
  1652. self.app.save.assert_called_once_with()
  1653. @unittest.mock.patch('qubes.storage.Storage.remove')
  1654. @unittest.mock.patch('shutil.rmtree')
  1655. def test_501_vm_remove_running(self, mock_rmtree, mock_remove):
  1656. mock_remove.side_effect = self.dummy_coro
  1657. with unittest.mock.patch.object(
  1658. self.vm, 'get_power_state', lambda: 'Running'):
  1659. with self.assertRaises(qubes.exc.QubesVMNotHaltedError):
  1660. self.call_mgmt_func(b'admin.vm.Remove', b'test-vm1')
  1661. self.assertFalse(mock_rmtree.called)
  1662. self.assertFalse(mock_remove.called)
  1663. self.assertFalse(self.app.save.called)
  1664. @unittest.mock.patch('qubes.storage.Storage.remove')
  1665. @unittest.mock.patch('shutil.rmtree')
  1666. def test_502_vm_remove_attached(self, mock_rmtree, mock_remove):
  1667. self.setup_for_clone()
  1668. assignment = qubes.devices.DeviceAssignment(
  1669. self.vm, '1234', persistent=True)
  1670. self.loop.run_until_complete(
  1671. self.vm2.devices['testclass'].attach(assignment))
  1672. mock_remove.side_effect = self.dummy_coro
  1673. with self.assertRaises(qubes.exc.QubesVMInUseError):
  1674. self.call_mgmt_func(b'admin.vm.Remove', b'test-vm1')
  1675. self.assertFalse(mock_rmtree.called)
  1676. self.assertFalse(mock_remove.called)
  1677. self.assertFalse(self.app.save.called)
  1678. # Import tests
  1679. # (internal methods, normally called from qubes-rpc script)
  1680. def test_510_vm_volume_import(self):
  1681. value = self.call_internal_mgmt_func(
  1682. b'internal.vm.volume.ImportBegin', b'test-vm1', b'private')
  1683. self.assertEqual(value, '{} {}'.format(
  1684. 2*2**30, '/tmp/qubes-test-dir/appvms/test-vm1/private-import.img'))
  1685. self.assertFalse(self.app.save.called)
  1686. def test_511_vm_volume_import_running(self):
  1687. with unittest.mock.patch.object(
  1688. self.vm, 'get_power_state', lambda: 'Running'):
  1689. with self.assertRaises(qubes.exc.QubesVMNotHaltedError):
  1690. self.call_internal_mgmt_func(
  1691. b'internal.vm.volume.ImportBegin', b'test-vm1', b'private')
  1692. def test_512_vm_volume_import_with_size(self):
  1693. new_size = 4 * 2**30
  1694. file_name = '/tmp/qubes-test-dir/appvms/test-vm1/private-import.img'
  1695. value = self.call_internal_mgmt_func(
  1696. b'internal.vm.volume.ImportBegin', b'test-vm1',
  1697. b'private', payload=str(new_size).encode())
  1698. self.assertEqual(value, '{} {}'.format(
  1699. new_size, file_name))
  1700. self.assertFalse(self.app.save.called)
  1701. self.assertEqual(os.stat(file_name).st_size, new_size)
  1702. def test_515_vm_volume_import_fire_event(self):
  1703. self.call_internal_mgmt_func(
  1704. b'internal.vm.volume.ImportBegin', b'test-vm1', b'private')
  1705. self.assertEventFired(
  1706. self.emitter, 'admin-permission:admin.vm.volume.Import')
  1707. def test_516_vm_volume_import_fire_event_with_size(self):
  1708. self.call_internal_mgmt_func(
  1709. b'internal.vm.volume.ImportBegin', b'test-vm1', b'private',
  1710. b'123')
  1711. self.assertEventFired(
  1712. self.emitter, 'admin-permission:admin.vm.volume.ImportWithSize')
  1713. def test_510_vm_volume_import_end_success(self):
  1714. import_data_end_mock, self.vm.storage.import_data_end = \
  1715. self.coroutine_mock()
  1716. self.call_internal_mgmt_func(
  1717. b'internal.vm.volume.ImportEnd', b'test-vm1', b'private',
  1718. payload=b'ok')
  1719. self.assertEqual(import_data_end_mock.mock_calls, [
  1720. unittest.mock.call('private', success=True)
  1721. ])
  1722. def test_510_vm_volume_import_end_failure(self):
  1723. import_data_end_mock, self.vm.storage.import_data_end = \
  1724. self.coroutine_mock()
  1725. with self.assertRaisesRegexp(
  1726. qubes.exc.QubesException, 'error message'):
  1727. self.call_internal_mgmt_func(
  1728. b'internal.vm.volume.ImportEnd', b'test-vm1', b'private',
  1729. payload=b'fail\nerror message')
  1730. self.assertEqual(import_data_end_mock.mock_calls, [
  1731. unittest.mock.call('private', success=False)
  1732. ])
  1733. def setup_for_clone(self):
  1734. self.pool = unittest.mock.MagicMock()
  1735. self.app.pools['test'] = self.pool
  1736. self.vm2 = self.app.add_new_vm('AppVM', label='red',
  1737. name='test-vm2',
  1738. template='test-template', kernel='')
  1739. self.pool.configure_mock(**{
  1740. 'volumes': qubes.storage.VolumesCollection(self.pool),
  1741. 'init_volume.return_value.pool': self.pool,
  1742. '__str__.return_value': 'test',
  1743. 'get_volume.side_effect': (lambda vid:
  1744. self.vm.volumes['private']
  1745. if vid is self.vm.volumes['private'].vid
  1746. else self.vm2.volumes['private']
  1747. ),
  1748. })
  1749. self.loop.run_until_complete(
  1750. self.vm.create_on_disk(pool='test'))
  1751. self.loop.run_until_complete(
  1752. self.vm2.create_on_disk(pool='test'))
  1753. # the call replaces self.vm.volumes[...] with result of import
  1754. # operation - make sure it stays as the same object
  1755. self.vm.volumes['private'].import_volume.return_value = \
  1756. self.vm.volumes['private']
  1757. self.vm2.volumes['private'].import_volume.return_value = \
  1758. self.vm2.volumes['private']
  1759. self.addCleanup(self.cleanup_for_clone)
  1760. def cleanup_for_clone(self):
  1761. del self.vm2
  1762. del self.pool
  1763. def test_520_vm_volume_clone(self):
  1764. self.setup_for_clone()
  1765. token = self.call_mgmt_func(b'admin.vm.volume.CloneFrom',
  1766. b'test-vm1', b'private', b'')
  1767. # token
  1768. self.assertEqual(len(token), 32)
  1769. self.assertFalse(self.app.save.called)
  1770. value = self.call_mgmt_func(b'admin.vm.volume.CloneTo',
  1771. b'test-vm2', b'private', token.encode())
  1772. self.assertIsNone(value)
  1773. self.vm2.volumes['private'].import_volume.assert_called_once_with(
  1774. self.vm.volumes['private']
  1775. )
  1776. self.vm2.volumes['private'].import_volume.assert_called_once_with(
  1777. self.vm2.volumes['private']
  1778. )
  1779. self.app.save.assert_called_once_with()
  1780. def test_521_vm_volume_clone_invalid_volume(self):
  1781. self.setup_for_clone()
  1782. with self.assertRaises(qubes.api.PermissionDenied):
  1783. self.call_mgmt_func(b'admin.vm.volume.CloneFrom',
  1784. b'test-vm1', b'private123', b'')
  1785. self.assertNotIn('init_volume().import_volume',
  1786. map(operator.itemgetter(0), self.pool.mock_calls))
  1787. self.assertFalse(self.app.save.called)
  1788. def test_522_vm_volume_clone_invalid_volume2(self):
  1789. self.setup_for_clone()
  1790. token = self.call_mgmt_func(b'admin.vm.volume.CloneFrom',
  1791. b'test-vm1', b'private', b'')
  1792. with self.assertRaises(qubes.api.PermissionDenied):
  1793. self.call_mgmt_func(b'admin.vm.volume.CloneTo',
  1794. b'test-vm1', b'private123', token.encode())
  1795. self.assertNotIn('init_volume().import_volume',
  1796. map(operator.itemgetter(0), self.pool.mock_calls))
  1797. self.assertFalse(self.app.save.called)
  1798. def test_523_vm_volume_clone_removed_volume(self):
  1799. self.setup_for_clone()
  1800. token = self.call_mgmt_func(b'admin.vm.volume.CloneFrom',
  1801. b'test-vm1', b'private', b'')
  1802. def get_volume(vid):
  1803. if vid == self.vm.volumes['private']:
  1804. raise KeyError(vid)
  1805. else:
  1806. return unittest.mock.DEFAULT
  1807. self.pool.get_volume.side_effect = get_volume
  1808. with self.assertRaises(qubes.api.PermissionDenied):
  1809. self.call_mgmt_func(b'admin.vm.volume.CloneTo',
  1810. b'test-vm1', b'private', token.encode())
  1811. self.assertNotIn('init_volume().import_volume',
  1812. map(operator.itemgetter(0), self.pool.mock_calls))
  1813. self.assertFalse(self.app.save.called)
  1814. def test_524_vm_volume_clone_invlid_token(self):
  1815. self.setup_for_clone()
  1816. with self.assertRaises(qubes.api.PermissionDenied):
  1817. self.call_mgmt_func(b'admin.vm.volume.CloneTo',
  1818. b'test-vm1', b'private', b'no-such-token')
  1819. self.assertNotIn('init_volume().import_volume',
  1820. map(operator.itemgetter(0), self.pool.mock_calls))
  1821. self.assertFalse(self.app.save.called)
  1822. def test_530_tag_list(self):
  1823. self.vm.tags.add('tag1')
  1824. self.vm.tags.add('tag2')
  1825. value = self.call_mgmt_func(b'admin.vm.tag.List', b'test-vm1')
  1826. self.assertEqual(value, 'audiovm-dom0\nguivm-dom0\ntag1\ntag2\n')
  1827. self.assertFalse(self.app.save.called)
  1828. def test_540_tag_get(self):
  1829. self.vm.tags.add('tag1')
  1830. value = self.call_mgmt_func(b'admin.vm.tag.Get', b'test-vm1',
  1831. b'tag1')
  1832. self.assertEqual(value, '1')
  1833. self.assertFalse(self.app.save.called)
  1834. def test_541_tag_get_absent(self):
  1835. value = self.call_mgmt_func(b'admin.vm.tag.Get', b'test-vm1', b'tag1')
  1836. self.assertEqual(value, '0')
  1837. self.assertFalse(self.app.save.called)
  1838. def test_550_tag_remove(self):
  1839. self.vm.tags.add('tag1')
  1840. value = self.call_mgmt_func(b'admin.vm.tag.Remove', b'test-vm1',
  1841. b'tag1')
  1842. self.assertIsNone(value, None)
  1843. self.assertNotIn('tag1', self.vm.tags)
  1844. self.assertTrue(self.app.save.called)
  1845. def test_551_tag_remove_absent(self):
  1846. with self.assertRaises(qubes.exc.QubesTagNotFoundError):
  1847. self.call_mgmt_func(b'admin.vm.tag.Remove',
  1848. b'test-vm1', b'tag1')
  1849. self.assertFalse(self.app.save.called)
  1850. def test_560_tag_set(self):
  1851. value = self.call_mgmt_func(b'admin.vm.tag.Set',
  1852. b'test-vm1', b'tag1')
  1853. self.assertIsNone(value)
  1854. self.assertIn('tag1', self.vm.tags)
  1855. self.assertTrue(self.app.save.called)
  1856. def test_561_tag_set_invalid(self):
  1857. with self.assertRaises(ValueError):
  1858. self.call_mgmt_func(b'admin.vm.tag.Set',
  1859. b'test-vm1', b'+.some-tag')
  1860. self.assertNotIn('+.some-tag', self.vm.tags)
  1861. self.assertFalse(self.app.save.called)
  1862. def test_570_firewall_get(self):
  1863. self.vm.firewall.save = unittest.mock.Mock()
  1864. value = self.call_mgmt_func(b'admin.vm.firewall.Get',
  1865. b'test-vm1', b'')
  1866. self.assertEqual(value, 'action=accept\n')
  1867. self.assertFalse(self.vm.firewall.save.called)
  1868. self.assertFalse(self.app.save.called)
  1869. def test_571_firewall_get_non_default(self):
  1870. self.vm.firewall.save = unittest.mock.Mock()
  1871. self.vm.firewall.rules = [
  1872. qubes.firewall.Rule(action='accept', proto='tcp',
  1873. dstports='1-1024'),
  1874. qubes.firewall.Rule(action='drop', proto='icmp',
  1875. comment='No ICMP'),
  1876. # should not output expired rule
  1877. qubes.firewall.Rule(action='drop', proto='udp',
  1878. expire='1499450306'),
  1879. qubes.firewall.Rule(action='drop', proto='udp',
  1880. expire='2099450306'),
  1881. qubes.firewall.Rule(action='accept'),
  1882. ]
  1883. value = self.call_mgmt_func(b'admin.vm.firewall.Get',
  1884. b'test-vm1', b'')
  1885. self.assertEqual(value,
  1886. 'action=accept proto=tcp dstports=1-1024\n'
  1887. 'action=drop proto=icmp comment=No ICMP\n'
  1888. 'action=drop expire=2099450306 proto=udp\n'
  1889. 'action=accept\n')
  1890. self.assertFalse(self.vm.firewall.save.called)
  1891. self.assertFalse(self.app.save.called)
  1892. def test_580_firewall_set_simple(self):
  1893. self.vm.firewall.save = unittest.mock.Mock()
  1894. value = self.call_mgmt_func(b'admin.vm.firewall.Set',
  1895. b'test-vm1', b'', b'action=accept\n')
  1896. self.assertEqual(self.vm.firewall.rules,
  1897. ['action=accept'])
  1898. self.assertTrue(self.vm.firewall.save.called)
  1899. self.assertFalse(self.app.save.called)
  1900. def test_581_firewall_set_multi(self):
  1901. self.vm.firewall.save = unittest.mock.Mock()
  1902. rules = [
  1903. qubes.firewall.Rule(action='accept', proto='tcp',
  1904. dstports='1-1024'),
  1905. qubes.firewall.Rule(action='drop', proto='icmp',
  1906. comment='No ICMP'),
  1907. qubes.firewall.Rule(action='drop', proto='udp',
  1908. expire='1499450306'),
  1909. qubes.firewall.Rule(action='accept'),
  1910. ]
  1911. rules_txt = (
  1912. 'action=accept proto=tcp dstports=1-1024\n'
  1913. 'action=drop proto=icmp comment=No ICMP\n'
  1914. 'action=drop expire=1499450306 proto=udp\n'
  1915. 'action=accept\n')
  1916. value = self.call_mgmt_func(b'admin.vm.firewall.Set',
  1917. b'test-vm1', b'', rules_txt.encode())
  1918. self.assertEqual(self.vm.firewall.rules, rules)
  1919. self.assertTrue(self.vm.firewall.save.called)
  1920. self.assertFalse(self.app.save.called)
  1921. def test_582_firewall_set_invalid(self):
  1922. self.vm.firewall.save = unittest.mock.Mock()
  1923. rules_txt = (
  1924. 'action=accept protoxyz=tcp dst4=127.0.0.1\n'
  1925. 'action=drop\n')
  1926. with self.assertRaises(ValueError):
  1927. self.call_mgmt_func(b'admin.vm.firewall.Set',
  1928. b'test-vm1', b'', rules_txt.encode())
  1929. self.assertEqual(self.vm.firewall.rules,
  1930. [qubes.firewall.Rule(action='accept')])
  1931. self.assertFalse(self.vm.firewall.save.called)
  1932. self.assertFalse(self.app.save.called)
  1933. def test_583_firewall_set_invalid(self):
  1934. self.vm.firewall.save = unittest.mock.Mock()
  1935. rules_txt = (
  1936. 'proto=tcp dstports=1-1024\n'
  1937. 'action=drop\n')
  1938. with self.assertRaises(ValueError):
  1939. self.call_mgmt_func(b'admin.vm.firewall.Set',
  1940. b'test-vm1', b'', rules_txt.encode())
  1941. self.assertEqual(self.vm.firewall.rules,
  1942. [qubes.firewall.Rule(action='accept')])
  1943. self.assertFalse(self.vm.firewall.save.called)
  1944. self.assertFalse(self.app.save.called)
  1945. def test_584_firewall_set_invalid(self):
  1946. self.vm.firewall.save = unittest.mock.Mock()
  1947. rules_txt = (
  1948. 'action=accept proto=tcp dstports=1-1024 '
  1949. 'action=drop\n')
  1950. with self.assertRaises(ValueError):
  1951. self.call_mgmt_func(b'admin.vm.firewall.Set',
  1952. b'test-vm1', b'', rules_txt.encode())
  1953. self.assertEqual(self.vm.firewall.rules,
  1954. [qubes.firewall.Rule(action='accept')])
  1955. self.assertFalse(self.vm.firewall.save.called)
  1956. self.assertFalse(self.app.save.called)
  1957. def test_585_firewall_set_invalid(self):
  1958. self.vm.firewall.save = unittest.mock.Mock()
  1959. rules_txt = (
  1960. 'action=accept dstports=1-1024 comment=ążźł\n'
  1961. 'action=drop\n')
  1962. with self.assertRaises(UnicodeDecodeError):
  1963. self.call_mgmt_func(b'admin.vm.firewall.Set',
  1964. b'test-vm1', b'', rules_txt.encode())
  1965. self.assertEqual(self.vm.firewall.rules,
  1966. [qubes.firewall.Rule(action='accept')])
  1967. self.assertFalse(self.vm.firewall.save.called)
  1968. self.assertFalse(self.app.save.called)
  1969. def test_590_firewall_reload(self):
  1970. self.vm.firewall.save = unittest.mock.Mock()
  1971. self.app.domains['test-vm1'].fire_event = self.emitter.fire_event
  1972. value = self.call_mgmt_func(b'admin.vm.firewall.Reload',
  1973. b'test-vm1', b'')
  1974. self.assertIsNone(value)
  1975. self.assertEventFired(self.emitter, 'firewall-changed')
  1976. self.assertFalse(self.vm.firewall.save.called)
  1977. self.assertFalse(self.app.save.called)
  1978. def test_600_backup_info(self):
  1979. backup_profile = (
  1980. 'include:\n'
  1981. ' - test-vm1\n'
  1982. 'destination_vm: test-vm1\n'
  1983. 'destination_path: /var/tmp\n'
  1984. 'passphrase_text: test\n'
  1985. )
  1986. expected_info = (
  1987. '------------------+--------------+--------------+\n'
  1988. ' VM | type | size |\n'
  1989. '------------------+--------------+--------------+\n'
  1990. ' test-vm1 | VM | 0 |\n'
  1991. '------------------+--------------+--------------+\n'
  1992. ' Total size: | 0 |\n'
  1993. '------------------+--------------+--------------+\n'
  1994. 'VMs not selected for backup:\n'
  1995. ' - dom0\n'
  1996. ' - test-template\n'
  1997. )
  1998. with tempfile.TemporaryDirectory() as profile_dir:
  1999. with open(os.path.join(profile_dir, 'testprofile.conf'), 'w') as \
  2000. profile_file:
  2001. profile_file.write(backup_profile)
  2002. with unittest.mock.patch('qubes.config.backup_profile_dir',
  2003. profile_dir):
  2004. result = self.call_mgmt_func(b'admin.backup.Info', b'dom0',
  2005. b'testprofile')
  2006. self.assertEqual(result, expected_info)
  2007. def test_601_backup_info_profile_missing_destination_path(self):
  2008. backup_profile = (
  2009. 'include:\n'
  2010. ' - test-vm1\n'
  2011. 'destination_vm: test-vm1\n'
  2012. 'passphrase_text: test\n'
  2013. )
  2014. with tempfile.TemporaryDirectory() as profile_dir:
  2015. with open(os.path.join(profile_dir, 'testprofile.conf'), 'w') as \
  2016. profile_file:
  2017. profile_file.write(backup_profile)
  2018. with unittest.mock.patch('qubes.config.backup_profile_dir',
  2019. profile_dir):
  2020. with self.assertRaises(qubes.exc.QubesException):
  2021. self.call_mgmt_func(b'admin.backup.Info', b'dom0',
  2022. b'testprofile')
  2023. def test_602_backup_info_profile_missing_destination_vm(self):
  2024. backup_profile = (
  2025. 'include:\n'
  2026. ' - test-vm1\n'
  2027. 'destination_path: /home/user\n'
  2028. 'passphrase_text: test\n'
  2029. )
  2030. with tempfile.TemporaryDirectory() as profile_dir:
  2031. with open(os.path.join(profile_dir, 'testprofile.conf'), 'w') as \
  2032. profile_file:
  2033. profile_file.write(backup_profile)
  2034. with unittest.mock.patch('qubes.config.backup_profile_dir',
  2035. profile_dir):
  2036. with self.assertRaises(qubes.exc.QubesException):
  2037. self.call_mgmt_func(b'admin.backup.Info', b'dom0',
  2038. b'testprofile')
  2039. def test_610_backup_cancel_not_running(self):
  2040. with self.assertRaises(qubes.exc.QubesException):
  2041. self.call_mgmt_func(b'admin.backup.Cancel', b'dom0',
  2042. b'testprofile')
  2043. def test_611_backup_already_running(self):
  2044. if not hasattr(self.app, 'api_admin_running_backups'):
  2045. self.app.api_admin_running_backups = {}
  2046. self.app.api_admin_running_backups['testprofile'] = 'test'
  2047. self.addCleanup(self.app.api_admin_running_backups.pop, 'testprofile')
  2048. backup_profile = (
  2049. 'include:\n'
  2050. ' - test-vm1\n'
  2051. 'destination_vm: test-vm1\n'
  2052. 'destination_path: /home/user\n'
  2053. 'passphrase_text: test\n'
  2054. )
  2055. with tempfile.TemporaryDirectory() as profile_dir:
  2056. with open(os.path.join(profile_dir, 'testprofile.conf'), 'w') as \
  2057. profile_file:
  2058. profile_file.write(backup_profile)
  2059. with unittest.mock.patch('qubes.config.backup_profile_dir',
  2060. profile_dir):
  2061. with self.assertRaises(qubes.exc.BackupAlreadyRunningError):
  2062. self.call_mgmt_func(b'admin.backup.Execute', b'dom0',
  2063. b'testprofile')
  2064. @unittest.mock.patch('qubes.backup.Backup')
  2065. def test_620_backup_execute(self, mock_backup):
  2066. backup_profile = (
  2067. 'include:\n'
  2068. ' - test-vm1\n'
  2069. 'destination_vm: test-vm1\n'
  2070. 'destination_path: /home/user\n'
  2071. 'passphrase_text: test\n'
  2072. )
  2073. mock_backup.return_value.backup_do.side_effect = self.dummy_coro
  2074. with tempfile.TemporaryDirectory() as profile_dir:
  2075. with open(os.path.join(profile_dir, 'testprofile.conf'), 'w') as \
  2076. profile_file:
  2077. profile_file.write(backup_profile)
  2078. with unittest.mock.patch('qubes.config.backup_profile_dir',
  2079. profile_dir):
  2080. result = self.call_mgmt_func(b'admin.backup.Execute', b'dom0',
  2081. b'testprofile')
  2082. self.assertIsNone(result)
  2083. mock_backup.assert_called_once_with(
  2084. self.app,
  2085. {self.vm},
  2086. set(),
  2087. target_vm=self.vm,
  2088. target_dir='/home/user',
  2089. compressed=True,
  2090. passphrase='test')
  2091. mock_backup.return_value.backup_do.assert_called_once_with()
  2092. @unittest.mock.patch('qubes.backup.Backup')
  2093. def test_621_backup_execute_passphrase_service(self, mock_backup):
  2094. backup_profile = (
  2095. 'include:\n'
  2096. ' - test-vm1\n'
  2097. 'destination_vm: test-vm1\n'
  2098. 'destination_path: /home/user\n'
  2099. 'passphrase_vm: test-vm1\n'
  2100. )
  2101. @asyncio.coroutine
  2102. def service_passphrase(*args, **kwargs):
  2103. return (b'pass-from-vm', None)
  2104. mock_backup.return_value.backup_do.side_effect = self.dummy_coro
  2105. self.vm.run_service_for_stdio = unittest.mock.Mock(
  2106. side_effect=service_passphrase)
  2107. with tempfile.TemporaryDirectory() as profile_dir:
  2108. with open(os.path.join(profile_dir, 'testprofile.conf'), 'w') as \
  2109. profile_file:
  2110. profile_file.write(backup_profile)
  2111. with unittest.mock.patch('qubes.config.backup_profile_dir',
  2112. profile_dir):
  2113. result = self.call_mgmt_func(b'admin.backup.Execute', b'dom0',
  2114. b'testprofile')
  2115. self.assertIsNone(result)
  2116. mock_backup.assert_called_once_with(
  2117. self.app,
  2118. {self.vm},
  2119. set(),
  2120. target_vm=self.vm,
  2121. target_dir='/home/user',
  2122. compressed=True,
  2123. passphrase=b'pass-from-vm')
  2124. mock_backup.return_value.backup_do.assert_called_once_with()
  2125. self.vm.run_service_for_stdio.assert_called_with(
  2126. 'qubes.BackupPassphrase+testprofile')
  2127. def test_630_vm_stats(self):
  2128. send_event = unittest.mock.Mock(spec=[])
  2129. stats1 = {
  2130. 0: {
  2131. 'cpu_time': 243951379111104 // 8,
  2132. 'cpu_usage': 0,
  2133. 'cpu_usage_raw': 0,
  2134. 'memory_kb': 3733212,
  2135. },
  2136. 1: {
  2137. 'cpu_time': 2849496569205,
  2138. 'cpu_usage': 0,
  2139. 'cpu_usage_raw': 0,
  2140. 'memory_kb': 303916,
  2141. },
  2142. }
  2143. stats2 = copy.deepcopy(stats1)
  2144. stats2[0]['cpu_time'] += 100000000
  2145. stats2[0]['cpu_usage'] = 10
  2146. stats2[0]['cpu_usage_raw'] = 10
  2147. stats2[1]['cpu_usage'] = 5
  2148. stats2[1]['cpu_usage_raw'] = 5
  2149. self.app.host.get_vm_stats = unittest.mock.Mock()
  2150. self.app.host.get_vm_stats.side_effect = [
  2151. (0, stats1), (1, stats2),
  2152. ]
  2153. self.app.stats_interval = 1
  2154. mgmt_obj = qubes.api.admin.QubesAdminAPI(
  2155. self.app, b'dom0', b'admin.vm.Stats',
  2156. b'dom0', b'', send_event=send_event)
  2157. def cancel_call():
  2158. mgmt_obj.cancel()
  2159. class MockVM(object):
  2160. def __init__(self, name):
  2161. self._name = name
  2162. def name(self):
  2163. return self._name
  2164. loop = asyncio.get_event_loop()
  2165. self.app.vmm.libvirt_conn.lookupByID.side_effect = lambda xid: {
  2166. 0: MockVM('Domain-0'),
  2167. 1: MockVM('test-template'),
  2168. 2: MockVM('test-vm1')}[xid]
  2169. execute_task = asyncio.ensure_future(
  2170. mgmt_obj.execute(untrusted_payload=b''))
  2171. loop.call_later(1.1, cancel_call)
  2172. loop.run_until_complete(execute_task)
  2173. self.assertIsNone(execute_task.result())
  2174. self.assertEventFired(self.emitter,
  2175. 'admin-permission:' + 'admin.vm.Stats')
  2176. self.assertEqual(self.app.host.get_vm_stats.mock_calls, [
  2177. unittest.mock.call(None, None, only_vm=None),
  2178. unittest.mock.call(0, stats1, only_vm=None),
  2179. ])
  2180. self.assertEqual(send_event.mock_calls, [
  2181. unittest.mock.call(self.app, 'connection-established'),
  2182. unittest.mock.call('dom0', 'vm-stats',
  2183. cpu_time=stats1[0]['cpu_time'] // 1000000,
  2184. cpu_usage=stats1[0]['cpu_usage'],
  2185. cpu_usage_raw=stats1[0]['cpu_usage_raw'],
  2186. memory_kb=stats1[0]['memory_kb']),
  2187. unittest.mock.call('test-template', 'vm-stats',
  2188. cpu_time=stats1[1]['cpu_time'] // 1000000,
  2189. cpu_usage=stats1[1]['cpu_usage'],
  2190. cpu_usage_raw=stats1[1]['cpu_usage_raw'],
  2191. memory_kb=stats1[1]['memory_kb']),
  2192. unittest.mock.call('dom0', 'vm-stats',
  2193. cpu_time=stats2[0]['cpu_time'] // 1000000,
  2194. cpu_usage=stats2[0]['cpu_usage'],
  2195. cpu_usage_raw=stats2[0]['cpu_usage_raw'],
  2196. memory_kb=stats2[0]['memory_kb']),
  2197. unittest.mock.call('test-template', 'vm-stats',
  2198. cpu_time=stats2[1]['cpu_time'] // 1000000,
  2199. cpu_usage=stats2[1]['cpu_usage'],
  2200. cpu_usage_raw=stats2[1]['cpu_usage_raw'],
  2201. memory_kb=stats2[1]['memory_kb']),
  2202. ])
  2203. def test_631_vm_stats_single_vm(self):
  2204. send_event = unittest.mock.Mock(spec=[])
  2205. stats1 = {
  2206. 2: {
  2207. 'cpu_time': 2849496569205,
  2208. 'cpu_usage': 0,
  2209. 'cpu_usage_raw': 0,
  2210. 'memory_kb': 303916,
  2211. },
  2212. }
  2213. stats2 = copy.deepcopy(stats1)
  2214. stats2[2]['cpu_usage'] = 5
  2215. stats2[2]['cpu_usage_raw'] = 5
  2216. self.app.host.get_vm_stats = unittest.mock.Mock()
  2217. self.app.host.get_vm_stats.side_effect = [
  2218. (0, stats1), (1, stats2),
  2219. ]
  2220. self.app.stats_interval = 1
  2221. mgmt_obj = qubes.api.admin.QubesAdminAPI(
  2222. self.app, b'dom0', b'admin.vm.Stats',
  2223. b'test-vm1', b'', send_event=send_event)
  2224. def cancel_call():
  2225. mgmt_obj.cancel()
  2226. class MockVM(object):
  2227. def __init__(self, name):
  2228. self._name = name
  2229. def name(self):
  2230. return self._name
  2231. loop = asyncio.get_event_loop()
  2232. self.app.vmm.libvirt_conn.lookupByID.side_effect = lambda xid: {
  2233. 0: MockVM('Domain-0'),
  2234. 1: MockVM('test-template'),
  2235. 2: MockVM('test-vm1')}[xid]
  2236. execute_task = asyncio.ensure_future(
  2237. mgmt_obj.execute(untrusted_payload=b''))
  2238. loop.call_later(1.1, cancel_call)
  2239. loop.run_until_complete(execute_task)
  2240. self.assertIsNone(execute_task.result())
  2241. self.assertEventFired(self.emitter,
  2242. 'admin-permission:' + 'admin.vm.Stats')
  2243. self.assertEqual(self.app.host.get_vm_stats.mock_calls, [
  2244. unittest.mock.call(None, None, only_vm=self.vm),
  2245. unittest.mock.call(0, stats1, only_vm=self.vm),
  2246. ])
  2247. self.assertEqual(send_event.mock_calls, [
  2248. unittest.mock.call(self.app, 'connection-established'),
  2249. unittest.mock.call('test-vm1', 'vm-stats',
  2250. cpu_time=stats1[2]['cpu_time'] // 1000000,
  2251. cpu_usage=stats1[2]['cpu_usage'],
  2252. cpu_usage_raw=stats1[2]['cpu_usage_raw'],
  2253. memory_kb=stats1[2]['memory_kb']),
  2254. unittest.mock.call('test-vm1', 'vm-stats',
  2255. cpu_time=stats2[2]['cpu_time'] // 1000000,
  2256. cpu_usage=stats2[2]['cpu_usage'],
  2257. cpu_usage_raw=stats2[2]['cpu_usage_raw'],
  2258. memory_kb=stats2[2]['memory_kb']),
  2259. ])
  2260. @unittest.mock.patch('qubes.storage.Storage.create')
  2261. def test_640_vm_create_disposable(self, mock_storage):
  2262. mock_storage.side_effect = self.dummy_coro
  2263. self.vm.template_for_dispvms = True
  2264. retval = self.call_mgmt_func(b'admin.vm.CreateDisposable',
  2265. b'test-vm1')
  2266. self.assertTrue(retval.startswith('disp'))
  2267. self.assertIn(retval, self.app.domains)
  2268. dispvm = self.app.domains[retval]
  2269. self.assertEqual(dispvm.template, self.vm)
  2270. mock_storage.assert_called_once_with()
  2271. self.assertTrue(self.app.save.called)
  2272. @unittest.mock.patch('qubes.storage.Storage.create')
  2273. def test_641_vm_create_disposable_default(self, mock_storage):
  2274. mock_storage.side_effect = self.dummy_coro
  2275. self.vm.template_for_dispvms = True
  2276. self.app.default_dispvm = self.vm
  2277. retval = self.call_mgmt_func(b'admin.vm.CreateDisposable',
  2278. b'dom0')
  2279. self.assertTrue(retval.startswith('disp'))
  2280. mock_storage.assert_called_once_with()
  2281. self.assertTrue(self.app.save.called)
  2282. @unittest.mock.patch('qubes.storage.Storage.create')
  2283. def test_642_vm_create_disposable_not_allowed(self, storage_mock):
  2284. storage_mock.side_effect = self.dummy_coro
  2285. with self.assertRaises(qubes.exc.QubesException):
  2286. self.call_mgmt_func(b'admin.vm.CreateDisposable',
  2287. b'test-vm1')
  2288. self.assertFalse(self.app.save.called)
  2289. def test_650_vm_device_set_persistent_true(self):
  2290. self.vm.add_handler('device-list:testclass',
  2291. self.device_list_testclass)
  2292. self.vm.add_handler('device-list-attached:testclass',
  2293. self.device_list_attached_testclass)
  2294. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  2295. 'is_halted', lambda _: False):
  2296. value = self.call_mgmt_func(
  2297. b'admin.vm.device.testclass.Set.persistent',
  2298. b'test-vm1', b'test-vm1+1234', b'True')
  2299. self.assertIsNone(value)
  2300. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  2301. self.assertIn(dev, self.vm.devices['testclass'].persistent())
  2302. self.app.save.assert_called_once_with()
  2303. def test_651_vm_device_set_persistent_false_unchanged(self):
  2304. self.vm.add_handler('device-list:testclass',
  2305. self.device_list_testclass)
  2306. self.vm.add_handler('device-list-attached:testclass',
  2307. self.device_list_attached_testclass)
  2308. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  2309. 'is_halted', lambda _: False):
  2310. value = self.call_mgmt_func(
  2311. b'admin.vm.device.testclass.Set.persistent',
  2312. b'test-vm1', b'test-vm1+1234', b'False')
  2313. self.assertIsNone(value)
  2314. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  2315. self.assertNotIn(dev, self.vm.devices['testclass'].persistent())
  2316. self.app.save.assert_called_once_with()
  2317. def test_652_vm_device_set_persistent_false(self):
  2318. self.vm.add_handler('device-list:testclass',
  2319. self.device_list_testclass)
  2320. assignment = qubes.devices.DeviceAssignment(self.vm, '1234', {},
  2321. True)
  2322. self.loop.run_until_complete(
  2323. self.vm.devices['testclass'].attach(assignment))
  2324. self.vm.add_handler('device-list-attached:testclass',
  2325. self.device_list_attached_testclass)
  2326. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  2327. self.assertIn(dev, self.vm.devices['testclass'].persistent())
  2328. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  2329. 'is_halted', lambda _: False):
  2330. value = self.call_mgmt_func(
  2331. b'admin.vm.device.testclass.Set.persistent',
  2332. b'test-vm1', b'test-vm1+1234', b'False')
  2333. self.assertIsNone(value)
  2334. self.assertNotIn(dev, self.vm.devices['testclass'].persistent())
  2335. self.assertIn(dev, self.vm.devices['testclass'].attached())
  2336. self.app.save.assert_called_once_with()
  2337. def test_653_vm_device_set_persistent_true_unchanged(self):
  2338. self.vm.add_handler('device-list:testclass',
  2339. self.device_list_testclass)
  2340. assignment = qubes.devices.DeviceAssignment(self.vm, '1234', {},
  2341. True)
  2342. self.loop.run_until_complete(
  2343. self.vm.devices['testclass'].attach(assignment))
  2344. self.vm.add_handler('device-list-attached:testclass',
  2345. self.device_list_attached_testclass)
  2346. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  2347. 'is_halted', lambda _: False):
  2348. value = self.call_mgmt_func(
  2349. b'admin.vm.device.testclass.Set.persistent',
  2350. b'test-vm1', b'test-vm1+1234', b'True')
  2351. self.assertIsNone(value)
  2352. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  2353. self.assertIn(dev, self.vm.devices['testclass'].persistent())
  2354. self.assertIn(dev, self.vm.devices['testclass'].attached())
  2355. self.app.save.assert_called_once_with()
  2356. def test_654_vm_device_set_persistent_not_attached(self):
  2357. self.vm.add_handler('device-list:testclass',
  2358. self.device_list_testclass)
  2359. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  2360. 'is_halted', lambda _: False):
  2361. with self.assertRaises(qubes.api.PermissionDenied):
  2362. self.call_mgmt_func(
  2363. b'admin.vm.device.testclass.Set.persistent',
  2364. b'test-vm1', b'test-vm1+1234', b'True')
  2365. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  2366. self.assertNotIn(dev, self.vm.devices['testclass'].persistent())
  2367. self.assertFalse(self.app.save.called)
  2368. def test_655_vm_device_set_persistent_invalid_value(self):
  2369. self.vm.add_handler('device-list:testclass',
  2370. self.device_list_testclass)
  2371. with unittest.mock.patch.object(qubes.vm.qubesvm.QubesVM,
  2372. 'is_halted', lambda _: False):
  2373. with self.assertRaises(qubes.api.PermissionDenied):
  2374. self.call_mgmt_func(
  2375. b'admin.vm.device.testclass.Set.persistent',
  2376. b'test-vm1', b'test-vm1+1234', b'maybe')
  2377. dev = qubes.devices.DeviceInfo(self.vm, '1234')
  2378. self.assertNotIn(dev, self.vm.devices['testclass'].persistent())
  2379. self.assertFalse(self.app.save.called)
  2380. def test_660_pool_set_revisions_to_keep(self):
  2381. self.app.pools['test-pool'] = unittest.mock.Mock()
  2382. value = self.call_mgmt_func(b'admin.pool.Set.revisions_to_keep',
  2383. b'dom0', b'test-pool', b'2')
  2384. self.assertIsNone(value)
  2385. self.assertEqual(self.app.pools['test-pool'].mock_calls, [])
  2386. self.assertEqual(self.app.pools['test-pool'].revisions_to_keep, 2)
  2387. self.app.save.assert_called_once_with()
  2388. def test_661_pool_set_revisions_to_keep_negative(self):
  2389. self.app.pools['test-pool'] = unittest.mock.Mock()
  2390. with self.assertRaises(qubes.api.PermissionDenied):
  2391. self.call_mgmt_func(b'admin.pool.Set.revisions_to_keep',
  2392. b'dom0', b'test-pool', b'-2')
  2393. self.assertEqual(self.app.pools['test-pool'].mock_calls, [])
  2394. self.assertFalse(self.app.save.called)
  2395. def test_662_pool_set_revisions_to_keep_not_a_number(self):
  2396. self.app.pools['test-pool'] = unittest.mock.Mock()
  2397. with self.assertRaises(qubes.api.ProtocolError):
  2398. self.call_mgmt_func(b'admin.pool.Set.revisions_to_keep',
  2399. b'dom0', b'test-pool', b'abc')
  2400. self.assertEqual(self.app.pools['test-pool'].mock_calls, [])
  2401. self.assertFalse(self.app.save.called)
  2402. def test_670_vm_volume_set_revisions_to_keep(self):
  2403. self.vm.volumes = unittest.mock.MagicMock()
  2404. volumes_conf = {
  2405. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  2406. }
  2407. self.vm.volumes.configure_mock(**volumes_conf)
  2408. self.vm.storage = unittest.mock.Mock()
  2409. value = self.call_mgmt_func(b'admin.vm.volume.Set.revisions_to_keep',
  2410. b'test-vm1', b'private', b'2')
  2411. self.assertIsNone(value)
  2412. self.assertEqual(self.vm.volumes.mock_calls,
  2413. [unittest.mock.call.keys(),
  2414. ('__getitem__', ('private',), {})])
  2415. self.assertEqual(self.vm.volumes['private'].revisions_to_keep, 2)
  2416. self.app.save.assert_called_once_with()
  2417. def test_671_vm_volume_set_revisions_to_keep_negative(self):
  2418. self.vm.volumes = unittest.mock.MagicMock()
  2419. volumes_conf = {
  2420. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  2421. }
  2422. self.vm.volumes.configure_mock(**volumes_conf)
  2423. self.vm.storage = unittest.mock.Mock()
  2424. with self.assertRaises(qubes.api.PermissionDenied):
  2425. self.call_mgmt_func(b'admin.vm.volume.Set.revisions_to_keep',
  2426. b'test-vm1', b'private', b'-2')
  2427. def test_672_vm_volume_set_revisions_to_keep_not_a_number(self):
  2428. self.vm.volumes = unittest.mock.MagicMock()
  2429. volumes_conf = {
  2430. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  2431. }
  2432. self.vm.volumes.configure_mock(**volumes_conf)
  2433. self.vm.storage = unittest.mock.Mock()
  2434. with self.assertRaises(qubes.api.ProtocolError):
  2435. self.call_mgmt_func(b'admin.vm.volume.Set.revisions_to_keep',
  2436. b'test-vm1', b'private', b'abc')
  2437. def test_680_vm_volume_set_rw(self):
  2438. self.vm.volumes = unittest.mock.MagicMock()
  2439. volumes_conf = {
  2440. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  2441. }
  2442. self.vm.volumes.configure_mock(**volumes_conf)
  2443. self.vm.storage = unittest.mock.Mock()
  2444. value = self.call_mgmt_func(b'admin.vm.volume.Set.rw',
  2445. b'test-vm1', b'private', b'True')
  2446. self.assertIsNone(value)
  2447. self.assertEqual(self.vm.volumes.mock_calls,
  2448. [unittest.mock.call.keys(),
  2449. ('__getitem__', ('private',), {})])
  2450. self.assertEqual(self.vm.volumes['private'].rw, True)
  2451. self.app.save.assert_called_once_with()
  2452. def test_681_vm_volume_set_rw_invalid(self):
  2453. self.vm.volumes = unittest.mock.MagicMock()
  2454. volumes_conf = {
  2455. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  2456. }
  2457. self.vm.volumes.configure_mock(**volumes_conf)
  2458. self.vm.storage = unittest.mock.Mock()
  2459. with self.assertRaises(qubes.api.ProtocolError):
  2460. self.call_mgmt_func(b'admin.vm.volume.Set.revisions_to_keep',
  2461. b'test-vm1', b'private', b'abc')
  2462. self.assertFalse(self.app.save.called)
  2463. def test_690_vm_console(self):
  2464. self.vm._libvirt_domain = unittest.mock.Mock()
  2465. xml_desc = (
  2466. '<domain type=\'xen\' id=\'42\'>\n'
  2467. '<name>test-vm1</name>\n'
  2468. '<devices>\n'
  2469. '<console type=\'pty\' tty=\'/dev/pts/42\'>\n'
  2470. '<source path=\'/dev/pts/42\'/>\n'
  2471. '<target type=\'xen\' port=\'0\'/>\n'
  2472. '</console>\n'
  2473. '</devices>\n'
  2474. '</domain>\n'
  2475. )
  2476. self.vm._libvirt_domain.configure_mock(
  2477. **{'XMLDesc.return_value': xml_desc,
  2478. 'isActive.return_value': True}
  2479. )
  2480. self.app.vmm.configure_mock(offline_mode=False)
  2481. value = self.call_mgmt_func(b'admin.vm.Console', b'test-vm1')
  2482. self.assertEqual(value, '/dev/pts/42')
  2483. def test_691_vm_console_not_running(self):
  2484. self.vm._libvirt_domain = unittest.mock.Mock()
  2485. xml_desc = (
  2486. '<domain type=\'xen\' id=\'42\'>\n'
  2487. '<name>test-vm1</name>\n'
  2488. '<devices>\n'
  2489. '<console type=\'pty\' tty=\'/dev/pts/42\'>\n'
  2490. '<source path=\'/dev/pts/42\'/>\n'
  2491. '<target type=\'xen\' port=\'0\'/>\n'
  2492. '</console>\n'
  2493. '</devices>\n'
  2494. '</domain>\n'
  2495. )
  2496. self.vm._libvirt_domain.configure_mock(
  2497. **{'XMLDesc.return_value': xml_desc,
  2498. 'isActive.return_value': False}
  2499. )
  2500. with self.assertRaises(qubes.exc.QubesVMNotRunningError):
  2501. self.call_mgmt_func(b'admin.vm.Console', b'test-vm1')
  2502. def test_700_pool_volume_list(self):
  2503. self.app.pools = {
  2504. 'pool1': unittest.mock.Mock(config={
  2505. 'param1': 'value1', 'param2': 'value2'},
  2506. usage=102400,
  2507. size=204800,
  2508. volumes={'vol1': unittest.mock.Mock(),
  2509. 'vol2': unittest.mock.Mock()})
  2510. }
  2511. value = self.call_mgmt_func(b'admin.pool.volume.List', b'dom0', b'pool1')
  2512. self.assertEqual(value, 'vol1\nvol2\n')
  2513. def test_710_vm_volume_clear(self):
  2514. with tempfile.TemporaryDirectory() as tmpdir:
  2515. tmpfile = os.path.join(tmpdir, 'testfile')
  2516. @asyncio.coroutine
  2517. def coroutine_mock(*args, **kwargs):
  2518. return tmpfile
  2519. self.vm.volumes = unittest.mock.MagicMock()
  2520. volumes_conf = {
  2521. 'keys.return_value': ['root', 'private', 'volatile', 'kernel'],
  2522. '__getitem__.return_value.size': 0xdeadbeef
  2523. }
  2524. self.vm.volumes.configure_mock(**volumes_conf)
  2525. self.vm.storage = unittest.mock.Mock()
  2526. storage_conf = {
  2527. 'import_data.side_effect': coroutine_mock,
  2528. 'import_data_end.side_effect': self.dummy_coro
  2529. }
  2530. self.vm.storage.configure_mock(**storage_conf)
  2531. self.app.domains['test-vm1'].fire_event = self.emitter.fire_event
  2532. value = self.call_mgmt_func(b'admin.vm.volume.Clear',
  2533. b'test-vm1', b'private')
  2534. self.assertIsNone(value)
  2535. self.assertTrue(os.path.exists(tmpfile))
  2536. self.assertEqual(self.vm.volumes.mock_calls, [
  2537. unittest.mock.call.keys(),
  2538. unittest.mock.call.__getattr__('__getitem__')('private')])
  2539. self.assertEqual(self.vm.storage.mock_calls, [
  2540. unittest.mock.call.import_data('private', 0xdeadbeef),
  2541. unittest.mock.call.import_data_end('private', True)])
  2542. self.assertEventFired(
  2543. self.emitter, 'admin-permission:admin.vm.volume.Clear')
  2544. self.assertEventFired(
  2545. self.emitter, 'domain-volume-import-begin')
  2546. self.assertEventFired(
  2547. self.emitter, 'domain-volume-import-end')
  2548. def test_800_current_state_default(self):
  2549. value = self.call_mgmt_func(b'admin.vm.CurrentState', b'test-vm1')
  2550. self.assertEqual(
  2551. value, 'mem=0 mem_static_max=0 cputime=0 power_state=Halted')
  2552. def test_801_current_state_changed(self):
  2553. self.vm.get_mem = lambda: 512
  2554. self.vm.get_mem_static_max = lambda: 1024
  2555. self.vm.get_cputime = lambda: 100
  2556. self.vm.get_power_state = lambda: 'Running'
  2557. value = self.call_mgmt_func(b'admin.vm.CurrentState', b'test-vm1')
  2558. self.assertEqual(
  2559. value, 'mem=512 mem_static_max=1024 cputime=100 power_state=Running')
  2560. def test_990_vm_unexpected_payload(self):
  2561. methods_with_no_payload = [
  2562. b'admin.vm.List',
  2563. b'admin.vm.Remove',
  2564. b'admin.vm.property.List',
  2565. b'admin.vm.property.Get',
  2566. b'admin.vm.property.Help',
  2567. #b'admin.vm.property.HelpRst',
  2568. b'admin.vm.property.Reset',
  2569. b'admin.vm.feature.List',
  2570. b'admin.vm.feature.Get',
  2571. b'admin.vm.feature.CheckWithTemplate',
  2572. b'admin.vm.feature.Remove',
  2573. b'admin.vm.tag.List',
  2574. b'admin.vm.tag.Get',
  2575. b'admin.vm.tag.Remove',
  2576. b'admin.vm.tag.Set',
  2577. b'admin.vm.firewall.Get',
  2578. b'admin.vm.firewall.Reload',
  2579. b'admin.vm.device.pci.Detach',
  2580. b'admin.vm.device.pci.List',
  2581. b'admin.vm.device.pci.Available',
  2582. b'admin.vm.volume.ListSnapshots',
  2583. b'admin.vm.volume.List',
  2584. b'admin.vm.volume.Info',
  2585. b'admin.vm.Start',
  2586. b'admin.vm.Shutdown',
  2587. b'admin.vm.Pause',
  2588. b'admin.vm.Unpause',
  2589. b'admin.vm.Kill',
  2590. b'admin.vm.Console',
  2591. b'admin.Events',
  2592. b'admin.vm.feature.List',
  2593. b'admin.vm.feature.Get',
  2594. b'admin.vm.feature.Remove',
  2595. b'admin.vm.feature.CheckWithTemplate',
  2596. ]
  2597. # make sure also no methods on actual VM gets called
  2598. vm_mock = unittest.mock.MagicMock()
  2599. vm_mock.name = self.vm.name
  2600. vm_mock.qid = self.vm.qid
  2601. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  2602. self.app.domains._dict[self.vm.qid] = vm_mock
  2603. for method in methods_with_no_payload:
  2604. # should reject payload regardless of having argument or not
  2605. with self.subTest(method.decode('ascii')):
  2606. with self.assertRaises(qubes.api.ProtocolError):
  2607. self.call_mgmt_func(method, b'test-vm1', b'',
  2608. b'unexpected-payload')
  2609. self.assertFalse(vm_mock.called)
  2610. self.assertFalse(self.app.save.called)
  2611. with self.subTest(method.decode('ascii') + '+arg'):
  2612. with self.assertRaises(qubes.api.ProtocolError):
  2613. self.call_mgmt_func(method, b'test-vm1', b'some-arg',
  2614. b'unexpected-payload')
  2615. self.assertFalse(vm_mock.called)
  2616. self.assertFalse(self.app.save.called)
  2617. def test_991_vm_unexpected_argument(self):
  2618. methods_with_no_argument = [
  2619. b'admin.vm.List',
  2620. b'admin.vm.Remove',
  2621. b'admin.vm.property.List',
  2622. b'admin.vm.feature.List',
  2623. b'admin.vm.tag.List',
  2624. b'admin.vm.firewall.Get',
  2625. b'admin.vm.firewall.Set',
  2626. b'admin.vm.firewall.Reload',
  2627. b'admin.vm.volume.List',
  2628. b'admin.vm.Start',
  2629. b'admin.vm.Pause',
  2630. b'admin.vm.Unpause',
  2631. b'admin.vm.Kill',
  2632. b'admin.vm.Console',
  2633. b'admin.Events',
  2634. b'admin.vm.feature.List',
  2635. ]
  2636. # make sure also no methods on actual VM gets called
  2637. vm_mock = unittest.mock.MagicMock()
  2638. vm_mock.name = self.vm.name
  2639. vm_mock.qid = self.vm.qid
  2640. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  2641. self.app.domains._dict[self.vm.qid] = vm_mock
  2642. exceptions = (qubes.api.PermissionDenied, qubes.api.ProtocolError)
  2643. for method in methods_with_no_argument:
  2644. # should reject argument regardless of having payload or not
  2645. with self.subTest(method.decode('ascii')):
  2646. with self.assertRaises(qubes.api.PermissionDenied):
  2647. self.call_mgmt_func(method, b'test-vm1', b'some-arg',
  2648. b'')
  2649. self.assertFalse(vm_mock.called)
  2650. self.assertFalse(self.app.save.called)
  2651. with self.subTest(method.decode('ascii') + '+payload'):
  2652. with self.assertRaises(exceptions):
  2653. self.call_mgmt_func(method, b'test-vm1', b'unexpected-arg',
  2654. b'some-payload')
  2655. self.assertFalse(vm_mock.called)
  2656. self.assertFalse(self.app.save.called)
  2657. def test_992_dom0_unexpected_payload(self):
  2658. methods_with_no_payload = [
  2659. b'admin.deviceclass.List',
  2660. b'admin.vmclass.List',
  2661. b'admin.vm.List',
  2662. b'admin.pool.volume.List',
  2663. b'admin.label.List',
  2664. b'admin.label.Get',
  2665. b'admin.label.Remove',
  2666. b'admin.property.List',
  2667. b'admin.property.Get',
  2668. b'admin.property.Help',
  2669. #b'admin.property.HelpRst',
  2670. b'admin.property.Reset',
  2671. b'admin.pool.List',
  2672. b'admin.pool.ListDrivers',
  2673. b'admin.pool.Info',
  2674. b'admin.pool.Remove',
  2675. b'admin.backup.Execute',
  2676. b'admin.Events',
  2677. ]
  2678. # make sure also no methods on actual VM gets called
  2679. vm_mock = unittest.mock.MagicMock()
  2680. vm_mock.name = self.vm.name
  2681. vm_mock.qid = self.vm.qid
  2682. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  2683. self.app.domains._dict[self.vm.qid] = vm_mock
  2684. for method in methods_with_no_payload:
  2685. # should reject payload regardless of having argument or not
  2686. with self.subTest(method.decode('ascii')):
  2687. with self.assertRaises(qubes.api.ProtocolError):
  2688. self.call_mgmt_func(method, b'dom0', b'',
  2689. b'unexpected-payload')
  2690. self.assertFalse(vm_mock.called)
  2691. self.assertFalse(self.app.save.called)
  2692. with self.subTest(method.decode('ascii') + '+arg'):
  2693. with self.assertRaises(qubes.api.ProtocolError):
  2694. self.call_mgmt_func(method, b'dom0', b'some-arg',
  2695. b'unexpected-payload')
  2696. self.assertFalse(vm_mock.called)
  2697. self.assertFalse(self.app.save.called)
  2698. def test_993_dom0_unexpected_argument(self):
  2699. methods_with_no_argument = [
  2700. b'admin.deviceclass.List',
  2701. b'admin.vmclass.List',
  2702. b'admin.vm.List',
  2703. b'admin.label.List',
  2704. b'admin.property.List',
  2705. b'admin.pool.List',
  2706. b'admin.pool.ListDrivers',
  2707. b'admin.Events',
  2708. ]
  2709. # make sure also no methods on actual VM gets called
  2710. vm_mock = unittest.mock.MagicMock()
  2711. vm_mock.name = self.vm.name
  2712. vm_mock.qid = self.vm.qid
  2713. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  2714. self.app.domains._dict[self.vm.qid] = vm_mock
  2715. exceptions = (qubes.api.PermissionDenied, qubes.api.ProtocolError)
  2716. for method in methods_with_no_argument:
  2717. # should reject argument regardless of having payload or not
  2718. with self.subTest(method.decode('ascii')):
  2719. with self.assertRaises(qubes.api.PermissionDenied):
  2720. self.call_mgmt_func(method, b'dom0', b'some-arg',
  2721. b'')
  2722. self.assertFalse(vm_mock.called)
  2723. self.assertFalse(self.app.save.called)
  2724. with self.subTest(method.decode('ascii') + '+payload'):
  2725. with self.assertRaises(exceptions):
  2726. self.call_mgmt_func(method, b'dom0', b'unexpected-arg',
  2727. b'some-payload')
  2728. self.assertFalse(vm_mock.called)
  2729. self.assertFalse(self.app.save.called)
  2730. def test_994_dom0_only_calls(self):
  2731. # TODO set some better arguments, to make sure the call was rejected
  2732. # because of invalid destination, not invalid arguments
  2733. methods_for_dom0_only = [
  2734. b'admin.deviceclass.List',
  2735. b'admin.vmclass.List',
  2736. b'admin.vm.Create.AppVM',
  2737. b'admin.vm.CreateInPool.AppVM',
  2738. b'admin.label.List',
  2739. b'admin.label.Create',
  2740. b'admin.label.Get',
  2741. b'admin.label.Remove',
  2742. b'admin.pool.volume.List',
  2743. b'admin.property.List',
  2744. b'admin.property.Get',
  2745. b'admin.property.Set',
  2746. b'admin.property.Help',
  2747. #b'admin.property.HelpRst',
  2748. b'admin.property.Reset',
  2749. b'admin.pool.List',
  2750. b'admin.pool.ListDrivers',
  2751. b'admin.pool.Info',
  2752. b'admin.pool.Add',
  2753. b'admin.pool.Remove',
  2754. #b'admin.pool.volume.List',
  2755. #b'admin.pool.volume.Info',
  2756. #b'admin.pool.volume.ListSnapshots',
  2757. #b'admin.pool.volume.Snapshot',
  2758. #b'admin.pool.volume.Revert',
  2759. #b'admin.pool.volume.Resize',
  2760. b'admin.backup.Execute',
  2761. b'admin.backup.Info',
  2762. ]
  2763. # make sure also no methods on actual VM gets called
  2764. vm_mock = unittest.mock.MagicMock()
  2765. vm_mock.name = self.vm.name
  2766. vm_mock.qid = self.vm.qid
  2767. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  2768. self.app.domains._dict[self.vm.qid] = vm_mock
  2769. exceptions = (qubes.api.PermissionDenied, qubes.api.ProtocolError)
  2770. for method in methods_for_dom0_only:
  2771. # should reject call regardless of having payload or not
  2772. with self.subTest(method.decode('ascii')):
  2773. with self.assertRaises(exceptions):
  2774. self.call_mgmt_func(method, b'test-vm1', b'',
  2775. b'')
  2776. self.assertFalse(vm_mock.called)
  2777. self.assertFalse(self.app.save.called)
  2778. with self.subTest(method.decode('ascii') + '+arg'):
  2779. with self.assertRaises(exceptions):
  2780. self.call_mgmt_func(method, b'test-vm1', b'some-arg',
  2781. b'')
  2782. self.assertFalse(vm_mock.called)
  2783. self.assertFalse(self.app.save.called)
  2784. with self.subTest(method.decode('ascii') + '+payload'):
  2785. with self.assertRaises(exceptions):
  2786. self.call_mgmt_func(method, b'test-vm1', b'',
  2787. b'payload')
  2788. self.assertFalse(vm_mock.called)
  2789. self.assertFalse(self.app.save.called)
  2790. with self.subTest(method.decode('ascii') + '+arg+payload'):
  2791. with self.assertRaises(exceptions):
  2792. self.call_mgmt_func(method, b'test-vm1', b'some-arg',
  2793. b'some-payload')
  2794. self.assertFalse(vm_mock.called)
  2795. self.assertFalse(self.app.save.called)
  2796. @unittest.skip('undecided')
  2797. def test_995_vm_only_calls(self):
  2798. # XXX is it really a good idea to prevent those calls this early?
  2799. # TODO set some better arguments, to make sure the call was rejected
  2800. # because of invalid destination, not invalid arguments
  2801. methods_for_vm_only = [
  2802. b'admin.vm.Clone',
  2803. b'admin.vm.Remove',
  2804. b'admin.vm.property.List',
  2805. b'admin.vm.property.Get',
  2806. b'admin.vm.property.Set',
  2807. b'admin.vm.property.Help',
  2808. b'admin.vm.property.HelpRst',
  2809. b'admin.vm.property.Reset',
  2810. b'admin.vm.feature.List',
  2811. b'admin.vm.feature.Get',
  2812. b'admin.vm.feature.Set',
  2813. b'admin.vm.feature.CheckWithTemplate',
  2814. b'admin.vm.feature.Remove',
  2815. b'admin.vm.tag.List',
  2816. b'admin.vm.tag.Get',
  2817. b'admin.vm.tag.Remove',
  2818. b'admin.vm.tag.Set',
  2819. b'admin.vm.firewall.Get',
  2820. b'admin.vm.firewall.Set',
  2821. b'admin.vm.firewall.Reload',
  2822. b'admin.vm.device.pci.Attach',
  2823. b'admin.vm.device.pci.Detach',
  2824. b'admin.vm.device.pci.List',
  2825. b'admin.vm.device.pci.Available',
  2826. b'admin.vm.microphone.Attach',
  2827. b'admin.vm.microphone.Detach',
  2828. b'admin.vm.microphone.Status',
  2829. b'admin.vm.volume.ListSnapshots',
  2830. b'admin.vm.volume.List',
  2831. b'admin.vm.volume.Info',
  2832. b'admin.vm.volume.Revert',
  2833. b'admin.vm.volume.Resize',
  2834. b'admin.vm.volume.Clear',
  2835. b'admin.vm.Start',
  2836. b'admin.vm.Shutdown',
  2837. b'admin.vm.Pause',
  2838. b'admin.vm.Unpause',
  2839. b'admin.vm.Kill',
  2840. b'admin.vm.feature.List',
  2841. b'admin.vm.feature.Get',
  2842. b'admin.vm.feature.Set',
  2843. b'admin.vm.feature.Remove',
  2844. b'admin.vm.feature.CheckWithTemplate',
  2845. ]
  2846. # make sure also no methods on actual VM gets called
  2847. vm_mock = unittest.mock.MagicMock()
  2848. vm_mock.name = self.vm.name
  2849. vm_mock.qid = self.vm.qid
  2850. vm_mock.__lt__ = (lambda x, y: x.qid < y.qid)
  2851. self.app.domains._dict[self.vm.qid] = vm_mock
  2852. exceptions = (qubes.api.PermissionDenied, qubes.api.ProtocolError)
  2853. for method in methods_for_vm_only:
  2854. # should reject payload regardless of having argument or not
  2855. # should reject call regardless of having payload or not
  2856. with self.subTest(method.decode('ascii')):
  2857. with self.assertRaises(exceptions):
  2858. self.call_mgmt_func(method, b'dom0', b'',
  2859. b'')
  2860. self.assertFalse(vm_mock.called)
  2861. self.assertFalse(self.app.save.called)
  2862. with self.subTest(method.decode('ascii') + '+arg'):
  2863. with self.assertRaises(exceptions):
  2864. self.call_mgmt_func(method, b'dom0', b'some-arg',
  2865. b'')
  2866. self.assertFalse(vm_mock.called)
  2867. self.assertFalse(self.app.save.called)
  2868. with self.subTest(method.decode('ascii') + '+payload'):
  2869. with self.assertRaises(exceptions):
  2870. self.call_mgmt_func(method, b'dom0', b'',
  2871. b'payload')
  2872. self.assertFalse(vm_mock.called)
  2873. self.assertFalse(self.app.save.called)
  2874. with self.subTest(method.decode('ascii') + '+arg+payload'):
  2875. with self.assertRaises(exceptions):
  2876. self.call_mgmt_func(method, b'dom0', b'some-arg',
  2877. b'some-payload')
  2878. self.assertFalse(vm_mock.called)
  2879. self.assertFalse(self.app.save.called)