qubesvm.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  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 program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License along
  19. # with this program; if not, write to the Free Software Foundation, Inc.,
  20. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. #
  22. import os
  23. import unittest
  24. import uuid
  25. import datetime
  26. import lxml.etree
  27. import unittest.mock
  28. import qubes
  29. import qubes.exc
  30. import qubes.config
  31. import qubes.vm
  32. import qubes.vm.qubesvm
  33. import qubes.tests
  34. import qubes.tests.vm
  35. class TestApp(object):
  36. labels = {1: qubes.Label(1, '0xcc0000', 'red')}
  37. def __init__(self):
  38. self.domains = {}
  39. class TestProp(object):
  40. # pylint: disable=too-few-public-methods
  41. __name__ = 'testprop'
  42. class TestVM(object):
  43. # pylint: disable=too-few-public-methods
  44. app = TestApp()
  45. def __init__(self, **kwargs):
  46. self.running = False
  47. self.installed_by_rpm = False
  48. for k, v in kwargs.items():
  49. setattr(self, k, v)
  50. def is_running(self):
  51. return self.running
  52. class TC_00_setters(qubes.tests.QubesTestCase):
  53. def setUp(self):
  54. super().setUp()
  55. self.vm = TestVM()
  56. self.prop = TestProp()
  57. def test_000_setter_qid(self):
  58. self.assertEqual(
  59. qubes.vm.qubesvm._setter_qid(self.vm, self.prop, 5), 5)
  60. def test_001_setter_qid_lt_0(self):
  61. with self.assertRaises(ValueError):
  62. qubes.vm.qubesvm._setter_qid(self.vm, self.prop, -1)
  63. def test_002_setter_qid_gt_max(self):
  64. with self.assertRaises(ValueError):
  65. qubes.vm.qubesvm._setter_qid(self.vm,
  66. self.prop, qubes.config.max_qid + 5)
  67. @unittest.skip('test not implemented')
  68. def test_020_setter_kernel(self):
  69. pass
  70. def test_030_setter_label_object(self):
  71. label = TestApp.labels[1]
  72. self.assertIs(label,
  73. qubes.vm.setter_label(self.vm, self.prop, label))
  74. def test_031_setter_label_getitem(self):
  75. label = TestApp.labels[1]
  76. self.assertIs(label,
  77. qubes.vm.setter_label(self.vm, self.prop, 'label-1'))
  78. # there is no check for self.app.get_label()
  79. def test_040_setter_virt_mode(self):
  80. self.assertEqual(
  81. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'hvm'),
  82. 'hvm')
  83. self.assertEqual(
  84. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'HVM'),
  85. 'hvm')
  86. self.assertEqual(
  87. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'PV'),
  88. 'pv')
  89. with self.assertRaises(ValueError):
  90. qubes.vm.qubesvm._setter_virt_mode(self.vm, self.prop, 'True')
  91. class QubesVMTestsMixin(object):
  92. property_no_default = object()
  93. def setUp(self):
  94. super(QubesVMTestsMixin, self).setUp()
  95. self.app = qubes.tests.vm.TestApp()
  96. self.app.vmm.offline_mode = True
  97. def get_vm(self, **kwargs):
  98. vm = qubes.vm.qubesvm.QubesVM(self.app, None,
  99. qid=1, name=qubes.tests.VMPREFIX + 'test',
  100. **kwargs)
  101. self.addCleanup(vm.close)
  102. return vm
  103. def assertPropertyValue(self, vm, prop_name, set_value, expected_value,
  104. expected_xml_content=None):
  105. # FIXME: any better exception list? or maybe all of that should be a
  106. # single exception?
  107. with self.assertNotRaises((ValueError, TypeError, KeyError)):
  108. setattr(vm, prop_name, set_value)
  109. self.assertEqual(getattr(vm, prop_name), expected_value)
  110. if expected_xml_content is not None:
  111. xml = vm.__xml__()
  112. prop_xml = xml.xpath(
  113. './properties/property[@name=\'{}\']'.format(prop_name))
  114. self.assertEqual(len(prop_xml), 1, "Property not found in XML")
  115. self.assertEqual(prop_xml[0].text, expected_xml_content)
  116. def assertPropertyInvalidValue(self, vm, prop_name, set_value):
  117. orig_value_set = True
  118. orig_value = None
  119. try:
  120. orig_value = getattr(vm, prop_name)
  121. except AttributeError:
  122. orig_value_set = False
  123. # FIXME: any better exception list? or maybe all of that should be a
  124. # single exception?
  125. with self.assertRaises((ValueError, TypeError, KeyError)):
  126. setattr(vm, prop_name, set_value)
  127. if orig_value_set:
  128. self.assertEqual(getattr(vm, prop_name), orig_value)
  129. else:
  130. with self.assertRaises(AttributeError):
  131. getattr(vm, prop_name)
  132. def assertPropertyDefaultValue(self, vm, prop_name,
  133. expected_default=property_no_default):
  134. if expected_default is self.property_no_default:
  135. with self.assertRaises(AttributeError):
  136. getattr(vm, prop_name)
  137. else:
  138. with self.assertNotRaises(AttributeError):
  139. self.assertEqual(getattr(vm, prop_name), expected_default)
  140. xml = vm.__xml__()
  141. prop_xml = xml.xpath(
  142. './properties/property[@name=\'{}\']'.format(prop_name))
  143. self.assertEqual(len(prop_xml), 0, "Property still found in XML")
  144. def _test_generic_bool_property(self, vm, prop_name, default=False):
  145. self.assertPropertyDefaultValue(vm, prop_name, default)
  146. self.assertPropertyValue(vm, prop_name, False, False, 'False')
  147. self.assertPropertyValue(vm, prop_name, True, True, 'True')
  148. delattr(vm, prop_name)
  149. self.assertPropertyDefaultValue(vm, prop_name, default)
  150. self.assertPropertyValue(vm, prop_name, 'True', True, 'True')
  151. self.assertPropertyValue(vm, prop_name, 'False', False, 'False')
  152. self.assertPropertyInvalidValue(vm, prop_name, 'xxx')
  153. self.assertPropertyValue(vm, prop_name, 123, True)
  154. self.assertPropertyInvalidValue(vm, prop_name, '')
  155. class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
  156. def test_000_init(self):
  157. self.get_vm()
  158. def test_001_init_no_qid_or_name(self):
  159. with self.assertRaises(AssertionError):
  160. qubes.vm.qubesvm.QubesVM(self.app, None,
  161. name=qubes.tests.VMPREFIX + 'test')
  162. with self.assertRaises(AssertionError):
  163. qubes.vm.qubesvm.QubesVM(self.app, None,
  164. qid=1)
  165. def test_003_init_fire_domain_init(self):
  166. class TestVM2(qubes.vm.qubesvm.QubesVM):
  167. event_fired = False
  168. @qubes.events.handler('domain-init')
  169. def on_domain_init(self, event): # pylint: disable=unused-argument
  170. self.__class__.event_fired = True
  171. TestVM2(self.app, None, qid=1, name=qubes.tests.VMPREFIX + 'test')
  172. self.assertTrue(TestVM2.event_fired)
  173. def test_004_uuid_autogen(self):
  174. vm = self.get_vm()
  175. self.assertTrue(hasattr(vm, 'uuid'))
  176. def test_100_qid(self):
  177. vm = self.get_vm()
  178. self.assertIsInstance(vm.qid, int)
  179. with self.assertRaises(AttributeError):
  180. vm.qid = 2
  181. def test_110_name(self):
  182. vm = self.get_vm()
  183. self.assertIsInstance(vm.name, str)
  184. def test_120_uuid(self):
  185. my_uuid = uuid.uuid4()
  186. vm = self.get_vm(uuid=my_uuid)
  187. self.assertIsInstance(vm.uuid, uuid.UUID)
  188. self.assertIs(vm.uuid, my_uuid)
  189. with self.assertRaises(AttributeError):
  190. vm.uuid = uuid.uuid4()
  191. @unittest.skip('TODO: how to not fail on making an icon symlink here?')
  192. def test_130_label(self):
  193. vm = self.get_vm()
  194. self.assertPropertyDefaultValue(vm, 'label')
  195. self.assertPropertyValue(vm, 'label', self.app.labels[1],
  196. self.app.labels[1], 'label-1')
  197. del vm.label
  198. self.assertPropertyDefaultValue(vm, 'label')
  199. self.assertPropertyValue(vm, 'label', 'red',
  200. self.app.labels[1], 'label-1')
  201. self.assertPropertyValue(vm, 'label', 'label-1',
  202. self.app.labels[1], 'label-1')
  203. def test_131_label_invalid(self):
  204. vm = self.get_vm()
  205. self.assertPropertyInvalidValue(vm, 'label', 'invalid')
  206. self.assertPropertyInvalidValue(vm, 'label', 123)
  207. def test_160_memory(self):
  208. vm = self.get_vm()
  209. self.assertPropertyDefaultValue(vm, 'memory', 400)
  210. self.assertPropertyValue(vm, 'memory', 500, 500, '500')
  211. del vm.memory
  212. self.assertPropertyDefaultValue(vm, 'memory', 400)
  213. self.assertPropertyValue(vm, 'memory', '500', 500, '500')
  214. def test_161_memory_invalid(self):
  215. vm = self.get_vm()
  216. self.assertPropertyInvalidValue(vm, 'memory', -100)
  217. self.assertPropertyInvalidValue(vm, 'memory', '-100')
  218. self.assertPropertyInvalidValue(vm, 'memory', '')
  219. # TODO: higher than maxmem
  220. # TODO: human readable setter (500M, 4G)?
  221. def test_170_maxmem(self):
  222. vm = self.get_vm()
  223. self.assertPropertyDefaultValue(vm, 'maxmem',
  224. self.app.host.memory_total / 1024 / 2)
  225. self.assertPropertyValue(vm, 'maxmem', 500, 500, '500')
  226. del vm.maxmem
  227. self.assertPropertyDefaultValue(vm, 'maxmem',
  228. self.app.host.memory_total / 1024 / 2)
  229. self.assertPropertyValue(vm, 'maxmem', '500', 500, '500')
  230. def test_171_maxmem_invalid(self):
  231. vm = self.get_vm()
  232. self.assertPropertyInvalidValue(vm, 'maxmem', -100)
  233. self.assertPropertyInvalidValue(vm, 'maxmem', '-100')
  234. self.assertPropertyInvalidValue(vm, 'maxmem', '')
  235. # TODO: lower than memory
  236. # TODO: human readable setter (500M, 4G)?
  237. def test_190_vcpus(self):
  238. vm = self.get_vm()
  239. self.assertPropertyDefaultValue(vm, 'vcpus', 2)
  240. self.assertPropertyValue(vm, 'vcpus', 3, 3, '3')
  241. del vm.vcpus
  242. self.assertPropertyDefaultValue(vm, 'vcpus', 2)
  243. self.assertPropertyValue(vm, 'vcpus', '3', 3, '3')
  244. def test_191_vcpus_invalid(self):
  245. vm = self.get_vm()
  246. self.assertPropertyInvalidValue(vm, 'vcpus', 0)
  247. self.assertPropertyInvalidValue(vm, 'vcpus', -2)
  248. self.assertPropertyInvalidValue(vm, 'vcpus', '-2')
  249. self.assertPropertyInvalidValue(vm, 'vcpus', '')
  250. def test_200_debug(self):
  251. vm = self.get_vm()
  252. self._test_generic_bool_property(vm, 'debug', False)
  253. def test_210_installed_by_rpm(self):
  254. vm = self.get_vm()
  255. self._test_generic_bool_property(vm, 'installed_by_rpm', False)
  256. def test_220_include_in_backups(self):
  257. vm = self.get_vm()
  258. self._test_generic_bool_property(vm, 'include_in_backups', True)
  259. @qubes.tests.skipUnlessDom0
  260. def test_250_kernel(self):
  261. kernels = os.listdir(os.path.join(
  262. qubes.config.qubes_base_dir,
  263. qubes.config.system_path['qubes_kernels_base_dir']))
  264. if not len(kernels):
  265. self.skipTest('Needs at least one kernel installed')
  266. self.app.default_kernel = kernels[0]
  267. vm = self.get_vm()
  268. self.assertPropertyDefaultValue(vm, 'kernel', kernels[0])
  269. self.assertPropertyValue(vm, 'kernel', kernels[-1], kernels[-1],
  270. kernels[-1])
  271. del vm.kernel
  272. self.assertPropertyDefaultValue(vm, 'kernel', kernels[0])
  273. @qubes.tests.skipUnlessDom0
  274. def test_251_kernel_invalid(self):
  275. vm = self.get_vm()
  276. self.assertPropertyInvalidValue(vm, 'kernel', 123)
  277. self.assertPropertyInvalidValue(vm, 'kernel', 'invalid')
  278. def test_252_kernel_empty(self):
  279. vm = self.get_vm()
  280. self.assertPropertyValue(vm, 'kernel', '', '', '')
  281. self.assertPropertyValue(vm, 'kernel', None, '', '')
  282. def test_260_kernelopts(self):
  283. vm = self.get_vm()
  284. self.assertPropertyDefaultValue(vm, 'kernelopts',
  285. qubes.config.defaults['kernelopts'])
  286. self.assertPropertyValue(vm, 'kernelopts', 'some options',
  287. 'some options', 'some options')
  288. del vm.kernelopts
  289. self.assertPropertyDefaultValue(vm, 'kernelopts',
  290. qubes.config.defaults['kernelopts'])
  291. self.assertPropertyValue(vm, 'kernelopts', '',
  292. '', '')
  293. # TODO?
  294. # self.assertPropertyInvalidValue(vm, 'kernelopts', None),
  295. @unittest.skip('test not implemented')
  296. def test_261_kernelopts_pcidevs(self):
  297. vm = self.get_vm()
  298. # how to do that here? use dummy DeviceManager/DeviceCollection?
  299. # Disable events?
  300. vm.devices['pci'].attach('something')
  301. self.assertPropertyDefaultValue(vm, 'kernelopts',
  302. qubes.config.defaults['kernelopts_pcidevs'])
  303. def test_270_qrexec_timeout(self):
  304. vm = self.get_vm()
  305. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 60)
  306. self.assertPropertyValue(vm, 'qrexec_timeout', 3, 3, '3')
  307. del vm.qrexec_timeout
  308. self.assertPropertyDefaultValue(vm, 'qrexec_timeout', 60)
  309. self.assertPropertyValue(vm, 'qrexec_timeout', '3', 3, '3')
  310. def test_271_qrexec_timeout_invalid(self):
  311. vm = self.get_vm()
  312. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', -2)
  313. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', '-2')
  314. self.assertPropertyInvalidValue(vm, 'qrexec_timeout', '')
  315. def test_280_autostart(self):
  316. vm = self.get_vm()
  317. # FIXME any better idea to not involve systemctl call at this stage?
  318. vm.events_enabled = False
  319. self._test_generic_bool_property(vm, 'autostart', False)
  320. @qubes.tests.skipUnlessDom0
  321. def test_281_autostart_systemd(self):
  322. vm = self.get_vm()
  323. self.assertFalse(os.path.exists(
  324. '/etc/systemd/system/multi-user.target.wants/'
  325. 'qubes-vm@{}.service'.format(vm.name)),
  326. "systemd service enabled before setting autostart")
  327. vm.autostart = True
  328. self.assertTrue(os.path.exists(
  329. '/etc/systemd/system/multi-user.target.wants/'
  330. 'qubes-vm@{}.service'.format(vm.name)),
  331. "systemd service not enabled by autostart=True")
  332. vm.autostart = False
  333. self.assertFalse(os.path.exists(
  334. '/etc/systemd/system/multi-user.target.wants/'
  335. 'qubes-vm@{}.service'.format(vm.name)),
  336. "systemd service not disabled by autostart=False")
  337. vm.autostart = True
  338. del vm.autostart
  339. self.assertFalse(os.path.exists(
  340. '/etc/systemd/system/multi-user.target.wants/'
  341. 'qubes-vm@{}.service'.format(vm.name)),
  342. "systemd service not disabled by resetting autostart")
  343. @unittest.skip('TODO')
  344. def test_320_seamless_gui_mode(self):
  345. vm = self.get_vm()
  346. self._test_generic_bool_property(vm, 'seamless_gui_mode')
  347. # TODO: reject setting to True when guiagent_installed is false
  348. def test_330_mac(self):
  349. vm = self.get_vm()
  350. # TODO: calculate proper default here
  351. default_mac = vm.mac
  352. self.assertIsNotNone(default_mac)
  353. self.assertPropertyDefaultValue(vm, 'mac', default_mac)
  354. self.assertPropertyValue(vm, 'mac', '00:11:22:33:44:55',
  355. '00:11:22:33:44:55', '00:11:22:33:44:55')
  356. del vm.mac
  357. self.assertPropertyDefaultValue(vm, 'mac', default_mac)
  358. def test_331_mac_invalid(self):
  359. vm = self.get_vm()
  360. self.assertPropertyInvalidValue(vm, 'mac', 123)
  361. self.assertPropertyInvalidValue(vm, 'mac', 'invalid')
  362. self.assertPropertyInvalidValue(vm, 'mac', '00:11:22:33:44:55:66')
  363. def test_340_default_user(self):
  364. vm = self.get_vm()
  365. self.assertPropertyDefaultValue(vm, 'default_user', 'user')
  366. self.assertPropertyValue(vm, 'default_user', 'someuser', 'someuser',
  367. 'someuser')
  368. del vm.default_user
  369. self.assertPropertyDefaultValue(vm, 'default_user', 'user')
  370. self.assertPropertyValue(vm, 'default_user', 123, '123', '123')
  371. # TODO: check propagation for template-based VMs
  372. @unittest.skip('TODO')
  373. def test_350_timezone(self):
  374. vm = self.get_vm()
  375. self.assertPropertyDefaultValue(vm, 'timezone', 'localtime')
  376. self.assertPropertyValue(vm, 'timezone', 0, 0, '0')
  377. del vm.timezone
  378. self.assertPropertyDefaultValue(vm, 'timezone', 'localtime')
  379. self.assertPropertyValue(vm, 'timezone', '0', 0, '0')
  380. self.assertPropertyValue(vm, 'timezone', -3600, -3600, '-3600')
  381. self.assertPropertyValue(vm, 'timezone', 7200, 7200, '7200')
  382. @unittest.skip('TODO')
  383. def test_350_timezone_invalid(self):
  384. vm = self.get_vm()
  385. self.assertPropertyInvalidValue(vm, 'timezone', 'xxx')
  386. @unittest.skip('TODO')
  387. def test_360_drive(self):
  388. vm = self.get_vm()
  389. self.assertPropertyDefaultValue(vm, 'drive', None)
  390. # self.execute_tests('drive', [
  391. # ('hd:dom0:/tmp/drive.img', 'hd:dom0:/tmp/drive.img', True),
  392. # ('hd:/tmp/drive.img', 'hd:dom0:/tmp/drive.img', True),
  393. # ('cdrom:dom0:/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  394. # ('cdrom:/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  395. # ('/tmp/drive.img', 'cdrom:dom0:/tmp/drive.img', True),
  396. # ('hd:drive.img', '', False),
  397. # ('drive.img', '', False),
  398. # ])
  399. def test_400_backup_timestamp(self):
  400. vm = self.get_vm()
  401. timestamp = datetime.datetime(2016, 1, 1, 12, 14, 2)
  402. timestamp_str = timestamp.strftime('%s')
  403. self.assertPropertyDefaultValue(vm, 'backup_timestamp', None)
  404. self.assertPropertyValue(vm, 'backup_timestamp', timestamp,
  405. timestamp, timestamp_str)
  406. del vm.backup_timestamp
  407. self.assertPropertyDefaultValue(vm, 'backup_timestamp', None)
  408. self.assertPropertyValue(vm, 'backup_timestamp', timestamp_str,
  409. timestamp)
  410. def test_401_backup_timestamp_invalid(self):
  411. vm = self.get_vm()
  412. self.assertPropertyInvalidValue(vm, 'backup_timestamp', 'xxx')
  413. self.assertPropertyInvalidValue(vm, 'backup_timestamp', None)
  414. def test_500_property_migrate_virt_mode(self):
  415. xml_template = '''
  416. <domain class="QubesVM" id="domain-1">
  417. <properties>
  418. <property name="qid">1</property>
  419. <property name="name">testvm</property>
  420. <property name="label" ref="label-1" />
  421. <property name="hvm">{hvm_value}</property>
  422. </properties>
  423. </domain>
  424. '''
  425. xml = lxml.etree.XML(xml_template.format(hvm_value='True'))
  426. vm = qubes.vm.qubesvm.QubesVM(self.app, xml)
  427. self.assertEqual(vm.virt_mode, 'hvm')
  428. with self.assertRaises(AttributeError):
  429. vm.hvm
  430. xml = lxml.etree.XML(xml_template.format(hvm_value='False'))
  431. vm = qubes.vm.qubesvm.QubesVM(self.app, xml)
  432. self.assertEqual(vm.virt_mode, 'pv')
  433. with self.assertRaises(AttributeError):
  434. vm.hvm
  435. def test_600_libvirt_xml_pv(self):
  436. expected = '''<domain type="xen">
  437. <name>test-inst-test</name>
  438. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  439. <memory unit="MiB">500</memory>
  440. <currentMemory unit="MiB">400</currentMemory>
  441. <vcpu placement="static">2</vcpu>
  442. <os>
  443. <type arch="x86_64" machine="xenpv">linux</type>
  444. <kernel>/tmp/kernel/vmlinuz</kernel>
  445. <initrd>/tmp/kernel/initramfs</initrd>
  446. <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
  447. </os>
  448. <features>
  449. </features>
  450. <clock offset='utc' adjustment='reset'>
  451. <timer name="tsc" mode="native"/>
  452. </clock>
  453. <on_poweroff>destroy</on_poweroff>
  454. <on_reboot>destroy</on_reboot>
  455. <on_crash>destroy</on_crash>
  456. <devices>
  457. <disk type="block" device="disk">
  458. <driver name="phy" />
  459. <source dev="/tmp/kernel/modules.img" />
  460. <target dev="xvdd" />
  461. <backenddomain name="dom0" />
  462. </disk>
  463. <console type="pty">
  464. <target type="xen" port="0"/>
  465. </console>
  466. </devices>
  467. </domain>
  468. '''
  469. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  470. vm = self.get_vm(uuid=my_uuid)
  471. vm.netvm = None
  472. vm.virt_mode = 'pv'
  473. # tests for storage are later
  474. vm.volumes['kernel'] = unittest.mock.Mock(**{
  475. 'kernels_dir': '/tmp/kernel',
  476. 'block_device.return_value.domain': 'dom0',
  477. 'block_device.return_value.script': None,
  478. 'block_device.return_value.path': '/tmp/kernel/modules.img',
  479. 'block_device.return_value.devtype': 'disk',
  480. 'block_device.return_value.name': 'kernel',
  481. })
  482. libvirt_xml = vm.create_config_file()
  483. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  484. lxml.etree.XML(expected))
  485. def test_600_libvirt_xml_hvm(self):
  486. expected = '''<domain type="xen">
  487. <name>test-inst-test</name>
  488. <uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
  489. <memory unit="MiB">500</memory>
  490. <currentMemory unit="MiB">400</currentMemory>
  491. <vcpu placement="static">2</vcpu>
  492. <cpu mode='host-passthrough'>
  493. <!-- disable nested HVM -->
  494. <feature name='vmx' policy='disable'/>
  495. <feature name='svm' policy='disable'/>
  496. <!-- disable SMAP inside VM, because of Linux bug -->
  497. <feature name='smap' policy='disable'/>
  498. </cpu>
  499. <os>
  500. <type arch="x86_64" machine="xenfv">hvm</type>
  501. <!--
  502. For the libxl backend libvirt switches between OVMF (UEFI)
  503. and SeaBIOS based on the loader type. This has nothing to
  504. do with the hvmloader binary.
  505. -->
  506. <loader type="rom">hvmloader</loader>
  507. <boot dev="cdrom" />
  508. <boot dev="hd" />
  509. <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
  510. </os>
  511. <features>
  512. <pae/>
  513. <acpi/>
  514. <apic/>
  515. <viridian/>
  516. </features>
  517. <clock offset="variable" adjustment="0" basis="localtime" />
  518. <on_poweroff>destroy</on_poweroff>
  519. <on_reboot>destroy</on_reboot>
  520. <on_crash>destroy</on_crash>
  521. <devices>
  522. <emulator type="stubdom-linux" />
  523. <input type="tablet" bus="usb"/>
  524. <video>
  525. <model type="vga"/>
  526. </video>
  527. <graphics type="qubes"/>
  528. </devices>
  529. </domain>
  530. '''
  531. my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
  532. vm = self.get_vm(uuid=my_uuid)
  533. vm.netvm = None
  534. vm.virt_mode = 'hvm'
  535. libvirt_xml = vm.create_config_file()
  536. self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
  537. lxml.etree.XML(expected))