qubesvm.py 88 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222
  1. # pylint: disable=protected-access
  2. #
  3. # The Qubes OS Project, https://www.qubes-os.org/
  4. #
  5. # Copyright (C) 2014-2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
  6. # Copyright (C) 2014-2015 Wojtek Porczyk <woju@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. #
  21. import base64
  22. import os
  23. import subprocess
  24. import tempfile
  25. import unittest
  26. import uuid
  27. import datetime
  28. import asyncio
  29. import functools
  30. import lxml.etree
  31. import unittest.mock
  32. import shutil
  33. import qubes
  34. import qubes.exc
  35. import qubes.config
  36. import qubes.devices
  37. import qubes.vm
  38. import qubes.vm.qubesvm
  39. import qubes.tests
  40. import qubes.tests.vm
  41. class TestApp(object):
  42. labels = {1: qubes.Label(1, '0xcc0000', 'red'),
  43. 2: qubes.Label(2, '0x00cc00', 'green'),
  44. 3: qubes.Label(3, '0x0000cc', 'blue'),
  45. 4: qubes.Label(4, '0xcccccc', 'black')}
  46. def __init__(self):
  47. self.domains = {}
  48. self.host = unittest.mock.Mock()
  49. self.host.memory_total = 4096 * 1024
  50. class TestFeatures(dict):
  51. def __init__(self, vm, **kwargs) -> None:
  52. self.vm = vm
  53. super().__init__(**kwargs)
  54. def check_with_template(self, feature, default):
  55. vm = self.vm
  56. while vm is not None:
  57. try:
  58. return vm.features[feature]
  59. except KeyError:
  60. vm = getattr(vm, 'template', None)
  61. return default
  62. class TestProp(object):
  63. # pylint: disable=too-few-public-methods
  64. __name__ = 'testprop'
  65. class TestDeviceCollection(object):
  66. def __init__(self):
  67. self._list = []
  68. def persistent(self):
  69. return self._list
  70. class TestQubesDB(object):
  71. def __init__(self, data=None):
  72. self.data = {}
  73. if data:
  74. self.data = data
  75. def write(self, path, value):
  76. self.data[path] = value
  77. def rm(self, path):
  78. if path.endswith('/'):
  79. for key in [x for x in self.data if x.startswith(path)]:
  80. del self.data[key]
  81. else:
  82. self.data.pop(path, None)
  83. def list(self, prefix):
  84. return [key for key in self.data if key.startswith(prefix)]
  85. def close(self):
  86. pass
  87. class TestVM(object):
  88. # pylint: disable=too-few-public-methods
  89. app = TestApp()
  90. def __init__(self, **kwargs):
  91. self.running = False
  92. self.installed_by_rpm = False
  93. for k, v in kwargs.items():
  94. setattr(self, k, v)
  95. self.devices = {'pci': TestDeviceCollection()}
  96. self.features = TestFeatures(self)
  97. def is_running(self):
  98. return self.running
  99. class TC_00_setters(qubes.tests.QubesTestCase):
  100. def setUp(self):
  101. super().setUp()
  102. self.vm = TestVM()
  103. self.prop = TestProp()
  104. def test_000_setter_qid(self):
  105. self.assertEqual(
  106. qubes.vm._setter_qid(self.vm, self.prop, 5), 5)
  107. def test_001_setter_qid_lt_0(self):
  108. with self.assertRaises(ValueError):
  109. qubes.vm._setter_qid(self.vm, self.prop, -1)
  110. def test_002_setter_qid_gt_max(self):
  111. with self.assertRaises(ValueError):
  112. qubes.vm._setter_qid(self.vm,
  113. self.prop, qubes.config.max_qid + 5)
  114. @unittest.skip('test not implemented')
  115. def test_020_setter_kernel(self):
  116. pass
  117. def test_030_setter_label_object(self):
  118. label = TestApp.labels[1]
  119. self.assertIs(label,
  120. qubes.vm.setter_label(self.vm, self.prop, label))
  121. def test_031_setter_label_getitem(self):
  122. label = TestApp.labels[1]
  123. self.assertIs(label,
  124. qubes.vm.setter_label(self.vm, self.prop, 'label-1'))
  125. # there is no check for self.app.get_label()
  126. def test_040_setter_virt_mode(self):
  127. self.assertEqual(
  128. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'hvm'),
  129. 'hvm')
  130. self.assertEqual(
  131. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'HVM'),
  132. 'hvm')
  133. self.assertEqual(
  134. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'PV'),
  135. 'pv')
  136. self.assertEqual(
  137. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'pvh'),
  138. 'pvh')
  139. self.vm.devices['pci']._list.append(object())
  140. with self.assertRaises(ValueError):
  141. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'pvh')
  142. with self.assertRaises(ValueError):
  143. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'True')
  144. class TC_10_default(qubes.tests.QubesTestCase):
  145. def setUp(self):
  146. super().setUp()
  147. self.app = TestApp()
  148. self.vm = TestVM(app=self.app)
  149. self.prop = TestProp()
  150. def test_000_default_with_template_simple(self):
  151. default_getter = qubes.vm.qubesvm._default_with_template('kernel',
  152. 'dfl-kernel')
  153. self.assertEqual(default_getter(self.vm), 'dfl-kernel')
  154. self.vm.template = None
  155. self.assertEqual(default_getter(self.vm), 'dfl-kernel')
  156. self.vm.template = unittest.mock.Mock()
  157. self.vm.template.kernel = 'template-kernel'
  158. self.assertEqual(default_getter(self.vm), 'template-kernel')
  159. def test_001_default_with_template_callable(self):
  160. default_getter = qubes.vm.qubesvm._default_with_template('kernel',
  161. lambda x: x.app.default_kernel)
  162. self.app.default_kernel = 'global-dfl-kernel'
  163. self.assertEqual(default_getter(self.vm), 'global-dfl-kernel')
  164. self.vm.template = None
  165. self.assertEqual(default_getter(self.vm), 'global-dfl-kernel')
  166. self.vm.template = unittest.mock.Mock()
  167. self.vm.template.kernel = 'template-kernel'
  168. self.assertEqual(default_getter(self.vm), 'template-kernel')
  169. def test_010_default_virt_mode(self):
  170. self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
  171. 'pvh')
  172. self.vm.template = unittest.mock.Mock()
  173. self.vm.template.virt_mode = 'hvm'
  174. self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
  175. 'hvm')
  176. self.vm.template = None
  177. self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
  178. 'pvh')
  179. self.vm.devices['pci'].persistent().append('some-dev')
  180. self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
  181. 'hvm')
  182. def test_020_default_maxmem(self):
  183. default_maxmem = 2048
  184. self.vm.is_memory_balancing_possible = \
  185. lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
  186. self.vm)
  187. self.vm.virt_mode = 'pvh'
  188. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
  189. default_maxmem)
  190. self.vm.virt_mode = 'hvm'
  191. # HVM without qubes tools
  192. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
  193. # just 'qrexec' feature
  194. self.vm.features['qrexec'] = True
  195. print(self.vm.features.check_with_template('qrexec', False))
  196. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
  197. default_maxmem)
  198. # some supported-service.*, but not meminfo-writer
  199. self.vm.features['supported-service.qubes-firewall'] = True
  200. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
  201. # then add meminfo-writer
  202. self.vm.features['supported-service.meminfo-writer'] = True
  203. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
  204. default_maxmem)
  205. def test_021_default_maxmem_with_pcidevs(self):
  206. self.vm.is_memory_balancing_possible = \
  207. lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
  208. self.vm)
  209. self.vm.devices['pci'].persistent().append('00_00.0')
  210. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
  211. def test_022_default_maxmem_linux(self):
  212. self.vm.is_memory_balancing_possible = \
  213. lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
  214. self.vm)
  215. self.vm.virt_mode = 'pvh'
  216. self.vm.memory = 400
  217. self.vm.features['os'] = 'Linux'
  218. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 2048)
  219. self.vm.memory = 100
  220. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 1000)
  221. class QubesVMTestsMixin(object):
  222. property_no_default = object()
  223. def setUp(self):
  224. super(QubesVMTestsMixin, self).setUp()
  225. self.app = qubes.tests.vm.TestApp()
  226. self.app.vmm.offline_mode = True
  227. self.app.default_kernel = None
  228. # when full test run is called, extensions are loaded by earlier
  229. # tests, but if just this test class is run, load them manually here,
  230. # to have the same behaviour
  231. qubes.ext.get_extensions()
  232. def tearDown(self):
  233. try:
  234. # self.app is not a real events emiter, so make the call manually
  235. for handler in qubes.Qubes.__handlers__.get('qubes-close'):
  236. handler(self.app, 'qubes-close')
  237. self.app.domains.close()
  238. except AttributeError:
  239. pass
  240. super(QubesVMTestsMixin, self).tearDown()
  241. def get_vm(self, name='test', cls=qubes.vm.qubesvm.QubesVM, vm=None, **kwargs):
  242. if not vm:
  243. vm = cls(self.app, None,
  244. qid=kwargs.pop('qid', 1), name=qubes.tests.VMPREFIX + name,
  245. **kwargs)
  246. self.app.domains[vm.qid] = vm
  247. self.app.domains[vm.uuid] = vm
  248. self.app.domains[vm.name] = vm
  249. self.app.domains[vm] = vm
  250. self.addCleanup(vm.close)
  251. return vm
  252. def assertPropertyValue(self, vm, prop_name, set_value, expected_value,
  253. expected_xml_content=None):
  254. # FIXME: any better exception list? or maybe all of that should be a
  255. # single exception?
  256. with self.assertNotRaises((ValueError, TypeError, KeyError)):
  257. setattr(vm, prop_name, set_value)
  258. self.assertEqual(getattr(vm, prop_name), expected_value)
  259. if expected_xml_content is not None:
  260. xml = vm.__xml__()
  261. prop_xml = xml.xpath(
  262. './properties/property[@name=\'{}\']'.format(prop_name))
  263. self.assertEqual(len(prop_xml), 1, "Property not found in XML")
  264. self.assertEqual(prop_xml[0].text, expected_xml_content)
  265. def assertPropertyInvalidValue(self, vm, prop_name, set_value):
  266. orig_value_set = True
  267. orig_value = None
  268. try:
  269. orig_value = getattr(vm, prop_name)
  270. except AttributeError:
  271. orig_value_set = False
  272. # FIXME: any better exception list? or maybe all of that should be a
  273. # single exception?
  274. with self.assertRaises((ValueError, TypeError, KeyError)):
  275. setattr(vm, prop_name, set_value)
  276. if orig_value_set:
  277. self.assertEqual(getattr(vm, prop_name), orig_value)
  278. else:
  279. with self.assertRaises(AttributeError):
  280. getattr(vm, prop_name)
  281. def assertPropertyDefaultValue(self, vm, prop_name,
  282. expected_default=property_no_default):
  283. if expected_default is self.property_no_default:
  284. with self.assertRaises(AttributeError):
  285. getattr(vm, prop_name)
  286. else:
  287. with self.assertNotRaises(AttributeError):
  288. self.assertEqual(getattr(vm, prop_name), expected_default)
  289. xml = vm.__xml__()
  290. prop_xml = xml.xpath(
  291. './properties/property[@name=\'{}\']'.format(prop_name))
  292. self.assertEqual(len(prop_xml), 0, "Property still found in XML")
  293. def _test_generic_bool_property(self, vm, prop_name, default=False):
  294. self.assertPropertyDefaultValue(vm, prop_name, default)
  295. self.assertPropertyValue(vm, prop_name, False, False, 'False')
  296. self.assertPropertyValue(vm, prop_name, True, True, 'True')
  297. delattr(vm, prop_name)
  298. self.assertPropertyDefaultValue(vm, prop_name, default)
  299. self.assertPropertyValue(vm, prop_name, 'True', True, 'True')
  300. self.assertPropertyValue(vm, prop_name, 'False', False, 'False')
  301. self.assertPropertyInvalidValue(vm, prop_name, 'xxx')
  302. self.assertPropertyValue(vm, prop_name, 123, True)
  303. self.assertPropertyInvalidValue(vm, prop_name, '')
  304. class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
  305. def test_000_init(self):
  306. self.get_vm()
  307. def test_001_init_no_qid_or_name(self):
  308. with self.assertRaises(AssertionError):
  309. qubes.vm.qubesvm.QubesVM(self.app, None,
  310. name=qubes.tests.VMPREFIX + 'test')
  311. with self.assertRaises(AssertionError):
  312. qubes.vm.qubesvm.QubesVM(self.app, None,
  313. qid=1)
  314. def test_003_init_fire_domain_init(self):
  315. class TestVM2(qubes.vm.qubesvm.QubesVM):
  316. event_fired = False
  317. @qubes.events.handler('domain-init')
  318. def on_domain_init(self, event): # pylint: disable=unused-argument
  319. self.__class__.event_fired = True
  320. TestVM2(self.app, None, qid=1, name=qubes.tests.VMPREFIX + 'test')
  321. self.assertTrue(TestVM2.event_fired)
  322. def test_004_uuid_autogen(self):
  323. vm = self.get_vm()
  324. self.assertTrue(hasattr(vm, 'uuid'))
  325. def test_100_qid(self):
  326. vm = self.get_vm()
  327. self.assertIsInstance(vm.qid, int)
  328. with self.assertRaises(AttributeError):
  329. vm.qid = 2
  330. def test_110_name(self):
  331. vm = self.get_vm()
  332. self.assertIsInstance(vm.name, str)
  333. def test_120_uuid(self):
  334. my_uuid = uuid.uuid4()
  335. vm = self.get_vm(uuid=my_uuid)
  336. self.assertIsInstance(vm.uuid, uuid.UUID)
  337. self.assertIs(vm.uuid, my_uuid)
  338. with self.assertRaises(AttributeError):
  339. vm.uuid = uuid.uuid4()
  340. @unittest.mock.patch("os.symlink")
  341. def test_130_label(self, _):
  342. vm = self.get_vm()
  343. self.assertPropertyDefaultValue(vm, 'label')
  344. self.assertPropertyValue(vm, 'label', self.app.labels[1],
  345. self.app.labels[1], 'red')
  346. del vm.label
  347. self.assertPropertyDefaultValue(vm, 'label')
  348. self.assertPropertyValue(vm, 'label', 'red',
  349. self.app.labels[1], 'red')
  350. self.assertPropertyValue(vm, 'label', 'label-1',
  351. self.app.labels[1], 'red')
  352. def test_131_label_invalid(self):
  353. vm = self.get_vm()
  354. self.assertPropertyInvalidValue(vm, 'label', 'invalid')
  355. self.assertPropertyInvalidValue(vm, 'label', 123)
  356. @unittest.mock.patch("os.symlink")
  357. def test_135_icon(self, _):
  358. vm = self.get_vm(cls=qubes.vm.appvm.AppVM)
  359. vm.label = "red"
  360. self.assertEqual(vm.icon, "appvm-red")
  361. templatevm = self.get_vm(cls=qubes.vm.templatevm.TemplateVM)
  362. templatevm.label = "blue"
  363. self.assertEqual(templatevm.icon, "templatevm-blue")
  364. vm.template_for_dispvms = True
  365. dispvm = self.get_vm(cls=qubes.vm.dispvm.DispVM, template=vm,
  366. dispid=10)
  367. dispvm.label = "black"
  368. self.assertEqual(dispvm.icon, "dispvm-black")
  369. vm = self.get_vm()
  370. vm.label = "green"
  371. vm.features["servicevm"] = 1
  372. self.assertEqual(vm.icon, "servicevm-green")
  373. def test_160_memory(self):
  374. vm = self.get_vm()
  375. self.assertPropertyDefaultValue(vm, 'memory', 400)
  376. self.assertPropertyValue(vm, 'memory', 500, 500, '500')
  377. del vm.memory
  378. self.assertPropertyDefaultValue(vm, 'memory', 400)
  379. self.assertPropertyValue(vm, 'memory', '500', 500, '500')
  380. def test_161_memory_invalid(self):
  381. vm = self.get_vm()
  382. self.assertPropertyInvalidValue(vm, 'memory', -100)
  383. self.assertPropertyInvalidValue(vm, 'memory', '-100')
  384. self.assertPropertyInvalidValue(vm, 'memory', '')
  385. # TODO: higher than maxmem
  386. # TODO: human readable setter (500M, 4G)?
  387. def test_170_maxmem(self):
  388. vm = self.get_vm()
  389. self.assertPropertyDefaultValue(vm, 'maxmem',
  390. self.app.host.memory_total / 1024 / 2)
  391. self.assertPropertyValue(vm, 'maxmem', 500, 500, '500')
  392. del vm.maxmem
  393. self.assertPropertyDefaultValue(vm, 'maxmem',
  394. self.app.host.memory_total / 1024 / 2)
  395. self.assertPropertyValue(vm, 'maxmem', '500', 500, '500')
  396. def test_171_maxmem_invalid(self):
  397. vm = self.get_vm()
  398. self.assertPropertyInvalidValue(vm, 'maxmem', -100)
  399. self.assertPropertyInvalidValue(vm, 'maxmem', '-100')
  400. self.assertPropertyInvalidValue(vm, 'maxmem', '')
  401. # TODO: lower than memory
  402. # TODO: human readable setter (500M, 4G)?
  403. def test_190_vcpus(self):
  404. vm = self.get_vm()
  405. self.assertPropertyDefaultValue(vm, 'vcpus', 2)
  406. self.assertPropertyValue(vm, 'vcpus', 3, 3, '3')
  407. del vm.vcpus
  408. self.assertPropertyDefaultValue(vm, 'vcpus', 2)
  409. self.assertPropertyValue(vm, 'vcpus', '3', 3, '3')
  410. def test_191_vcpus_invalid(self):
  411. vm = self.get_vm()
  412. self.assertPropertyInvalidValue(vm, 'vcpus', 0)
  413. self.assertPropertyInvalidValue(vm, 'vcpus', -2)
  414. self.assertPropertyInvalidValue(vm, 'vcpus', '-2')
  415. self.assertPropertyInvalidValue(vm, 'vcpus', '')
  416. def test_200_debug(self):
  417. vm = self.get_vm()
  418. self._test_generic_bool_property(vm, 'debug', False)
  419. def test_210_installed_by_rpm(self):
  420. vm = self.get_vm()
  421. self._test_generic_bool_property(vm, 'installed_by_rpm', False)
  422. def test_220_include_in_backups(self):
  423. vm = self.get_vm()
  424. self._test_generic_bool_property(vm, 'include_in_backups', True)
  425. @qubes.tests.skipUnlessDom0
  426. def test_250_kernel(self):
  427. kernels = os.listdir(os.path.join(
  428. qubes.config.qubes_base_dir,
  429. qubes.config.system_path['qubes_kernels_base_dir']))
  430. if not len(kernels):
  431. self.skipTest('Needs at least one kernel installed')
  432. self.app.default_kernel = kernels[0]
  433. vm = self.get_vm()
  434. self.assertPropertyDefaultValue(vm, 'kernel', kernels[0])
  435. self.assertPropertyValue(vm, 'kernel', kernels[-1], kernels[-1],
  436. kernels[-1])
  437. del vm.kernel
  438. self.assertPropertyDefaultValue(vm, 'kernel', kernels[0])
  439. @qubes.tests.skipUnlessDom0
  440. def test_251_kernel_invalid(self):
  441. vm = self.get_vm()
  442. self.assertPropertyInvalidValue(vm, 'kernel', 123)
  443. self.assertPropertyInvalidValue(vm, 'kernel', 'invalid')
  444. def test_252_kernel_empty(self):
  445. vm = self.get_vm()
  446. self.assertPropertyValue(vm, 'kernel', '', '', '')
  447. self.assertPropertyValue(vm, 'kernel', None, '', '')
  448. @unittest.mock.patch.dict(qubes.config.system_path,
  449. {'qubes_kernels_base_dir': '/tmp'})
  450. def test_260_kernelopts(self):
  451. d = tempfile.mkdtemp(prefix='/tmp/')
  452. self.addCleanup(shutil.rmtree, d)
  453. open(d + '/vmlinuz', 'w').close()
  454. open(d + '/initramfs', 'w').close()
  455. vm = self.get_vm()
  456. vm.kernel = os.path.basename(d)
  457. self.assertPropertyDefaultValue(vm, 'kernelopts',
  458. qubes.config.defaults['kernelopts'])
  459. self.assertPropertyValue(vm, 'kernelopts', 'some options',
  460. 'some options', 'some options')
  461. del vm.kernelopts
  462. self.assertPropertyDefaultValue(vm, 'kernelopts',
  463. qubes.config.defaults['kernelopts'])
  464. self.assertPropertyValue(vm, 'kernelopts', '',
  465. '', '')
  466. # TODO?
  467. # self.assertPropertyInvalidValue(vm, 'kernelopts', None),
  468. @unittest.skip('test not implemented')
  469. def test_261_kernelopts_pcidevs(self):
  470. vm = self.get_vm()
  471. # how to do that here? use dummy DeviceManager/DeviceCollection?
  472. # Disable events?
  473. vm.devices['pci'].attach('something')
  474. self.assertPropertyDefaultValue(vm, 'kernelopts',
  475. qubes.config.defaults['kernelopts_pcidevs'])
  476. @unittest.mock.patch.dict(qubes.config.system_path,
  477. {'qubes_kernels_base_dir': '/tmp'})
  478. def test_262_kernelopts(self):
  479. d = tempfile.mkdtemp(prefix='/tmp/')
  480. self.addCleanup(shutil.rmtree, d)
  481. open(d + '/vmlinuz', 'w').close()
  482. open(d + '/initramfs', 'w').close()
  483. with open(d + '/default-kernelopts-nopci.txt', 'w') as f:
  484. f.write('some default options')
  485. vm = self.get_vm()
  486. vm.kernel = os.path.basename(d)
  487. self.assertPropertyDefaultValue(vm, 'kernelopts',
  488. 'some default options')
  489. self.assertPropertyValue(vm, 'kernelopts', 'some options',
  490. 'some options', 'some options')
  491. del vm.kernelopts
  492. self.assertPropertyDefaultValue(vm, 'kernelopts',
  493. 'some default options')
  494. self.assertPropertyValue(vm, 'kernelopts', '',
  495. '', '')
  496. def test_270_qrexec_timeout(self):
  497. vm = self.get_vm()
  498. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 60)
  499. self.assertPropertyValue(vm, 'qrexec_timeout', 3, 3, '3')
  500. del vm.qrexec_timeout
  501. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 60)
  502. self.assertPropertyValue(vm, 'qrexec_timeout', '3', 3, '3')
  503. def test_271_qrexec_timeout_invalid(self):
  504. vm = self.get_vm()
  505. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', -2)
  506. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', '-2')
  507. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', '')
  508. def test_272_qrexec_timeout_global_changed(self):
  509. self.app.default_qrexec_timeout = 123
  510. vm = self.get_vm()
  511. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 123)
  512. self.assertPropertyValue(vm, 'qrexec_timeout', 3, 3, '3')
  513. del vm.qrexec_timeout
  514. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 123)
  515. self.assertPropertyValue(vm, 'qrexec_timeout', '3', 3, '3')
  516. def test_280_autostart(self):
  517. vm = self.get_vm()
  518. # FIXME any better idea to not involve systemctl call at this stage?
  519. vm.events_enabled = False
  520. self._test_generic_bool_property(vm, 'autostart', False)
  521. @qubes.tests.skipUnlessDom0
  522. def test_281_autostart_systemd(self):
  523. vm = self.get_vm()
  524. self.assertFalse(os.path.exists(
  525. '/etc/systemd/system/multi-user.target.wants/'
  526. 'qubes-vm@{}.service'.format(vm.name)),
  527. "systemd service enabled before setting autostart")
  528. vm.autostart = True
  529. self.assertTrue(os.path.exists(
  530. '/etc/systemd/system/multi-user.target.wants/'
  531. 'qubes-vm@{}.service'.format(vm.name)),
  532. "systemd service not enabled by autostart=True")
  533. vm.autostart = False
  534. self.assertFalse(os.path.exists(
  535. '/etc/systemd/system/multi-user.target.wants/'
  536. 'qubes-vm@{}.service'.format(vm.name)),
  537. "systemd service not disabled by autostart=False")
  538. vm.autostart = True
  539. del vm.autostart
  540. self.assertFalse(os.path.exists(
  541. '/etc/systemd/system/multi-user.target.wants/'
  542. 'qubes-vm@{}.service'.format(vm.name)),
  543. "systemd service not disabled by resetting autostart")
  544. def test_290_management_dispvm(self):
  545. vm = self.get_vm()
  546. vm2 = self.get_vm('test2', qid=2)
  547. self.app.management_dispvm = None
  548. self.assertPropertyDefaultValue(vm, 'management_dispvm', None)
  549. self.app.management_dispvm = vm
  550. try:
  551. self.assertPropertyDefaultValue(vm, 'management_dispvm', vm)
  552. self.assertPropertyValue(vm, 'management_dispvm',
  553. 'test-inst-test2', vm2)
  554. finally:
  555. self.app.management_dispvm = None
  556. def test_291_management_dispvm_template_based(self):
  557. tpl = self.get_vm(name='tpl', cls=qubes.vm.templatevm.TemplateVM)
  558. vm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=tpl, qid=2)
  559. vm2 = self.get_vm('test2', qid=3)
  560. del vm.volumes
  561. self.app.management_dispvm = None
  562. try:
  563. self.assertPropertyDefaultValue(vm, 'management_dispvm', None)
  564. self.app.management_dispvm = vm
  565. self.assertPropertyDefaultValue(vm, 'management_dispvm', vm)
  566. tpl.management_dispvm = vm2
  567. self.assertPropertyDefaultValue(vm, 'management_dispvm', vm2)
  568. self.assertPropertyValue(vm, 'management_dispvm',
  569. 'test-inst-test2', vm2)
  570. finally:
  571. self.app.management_dispvm = None
  572. @unittest.skip('TODO')
  573. def test_320_seamless_gui_mode(self):
  574. vm = self.get_vm()
  575. self._test_generic_bool_property(vm, 'seamless_gui_mode')
  576. # TODO: reject setting to True when guiagent_installed is false
  577. def test_330_mac(self):
  578. vm = self.get_vm()
  579. # TODO: calculate proper default here
  580. default_mac = vm.mac
  581. self.assertIsNotNone(default_mac)
  582. self.assertPropertyDefaultValue(vm, 'mac', default_mac)
  583. self.assertPropertyValue(vm, 'mac', '00:11:22:33:44:55',
  584. '00:11:22:33:44:55', '00:11:22:33:44:55')
  585. del vm.mac
  586. self.assertPropertyDefaultValue(vm, 'mac', default_mac)
  587. def test_331_mac_invalid(self):
  588. vm = self.get_vm()
  589. self.assertPropertyInvalidValue(vm, 'mac', 123)
  590. self.assertPropertyInvalidValue(vm, 'mac', 'invalid')
  591. self.assertPropertyInvalidValue(vm, 'mac', '00:11:22:33:44:55:66')
  592. def test_340_default_user(self):
  593. vm = self.get_vm()
  594. self.assertPropertyDefaultValue(vm, 'default_user', 'user')
  595. self.assertPropertyValue(vm, 'default_user', 'someuser', 'someuser',
  596. 'someuser')
  597. del vm.default_user
  598. self.assertPropertyDefaultValue(vm, 'default_user', 'user')
  599. self.assertPropertyValue(vm, 'default_user', 123, '123', '123')
  600. vm.default_user = 'user'
  601. # TODO: check propagation for template-based VMs
  602. @unittest.skip('TODO')
  603. def test_350_timezone(self):
  604. vm = self.get_vm()
  605. self.assertPropertyDefaultValue(vm, 'timezone', 'localtime')
  606. self.assertPropertyValue(vm, 'timezone', 0, 0, '0')
  607. del vm.timezone
  608. self.assertPropertyDefaultValue(vm, 'timezone', 'localtime')
  609. self.assertPropertyValue(vm, 'timezone', '0', 0, '0')
  610. self.assertPropertyValue(vm, 'timezone', -3600, -3600, '-3600')
  611. self.assertPropertyValue(vm, 'timezone', 7200, 7200, '7200')
  612. @unittest.skip('TODO')
  613. def test_350_timezone_invalid(self):
  614. vm = self.get_vm()
  615. self.assertPropertyInvalidValue(vm, 'timezone', 'xxx')
  616. @unittest.skip('TODO')
  617. def test_360_drive(self):
  618. vm = self.get_vm()
  619. self.assertPropertyDefaultValue(vm, 'drive', None)
  620. # self.execute_tests('drive', [
  621. # ('hd:dom0:/tmp/drive.img', 'hd:dom0:/tmp/drive.img', True),
  622. # ('hd:/tmp/drive.img', 'hd:dom0:/tmp/drive.img', True),
  623. # ('cdrom:dom0:/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  624. # ('cdrom:/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  625. # ('/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  626. # ('hd:drive.img', '', False),
  627. # ('drive.img', '', False),
  628. # ])
  629. def test_400_backup_timestamp(self):
  630. vm = self.get_vm()
  631. timestamp = datetime.datetime(2016, 1, 1, 12, 14, 2)
  632. timestamp_str = timestamp.strftime('%s')
  633. self.assertPropertyDefaultValue(vm, 'backup_timestamp', None)
  634. self.assertPropertyValue(vm, 'backup_timestamp', int(timestamp_str),
  635. int(timestamp_str), timestamp_str)
  636. del vm.backup_timestamp
  637. self.assertPropertyDefaultValue(vm, 'backup_timestamp', None)
  638. self.assertPropertyValue(vm, 'backup_timestamp', timestamp_str,
  639. int(timestamp_str))
  640. def test_401_backup_timestamp_invalid(self):
  641. vm = self.get_vm()
  642. self.assertPropertyInvalidValue(vm, 'backup_timestamp', 'xxx')
  643. self.assertPropertyInvalidValue(vm, 'backup_timestamp', None)
  644. def test_500_property_migrate_virt_mode(self):
  645. xml_template = '''
  646. <domain class="QubesVM" id="domain-1">
  647. <properties>
  648. <property name="qid">1</property>
  649. <property name="name">testvm</property>
  650. <property name="label" ref="label-1" />
  651. <property name="hvm">{hvm_value}</property>
  652. </properties>
  653. </domain>
  654. '''
  655. xml = lxml.etree.XML(xml_template.format(hvm_value='True'))
  656. vm = qubes.vm.qubesvm.QubesVM(self.app, xml)
  657. self.assertEqual(vm.virt_mode, 'hvm')
  658. with self.assertRaises(AttributeError):
  659. vm.hvm
  660. xml = lxml.etree.XML(xml_template.format(hvm_value='False'))
  661. vm = qubes.vm.qubesvm.QubesVM(self.app, xml)
  662. self.assertEqual(vm.virt_mode, 'pv')
  663. with self.assertRaises(AttributeError):
  664. vm.hvm
  665. def test_600_libvirt_xml_pv(self):
  666. expected = '''<domain type="xen">
  667. <name>test-inst-test</name>
  668. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  669. <memory unit="MiB">500</memory>
  670. <currentMemory unit="MiB">400</currentMemory>
  671. <vcpu placement="static">2</vcpu>
  672. <os>
  673. <type arch="x86_64" machine="xenpv">linux</type>
  674. <kernel>/tmp/kernel/vmlinuz</kernel>
  675. <initrd>/tmp/kernel/initramfs</initrd>
  676. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0</cmdline>
  677. </os>
  678. <features>
  679. </features>
  680. <clock offset='utc' adjustment='reset'>
  681. <timer name="tsc" mode="native"/>
  682. </clock>
  683. <on_poweroff>destroy</on_poweroff>
  684. <on_reboot>destroy</on_reboot>
  685. <on_crash>destroy</on_crash>
  686. <devices>
  687. <disk type="block" device="disk">
  688. <driver name="phy" />
  689. <source dev="/tmp/kernel/modules.img" />
  690. <target dev="xvdd" />
  691. <backenddomain name="dom0" />
  692. </disk>
  693. <console type="pty">
  694. <target type="xen" port="0"/>
  695. </console>
  696. </devices>
  697. </domain>
  698. '''
  699. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  700. vm = self.get_vm(uuid=my_uuid)
  701. vm.netvm = None
  702. vm.virt_mode = 'pv'
  703. with unittest.mock.patch('qubes.config.qubes_base_dir',
  704. '/tmp/qubes-test'):
  705. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  706. os.makedirs(kernel_dir, exist_ok=True)
  707. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  708. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  709. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  710. vm.kernel = 'dummy'
  711. # tests for storage are later
  712. vm.volumes['kernel'] = unittest.mock.Mock(**{
  713. 'kernels_dir': '/tmp/kernel',
  714. 'block_device.return_value.domain': 'dom0',
  715. 'block_device.return_value.script': None,
  716. 'block_device.return_value.path': '/tmp/kernel/modules.img',
  717. 'block_device.return_value.devtype': 'disk',
  718. 'block_device.return_value.name': 'kernel',
  719. })
  720. libvirt_xml = vm.create_config_file()
  721. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  722. lxml.etree.XML(expected))
  723. def test_600_libvirt_xml_hvm(self):
  724. expected = '''<domain type="xen">
  725. <name>test-inst-test</name>
  726. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  727. <memory unit="MiB">400</memory>
  728. <currentMemory unit="MiB">400</currentMemory>
  729. <vcpu placement="static">2</vcpu>
  730. <cpu mode='host-passthrough'>
  731. <!-- disable nested HVM -->
  732. <feature name='vmx' policy='disable'/>
  733. <feature name='svm' policy='disable'/>
  734. </cpu>
  735. <os>
  736. <type arch="x86_64" machine="xenfv">hvm</type>
  737. <!--
  738. For the libxl backend libvirt switches between OVMF (UEFI)
  739. and SeaBIOS based on the loader type. This has nothing to
  740. do with the hvmloader binary.
  741. -->
  742. <loader type="rom">hvmloader</loader>
  743. <boot dev="cdrom" />
  744. <boot dev="hd" />
  745. </os>
  746. <features>
  747. <pae/>
  748. <acpi/>
  749. <apic/>
  750. <viridian/>
  751. </features>
  752. <clock offset="variable" adjustment="0" basis="localtime" />
  753. <on_poweroff>destroy</on_poweroff>
  754. <on_reboot>destroy</on_reboot>
  755. <on_crash>destroy</on_crash>
  756. <devices>
  757. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  758. <emulator type="stubdom-linux" />
  759. <input type="tablet" bus="usb"/>
  760. <video>
  761. <model type="vga"/>
  762. </video>
  763. <graphics type="qubes"/>
  764. <console type="pty">
  765. <target type="xen" port="0"/>
  766. </console>
  767. </devices>
  768. </domain>
  769. '''
  770. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  771. vm = self.get_vm(uuid=my_uuid)
  772. vm.netvm = None
  773. vm.virt_mode = 'hvm'
  774. libvirt_xml = vm.create_config_file()
  775. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  776. lxml.etree.XML(expected))
  777. def test_600_libvirt_xml_hvm_dom0_kernel(self):
  778. expected = '''<domain type="xen">
  779. <name>test-inst-test</name>
  780. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  781. <memory unit="MiB">500</memory>
  782. <currentMemory unit="MiB">400</currentMemory>
  783. <vcpu placement="static">2</vcpu>
  784. <cpu mode='host-passthrough'>
  785. <!-- disable nested HVM -->
  786. <feature name='vmx' policy='disable'/>
  787. <feature name='svm' policy='disable'/>
  788. </cpu>
  789. <os>
  790. <type arch="x86_64" machine="xenfv">hvm</type>
  791. <!--
  792. For the libxl backend libvirt switches between OVMF (UEFI)
  793. and SeaBIOS based on the loader type. This has nothing to
  794. do with the hvmloader binary.
  795. -->
  796. <loader type="rom">hvmloader</loader>
  797. <boot dev="cdrom" />
  798. <boot dev="hd" />
  799. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0</cmdline>
  800. </os>
  801. <features>
  802. <pae/>
  803. <acpi/>
  804. <apic/>
  805. <viridian/>
  806. </features>
  807. <clock offset="variable" adjustment="0" basis="localtime" />
  808. <on_poweroff>destroy</on_poweroff>
  809. <on_reboot>destroy</on_reboot>
  810. <on_crash>destroy</on_crash>
  811. <devices>
  812. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  813. <emulator type="stubdom-linux" />
  814. <input type="tablet" bus="usb"/>
  815. <video>
  816. <model type="vga"/>
  817. </video>
  818. <graphics type="qubes"/>
  819. <console type="pty">
  820. <target type="xen" port="0"/>
  821. </console>
  822. </devices>
  823. </domain>
  824. '''
  825. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  826. vm = self.get_vm(uuid=my_uuid)
  827. vm.netvm = None
  828. vm.virt_mode = 'hvm'
  829. vm.features['qrexec'] = True
  830. with unittest.mock.patch('qubes.config.qubes_base_dir',
  831. '/tmp/qubes-test'):
  832. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  833. os.makedirs(kernel_dir, exist_ok=True)
  834. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  835. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  836. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  837. vm.kernel = 'dummy'
  838. libvirt_xml = vm.create_config_file()
  839. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  840. lxml.etree.XML(expected))
  841. def test_600_libvirt_xml_hvm_dom0_kernel_kernelopts(self):
  842. expected = '''<domain type="xen">
  843. <name>test-inst-test</name>
  844. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  845. <memory unit="MiB">500</memory>
  846. <currentMemory unit="MiB">400</currentMemory>
  847. <vcpu placement="static">2</vcpu>
  848. <cpu mode='host-passthrough'>
  849. <!-- disable nested HVM -->
  850. <feature name='vmx' policy='disable'/>
  851. <feature name='svm' policy='disable'/>
  852. </cpu>
  853. <os>
  854. <type arch="x86_64" machine="xenfv">hvm</type>
  855. <!--
  856. For the libxl backend libvirt switches between OVMF (UEFI)
  857. and SeaBIOS based on the loader type. This has nothing to
  858. do with the hvmloader binary.
  859. -->
  860. <loader type="rom">hvmloader</loader>
  861. <boot dev="cdrom" />
  862. <boot dev="hd" />
  863. <cmdline>kernel &lt;text&gt; specific options</cmdline>
  864. </os>
  865. <features>
  866. <pae/>
  867. <acpi/>
  868. <apic/>
  869. <viridian/>
  870. </features>
  871. <clock offset="variable" adjustment="0" basis="localtime" />
  872. <on_poweroff>destroy</on_poweroff>
  873. <on_reboot>destroy</on_reboot>
  874. <on_crash>destroy</on_crash>
  875. <devices>
  876. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  877. <emulator type="stubdom-linux" />
  878. <input type="tablet" bus="usb"/>
  879. <video>
  880. <model type="vga"/>
  881. </video>
  882. <graphics type="qubes"/>
  883. <console type="pty">
  884. <target type="xen" port="0"/>
  885. </console>
  886. </devices>
  887. </domain>
  888. '''
  889. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  890. vm = self.get_vm(uuid=my_uuid)
  891. vm.netvm = None
  892. vm.virt_mode = 'hvm'
  893. vm.features['qrexec'] = True
  894. with unittest.mock.patch('qubes.config.qubes_base_dir',
  895. '/tmp/qubes-test'):
  896. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  897. os.makedirs(kernel_dir, exist_ok=True)
  898. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  899. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  900. with open(os.path.join(kernel_dir,
  901. 'default-kernelopts-common.txt'), 'w') as f:
  902. f.write('kernel <text> specific options \n')
  903. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  904. vm.kernel = 'dummy'
  905. libvirt_xml = vm.create_config_file()
  906. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  907. lxml.etree.XML(expected))
  908. def test_600_libvirt_xml_pvh(self):
  909. expected = '''<domain type="xen">
  910. <name>test-inst-test</name>
  911. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  912. <memory unit="MiB">500</memory>
  913. <currentMemory unit="MiB">400</currentMemory>
  914. <vcpu placement="static">2</vcpu>
  915. <cpu mode='host-passthrough'>
  916. <!-- disable nested HVM -->
  917. <feature name='vmx' policy='disable'/>
  918. <feature name='svm' policy='disable'/>
  919. </cpu>
  920. <os>
  921. <type arch="x86_64" machine="xenpvh">xenpvh</type>
  922. <kernel>/tmp/kernel/vmlinuz</kernel>
  923. <initrd>/tmp/kernel/initramfs</initrd>
  924. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0</cmdline>
  925. </os>
  926. <features>
  927. <pae/>
  928. <acpi/>
  929. <apic/>
  930. <viridian/>
  931. </features>
  932. <clock offset='utc' adjustment='reset'>
  933. <timer name="tsc" mode="native"/>
  934. </clock>
  935. <on_poweroff>destroy</on_poweroff>
  936. <on_reboot>destroy</on_reboot>
  937. <on_crash>destroy</on_crash>
  938. <devices>
  939. <disk type="block" device="disk">
  940. <driver name="phy" />
  941. <source dev="/tmp/kernel/modules.img" />
  942. <target dev="xvdd" />
  943. <backenddomain name="dom0" />
  944. </disk>
  945. <console type="pty">
  946. <target type="xen" port="0"/>
  947. </console>
  948. </devices>
  949. </domain>
  950. '''
  951. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  952. vm = self.get_vm(uuid=my_uuid)
  953. vm.netvm = None
  954. vm.virt_mode = 'pvh'
  955. with unittest.mock.patch('qubes.config.qubes_base_dir',
  956. '/tmp/qubes-test'):
  957. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  958. os.makedirs(kernel_dir, exist_ok=True)
  959. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  960. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  961. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  962. vm.kernel = 'dummy'
  963. # tests for storage are later
  964. vm.volumes['kernel'] = unittest.mock.Mock(**{
  965. 'kernels_dir': '/tmp/kernel',
  966. 'block_device.return_value.domain': 'dom0',
  967. 'block_device.return_value.script': None,
  968. 'block_device.return_value.path': '/tmp/kernel/modules.img',
  969. 'block_device.return_value.devtype': 'disk',
  970. 'block_device.return_value.name': 'kernel',
  971. })
  972. libvirt_xml = vm.create_config_file()
  973. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  974. lxml.etree.XML(expected))
  975. def test_600_libvirt_xml_pvh_no_membalance(self):
  976. expected = '''<domain type="xen">
  977. <name>test-inst-test</name>
  978. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  979. <memory unit="MiB">400</memory>
  980. <currentMemory unit="MiB">400</currentMemory>
  981. <vcpu placement="static">2</vcpu>
  982. <cpu mode='host-passthrough'>
  983. <!-- disable nested HVM -->
  984. <feature name='vmx' policy='disable'/>
  985. <feature name='svm' policy='disable'/>
  986. </cpu>
  987. <os>
  988. <type arch="x86_64" machine="xenpvh">xenpvh</type>
  989. <kernel>/tmp/kernel/vmlinuz</kernel>
  990. <initrd>/tmp/kernel/initramfs</initrd>
  991. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0</cmdline>
  992. </os>
  993. <features>
  994. <pae/>
  995. <acpi/>
  996. <apic/>
  997. <viridian/>
  998. </features>
  999. <clock offset='utc' adjustment='reset'>
  1000. <timer name="tsc" mode="native"/>
  1001. </clock>
  1002. <on_poweroff>destroy</on_poweroff>
  1003. <on_reboot>destroy</on_reboot>
  1004. <on_crash>destroy</on_crash>
  1005. <devices>
  1006. <disk type="block" device="disk">
  1007. <driver name="phy" />
  1008. <source dev="/tmp/kernel/modules.img" />
  1009. <target dev="xvdd" />
  1010. <backenddomain name="dom0" />
  1011. </disk>
  1012. <console type="pty">
  1013. <target type="xen" port="0"/>
  1014. </console>
  1015. </devices>
  1016. </domain>
  1017. '''
  1018. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  1019. vm = self.get_vm(uuid=my_uuid)
  1020. vm.netvm = None
  1021. vm.virt_mode = 'pvh'
  1022. vm.maxmem = 0
  1023. with unittest.mock.patch('qubes.config.qubes_base_dir',
  1024. '/tmp/qubes-test'):
  1025. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  1026. os.makedirs(kernel_dir, exist_ok=True)
  1027. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  1028. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  1029. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  1030. vm.kernel = 'dummy'
  1031. # tests for storage are later
  1032. vm.volumes['kernel'] = unittest.mock.Mock(**{
  1033. 'kernels_dir': '/tmp/kernel',
  1034. 'block_device.return_value.domain': 'dom0',
  1035. 'block_device.return_value.script': None,
  1036. 'block_device.return_value.path': '/tmp/kernel/modules.img',
  1037. 'block_device.return_value.devtype': 'disk',
  1038. 'block_device.return_value.name': 'kernel',
  1039. })
  1040. libvirt_xml = vm.create_config_file()
  1041. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1042. lxml.etree.XML(expected))
  1043. def test_600_libvirt_xml_hvm_pcidev(self):
  1044. expected = '''<domain type="xen">
  1045. <name>test-inst-test</name>
  1046. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  1047. <memory unit="MiB">400</memory>
  1048. <currentMemory unit="MiB">400</currentMemory>
  1049. <vcpu placement="static">2</vcpu>
  1050. <cpu mode='host-passthrough'>
  1051. <!-- disable nested HVM -->
  1052. <feature name='vmx' policy='disable'/>
  1053. <feature name='svm' policy='disable'/>
  1054. </cpu>
  1055. <os>
  1056. <type arch="x86_64" machine="xenfv">hvm</type>
  1057. <!--
  1058. For the libxl backend libvirt switches between OVMF (UEFI)
  1059. and SeaBIOS based on the loader type. This has nothing to
  1060. do with the hvmloader binary.
  1061. -->
  1062. <loader type="rom">hvmloader</loader>
  1063. <boot dev="cdrom" />
  1064. <boot dev="hd" />
  1065. </os>
  1066. <features>
  1067. <pae/>
  1068. <acpi/>
  1069. <apic/>
  1070. <viridian/>
  1071. <xen>
  1072. <e820_host state="on"/>
  1073. </xen>
  1074. </features>
  1075. <clock offset="variable" adjustment="0" basis="localtime" />
  1076. <on_poweroff>destroy</on_poweroff>
  1077. <on_reboot>destroy</on_reboot>
  1078. <on_crash>destroy</on_crash>
  1079. <devices>
  1080. <hostdev type="pci" managed="yes">
  1081. <source>
  1082. <address
  1083. bus="0x00"
  1084. slot="0x00"
  1085. function="0x0" />
  1086. </source>
  1087. </hostdev>
  1088. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  1089. <emulator type="stubdom-linux" />
  1090. <input type="tablet" bus="usb"/>
  1091. <video>
  1092. <model type="vga"/>
  1093. </video>
  1094. <graphics type="qubes"/>
  1095. <console type="pty">
  1096. <target type="xen" port="0"/>
  1097. </console>
  1098. </devices>
  1099. </domain>
  1100. '''
  1101. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  1102. # required for PCI devices listing
  1103. self.app.vmm.offline_mode = False
  1104. vm = self.get_vm(uuid=my_uuid)
  1105. vm.netvm = None
  1106. vm.virt_mode = 'hvm'
  1107. vm.kernel = None
  1108. # even with meminfo-writer enabled, should have memory==maxmem
  1109. vm.features['service.meminfo-writer'] = True
  1110. assignment = qubes.devices.DeviceAssignment(
  1111. vm, # this is violation of API, but for PCI the argument
  1112. # is unused
  1113. '00_00.0',
  1114. bus='pci',
  1115. persistent=True)
  1116. vm.devices['pci']._set.add(
  1117. assignment)
  1118. libvirt_xml = vm.create_config_file()
  1119. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1120. lxml.etree.XML(expected))
  1121. def test_600_libvirt_xml_hvm_cdrom_boot(self):
  1122. expected = '''<domain type="xen">
  1123. <name>test-inst-test</name>
  1124. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  1125. <memory unit="MiB">400</memory>
  1126. <currentMemory unit="MiB">400</currentMemory>
  1127. <vcpu placement="static">2</vcpu>
  1128. <cpu mode='host-passthrough'>
  1129. <!-- disable nested HVM -->
  1130. <feature name='vmx' policy='disable'/>
  1131. <feature name='svm' policy='disable'/>
  1132. </cpu>
  1133. <os>
  1134. <type arch="x86_64" machine="xenfv">hvm</type>
  1135. <!--
  1136. For the libxl backend libvirt switches between OVMF (UEFI)
  1137. and SeaBIOS based on the loader type. This has nothing to
  1138. do with the hvmloader binary.
  1139. -->
  1140. <loader type="rom">hvmloader</loader>
  1141. <boot dev="cdrom" />
  1142. <boot dev="hd" />
  1143. </os>
  1144. <features>
  1145. <pae/>
  1146. <acpi/>
  1147. <apic/>
  1148. <viridian/>
  1149. </features>
  1150. <clock offset="variable" adjustment="0" basis="localtime" />
  1151. <on_poweroff>destroy</on_poweroff>
  1152. <on_reboot>destroy</on_reboot>
  1153. <on_crash>destroy</on_crash>
  1154. <devices>
  1155. <disk type="block" device="cdrom">
  1156. <driver name="phy" />
  1157. <source dev="/dev/sda" />
  1158. <!-- prefer xvdd for CDROM -->
  1159. <target dev="xvdd" />
  1160. <readonly/>
  1161. </disk>
  1162. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  1163. <emulator type="stubdom-linux" />
  1164. <input type="tablet" bus="usb"/>
  1165. <video>
  1166. <model type="vga"/>
  1167. </video>
  1168. <graphics type="qubes"/>
  1169. <console type="pty">
  1170. <target type="xen" port="0"/>
  1171. </console>
  1172. </devices>
  1173. </domain>
  1174. '''
  1175. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  1176. qdb = {
  1177. '/qubes-block-devices/sda': b'',
  1178. '/qubes-block-devices/sda/desc': b'Test device',
  1179. '/qubes-block-devices/sda/size': b'1024000',
  1180. '/qubes-block-devices/sda/mode': b'r',
  1181. }
  1182. test_qdb = TestQubesDB(qdb)
  1183. dom0 = qubes.vm.adminvm.AdminVM(self.app, None)
  1184. dom0._qdb_connection = test_qdb
  1185. self.get_vm('dom0', vm=dom0)
  1186. vm = self.get_vm(uuid=my_uuid)
  1187. vm.netvm = None
  1188. vm.virt_mode = 'hvm'
  1189. vm.kernel = None
  1190. dom0.events_enabled = True
  1191. self.app.vmm.offline_mode = False
  1192. dev = qubes.devices.DeviceAssignment(
  1193. dom0, 'sda',
  1194. {'devtype': 'cdrom', 'read-only': 'yes'}, persistent=True)
  1195. self.loop.run_until_complete(vm.devices['block'].attach(dev))
  1196. libvirt_xml = vm.create_config_file()
  1197. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1198. lxml.etree.XML(expected))
  1199. def test_600_libvirt_xml_hvm_cdrom_dom0_kernel_boot(self):
  1200. expected = '''<domain type="xen">
  1201. <name>test-inst-test</name>
  1202. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  1203. <memory unit="MiB">400</memory>
  1204. <currentMemory unit="MiB">400</currentMemory>
  1205. <vcpu placement="static">2</vcpu>
  1206. <cpu mode='host-passthrough'>
  1207. <!-- disable nested HVM -->
  1208. <feature name='vmx' policy='disable'/>
  1209. <feature name='svm' policy='disable'/>
  1210. </cpu>
  1211. <os>
  1212. <type arch="x86_64" machine="xenfv">hvm</type>
  1213. <!--
  1214. For the libxl backend libvirt switches between OVMF (UEFI)
  1215. and SeaBIOS based on the loader type. This has nothing to
  1216. do with the hvmloader binary.
  1217. -->
  1218. <loader type="rom">hvmloader</loader>
  1219. <boot dev="cdrom" />
  1220. <boot dev="hd" />
  1221. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0</cmdline>
  1222. </os>
  1223. <features>
  1224. <pae/>
  1225. <acpi/>
  1226. <apic/>
  1227. <viridian/>
  1228. </features>
  1229. <clock offset="variable" adjustment="0" basis="localtime" />
  1230. <on_poweroff>destroy</on_poweroff>
  1231. <on_reboot>destroy</on_reboot>
  1232. <on_crash>destroy</on_crash>
  1233. <devices>
  1234. <disk type="block" device="disk">
  1235. <driver name="phy" />
  1236. <source dev="/tmp/kernel/modules.img" />
  1237. <target dev="xvdd" />
  1238. <backenddomain name="dom0" />
  1239. </disk>
  1240. <disk type="block" device="cdrom">
  1241. <driver name="phy" />
  1242. <source dev="/dev/sda" />
  1243. <target dev="xvdi" />
  1244. <readonly/>
  1245. </disk>
  1246. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  1247. <emulator type="stubdom-linux" />
  1248. <input type="tablet" bus="usb"/>
  1249. <video>
  1250. <model type="vga"/>
  1251. </video>
  1252. <graphics type="qubes"/>
  1253. <console type="pty">
  1254. <target type="xen" port="0"/>
  1255. </console>
  1256. </devices>
  1257. </domain>
  1258. '''
  1259. qdb = {
  1260. '/qubes-block-devices/sda': b'',
  1261. '/qubes-block-devices/sda/desc': b'Test device',
  1262. '/qubes-block-devices/sda/size': b'1024000',
  1263. '/qubes-block-devices/sda/mode': b'r',
  1264. }
  1265. test_qdb = TestQubesDB(qdb)
  1266. dom0 = qubes.vm.adminvm.AdminVM(self.app, None)
  1267. dom0._qdb_connection = test_qdb
  1268. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  1269. vm = self.get_vm(uuid=my_uuid)
  1270. vm.netvm = None
  1271. vm.virt_mode = 'hvm'
  1272. with unittest.mock.patch('qubes.config.qubes_base_dir',
  1273. '/tmp/qubes-test'):
  1274. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  1275. os.makedirs(kernel_dir, exist_ok=True)
  1276. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  1277. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  1278. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  1279. vm.kernel = 'dummy'
  1280. # tests for storage are later
  1281. vm.volumes['kernel'] = unittest.mock.Mock(**{
  1282. 'kernels_dir': '/tmp/kernel',
  1283. 'block_device.return_value.domain': 'dom0',
  1284. 'block_device.return_value.script': None,
  1285. 'block_device.return_value.path': '/tmp/kernel/modules.img',
  1286. 'block_device.return_value.devtype': 'disk',
  1287. 'block_device.return_value.name': 'kernel',
  1288. })
  1289. dom0.events_enabled = True
  1290. self.app.vmm.offline_mode = False
  1291. dev = qubes.devices.DeviceAssignment(
  1292. dom0, 'sda',
  1293. {'devtype': 'cdrom', 'read-only': 'yes'}, persistent=True)
  1294. self.loop.run_until_complete(vm.devices['block'].attach(dev))
  1295. libvirt_xml = vm.create_config_file()
  1296. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1297. lxml.etree.XML(expected))
  1298. def test_610_libvirt_xml_network(self):
  1299. expected = '''<domain type="xen">
  1300. <name>test-inst-test</name>
  1301. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  1302. <memory unit="MiB">500</memory>
  1303. <currentMemory unit="MiB">400</currentMemory>
  1304. <vcpu placement="static">2</vcpu>
  1305. <cpu mode='host-passthrough'>
  1306. <!-- disable nested HVM -->
  1307. <feature name='vmx' policy='disable'/>
  1308. <feature name='svm' policy='disable'/>
  1309. </cpu>
  1310. <os>
  1311. <type arch="x86_64" machine="xenfv">hvm</type>
  1312. <!--
  1313. For the libxl backend libvirt switches between OVMF (UEFI)
  1314. and SeaBIOS based on the loader type. This has nothing to
  1315. do with the hvmloader binary.
  1316. -->
  1317. <loader type="rom">hvmloader</loader>
  1318. <boot dev="cdrom" />
  1319. <boot dev="hd" />
  1320. </os>
  1321. <features>
  1322. <pae/>
  1323. <acpi/>
  1324. <apic/>
  1325. <viridian/>
  1326. </features>
  1327. <clock offset="variable" adjustment="0" basis="localtime" />
  1328. <on_poweroff>destroy</on_poweroff>
  1329. <on_reboot>destroy</on_reboot>
  1330. <on_crash>destroy</on_crash>
  1331. <devices>
  1332. <interface type="ethernet">
  1333. <mac address="00:16:3e:5e:6c:00" />
  1334. <ip address="10.137.0.1" />
  1335. {extra_ip}
  1336. <backenddomain name="test-inst-netvm" />
  1337. <script path="vif-route-qubes" />
  1338. </interface>
  1339. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  1340. <emulator type="stubdom-linux" cmdline="-qubes-net:client_ip=10.137.0.1,dns_0=10.139.1.1,dns_1=10.139.1.2,gw=10.137.0.2,netmask=255.255.255.255" />
  1341. <input type="tablet" bus="usb"/>
  1342. <video>
  1343. <model type="vga"/>
  1344. </video>
  1345. <graphics type="qubes"/>
  1346. <console type="pty">
  1347. <target type="xen" port="0"/>
  1348. </console>
  1349. </devices>
  1350. </domain>
  1351. '''
  1352. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  1353. netvm = self.get_vm(qid=2, name='netvm', provides_network=True)
  1354. vm = self.get_vm(uuid=my_uuid)
  1355. vm.netvm = netvm
  1356. vm.virt_mode = 'hvm'
  1357. vm.features['qrexec'] = True
  1358. with self.subTest('ipv4_only'):
  1359. libvirt_xml = vm.create_config_file()
  1360. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1361. lxml.etree.XML(expected.format(extra_ip='')))
  1362. with self.subTest('ipv6'):
  1363. netvm.features['ipv6'] = True
  1364. libvirt_xml = vm.create_config_file()
  1365. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1366. lxml.etree.XML(expected.format(
  1367. extra_ip='<ip address="{}::a89:1" family=\'ipv6\'/>'.format(
  1368. qubes.config.qubes_ipv6_prefix.replace(':0000', '')))))
  1369. def test_615_libvirt_xml_block_devices(self):
  1370. expected = '''<domain type="xen">
  1371. <name>test-inst-test</name>
  1372. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  1373. <memory unit="MiB">400</memory>
  1374. <currentMemory unit="MiB">400</currentMemory>
  1375. <vcpu placement="static">2</vcpu>
  1376. <cpu mode='host-passthrough'>
  1377. <!-- disable nested HVM -->
  1378. <feature name='vmx' policy='disable'/>
  1379. <feature name='svm' policy='disable'/>
  1380. </cpu>
  1381. <os>
  1382. <type arch="x86_64" machine="xenfv">hvm</type>
  1383. <!--
  1384. For the libxl backend libvirt switches between OVMF (UEFI)
  1385. and SeaBIOS based on the loader type. This has nothing to
  1386. do with the hvmloader binary.
  1387. -->
  1388. <loader type="rom">hvmloader</loader>
  1389. <boot dev="cdrom" />
  1390. <boot dev="hd" />
  1391. </os>
  1392. <features>
  1393. <pae/>
  1394. <acpi/>
  1395. <apic/>
  1396. <viridian/>
  1397. </features>
  1398. <clock offset="variable" adjustment="0" basis="localtime" />
  1399. <on_poweroff>destroy</on_poweroff>
  1400. <on_reboot>destroy</on_reboot>
  1401. <on_crash>destroy</on_crash>
  1402. <devices>
  1403. <disk type="block" device="disk">
  1404. <driver name="phy" />
  1405. <source dev="/dev/loop0" />
  1406. <target dev="xvda" />
  1407. <backenddomain name="dom0" />
  1408. <script path="/tmp/script" />
  1409. </disk>
  1410. <disk type="block" device="disk">
  1411. <driver name="phy" />
  1412. <source dev="/dev/loop1" />
  1413. <target dev="xvde" />
  1414. <backenddomain name="dom0" />
  1415. </disk>
  1416. <disk type="block" device="disk">
  1417. <driver name="phy" />
  1418. <source dev="/dev/loop2" />
  1419. <target dev="xvdf" />
  1420. <backenddomain name="dom0" />
  1421. </disk>
  1422. <disk type="block" device="disk">
  1423. <driver name="phy" />
  1424. <source dev="/dev/sdb" />
  1425. <target dev="xvdl" />
  1426. </disk>
  1427. <disk type="block" device="cdrom">
  1428. <driver name="phy" />
  1429. <source dev="/dev/sda" />
  1430. <!-- prefer xvdd for CDROM -->
  1431. <target dev="xvdd" />
  1432. </disk>
  1433. <disk type="block" device="disk">
  1434. <driver name="phy" />
  1435. <source dev="/dev/loop0" />
  1436. <target dev="xvdi" />
  1437. <backenddomain name="backend0" />
  1438. </disk>
  1439. <disk type="block" device="disk">
  1440. <driver name="phy" />
  1441. <source dev="/dev/loop0" />
  1442. <target dev="xvdj" />
  1443. <backenddomain name="backend1" />
  1444. </disk>
  1445. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  1446. <emulator type="stubdom-linux" />
  1447. <input type="tablet" bus="usb"/>
  1448. <video>
  1449. <model type="vga"/>
  1450. </video>
  1451. <graphics type="qubes"/>
  1452. <console type="pty">
  1453. <target type="xen" port="0"/>
  1454. </console>
  1455. </devices>
  1456. </domain>
  1457. '''
  1458. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  1459. vm = self.get_vm(uuid=my_uuid)
  1460. vm.netvm = None
  1461. vm.virt_mode = 'hvm'
  1462. vm.volumes['root'] = unittest.mock.Mock(**{
  1463. 'block_device.return_value.name': 'root',
  1464. 'block_device.return_value.path': '/dev/loop0',
  1465. 'block_device.return_value.devtype': 'disk',
  1466. 'block_device.return_value.domain': 'dom0',
  1467. 'block_device.return_value.script': '/tmp/script',
  1468. })
  1469. vm.volumes['other'] = unittest.mock.Mock(**{
  1470. 'block_device.return_value.name': 'other',
  1471. 'block_device.return_value.path': '/dev/loop1',
  1472. 'block_device.return_value.devtype': 'disk',
  1473. 'block_device.return_value.domain': 'dom0',
  1474. 'block_device.return_value.script': None,
  1475. })
  1476. vm.volumes['other2'] = unittest.mock.Mock(**{
  1477. 'block_device.return_value.name': 'other',
  1478. 'block_device.return_value.path': '/dev/loop2',
  1479. 'block_device.return_value.devtype': 'disk',
  1480. 'block_device.return_value.domain': 'dom0',
  1481. 'block_device.return_value.script': None,
  1482. })
  1483. assignments = [
  1484. unittest.mock.Mock(**{
  1485. 'options': {'frontend-dev': 'xvdl'},
  1486. 'device.device_node': '/dev/sdb',
  1487. 'device.backend_domain.name': 'dom0',
  1488. }),
  1489. unittest.mock.Mock(**{
  1490. 'options': {'devtype': 'cdrom'},
  1491. 'device.device_node': '/dev/sda',
  1492. 'device.backend_domain.name': 'dom0',
  1493. }),
  1494. unittest.mock.Mock(**{
  1495. 'options': {'read-only': True},
  1496. 'device.device_node': '/dev/loop0',
  1497. 'device.backend_domain.name': 'backend0',
  1498. }),
  1499. unittest.mock.Mock(**{
  1500. 'options': {},
  1501. 'device.device_node': '/dev/loop0',
  1502. 'device.backend_domain.name': 'backend1',
  1503. }),
  1504. ]
  1505. vm.devices['block'].assignments = lambda persistent: assignments
  1506. libvirt_xml = vm.create_config_file()
  1507. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1508. lxml.etree.XML(expected))
  1509. @unittest.mock.patch('qubes.utils.get_timezone')
  1510. @unittest.mock.patch('qubes.utils.urandom')
  1511. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb')
  1512. def test_620_qdb_standalone(self, mock_qubesdb, mock_urandom,
  1513. mock_timezone):
  1514. mock_urandom.return_value = b'A' * 64
  1515. mock_timezone.return_value = 'UTC'
  1516. vm = self.get_vm(cls=qubes.vm.standalonevm.StandaloneVM)
  1517. vm.netvm = None
  1518. vm.events_enabled = True
  1519. test_qubesdb = TestQubesDB()
  1520. mock_qubesdb.write.side_effect = test_qubesdb.write
  1521. mock_qubesdb.rm.side_effect = test_qubesdb.rm
  1522. vm.create_qdb_entries()
  1523. self.maxDiff = None
  1524. iptables_header = (
  1525. '# Generated by Qubes Core on {}\n'
  1526. '*filter\n'
  1527. ':INPUT DROP [0:0]\n'
  1528. ':FORWARD DROP [0:0]\n'
  1529. ':OUTPUT ACCEPT [0:0]\n'
  1530. '-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n'
  1531. '-A INPUT -m conntrack --ctstate '
  1532. 'RELATED,ESTABLISHED -j ACCEPT\n'
  1533. '-A INPUT -p icmp -j ACCEPT\n'
  1534. '-A INPUT -i lo -j ACCEPT\n'
  1535. '-A INPUT -j REJECT --reject-with '
  1536. 'icmp-host-prohibited\n'
  1537. '-A FORWARD -m conntrack --ctstate '
  1538. 'RELATED,ESTABLISHED -j ACCEPT\n'
  1539. '-A FORWARD -i vif+ -o vif+ -j DROP\n'
  1540. 'COMMIT\n'.format(datetime.datetime.now().ctime()))
  1541. self.assertEqual(test_qubesdb.data, {
  1542. '/name': 'test-inst-test',
  1543. '/type': 'StandaloneVM',
  1544. '/default-user': 'user',
  1545. '/qubes-vm-type': 'AppVM',
  1546. '/qubes-debug-mode': '0',
  1547. '/qubes-base-template': '',
  1548. '/qubes-timezone': 'UTC',
  1549. '/qubes-random-seed': base64.b64encode(b'A' * 64),
  1550. '/qubes-vm-persistence': 'full',
  1551. '/qubes-vm-updateable': 'True',
  1552. '/qubes-block-devices': '',
  1553. '/qubes-usb-devices': '',
  1554. '/qubes-iptables': 'reload',
  1555. '/qubes-iptables-error': '',
  1556. '/qubes-iptables-header': iptables_header,
  1557. '/qubes-service/qubes-update-check': '0',
  1558. '/qubes-service/meminfo-writer': '1',
  1559. '/connected-ips': '',
  1560. '/connected-ips6': '',
  1561. })
  1562. @unittest.mock.patch('datetime.datetime')
  1563. @unittest.mock.patch('qubes.utils.get_timezone')
  1564. @unittest.mock.patch('qubes.utils.urandom')
  1565. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb')
  1566. def test_621_qdb_vm_with_network(self, mock_qubesdb, mock_urandom,
  1567. mock_timezone, mock_datetime):
  1568. mock_urandom.return_value = b'A' * 64
  1569. mock_timezone.return_value = 'UTC'
  1570. template = self.get_vm(cls=qubes.vm.templatevm.TemplateVM, name='template')
  1571. template.netvm = None
  1572. netvm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1573. name='netvm', qid=2, provides_network=True)
  1574. vm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1575. name='appvm', qid=3)
  1576. vm.netvm = netvm
  1577. vm.kernel = None
  1578. # pretend the VM is running...
  1579. vm._qubesprop_xid = 3
  1580. netvm.kernel = None
  1581. test_qubesdb = TestQubesDB()
  1582. mock_qubesdb.write.side_effect = test_qubesdb.write
  1583. mock_qubesdb.rm.side_effect = test_qubesdb.rm
  1584. self.maxDiff = None
  1585. mock_datetime.now.returnvalue = \
  1586. datetime.datetime(2019, 2, 27, 15, 12, 15, 385822)
  1587. iptables_header = (
  1588. '# Generated by Qubes Core on {}\n'
  1589. '*filter\n'
  1590. ':INPUT DROP [0:0]\n'
  1591. ':FORWARD DROP [0:0]\n'
  1592. ':OUTPUT ACCEPT [0:0]\n'
  1593. '-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n'
  1594. '-A INPUT -m conntrack --ctstate '
  1595. 'RELATED,ESTABLISHED -j ACCEPT\n'
  1596. '-A INPUT -p icmp -j ACCEPT\n'
  1597. '-A INPUT -i lo -j ACCEPT\n'
  1598. '-A INPUT -j REJECT --reject-with '
  1599. 'icmp-host-prohibited\n'
  1600. '-A FORWARD -m conntrack --ctstate '
  1601. 'RELATED,ESTABLISHED -j ACCEPT\n'
  1602. '-A FORWARD -i vif+ -o vif+ -j DROP\n'
  1603. 'COMMIT\n'.format(datetime.datetime.now().ctime()))
  1604. expected = {
  1605. '/name': 'test-inst-appvm',
  1606. '/type': 'AppVM',
  1607. '/default-user': 'user',
  1608. '/qubes-vm-type': 'AppVM',
  1609. '/qubes-debug-mode': '0',
  1610. '/qubes-base-template': 'test-inst-template',
  1611. '/qubes-timezone': 'UTC',
  1612. '/qubes-random-seed': base64.b64encode(b'A' * 64),
  1613. '/qubes-vm-persistence': 'rw-only',
  1614. '/qubes-vm-updateable': 'False',
  1615. '/qubes-block-devices': '',
  1616. '/qubes-usb-devices': '',
  1617. '/qubes-iptables': 'reload',
  1618. '/qubes-iptables-error': '',
  1619. '/qubes-iptables-header': iptables_header,
  1620. '/qubes-service/qubes-update-check': '0',
  1621. '/qubes-service/meminfo-writer': '1',
  1622. '/qubes-mac': '00:16:3e:5e:6c:00',
  1623. '/qubes-ip': '10.137.0.3',
  1624. '/qubes-netmask': '255.255.255.255',
  1625. '/qubes-gateway': '10.137.0.2',
  1626. '/qubes-primary-dns': '10.139.1.1',
  1627. '/qubes-secondary-dns': '10.139.1.2',
  1628. '/connected-ips': '',
  1629. '/connected-ips6': '',
  1630. }
  1631. with self.subTest('ipv4'):
  1632. vm.create_qdb_entries()
  1633. self.assertEqual(test_qubesdb.data, expected)
  1634. test_qubesdb.data.clear()
  1635. with self.subTest('ipv6'):
  1636. netvm.features['ipv6'] = True
  1637. expected['/qubes-ip6'] = \
  1638. qubes.config.qubes_ipv6_prefix.replace(':0000', '') + \
  1639. '::a89:3'
  1640. expected['/qubes-gateway6'] = expected['/qubes-ip6'][:-1] + '2'
  1641. vm.create_qdb_entries()
  1642. self.assertEqual(test_qubesdb.data, expected)
  1643. test_qubesdb.data.clear()
  1644. with self.subTest('ipv6_just_appvm'):
  1645. del netvm.features['ipv6']
  1646. vm.features['ipv6'] = True
  1647. expected['/qubes-ip6'] = \
  1648. qubes.config.qubes_ipv6_prefix.replace(':0000', '') + \
  1649. '::a89:3'
  1650. del expected['/qubes-gateway6']
  1651. vm.create_qdb_entries()
  1652. self.assertEqual(test_qubesdb.data, expected)
  1653. test_qubesdb.data.clear()
  1654. with self.subTest('proxy_ipv4'):
  1655. del vm.features['ipv6']
  1656. expected['/name'] = 'test-inst-netvm'
  1657. expected['/qubes-vm-type'] = 'NetVM'
  1658. del expected['/qubes-ip']
  1659. del expected['/qubes-gateway']
  1660. del expected['/qubes-netmask']
  1661. del expected['/qubes-ip6']
  1662. del expected['/qubes-primary-dns']
  1663. del expected['/qubes-secondary-dns']
  1664. del expected['/qubes-mac']
  1665. expected['/qubes-netvm-primary-dns'] = '10.139.1.1'
  1666. expected['/qubes-netvm-secondary-dns'] = '10.139.1.2'
  1667. expected['/qubes-netvm-network'] = '10.137.0.2'
  1668. expected['/qubes-netvm-gateway'] = '10.137.0.2'
  1669. expected['/qubes-netvm-netmask'] = '255.255.255.255'
  1670. expected['/qubes-iptables-domainrules/3'] = \
  1671. '*filter\n' \
  1672. '-A FORWARD -s 10.137.0.3 -j ACCEPT\n' \
  1673. '-A FORWARD -s 10.137.0.3 -j DROP\n' \
  1674. 'COMMIT\n'
  1675. expected['/mapped-ip/10.137.0.3/visible-ip'] = '10.137.0.3'
  1676. expected['/mapped-ip/10.137.0.3/visible-gateway'] = '10.137.0.2'
  1677. expected['/qubes-firewall/10.137.0.3'] = ''
  1678. expected['/qubes-firewall/10.137.0.3/0000'] = 'action=accept'
  1679. expected['/qubes-firewall/10.137.0.3/policy'] = 'drop'
  1680. expected['/connected-ips'] = '10.137.0.3'
  1681. with unittest.mock.patch('qubes.vm.qubesvm.QubesVM.is_running',
  1682. lambda _: True):
  1683. netvm.create_qdb_entries()
  1684. self.assertEqual(test_qubesdb.data, expected)
  1685. test_qubesdb.data.clear()
  1686. with self.subTest('proxy_ipv6'):
  1687. netvm.features['ipv6'] = True
  1688. ip6 = qubes.config.qubes_ipv6_prefix.replace(
  1689. ':0000', '') + '::a89:3'
  1690. expected['/qubes-netvm-gateway6'] = ip6[:-1] + '2'
  1691. expected['/qubes-firewall/' + ip6] = ''
  1692. expected['/qubes-firewall/' + ip6 + '/0000'] = 'action=accept'
  1693. expected['/qubes-firewall/' + ip6 + '/policy'] = 'drop'
  1694. expected['/connected-ips6'] = ip6
  1695. with unittest.mock.patch('qubes.vm.qubesvm.QubesVM.is_running',
  1696. lambda _: True):
  1697. netvm.create_qdb_entries()
  1698. self.assertEqual(test_qubesdb.data, expected)
  1699. @unittest.mock.patch('qubes.utils.get_timezone')
  1700. @unittest.mock.patch('qubes.utils.urandom')
  1701. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb')
  1702. def test_622_qdb_guivm_keyboard_layout(self, mock_qubesdb, mock_urandom,
  1703. mock_timezone):
  1704. mock_urandom.return_value = b'A' * 64
  1705. mock_timezone.return_value = 'UTC'
  1706. template = self.get_vm(
  1707. cls=qubes.vm.templatevm.TemplateVM, name='template')
  1708. template.netvm = None
  1709. guivm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1710. name='sys-gui', qid=2, provides_network=False)
  1711. vm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1712. name='appvm', qid=3)
  1713. vm.netvm = None
  1714. vm.guivm = guivm
  1715. vm.is_running = lambda: True
  1716. guivm.keyboard_layout = 'fr++'
  1717. guivm.is_running = lambda: True
  1718. vm.events_enabled = True
  1719. test_qubesdb = TestQubesDB()
  1720. mock_qubesdb.write.side_effect = test_qubesdb.write
  1721. mock_qubesdb.rm.side_effect = test_qubesdb.rm
  1722. vm.create_qdb_entries()
  1723. self.maxDiff = None
  1724. self.assertEqual(test_qubesdb.data, {
  1725. '/name': 'test-inst-appvm',
  1726. '/type': 'AppVM',
  1727. '/default-user': 'user',
  1728. '/keyboard-layout': 'fr++',
  1729. '/qubes-vm-type': 'AppVM',
  1730. '/qubes-gui-domain-xid': '{}'.format(guivm.xid),
  1731. '/qubes-debug-mode': '0',
  1732. '/qubes-base-template': 'test-inst-template',
  1733. '/qubes-timezone': 'UTC',
  1734. '/qubes-random-seed': base64.b64encode(b'A' * 64),
  1735. '/qubes-vm-persistence': 'rw-only',
  1736. '/qubes-vm-updateable': 'False',
  1737. '/qubes-block-devices': '',
  1738. '/qubes-usb-devices': '',
  1739. '/qubes-iptables': 'reload',
  1740. '/qubes-iptables-error': '',
  1741. '/qubes-iptables-header': unittest.mock.ANY,
  1742. '/qubes-service/qubes-update-check': '0',
  1743. '/qubes-service/meminfo-writer': '1',
  1744. '/connected-ips': '',
  1745. '/connected-ips6': '',
  1746. })
  1747. @unittest.mock.patch('qubes.utils.get_timezone')
  1748. @unittest.mock.patch('qubes.utils.urandom')
  1749. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb')
  1750. def test_623_qdb_audiovm(self, mock_qubesdb, mock_urandom,
  1751. mock_timezone):
  1752. mock_urandom.return_value = b'A' * 64
  1753. mock_timezone.return_value = 'UTC'
  1754. template = self.get_vm(
  1755. cls=qubes.vm.templatevm.TemplateVM, name='template')
  1756. template.netvm = None
  1757. audiovm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1758. name='sys-audio', qid=2, provides_network=False)
  1759. vm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1760. name='appvm', qid=3)
  1761. vm.netvm = None
  1762. vm.audiovm = audiovm
  1763. vm.is_running = lambda: True
  1764. audiovm.is_running = lambda: True
  1765. vm.events_enabled = True
  1766. test_qubesdb = TestQubesDB()
  1767. mock_qubesdb.write.side_effect = test_qubesdb.write
  1768. mock_qubesdb.rm.side_effect = test_qubesdb.rm
  1769. vm.create_qdb_entries()
  1770. self.maxDiff = None
  1771. self.assertEqual(test_qubesdb.data, {
  1772. '/name': 'test-inst-appvm',
  1773. '/type': 'AppVM',
  1774. '/default-user': 'user',
  1775. '/qubes-vm-type': 'AppVM',
  1776. '/qubes-audio-domain-xid': '{}'.format(audiovm.xid),
  1777. '/qubes-debug-mode': '0',
  1778. '/qubes-base-template': 'test-inst-template',
  1779. '/qubes-timezone': 'UTC',
  1780. '/qubes-random-seed': base64.b64encode(b'A' * 64),
  1781. '/qubes-vm-persistence': 'rw-only',
  1782. '/qubes-vm-updateable': 'False',
  1783. '/qubes-block-devices': '',
  1784. '/qubes-usb-devices': '',
  1785. '/qubes-iptables': 'reload',
  1786. '/qubes-iptables-error': '',
  1787. '/qubes-iptables-header': unittest.mock.ANY,
  1788. '/qubes-service/qubes-update-check': '0',
  1789. '/qubes-service/meminfo-writer': '1',
  1790. '/connected-ips': '',
  1791. '/connected-ips6': '',
  1792. })
  1793. @unittest.mock.patch('qubes.utils.get_timezone')
  1794. @unittest.mock.patch('qubes.utils.urandom')
  1795. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb')
  1796. def test_624_qdb_guivm_invalid_keyboard_layout(self, mock_qubesdb,
  1797. mock_urandom, mock_timezone):
  1798. mock_urandom.return_value = b'A' * 64
  1799. mock_timezone.return_value = 'UTC'
  1800. template = self.get_vm(
  1801. cls=qubes.vm.templatevm.TemplateVM, name='template')
  1802. guivm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1803. name='sys-gui', qid=2, provides_network=False)
  1804. guivm.is_running = lambda: True
  1805. guivm.events_enabled = True
  1806. with self.assertRaises(qubes.exc.QubesPropertyValueError):
  1807. guivm.keyboard_layout = 'fr123++'
  1808. with self.assertRaises(qubes.exc.QubesPropertyValueError):
  1809. guivm.keyboard_layout = 'fr+???+'
  1810. with self.assertRaises(qubes.exc.QubesPropertyValueError):
  1811. guivm.keyboard_layout = 'fr++variant?'
  1812. with self.assertRaises(qubes.exc.QubesPropertyValueError):
  1813. guivm.keyboard_layout = 'fr'
  1814. @unittest.mock.patch('qubes.utils.get_timezone')
  1815. @unittest.mock.patch('qubes.utils.urandom')
  1816. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb')
  1817. def test_625_qdb_keyboard_layout_change(self, mock_qubesdb, mock_urandom,
  1818. mock_timezone):
  1819. mock_urandom.return_value = b'A' * 64
  1820. mock_timezone.return_value = 'UTC'
  1821. template = self.get_vm(
  1822. cls=qubes.vm.templatevm.TemplateVM, name='template')
  1823. template.netvm = None
  1824. guivm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1825. name='sys-gui', qid=2, provides_network=False)
  1826. vm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1827. name='appvm', qid=3)
  1828. vm.netvm = None
  1829. vm.guivm = guivm
  1830. vm.is_running = lambda: True
  1831. guivm.keyboard_layout = 'fr++'
  1832. guivm.is_running = lambda: True
  1833. vm.events_enabled = True
  1834. test_qubesdb = TestQubesDB()
  1835. mock_qubesdb.write.side_effect = test_qubesdb.write
  1836. mock_qubesdb.rm.side_effect = test_qubesdb.rm
  1837. vm.create_qdb_entries()
  1838. self.maxDiff = None
  1839. expected = {
  1840. '/name': 'test-inst-appvm',
  1841. '/type': 'AppVM',
  1842. '/default-user': 'user',
  1843. '/keyboard-layout': 'fr++',
  1844. '/qubes-vm-type': 'AppVM',
  1845. '/qubes-gui-domain-xid': '{}'.format(guivm.xid),
  1846. '/qubes-debug-mode': '0',
  1847. '/qubes-base-template': 'test-inst-template',
  1848. '/qubes-timezone': 'UTC',
  1849. '/qubes-random-seed': base64.b64encode(b'A' * 64),
  1850. '/qubes-vm-persistence': 'rw-only',
  1851. '/qubes-vm-updateable': 'False',
  1852. '/qubes-block-devices': '',
  1853. '/qubes-usb-devices': '',
  1854. '/qubes-iptables': 'reload',
  1855. '/qubes-iptables-error': '',
  1856. '/qubes-iptables-header': unittest.mock.ANY,
  1857. '/qubes-service/qubes-update-check': '0',
  1858. '/qubes-service/meminfo-writer': '1',
  1859. '/connected-ips': '',
  1860. '/connected-ips6': '',
  1861. }
  1862. with self.subTest('default'):
  1863. self.assertEqual(test_qubesdb.data, expected)
  1864. with self.subTest('value_change'):
  1865. vm.keyboard_layout = 'de++'
  1866. expected['/keyboard-layout'] = 'de++'
  1867. self.assertEqual(test_qubesdb.data, expected)
  1868. with self.subTest('value_revert'):
  1869. vm.keyboard_layout = qubes.property.DEFAULT
  1870. expected['/keyboard-layout'] = 'fr++'
  1871. self.assertEqual(test_qubesdb.data, expected)
  1872. with self.subTest('no_default'):
  1873. guivm.keyboard_layout = qubes.property.DEFAULT
  1874. vm.keyboard_layout = qubes.property.DEFAULT
  1875. expected['/keyboard-layout'] = 'us++'
  1876. self.assertEqual(test_qubesdb.data, expected)
  1877. @asyncio.coroutine
  1878. def coroutine_mock(self, mock, *args, **kwargs):
  1879. return mock(*args, **kwargs)
  1880. @unittest.mock.patch('asyncio.create_subprocess_exec')
  1881. def test_700_run_service(self, mock_subprocess):
  1882. start_mock = unittest.mock.AsyncMock()
  1883. vm = self.get_vm(cls=qubes.vm.standalonevm.StandaloneVM,
  1884. name='vm')
  1885. vm.is_running = lambda: True
  1886. vm.is_qrexec_running = lambda: True
  1887. vm.start = start_mock
  1888. with self.subTest('running'):
  1889. self.loop.run_until_complete(vm.run_service('test.service'))
  1890. mock_subprocess.assert_called_once_with(
  1891. '/usr/bin/qrexec-client', '-d', 'test-inst-vm',
  1892. 'user:QUBESRPC test.service dom0')
  1893. self.assertFalse(start_mock.called)
  1894. mock_subprocess.reset_mock()
  1895. start_mock.reset_mock()
  1896. with self.subTest('not_running'):
  1897. vm.is_running = lambda: False
  1898. with self.assertRaises(qubes.exc.QubesVMNotRunningError):
  1899. self.loop.run_until_complete(vm.run_service('test.service'))
  1900. self.assertFalse(mock_subprocess.called)
  1901. mock_subprocess.reset_mock()
  1902. start_mock.reset_mock()
  1903. with self.subTest('autostart'):
  1904. vm.is_running = lambda: False
  1905. self.loop.run_until_complete(vm.run_service(
  1906. 'test.service', autostart=True))
  1907. mock_subprocess.assert_called_once_with(
  1908. '/usr/bin/qrexec-client', '-d', 'test-inst-vm',
  1909. 'user:QUBESRPC test.service dom0')
  1910. self.assertTrue(start_mock.called)
  1911. mock_subprocess.reset_mock()
  1912. start_mock.reset_mock()
  1913. with self.subTest('no_qrexec'):
  1914. vm.is_running = lambda: True
  1915. vm.is_qrexec_running = lambda: False
  1916. with self.assertRaises(qubes.exc.QubesVMError):
  1917. self.loop.run_until_complete(vm.run_service('test.service'))
  1918. self.assertFalse(start_mock.called)
  1919. self.assertFalse(mock_subprocess.called)
  1920. mock_subprocess.reset_mock()
  1921. start_mock.reset_mock()
  1922. with self.subTest('other_user'):
  1923. vm.is_running = lambda: True
  1924. vm.is_qrexec_running = lambda: True
  1925. self.loop.run_until_complete(vm.run_service('test.service',
  1926. user='other'))
  1927. mock_subprocess.assert_called_once_with(
  1928. '/usr/bin/qrexec-client', '-d', 'test-inst-vm',
  1929. 'other:QUBESRPC test.service dom0')
  1930. self.assertFalse(start_mock.called)
  1931. mock_subprocess.reset_mock()
  1932. start_mock.reset_mock()
  1933. with self.subTest('other_source'):
  1934. vm.is_running = lambda: True
  1935. vm.is_qrexec_running = lambda: True
  1936. self.loop.run_until_complete(vm.run_service('test.service',
  1937. source='test-inst-vm'))
  1938. mock_subprocess.assert_called_once_with(
  1939. '/usr/bin/qrexec-client', '-d', 'test-inst-vm',
  1940. 'user:QUBESRPC test.service test-inst-vm')
  1941. self.assertFalse(start_mock.called)
  1942. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.run')
  1943. def test_710_run_for_stdio(self, mock_run):
  1944. vm = self.get_vm(cls=qubes.vm.standalonevm.StandaloneVM,
  1945. name='vm')
  1946. communicate_mock = mock_run.return_value.communicate
  1947. communicate_mock.return_value = (b'stdout', b'stderr')
  1948. mock_run.return_value.returncode = 0
  1949. with self.subTest('default'):
  1950. value = self.loop.run_until_complete(
  1951. vm.run_for_stdio('cat'))
  1952. mock_run.assert_called_once_with(
  1953. 'cat',
  1954. stdout=subprocess.PIPE,
  1955. stderr=subprocess.PIPE,
  1956. stdin=subprocess.PIPE)
  1957. communicate_mock.assert_called_once_with(input=b'')
  1958. self.assertEqual(value, (b'stdout', b'stderr'))
  1959. mock_run.reset_mock()
  1960. communicate_mock.reset_mock()
  1961. with self.subTest('with_input'):
  1962. value = self.loop.run_until_complete(
  1963. vm.run_for_stdio('cat', input=b'abc'))
  1964. mock_run.assert_called_once_with(
  1965. 'cat',
  1966. stdout=subprocess.PIPE,
  1967. stderr=subprocess.PIPE,
  1968. stdin=subprocess.PIPE)
  1969. communicate_mock.assert_called_once_with(input=b'abc')
  1970. self.assertEqual(value, (b'stdout', b'stderr'))
  1971. mock_run.reset_mock()
  1972. communicate_mock.reset_mock()
  1973. with self.subTest('error'):
  1974. mock_run.return_value.returncode = 1
  1975. with self.assertRaises(subprocess.CalledProcessError) as exc:
  1976. self.loop.run_until_complete(
  1977. vm.run_for_stdio('cat'))
  1978. mock_run.assert_called_once_with(
  1979. 'cat',
  1980. stdout=subprocess.PIPE,
  1981. stderr=subprocess.PIPE,
  1982. stdin=subprocess.PIPE)
  1983. communicate_mock.assert_called_once_with(input=b'')
  1984. self.assertEqual(exc.exception.returncode, 1)
  1985. self.assertEqual(exc.exception.output, b'stdout')
  1986. self.assertEqual(exc.exception.stderr, b'stderr')
  1987. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.run_service')
  1988. def test_711_run_service_for_stdio(self, mock_run_service):
  1989. vm = self.get_vm(cls=qubes.vm.standalonevm.StandaloneVM,
  1990. name='vm')
  1991. communicate_mock = mock_run_service.return_value.communicate
  1992. communicate_mock.return_value = (b'stdout', b'stderr')
  1993. mock_run_service.return_value.returncode = 0
  1994. with self.subTest('default'):
  1995. value = self.loop.run_until_complete(
  1996. vm.run_service_for_stdio('test.service'))
  1997. mock_run_service.assert_called_once_with(
  1998. 'test.service',
  1999. stdout=subprocess.PIPE,
  2000. stderr=subprocess.PIPE,
  2001. stdin=subprocess.PIPE)
  2002. communicate_mock.assert_called_once_with(input=b'')
  2003. self.assertEqual(value, (b'stdout', b'stderr'))
  2004. mock_run_service.reset_mock()
  2005. communicate_mock.reset_mock()
  2006. with self.subTest('with_input'):
  2007. value = self.loop.run_until_complete(
  2008. vm.run_service_for_stdio('test.service', input=b'abc'))
  2009. mock_run_service.assert_called_once_with(
  2010. 'test.service',
  2011. stdout=subprocess.PIPE,
  2012. stderr=subprocess.PIPE,
  2013. stdin=subprocess.PIPE)
  2014. communicate_mock.assert_called_once_with(input=b'abc')
  2015. self.assertEqual(value, (b'stdout', b'stderr'))
  2016. mock_run_service.reset_mock()
  2017. communicate_mock.reset_mock()
  2018. with self.subTest('error'):
  2019. mock_run_service.return_value.returncode = 1
  2020. with self.assertRaises(subprocess.CalledProcessError) as exc:
  2021. self.loop.run_until_complete(
  2022. vm.run_service_for_stdio('test.service'))
  2023. mock_run_service.assert_called_once_with(
  2024. 'test.service',
  2025. stdout=subprocess.PIPE,
  2026. stderr=subprocess.PIPE,
  2027. stdin=subprocess.PIPE)
  2028. communicate_mock.assert_called_once_with(input=b'')
  2029. self.assertEqual(exc.exception.returncode, 1)
  2030. self.assertEqual(exc.exception.output, b'stdout')
  2031. self.assertEqual(exc.exception.stderr, b'stderr')
  2032. @unittest.mock.patch('os.path.exists')
  2033. def test_720_is_fully_usable(self, mock_os_path_exists):
  2034. vm_name = 'workvm'
  2035. qrexec_file_name = '/var/run/qubes/qrexec.{}'.format(
  2036. 'test-inst-{}'.format(vm_name))
  2037. vm = self.get_vm(cls=qubes.vm.appvm.AppVM, name=vm_name)
  2038. # Dummy xid; greater than 0 to indicate a running AppVM
  2039. vm._qubesprop_xid = 10
  2040. self.assertGreater(vm.xid, 0)
  2041. with self.subTest('with_qrexec_started'):
  2042. mock_os_path_exists.return_value = True
  2043. vm.features['qrexec'] = True
  2044. fully_usable = vm.is_fully_usable()
  2045. mock_os_path_exists.assert_called_once_with(qrexec_file_name)
  2046. self.assertEqual(fully_usable, True)
  2047. mock_os_path_exists.reset_mock()
  2048. with self.subTest('with_qrexec_error'):
  2049. mock_os_path_exists.return_value = False
  2050. vm.features['qrexec'] = True
  2051. fully_usable = vm.is_fully_usable()
  2052. mock_os_path_exists.assert_called_once_with(qrexec_file_name)
  2053. self.assertEqual(fully_usable, False)
  2054. mock_os_path_exists.reset_mock()
  2055. with self.subTest('without_qrexec'):
  2056. vm.features['qrexec'] = False
  2057. fully_usable = vm.is_fully_usable()
  2058. mock_os_path_exists.assert_not_called()
  2059. self.assertEqual(fully_usable, True)