qubesvm.py 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349
  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 unittest
  24. import uuid
  25. import datetime
  26. import lxml.etree
  27. import unittest.mock
  28. import shutil
  29. import qubes
  30. import qubes.exc
  31. import qubes.config
  32. import qubes.vm
  33. import qubes.vm.qubesvm
  34. import qubes.tests
  35. import qubes.tests.vm
  36. class TestApp(object):
  37. labels = {1: qubes.Label(1, '0xcc0000', 'red')}
  38. def __init__(self):
  39. self.domains = {}
  40. self.host = unittest.mock.Mock()
  41. self.host.memory_total = 4096 * 1024
  42. class TestFeatures(dict):
  43. def __init__(self, vm, **kwargs) -> None:
  44. self.vm = vm
  45. super().__init__(**kwargs)
  46. def check_with_template(self, feature, default):
  47. vm = self.vm
  48. while vm is not None:
  49. try:
  50. return vm.features[feature]
  51. except KeyError:
  52. vm = getattr(vm, 'template', None)
  53. return default
  54. class TestProp(object):
  55. # pylint: disable=too-few-public-methods
  56. __name__ = 'testprop'
  57. class TestDeviceCollection(object):
  58. def __init__(self):
  59. self._list = []
  60. def persistent(self):
  61. return self._list
  62. class TestQubesDB(object):
  63. def __init__(self):
  64. self.data = {}
  65. def write(self, path, value):
  66. self.data[path] = value
  67. def rm(self, path):
  68. if path.endswith('/'):
  69. for key in [x for x in self.data if x.startswith(path)]:
  70. del self.data[key]
  71. else:
  72. self.data.pop(path, None)
  73. class TestVM(object):
  74. # pylint: disable=too-few-public-methods
  75. app = TestApp()
  76. def __init__(self, **kwargs):
  77. self.running = False
  78. self.installed_by_rpm = False
  79. for k, v in kwargs.items():
  80. setattr(self, k, v)
  81. self.devices = {'pci': TestDeviceCollection()}
  82. self.features = TestFeatures(self)
  83. def is_running(self):
  84. return self.running
  85. class TC_00_setters(qubes.tests.QubesTestCase):
  86. def setUp(self):
  87. super().setUp()
  88. self.vm = TestVM()
  89. self.prop = TestProp()
  90. def test_000_setter_qid(self):
  91. self.assertEqual(
  92. qubes.vm._setter_qid(self.vm, self.prop, 5), 5)
  93. def test_001_setter_qid_lt_0(self):
  94. with self.assertRaises(ValueError):
  95. qubes.vm._setter_qid(self.vm, self.prop, -1)
  96. def test_002_setter_qid_gt_max(self):
  97. with self.assertRaises(ValueError):
  98. qubes.vm._setter_qid(self.vm,
  99. self.prop, qubes.config.max_qid + 5)
  100. @unittest.skip('test not implemented')
  101. def test_020_setter_kernel(self):
  102. pass
  103. def test_030_setter_label_object(self):
  104. label = TestApp.labels[1]
  105. self.assertIs(label,
  106. qubes.vm.setter_label(self.vm, self.prop, label))
  107. def test_031_setter_label_getitem(self):
  108. label = TestApp.labels[1]
  109. self.assertIs(label,
  110. qubes.vm.setter_label(self.vm, self.prop, 'label-1'))
  111. # there is no check for self.app.get_label()
  112. def test_040_setter_virt_mode(self):
  113. self.assertEqual(
  114. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'hvm'),
  115. 'hvm')
  116. self.assertEqual(
  117. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'HVM'),
  118. 'hvm')
  119. self.assertEqual(
  120. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'PV'),
  121. 'pv')
  122. self.assertEqual(
  123. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'pvh'),
  124. 'pvh')
  125. self.vm.devices['pci']._list.append(object())
  126. with self.assertRaises(ValueError):
  127. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'pvh')
  128. with self.assertRaises(ValueError):
  129. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'True')
  130. class TC_10_default(qubes.tests.QubesTestCase):
  131. def setUp(self):
  132. super().setUp()
  133. self.app = TestApp()
  134. self.vm = TestVM(app=self.app)
  135. self.prop = TestProp()
  136. def test_000_default_with_template_simple(self):
  137. default_getter = qubes.vm.qubesvm._default_with_template('kernel',
  138. 'dfl-kernel')
  139. self.assertEqual(default_getter(self.vm), 'dfl-kernel')
  140. self.vm.template = None
  141. self.assertEqual(default_getter(self.vm), 'dfl-kernel')
  142. self.vm.template = unittest.mock.Mock()
  143. self.vm.template.kernel = 'template-kernel'
  144. self.assertEqual(default_getter(self.vm), 'template-kernel')
  145. def test_001_default_with_template_callable(self):
  146. default_getter = qubes.vm.qubesvm._default_with_template('kernel',
  147. lambda x: x.app.default_kernel)
  148. self.app.default_kernel = 'global-dfl-kernel'
  149. self.assertEqual(default_getter(self.vm), 'global-dfl-kernel')
  150. self.vm.template = None
  151. self.assertEqual(default_getter(self.vm), 'global-dfl-kernel')
  152. self.vm.template = unittest.mock.Mock()
  153. self.vm.template.kernel = 'template-kernel'
  154. self.assertEqual(default_getter(self.vm), 'template-kernel')
  155. def test_010_default_virt_mode(self):
  156. self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
  157. 'pvh')
  158. self.vm.template = unittest.mock.Mock()
  159. self.vm.template.virt_mode = 'hvm'
  160. self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
  161. 'hvm')
  162. self.vm.template = None
  163. self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
  164. 'pvh')
  165. self.vm.devices['pci'].persistent().append('some-dev')
  166. self.assertEqual(qubes.vm.qubesvm._default_virt_mode(self.vm),
  167. 'hvm')
  168. def test_020_default_maxmem(self):
  169. default_maxmem = 2048
  170. self.vm.is_memory_balancing_possible = \
  171. lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
  172. self.vm)
  173. self.vm.virt_mode = 'pvh'
  174. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
  175. default_maxmem)
  176. self.vm.virt_mode = 'hvm'
  177. # HVM without qubes tools
  178. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
  179. # just 'qrexec' feature
  180. self.vm.features['qrexec'] = True
  181. print(self.vm.features.check_with_template('qrexec', False))
  182. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
  183. default_maxmem)
  184. # some supported-service.*, but not meminfo-writer
  185. self.vm.features['supported-service.qubes-firewall'] = True
  186. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
  187. # then add meminfo-writer
  188. self.vm.features['supported-service.meminfo-writer'] = True
  189. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm),
  190. default_maxmem)
  191. def test_021_default_maxmem_with_pcidevs(self):
  192. self.vm.is_memory_balancing_possible = \
  193. lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
  194. self.vm)
  195. self.vm.devices['pci'].persistent().append('00_00.0')
  196. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 0)
  197. def test_022_default_maxmem_linux(self):
  198. self.vm.is_memory_balancing_possible = \
  199. lambda: qubes.vm.qubesvm.QubesVM.is_memory_balancing_possible(
  200. self.vm)
  201. self.vm.virt_mode = 'pvh'
  202. self.vm.memory = 400
  203. self.vm.features['os'] = 'Linux'
  204. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 2048)
  205. self.vm.memory = 100
  206. self.assertEqual(qubes.vm.qubesvm._default_maxmem(self.vm), 1000)
  207. class QubesVMTestsMixin(object):
  208. property_no_default = object()
  209. def setUp(self):
  210. super(QubesVMTestsMixin, self).setUp()
  211. self.app = qubes.tests.vm.TestApp()
  212. self.app.vmm.offline_mode = True
  213. self.app.default_kernel = None
  214. # when full test run is called, extensions are loaded by earlier
  215. # tests, but if just this test class is run, load them manually here,
  216. # to have the same behaviour
  217. qubes.ext.get_extensions()
  218. def tearDown(self):
  219. try:
  220. self.app.domains.close()
  221. except AttributeError:
  222. pass
  223. super(QubesVMTestsMixin, self).tearDown()
  224. def get_vm(self, name='test', cls=qubes.vm.qubesvm.QubesVM, **kwargs):
  225. vm = cls(self.app, None,
  226. qid=kwargs.pop('qid', 1), name=qubes.tests.VMPREFIX + name,
  227. **kwargs)
  228. self.app.domains[vm.qid] = vm
  229. self.app.domains[vm.uuid] = vm
  230. self.app.domains[vm.name] = vm
  231. self.app.domains[vm] = vm
  232. self.addCleanup(vm.close)
  233. return vm
  234. def assertPropertyValue(self, vm, prop_name, set_value, expected_value,
  235. expected_xml_content=None):
  236. # FIXME: any better exception list? or maybe all of that should be a
  237. # single exception?
  238. with self.assertNotRaises((ValueError, TypeError, KeyError)):
  239. setattr(vm, prop_name, set_value)
  240. self.assertEqual(getattr(vm, prop_name), expected_value)
  241. if expected_xml_content is not None:
  242. xml = vm.__xml__()
  243. prop_xml = xml.xpath(
  244. './properties/property[@name=\'{}\']'.format(prop_name))
  245. self.assertEqual(len(prop_xml), 1, "Property not found in XML")
  246. self.assertEqual(prop_xml[0].text, expected_xml_content)
  247. def assertPropertyInvalidValue(self, vm, prop_name, set_value):
  248. orig_value_set = True
  249. orig_value = None
  250. try:
  251. orig_value = getattr(vm, prop_name)
  252. except AttributeError:
  253. orig_value_set = False
  254. # FIXME: any better exception list? or maybe all of that should be a
  255. # single exception?
  256. with self.assertRaises((ValueError, TypeError, KeyError)):
  257. setattr(vm, prop_name, set_value)
  258. if orig_value_set:
  259. self.assertEqual(getattr(vm, prop_name), orig_value)
  260. else:
  261. with self.assertRaises(AttributeError):
  262. getattr(vm, prop_name)
  263. def assertPropertyDefaultValue(self, vm, prop_name,
  264. expected_default=property_no_default):
  265. if expected_default is self.property_no_default:
  266. with self.assertRaises(AttributeError):
  267. getattr(vm, prop_name)
  268. else:
  269. with self.assertNotRaises(AttributeError):
  270. self.assertEqual(getattr(vm, prop_name), expected_default)
  271. xml = vm.__xml__()
  272. prop_xml = xml.xpath(
  273. './properties/property[@name=\'{}\']'.format(prop_name))
  274. self.assertEqual(len(prop_xml), 0, "Property still found in XML")
  275. def _test_generic_bool_property(self, vm, prop_name, default=False):
  276. self.assertPropertyDefaultValue(vm, prop_name, default)
  277. self.assertPropertyValue(vm, prop_name, False, False, 'False')
  278. self.assertPropertyValue(vm, prop_name, True, True, 'True')
  279. delattr(vm, prop_name)
  280. self.assertPropertyDefaultValue(vm, prop_name, default)
  281. self.assertPropertyValue(vm, prop_name, 'True', True, 'True')
  282. self.assertPropertyValue(vm, prop_name, 'False', False, 'False')
  283. self.assertPropertyInvalidValue(vm, prop_name, 'xxx')
  284. self.assertPropertyValue(vm, prop_name, 123, True)
  285. self.assertPropertyInvalidValue(vm, prop_name, '')
  286. class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
  287. def test_000_init(self):
  288. self.get_vm()
  289. def test_001_init_no_qid_or_name(self):
  290. with self.assertRaises(AssertionError):
  291. qubes.vm.qubesvm.QubesVM(self.app, None,
  292. name=qubes.tests.VMPREFIX + 'test')
  293. with self.assertRaises(AssertionError):
  294. qubes.vm.qubesvm.QubesVM(self.app, None,
  295. qid=1)
  296. def test_003_init_fire_domain_init(self):
  297. class TestVM2(qubes.vm.qubesvm.QubesVM):
  298. event_fired = False
  299. @qubes.events.handler('domain-init')
  300. def on_domain_init(self, event): # pylint: disable=unused-argument
  301. self.__class__.event_fired = True
  302. TestVM2(self.app, None, qid=1, name=qubes.tests.VMPREFIX + 'test')
  303. self.assertTrue(TestVM2.event_fired)
  304. def test_004_uuid_autogen(self):
  305. vm = self.get_vm()
  306. self.assertTrue(hasattr(vm, 'uuid'))
  307. def test_100_qid(self):
  308. vm = self.get_vm()
  309. self.assertIsInstance(vm.qid, int)
  310. with self.assertRaises(AttributeError):
  311. vm.qid = 2
  312. def test_110_name(self):
  313. vm = self.get_vm()
  314. self.assertIsInstance(vm.name, str)
  315. def test_120_uuid(self):
  316. my_uuid = uuid.uuid4()
  317. vm = self.get_vm(uuid=my_uuid)
  318. self.assertIsInstance(vm.uuid, uuid.UUID)
  319. self.assertIs(vm.uuid, my_uuid)
  320. with self.assertRaises(AttributeError):
  321. vm.uuid = uuid.uuid4()
  322. @unittest.skip('TODO: how to not fail on making an icon symlink here?')
  323. def test_130_label(self):
  324. vm = self.get_vm()
  325. self.assertPropertyDefaultValue(vm, 'label')
  326. self.assertPropertyValue(vm, 'label', self.app.labels[1],
  327. self.app.labels[1], 'label-1')
  328. del vm.label
  329. self.assertPropertyDefaultValue(vm, 'label')
  330. self.assertPropertyValue(vm, 'label', 'red',
  331. self.app.labels[1], 'label-1')
  332. self.assertPropertyValue(vm, 'label', 'label-1',
  333. self.app.labels[1], 'label-1')
  334. def test_131_label_invalid(self):
  335. vm = self.get_vm()
  336. self.assertPropertyInvalidValue(vm, 'label', 'invalid')
  337. self.assertPropertyInvalidValue(vm, 'label', 123)
  338. def test_160_memory(self):
  339. vm = self.get_vm()
  340. self.assertPropertyDefaultValue(vm, 'memory', 400)
  341. self.assertPropertyValue(vm, 'memory', 500, 500, '500')
  342. del vm.memory
  343. self.assertPropertyDefaultValue(vm, 'memory', 400)
  344. self.assertPropertyValue(vm, 'memory', '500', 500, '500')
  345. def test_161_memory_invalid(self):
  346. vm = self.get_vm()
  347. self.assertPropertyInvalidValue(vm, 'memory', -100)
  348. self.assertPropertyInvalidValue(vm, 'memory', '-100')
  349. self.assertPropertyInvalidValue(vm, 'memory', '')
  350. # TODO: higher than maxmem
  351. # TODO: human readable setter (500M, 4G)?
  352. def test_170_maxmem(self):
  353. vm = self.get_vm()
  354. self.assertPropertyDefaultValue(vm, 'maxmem',
  355. self.app.host.memory_total / 1024 / 2)
  356. self.assertPropertyValue(vm, 'maxmem', 500, 500, '500')
  357. del vm.maxmem
  358. self.assertPropertyDefaultValue(vm, 'maxmem',
  359. self.app.host.memory_total / 1024 / 2)
  360. self.assertPropertyValue(vm, 'maxmem', '500', 500, '500')
  361. def test_171_maxmem_invalid(self):
  362. vm = self.get_vm()
  363. self.assertPropertyInvalidValue(vm, 'maxmem', -100)
  364. self.assertPropertyInvalidValue(vm, 'maxmem', '-100')
  365. self.assertPropertyInvalidValue(vm, 'maxmem', '')
  366. # TODO: lower than memory
  367. # TODO: human readable setter (500M, 4G)?
  368. def test_190_vcpus(self):
  369. vm = self.get_vm()
  370. self.assertPropertyDefaultValue(vm, 'vcpus', 2)
  371. self.assertPropertyValue(vm, 'vcpus', 3, 3, '3')
  372. del vm.vcpus
  373. self.assertPropertyDefaultValue(vm, 'vcpus', 2)
  374. self.assertPropertyValue(vm, 'vcpus', '3', 3, '3')
  375. def test_191_vcpus_invalid(self):
  376. vm = self.get_vm()
  377. self.assertPropertyInvalidValue(vm, 'vcpus', 0)
  378. self.assertPropertyInvalidValue(vm, 'vcpus', -2)
  379. self.assertPropertyInvalidValue(vm, 'vcpus', '-2')
  380. self.assertPropertyInvalidValue(vm, 'vcpus', '')
  381. def test_200_debug(self):
  382. vm = self.get_vm()
  383. self._test_generic_bool_property(vm, 'debug', False)
  384. def test_210_installed_by_rpm(self):
  385. vm = self.get_vm()
  386. self._test_generic_bool_property(vm, 'installed_by_rpm', False)
  387. def test_220_include_in_backups(self):
  388. vm = self.get_vm()
  389. self._test_generic_bool_property(vm, 'include_in_backups', True)
  390. @qubes.tests.skipUnlessDom0
  391. def test_250_kernel(self):
  392. kernels = os.listdir(os.path.join(
  393. qubes.config.qubes_base_dir,
  394. qubes.config.system_path['qubes_kernels_base_dir']))
  395. if not len(kernels):
  396. self.skipTest('Needs at least one kernel installed')
  397. self.app.default_kernel = kernels[0]
  398. vm = self.get_vm()
  399. self.assertPropertyDefaultValue(vm, 'kernel', kernels[0])
  400. self.assertPropertyValue(vm, 'kernel', kernels[-1], kernels[-1],
  401. kernels[-1])
  402. del vm.kernel
  403. self.assertPropertyDefaultValue(vm, 'kernel', kernels[0])
  404. @qubes.tests.skipUnlessDom0
  405. def test_251_kernel_invalid(self):
  406. vm = self.get_vm()
  407. self.assertPropertyInvalidValue(vm, 'kernel', 123)
  408. self.assertPropertyInvalidValue(vm, 'kernel', 'invalid')
  409. def test_252_kernel_empty(self):
  410. vm = self.get_vm()
  411. self.assertPropertyValue(vm, 'kernel', '', '', '')
  412. self.assertPropertyValue(vm, 'kernel', None, '', '')
  413. def test_260_kernelopts(self):
  414. vm = self.get_vm()
  415. self.assertPropertyDefaultValue(vm, 'kernelopts',
  416. qubes.config.defaults['kernelopts'])
  417. self.assertPropertyValue(vm, 'kernelopts', 'some options',
  418. 'some options', 'some options')
  419. del vm.kernelopts
  420. self.assertPropertyDefaultValue(vm, 'kernelopts',
  421. qubes.config.defaults['kernelopts'])
  422. self.assertPropertyValue(vm, 'kernelopts', '',
  423. '', '')
  424. # TODO?
  425. # self.assertPropertyInvalidValue(vm, 'kernelopts', None),
  426. @unittest.skip('test not implemented')
  427. def test_261_kernelopts_pcidevs(self):
  428. vm = self.get_vm()
  429. # how to do that here? use dummy DeviceManager/DeviceCollection?
  430. # Disable events?
  431. vm.devices['pci'].attach('something')
  432. self.assertPropertyDefaultValue(vm, 'kernelopts',
  433. qubes.config.defaults['kernelopts_pcidevs'])
  434. def test_270_qrexec_timeout(self):
  435. vm = self.get_vm()
  436. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 60)
  437. self.assertPropertyValue(vm, 'qrexec_timeout', 3, 3, '3')
  438. del vm.qrexec_timeout
  439. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 60)
  440. self.assertPropertyValue(vm, 'qrexec_timeout', '3', 3, '3')
  441. def test_271_qrexec_timeout_invalid(self):
  442. vm = self.get_vm()
  443. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', -2)
  444. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', '-2')
  445. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', '')
  446. def test_272_qrexec_timeout_global_changed(self):
  447. self.app.default_qrexec_timeout = 123
  448. vm = self.get_vm()
  449. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 123)
  450. self.assertPropertyValue(vm, 'qrexec_timeout', 3, 3, '3')
  451. del vm.qrexec_timeout
  452. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 123)
  453. self.assertPropertyValue(vm, 'qrexec_timeout', '3', 3, '3')
  454. def test_280_autostart(self):
  455. vm = self.get_vm()
  456. # FIXME any better idea to not involve systemctl call at this stage?
  457. vm.events_enabled = False
  458. self._test_generic_bool_property(vm, 'autostart', False)
  459. @qubes.tests.skipUnlessDom0
  460. def test_281_autostart_systemd(self):
  461. vm = self.get_vm()
  462. self.assertFalse(os.path.exists(
  463. '/etc/systemd/system/multi-user.target.wants/'
  464. 'qubes-vm@{}.service'.format(vm.name)),
  465. "systemd service enabled before setting autostart")
  466. vm.autostart = True
  467. self.assertTrue(os.path.exists(
  468. '/etc/systemd/system/multi-user.target.wants/'
  469. 'qubes-vm@{}.service'.format(vm.name)),
  470. "systemd service not enabled by autostart=True")
  471. vm.autostart = False
  472. self.assertFalse(os.path.exists(
  473. '/etc/systemd/system/multi-user.target.wants/'
  474. 'qubes-vm@{}.service'.format(vm.name)),
  475. "systemd service not disabled by autostart=False")
  476. vm.autostart = True
  477. del vm.autostart
  478. self.assertFalse(os.path.exists(
  479. '/etc/systemd/system/multi-user.target.wants/'
  480. 'qubes-vm@{}.service'.format(vm.name)),
  481. "systemd service not disabled by resetting autostart")
  482. def test_290_management_dispvm(self):
  483. vm = self.get_vm()
  484. vm2 = self.get_vm('test2', qid=2)
  485. self.app.management_dispvm = None
  486. self.assertPropertyDefaultValue(vm, 'management_dispvm', None)
  487. self.app.management_dispvm = vm
  488. try:
  489. self.assertPropertyDefaultValue(vm, 'management_dispvm', vm)
  490. self.assertPropertyValue(vm, 'management_dispvm',
  491. 'test-inst-test2', vm2)
  492. finally:
  493. self.app.management_dispvm = None
  494. def test_291_management_dispvm_template_based(self):
  495. tpl = self.get_vm(name='tpl', cls=qubes.vm.templatevm.TemplateVM)
  496. vm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=tpl, qid=2)
  497. vm2 = self.get_vm('test2', qid=3)
  498. del vm.volumes
  499. self.app.management_dispvm = None
  500. try:
  501. self.assertPropertyDefaultValue(vm, 'management_dispvm', None)
  502. self.app.management_dispvm = vm
  503. self.assertPropertyDefaultValue(vm, 'management_dispvm', vm)
  504. tpl.management_dispvm = vm2
  505. self.assertPropertyDefaultValue(vm, 'management_dispvm', vm2)
  506. self.assertPropertyValue(vm, 'management_dispvm',
  507. 'test-inst-test2', vm2)
  508. finally:
  509. self.app.management_dispvm = None
  510. @unittest.skip('TODO')
  511. def test_320_seamless_gui_mode(self):
  512. vm = self.get_vm()
  513. self._test_generic_bool_property(vm, 'seamless_gui_mode')
  514. # TODO: reject setting to True when guiagent_installed is false
  515. def test_330_mac(self):
  516. vm = self.get_vm()
  517. # TODO: calculate proper default here
  518. default_mac = vm.mac
  519. self.assertIsNotNone(default_mac)
  520. self.assertPropertyDefaultValue(vm, 'mac', default_mac)
  521. self.assertPropertyValue(vm, 'mac', '00:11:22:33:44:55',
  522. '00:11:22:33:44:55', '00:11:22:33:44:55')
  523. del vm.mac
  524. self.assertPropertyDefaultValue(vm, 'mac', default_mac)
  525. def test_331_mac_invalid(self):
  526. vm = self.get_vm()
  527. self.assertPropertyInvalidValue(vm, 'mac', 123)
  528. self.assertPropertyInvalidValue(vm, 'mac', 'invalid')
  529. self.assertPropertyInvalidValue(vm, 'mac', '00:11:22:33:44:55:66')
  530. def test_340_default_user(self):
  531. vm = self.get_vm()
  532. self.assertPropertyDefaultValue(vm, 'default_user', 'user')
  533. self.assertPropertyValue(vm, 'default_user', 'someuser', 'someuser',
  534. 'someuser')
  535. del vm.default_user
  536. self.assertPropertyDefaultValue(vm, 'default_user', 'user')
  537. self.assertPropertyValue(vm, 'default_user', 123, '123', '123')
  538. vm.default_user = 'user'
  539. # TODO: check propagation for template-based VMs
  540. @unittest.skip('TODO')
  541. def test_350_timezone(self):
  542. vm = self.get_vm()
  543. self.assertPropertyDefaultValue(vm, 'timezone', 'localtime')
  544. self.assertPropertyValue(vm, 'timezone', 0, 0, '0')
  545. del vm.timezone
  546. self.assertPropertyDefaultValue(vm, 'timezone', 'localtime')
  547. self.assertPropertyValue(vm, 'timezone', '0', 0, '0')
  548. self.assertPropertyValue(vm, 'timezone', -3600, -3600, '-3600')
  549. self.assertPropertyValue(vm, 'timezone', 7200, 7200, '7200')
  550. @unittest.skip('TODO')
  551. def test_350_timezone_invalid(self):
  552. vm = self.get_vm()
  553. self.assertPropertyInvalidValue(vm, 'timezone', 'xxx')
  554. @unittest.skip('TODO')
  555. def test_360_drive(self):
  556. vm = self.get_vm()
  557. self.assertPropertyDefaultValue(vm, 'drive', None)
  558. # self.execute_tests('drive', [
  559. # ('hd:dom0:/tmp/drive.img', 'hd:dom0:/tmp/drive.img', True),
  560. # ('hd:/tmp/drive.img', 'hd:dom0:/tmp/drive.img', True),
  561. # ('cdrom:dom0:/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  562. # ('cdrom:/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  563. # ('/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  564. # ('hd:drive.img', '', False),
  565. # ('drive.img', '', False),
  566. # ])
  567. def test_400_backup_timestamp(self):
  568. vm = self.get_vm()
  569. timestamp = datetime.datetime(2016, 1, 1, 12, 14, 2)
  570. timestamp_str = timestamp.strftime('%s')
  571. self.assertPropertyDefaultValue(vm, 'backup_timestamp', None)
  572. self.assertPropertyValue(vm, 'backup_timestamp', int(timestamp_str),
  573. int(timestamp_str), timestamp_str)
  574. del vm.backup_timestamp
  575. self.assertPropertyDefaultValue(vm, 'backup_timestamp', None)
  576. self.assertPropertyValue(vm, 'backup_timestamp', timestamp_str,
  577. int(timestamp_str))
  578. def test_401_backup_timestamp_invalid(self):
  579. vm = self.get_vm()
  580. self.assertPropertyInvalidValue(vm, 'backup_timestamp', 'xxx')
  581. self.assertPropertyInvalidValue(vm, 'backup_timestamp', None)
  582. def test_500_property_migrate_virt_mode(self):
  583. xml_template = '''
  584. <domain class="QubesVM" id="domain-1">
  585. <properties>
  586. <property name="qid">1</property>
  587. <property name="name">testvm</property>
  588. <property name="label" ref="label-1" />
  589. <property name="hvm">{hvm_value}</property>
  590. </properties>
  591. </domain>
  592. '''
  593. xml = lxml.etree.XML(xml_template.format(hvm_value='True'))
  594. vm = qubes.vm.qubesvm.QubesVM(self.app, xml)
  595. self.assertEqual(vm.virt_mode, 'hvm')
  596. with self.assertRaises(AttributeError):
  597. vm.hvm
  598. xml = lxml.etree.XML(xml_template.format(hvm_value='False'))
  599. vm = qubes.vm.qubesvm.QubesVM(self.app, xml)
  600. self.assertEqual(vm.virt_mode, 'pv')
  601. with self.assertRaises(AttributeError):
  602. vm.hvm
  603. def test_600_libvirt_xml_pv(self):
  604. expected = '''<domain type="xen">
  605. <name>test-inst-test</name>
  606. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  607. <memory unit="MiB">500</memory>
  608. <currentMemory unit="MiB">400</currentMemory>
  609. <vcpu placement="static">2</vcpu>
  610. <os>
  611. <type arch="x86_64" machine="xenpv">linux</type>
  612. <kernel>/tmp/kernel/vmlinuz</kernel>
  613. <initrd>/tmp/kernel/initramfs</initrd>
  614. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
  615. </os>
  616. <features>
  617. </features>
  618. <clock offset='utc' adjustment='reset'>
  619. <timer name="tsc" mode="native"/>
  620. </clock>
  621. <on_poweroff>destroy</on_poweroff>
  622. <on_reboot>destroy</on_reboot>
  623. <on_crash>destroy</on_crash>
  624. <devices>
  625. <disk type="block" device="disk">
  626. <driver name="phy" />
  627. <source dev="/tmp/kernel/modules.img" />
  628. <target dev="xvdd" />
  629. <backenddomain name="dom0" />
  630. </disk>
  631. <console type="pty">
  632. <target type="xen" port="0"/>
  633. </console>
  634. </devices>
  635. </domain>
  636. '''
  637. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  638. vm = self.get_vm(uuid=my_uuid)
  639. vm.netvm = None
  640. vm.virt_mode = 'pv'
  641. with unittest.mock.patch('qubes.config.qubes_base_dir',
  642. '/tmp/qubes-test'):
  643. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  644. os.makedirs(kernel_dir, exist_ok=True)
  645. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  646. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  647. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  648. vm.kernel = 'dummy'
  649. # tests for storage are later
  650. vm.volumes['kernel'] = unittest.mock.Mock(**{
  651. 'kernels_dir': '/tmp/kernel',
  652. 'block_device.return_value.domain': 'dom0',
  653. 'block_device.return_value.script': None,
  654. 'block_device.return_value.path': '/tmp/kernel/modules.img',
  655. 'block_device.return_value.devtype': 'disk',
  656. 'block_device.return_value.name': 'kernel',
  657. })
  658. libvirt_xml = vm.create_config_file()
  659. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  660. lxml.etree.XML(expected))
  661. def test_600_libvirt_xml_hvm(self):
  662. expected = '''<domain type="xen">
  663. <name>test-inst-test</name>
  664. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  665. <memory unit="MiB">400</memory>
  666. <currentMemory unit="MiB">400</currentMemory>
  667. <vcpu placement="static">2</vcpu>
  668. <cpu mode='host-passthrough'>
  669. <!-- disable nested HVM -->
  670. <feature name='vmx' policy='disable'/>
  671. <feature name='svm' policy='disable'/>
  672. <!-- disable SMAP inside VM, because of Linux bug -->
  673. <feature name='smap' policy='disable'/>
  674. </cpu>
  675. <os>
  676. <type arch="x86_64" machine="xenfv">hvm</type>
  677. <!--
  678. For the libxl backend libvirt switches between OVMF (UEFI)
  679. and SeaBIOS based on the loader type. This has nothing to
  680. do with the hvmloader binary.
  681. -->
  682. <loader type="rom">hvmloader</loader>
  683. <boot dev="cdrom" />
  684. <boot dev="hd" />
  685. </os>
  686. <features>
  687. <pae/>
  688. <acpi/>
  689. <apic/>
  690. <viridian/>
  691. </features>
  692. <clock offset="variable" adjustment="0" basis="localtime" />
  693. <on_poweroff>destroy</on_poweroff>
  694. <on_reboot>destroy</on_reboot>
  695. <on_crash>destroy</on_crash>
  696. <devices>
  697. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  698. <emulator type="stubdom-linux" />
  699. <input type="tablet" bus="usb"/>
  700. <video>
  701. <model type="vga"/>
  702. </video>
  703. <graphics type="qubes"/>
  704. </devices>
  705. </domain>
  706. '''
  707. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  708. vm = self.get_vm(uuid=my_uuid)
  709. vm.netvm = None
  710. vm.virt_mode = 'hvm'
  711. libvirt_xml = vm.create_config_file()
  712. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  713. lxml.etree.XML(expected))
  714. def test_600_libvirt_xml_hvm_dom0_kernel(self):
  715. expected = '''<domain type="xen">
  716. <name>test-inst-test</name>
  717. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  718. <memory unit="MiB">500</memory>
  719. <currentMemory unit="MiB">400</currentMemory>
  720. <vcpu placement="static">2</vcpu>
  721. <cpu mode='host-passthrough'>
  722. <!-- disable nested HVM -->
  723. <feature name='vmx' policy='disable'/>
  724. <feature name='svm' policy='disable'/>
  725. <!-- disable SMAP inside VM, because of Linux bug -->
  726. <feature name='smap' policy='disable'/>
  727. </cpu>
  728. <os>
  729. <type arch="x86_64" machine="xenfv">hvm</type>
  730. <!--
  731. For the libxl backend libvirt switches between OVMF (UEFI)
  732. and SeaBIOS based on the loader type. This has nothing to
  733. do with the hvmloader binary.
  734. -->
  735. <loader type="rom">hvmloader</loader>
  736. <boot dev="cdrom" />
  737. <boot dev="hd" />
  738. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
  739. </os>
  740. <features>
  741. <pae/>
  742. <acpi/>
  743. <apic/>
  744. <viridian/>
  745. </features>
  746. <clock offset="variable" adjustment="0" basis="localtime" />
  747. <on_poweroff>destroy</on_poweroff>
  748. <on_reboot>destroy</on_reboot>
  749. <on_crash>destroy</on_crash>
  750. <devices>
  751. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  752. <emulator type="stubdom-linux" />
  753. <input type="tablet" bus="usb"/>
  754. <video>
  755. <model type="vga"/>
  756. </video>
  757. <graphics type="qubes"/>
  758. </devices>
  759. </domain>
  760. '''
  761. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  762. vm = self.get_vm(uuid=my_uuid)
  763. vm.netvm = None
  764. vm.virt_mode = 'hvm'
  765. vm.features['qrexec'] = True
  766. with unittest.mock.patch('qubes.config.qubes_base_dir',
  767. '/tmp/qubes-test'):
  768. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  769. os.makedirs(kernel_dir, exist_ok=True)
  770. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  771. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  772. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  773. vm.kernel = 'dummy'
  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_pvh(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. <!-- disable SMAP inside VM, because of Linux bug -->
  789. <feature name='smap' policy='disable'/>
  790. </cpu>
  791. <os>
  792. <type arch="x86_64" machine="xenfv">pvh</type>
  793. <kernel>/tmp/kernel/vmlinuz</kernel>
  794. <initrd>/tmp/kernel/initramfs</initrd>
  795. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
  796. </os>
  797. <features>
  798. <pae/>
  799. <acpi/>
  800. <apic/>
  801. <viridian/>
  802. </features>
  803. <clock offset='utc' adjustment='reset'>
  804. <timer name="tsc" mode="native"/>
  805. </clock>
  806. <on_poweroff>destroy</on_poweroff>
  807. <on_reboot>destroy</on_reboot>
  808. <on_crash>destroy</on_crash>
  809. <devices>
  810. <disk type="block" device="disk">
  811. <driver name="phy" />
  812. <source dev="/tmp/kernel/modules.img" />
  813. <target dev="xvdd" />
  814. <backenddomain name="dom0" />
  815. </disk>
  816. <console type="pty">
  817. <target type="xen" port="0"/>
  818. </console>
  819. </devices>
  820. </domain>
  821. '''
  822. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  823. vm = self.get_vm(uuid=my_uuid)
  824. vm.netvm = None
  825. vm.virt_mode = 'pvh'
  826. with unittest.mock.patch('qubes.config.qubes_base_dir',
  827. '/tmp/qubes-test'):
  828. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  829. os.makedirs(kernel_dir, exist_ok=True)
  830. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  831. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  832. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  833. vm.kernel = 'dummy'
  834. # tests for storage are later
  835. vm.volumes['kernel'] = unittest.mock.Mock(**{
  836. 'kernels_dir': '/tmp/kernel',
  837. 'block_device.return_value.domain': 'dom0',
  838. 'block_device.return_value.script': None,
  839. 'block_device.return_value.path': '/tmp/kernel/modules.img',
  840. 'block_device.return_value.devtype': 'disk',
  841. 'block_device.return_value.name': 'kernel',
  842. })
  843. libvirt_xml = vm.create_config_file()
  844. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  845. lxml.etree.XML(expected))
  846. def test_600_libvirt_xml_pvh_no_membalance(self):
  847. expected = '''<domain type="xen">
  848. <name>test-inst-test</name>
  849. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  850. <memory unit="MiB">400</memory>
  851. <currentMemory unit="MiB">400</currentMemory>
  852. <vcpu placement="static">2</vcpu>
  853. <cpu mode='host-passthrough'>
  854. <!-- disable nested HVM -->
  855. <feature name='vmx' policy='disable'/>
  856. <feature name='svm' policy='disable'/>
  857. <!-- disable SMAP inside VM, because of Linux bug -->
  858. <feature name='smap' policy='disable'/>
  859. </cpu>
  860. <os>
  861. <type arch="x86_64" machine="xenfv">pvh</type>
  862. <kernel>/tmp/kernel/vmlinuz</kernel>
  863. <initrd>/tmp/kernel/initramfs</initrd>
  864. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
  865. </os>
  866. <features>
  867. <pae/>
  868. <acpi/>
  869. <apic/>
  870. <viridian/>
  871. </features>
  872. <clock offset='utc' adjustment='reset'>
  873. <timer name="tsc" mode="native"/>
  874. </clock>
  875. <on_poweroff>destroy</on_poweroff>
  876. <on_reboot>destroy</on_reboot>
  877. <on_crash>destroy</on_crash>
  878. <devices>
  879. <disk type="block" device="disk">
  880. <driver name="phy" />
  881. <source dev="/tmp/kernel/modules.img" />
  882. <target dev="xvdd" />
  883. <backenddomain name="dom0" />
  884. </disk>
  885. <console type="pty">
  886. <target type="xen" port="0"/>
  887. </console>
  888. </devices>
  889. </domain>
  890. '''
  891. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  892. vm = self.get_vm(uuid=my_uuid)
  893. vm.netvm = None
  894. vm.virt_mode = 'pvh'
  895. vm.maxmem = 0
  896. with unittest.mock.patch('qubes.config.qubes_base_dir',
  897. '/tmp/qubes-test'):
  898. kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
  899. os.makedirs(kernel_dir, exist_ok=True)
  900. open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
  901. open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
  902. self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
  903. vm.kernel = 'dummy'
  904. # tests for storage are later
  905. vm.volumes['kernel'] = unittest.mock.Mock(**{
  906. 'kernels_dir': '/tmp/kernel',
  907. 'block_device.return_value.domain': 'dom0',
  908. 'block_device.return_value.script': None,
  909. 'block_device.return_value.path': '/tmp/kernel/modules.img',
  910. 'block_device.return_value.devtype': 'disk',
  911. 'block_device.return_value.name': 'kernel',
  912. })
  913. libvirt_xml = vm.create_config_file()
  914. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  915. lxml.etree.XML(expected))
  916. def test_600_libvirt_xml_hvm_pcidev(self):
  917. expected = '''<domain type="xen">
  918. <name>test-inst-test</name>
  919. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  920. <memory unit="MiB">400</memory>
  921. <currentMemory unit="MiB">400</currentMemory>
  922. <vcpu placement="static">2</vcpu>
  923. <cpu mode='host-passthrough'>
  924. <!-- disable nested HVM -->
  925. <feature name='vmx' policy='disable'/>
  926. <feature name='svm' policy='disable'/>
  927. <!-- disable SMAP inside VM, because of Linux bug -->
  928. <feature name='smap' policy='disable'/>
  929. </cpu>
  930. <os>
  931. <type arch="x86_64" machine="xenfv">hvm</type>
  932. <!--
  933. For the libxl backend libvirt switches between OVMF (UEFI)
  934. and SeaBIOS based on the loader type. This has nothing to
  935. do with the hvmloader binary.
  936. -->
  937. <loader type="rom">hvmloader</loader>
  938. <boot dev="cdrom" />
  939. <boot dev="hd" />
  940. </os>
  941. <features>
  942. <pae/>
  943. <acpi/>
  944. <apic/>
  945. <viridian/>
  946. <xen>
  947. <e820_host state="on"/>
  948. </xen>
  949. </features>
  950. <clock offset="variable" adjustment="0" basis="localtime" />
  951. <on_poweroff>destroy</on_poweroff>
  952. <on_reboot>destroy</on_reboot>
  953. <on_crash>destroy</on_crash>
  954. <devices>
  955. <hostdev type="pci" managed="yes">
  956. <source>
  957. <address
  958. bus="0x00"
  959. slot="0x00"
  960. function="0x0" />
  961. </source>
  962. </hostdev>
  963. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  964. <emulator type="stubdom-linux" />
  965. <input type="tablet" bus="usb"/>
  966. <video>
  967. <model type="vga"/>
  968. </video>
  969. <graphics type="qubes"/>
  970. </devices>
  971. </domain>
  972. '''
  973. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  974. # required for PCI devices listing
  975. self.app.vmm.offline_mode = False
  976. vm = self.get_vm(uuid=my_uuid)
  977. vm.netvm = None
  978. vm.virt_mode = 'hvm'
  979. vm.kernel = None
  980. # even with meminfo-writer enabled, should have memory==maxmem
  981. vm.features['service.meminfo-writer'] = True
  982. assignment = qubes.devices.DeviceAssignment(
  983. vm, # this is violation of API, but for PCI the argument
  984. # is unused
  985. '00_00.0',
  986. bus='pci',
  987. persistent=True)
  988. vm.devices['pci']._set.add(
  989. assignment)
  990. libvirt_xml = vm.create_config_file()
  991. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  992. lxml.etree.XML(expected))
  993. def test_610_libvirt_xml_network(self):
  994. expected = '''<domain type="xen">
  995. <name>test-inst-test</name>
  996. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  997. <memory unit="MiB">500</memory>
  998. <currentMemory unit="MiB">400</currentMemory>
  999. <vcpu placement="static">2</vcpu>
  1000. <cpu mode='host-passthrough'>
  1001. <!-- disable nested HVM -->
  1002. <feature name='vmx' policy='disable'/>
  1003. <feature name='svm' policy='disable'/>
  1004. <!-- disable SMAP inside VM, because of Linux bug -->
  1005. <feature name='smap' policy='disable'/>
  1006. </cpu>
  1007. <os>
  1008. <type arch="x86_64" machine="xenfv">hvm</type>
  1009. <!--
  1010. For the libxl backend libvirt switches between OVMF (UEFI)
  1011. and SeaBIOS based on the loader type. This has nothing to
  1012. do with the hvmloader binary.
  1013. -->
  1014. <loader type="rom">hvmloader</loader>
  1015. <boot dev="cdrom" />
  1016. <boot dev="hd" />
  1017. </os>
  1018. <features>
  1019. <pae/>
  1020. <acpi/>
  1021. <apic/>
  1022. <viridian/>
  1023. </features>
  1024. <clock offset="variable" adjustment="0" basis="localtime" />
  1025. <on_poweroff>destroy</on_poweroff>
  1026. <on_reboot>destroy</on_reboot>
  1027. <on_crash>destroy</on_crash>
  1028. <devices>
  1029. <interface type="ethernet">
  1030. <mac address="00:16:3E:5E:6C:00" />
  1031. <ip address="10.137.0.1" />
  1032. {extra_ip}
  1033. <backenddomain name="test-inst-netvm" />
  1034. <script path="vif-route-qubes" />
  1035. </interface>
  1036. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  1037. <emulator type="stubdom-linux" />
  1038. <input type="tablet" bus="usb"/>
  1039. <video>
  1040. <model type="vga"/>
  1041. </video>
  1042. <graphics type="qubes"/>
  1043. </devices>
  1044. </domain>
  1045. '''
  1046. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  1047. netvm = self.get_vm(qid=2, name='netvm', provides_network=True)
  1048. vm = self.get_vm(uuid=my_uuid)
  1049. vm.netvm = netvm
  1050. vm.virt_mode = 'hvm'
  1051. vm.features['qrexec'] = True
  1052. with self.subTest('ipv4_only'):
  1053. libvirt_xml = vm.create_config_file()
  1054. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1055. lxml.etree.XML(expected.format(extra_ip='')))
  1056. with self.subTest('ipv6'):
  1057. netvm.features['ipv6'] = True
  1058. libvirt_xml = vm.create_config_file()
  1059. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  1060. lxml.etree.XML(expected.format(
  1061. extra_ip='<ip address="{}::a89:1" family=\'ipv6\'/>'.format(
  1062. qubes.config.qubes_ipv6_prefix.replace(':0000', '')))))
  1063. @unittest.mock.patch('qubes.utils.get_timezone')
  1064. @unittest.mock.patch('qubes.utils.urandom')
  1065. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb')
  1066. def test_620_qdb_standalone(self, mock_qubesdb, mock_urandom,
  1067. mock_timezone):
  1068. mock_urandom.return_value = b'A' * 64
  1069. mock_timezone.return_value = 'UTC'
  1070. vm = self.get_vm(cls=qubes.vm.standalonevm.StandaloneVM)
  1071. vm.netvm = None
  1072. vm.events_enabled = True
  1073. test_qubesdb = TestQubesDB()
  1074. mock_qubesdb.write.side_effect = test_qubesdb.write
  1075. mock_qubesdb.rm.side_effect = test_qubesdb.rm
  1076. vm.create_qdb_entries()
  1077. self.maxDiff = None
  1078. iptables_header = (
  1079. '# Generated by Qubes Core on {}\n'
  1080. '*filter\n'
  1081. ':INPUT DROP [0:0]\n'
  1082. ':FORWARD DROP [0:0]\n'
  1083. ':OUTPUT ACCEPT [0:0]\n'
  1084. '-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n'
  1085. '-A INPUT -m conntrack --ctstate '
  1086. 'RELATED,ESTABLISHED -j ACCEPT\n'
  1087. '-A INPUT -p icmp -j ACCEPT\n'
  1088. '-A INPUT -i lo -j ACCEPT\n'
  1089. '-A INPUT -j REJECT --reject-with '
  1090. 'icmp-host-prohibited\n'
  1091. '-A FORWARD -m conntrack --ctstate '
  1092. 'RELATED,ESTABLISHED -j ACCEPT\n'
  1093. '-A FORWARD -i vif+ -o vif+ -j DROP\n'
  1094. 'COMMIT\n'.format(datetime.datetime.now().ctime()))
  1095. self.assertEqual(test_qubesdb.data, {
  1096. '/name': 'test-inst-test',
  1097. '/type': 'StandaloneVM',
  1098. '/default-user': 'user',
  1099. '/qubes-vm-type': 'AppVM',
  1100. '/qubes-debug-mode': '0',
  1101. '/qubes-base-template': '',
  1102. '/qubes-timezone': 'UTC',
  1103. '/qubes-random-seed': base64.b64encode(b'A' * 64),
  1104. '/qubes-vm-persistence': 'full',
  1105. '/qubes-vm-updateable': 'True',
  1106. '/qubes-block-devices': '',
  1107. '/qubes-usb-devices': '',
  1108. '/qubes-iptables': 'reload',
  1109. '/qubes-iptables-error': '',
  1110. '/qubes-iptables-header': iptables_header,
  1111. '/qubes-service/qubes-update-check': '0',
  1112. '/qubes-service/meminfo-writer': '1',
  1113. })
  1114. @unittest.mock.patch('qubes.utils.get_timezone')
  1115. @unittest.mock.patch('qubes.utils.urandom')
  1116. @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb')
  1117. def test_621_qdb_vm_with_network(self, mock_qubesdb, mock_urandom,
  1118. mock_timezone):
  1119. mock_urandom.return_value = b'A' * 64
  1120. mock_timezone.return_value = 'UTC'
  1121. template = self.get_vm(cls=qubes.vm.templatevm.TemplateVM, name='template')
  1122. template.netvm = None
  1123. netvm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1124. name='netvm', qid=2, provides_network=True)
  1125. vm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template,
  1126. name='appvm', qid=3)
  1127. vm.netvm = netvm
  1128. vm.kernel = None
  1129. # pretend the VM is running...
  1130. vm._qubesprop_xid = 3
  1131. netvm.kernel = None
  1132. test_qubesdb = TestQubesDB()
  1133. mock_qubesdb.write.side_effect = test_qubesdb.write
  1134. mock_qubesdb.rm.side_effect = test_qubesdb.rm
  1135. self.maxDiff = None
  1136. iptables_header = (
  1137. '# Generated by Qubes Core on {}\n'
  1138. '*filter\n'
  1139. ':INPUT DROP [0:0]\n'
  1140. ':FORWARD DROP [0:0]\n'
  1141. ':OUTPUT ACCEPT [0:0]\n'
  1142. '-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n'
  1143. '-A INPUT -m conntrack --ctstate '
  1144. 'RELATED,ESTABLISHED -j ACCEPT\n'
  1145. '-A INPUT -p icmp -j ACCEPT\n'
  1146. '-A INPUT -i lo -j ACCEPT\n'
  1147. '-A INPUT -j REJECT --reject-with '
  1148. 'icmp-host-prohibited\n'
  1149. '-A FORWARD -m conntrack --ctstate '
  1150. 'RELATED,ESTABLISHED -j ACCEPT\n'
  1151. '-A FORWARD -i vif+ -o vif+ -j DROP\n'
  1152. 'COMMIT\n'.format(datetime.datetime.now().ctime()))
  1153. expected = {
  1154. '/name': 'test-inst-appvm',
  1155. '/type': 'AppVM',
  1156. '/default-user': 'user',
  1157. '/qubes-vm-type': 'AppVM',
  1158. '/qubes-debug-mode': '0',
  1159. '/qubes-base-template': 'test-inst-template',
  1160. '/qubes-timezone': 'UTC',
  1161. '/qubes-random-seed': base64.b64encode(b'A' * 64),
  1162. '/qubes-vm-persistence': 'rw-only',
  1163. '/qubes-vm-updateable': 'False',
  1164. '/qubes-block-devices': '',
  1165. '/qubes-usb-devices': '',
  1166. '/qubes-iptables': 'reload',
  1167. '/qubes-iptables-error': '',
  1168. '/qubes-iptables-header': iptables_header,
  1169. '/qubes-service/qubes-update-check': '0',
  1170. '/qubes-service/meminfo-writer': '1',
  1171. '/qubes-ip': '10.137.0.3',
  1172. '/qubes-netmask': '255.255.255.255',
  1173. '/qubes-gateway': '10.137.0.2',
  1174. '/qubes-primary-dns': '10.139.1.1',
  1175. '/qubes-secondary-dns': '10.139.1.2',
  1176. }
  1177. with self.subTest('ipv4'):
  1178. vm.create_qdb_entries()
  1179. self.assertEqual(test_qubesdb.data, expected)
  1180. test_qubesdb.data.clear()
  1181. with self.subTest('ipv6'):
  1182. netvm.features['ipv6'] = True
  1183. expected['/qubes-ip6'] = \
  1184. qubes.config.qubes_ipv6_prefix.replace(':0000', '') + \
  1185. '::a89:3'
  1186. expected['/qubes-gateway6'] = expected['/qubes-ip6'][:-1] + '2'
  1187. vm.create_qdb_entries()
  1188. self.assertEqual(test_qubesdb.data, expected)
  1189. test_qubesdb.data.clear()
  1190. with self.subTest('ipv6_just_appvm'):
  1191. del netvm.features['ipv6']
  1192. vm.features['ipv6'] = True
  1193. expected['/qubes-ip6'] = \
  1194. qubes.config.qubes_ipv6_prefix.replace(':0000', '') + \
  1195. '::a89:3'
  1196. del expected['/qubes-gateway6']
  1197. vm.create_qdb_entries()
  1198. self.assertEqual(test_qubesdb.data, expected)
  1199. test_qubesdb.data.clear()
  1200. with self.subTest('proxy_ipv4'):
  1201. del vm.features['ipv6']
  1202. expected['/name'] = 'test-inst-netvm'
  1203. expected['/qubes-vm-type'] = 'NetVM'
  1204. del expected['/qubes-ip']
  1205. del expected['/qubes-gateway']
  1206. del expected['/qubes-netmask']
  1207. del expected['/qubes-ip6']
  1208. del expected['/qubes-primary-dns']
  1209. del expected['/qubes-secondary-dns']
  1210. expected['/qubes-netvm-primary-dns'] = '10.139.1.1'
  1211. expected['/qubes-netvm-secondary-dns'] = '10.139.1.2'
  1212. expected['/qubes-netvm-network'] = '10.137.0.2'
  1213. expected['/qubes-netvm-gateway'] = '10.137.0.2'
  1214. expected['/qubes-netvm-netmask'] = '255.255.255.255'
  1215. expected['/qubes-iptables-domainrules/3'] = \
  1216. '*filter\n' \
  1217. '-A FORWARD -s 10.137.0.3 -j ACCEPT\n' \
  1218. '-A FORWARD -s 10.137.0.3 -j DROP\n' \
  1219. 'COMMIT\n'
  1220. expected['/mapped-ip/10.137.0.3/visible-ip'] = '10.137.0.3'
  1221. expected['/mapped-ip/10.137.0.3/visible-gateway'] = '10.137.0.2'
  1222. expected['/qubes-firewall/10.137.0.3'] = ''
  1223. expected['/qubes-firewall/10.137.0.3/0000'] = 'action=accept'
  1224. expected['/qubes-firewall/10.137.0.3/policy'] = 'drop'
  1225. with unittest.mock.patch('qubes.vm.qubesvm.QubesVM.is_running',
  1226. lambda _: True):
  1227. netvm.create_qdb_entries()
  1228. self.assertEqual(test_qubesdb.data, expected)
  1229. test_qubesdb.data.clear()
  1230. with self.subTest('proxy_ipv6'):
  1231. netvm.features['ipv6'] = True
  1232. ip6 = qubes.config.qubes_ipv6_prefix.replace(
  1233. ':0000', '') + '::a89:3'
  1234. expected['/qubes-netvm-gateway6'] = ip6[:-1] + '2'
  1235. expected['/qubes-firewall/' + ip6] = ''
  1236. expected['/qubes-firewall/' + ip6 + '/0000'] = 'action=accept'
  1237. expected['/qubes-firewall/' + ip6 + '/policy'] = 'drop'
  1238. with unittest.mock.patch('qubes.vm.qubesvm.QubesVM.is_running',
  1239. lambda _: True):
  1240. netvm.create_qdb_entries()
  1241. self.assertEqual(test_qubesdb.data, expected)