test_domain.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. import salt.modules.virt as virt
  2. from salt._compat import ElementTree as ET
  3. from tests.support.mock import MagicMock, patch
  4. from .test_helpers import append_to_XMLDesc
  5. def test_update_xen_disk_volumes(make_mock_vm, make_mock_storage_pool):
  6. xml_def = """
  7. <domain type='xen'>
  8. <name>my_vm</name>
  9. <memory unit='KiB'>524288</memory>
  10. <currentMemory unit='KiB'>524288</currentMemory>
  11. <vcpu placement='static'>1</vcpu>
  12. <os>
  13. <type arch='x86_64'>linux</type>
  14. <kernel>/usr/lib/grub2/x86_64-xen/grub.xen</kernel>
  15. </os>
  16. <devices>
  17. <disk type='file' device='disk'>
  18. <driver name='qemu' type='qcow2' cache='none' io='native'/>
  19. <source file='/path/to/default/my_vm_system'/>
  20. <target dev='xvda' bus='xen'/>
  21. </disk>
  22. <disk type='block' device='disk'>
  23. <driver name='qemu' type='raw' cache='none' io='native'/>
  24. <source dev='/path/to/my-iscsi/unit:0:0:1'/>
  25. <target dev='xvdb' bus='xen'/>
  26. </disk>
  27. <controller type='xenbus' index='0'/>
  28. </devices>
  29. </domain>"""
  30. domain_mock = make_mock_vm(xml_def)
  31. make_mock_storage_pool("default", "dir", ["my_vm_system"])
  32. make_mock_storage_pool("my-iscsi", "iscsi", ["unit:0:0:1"])
  33. make_mock_storage_pool("vdb", "disk", ["vdb1"])
  34. ret = virt.update(
  35. "my_vm",
  36. disks=[
  37. {"name": "system", "pool": "default"},
  38. {"name": "iscsi-data", "pool": "my-iscsi", "source_file": "unit:0:0:1"},
  39. {"name": "vdb-data", "pool": "vdb", "source_file": "vdb1"},
  40. {"name": "file-data", "pool": "default", "size": "10240"},
  41. ],
  42. )
  43. assert ret["definition"]
  44. define_mock = virt.libvirt.openAuth().defineXML
  45. setxml = ET.fromstring(define_mock.call_args[0][0])
  46. assert "block" == setxml.find(".//disk[3]").get("type")
  47. assert "/path/to/vdb/vdb1" == setxml.find(".//disk[3]/source").get("dev")
  48. # Note that my_vm-file-data was not an existing volume before the update
  49. assert "file" == setxml.find(".//disk[4]").get("type")
  50. assert "/path/to/default/my_vm_file-data" == setxml.find(".//disk[4]/source").get(
  51. "file"
  52. )
  53. def test_get_disks(make_mock_vm, make_mock_storage_pool):
  54. # test with volumes
  55. vm_def = """<domain type='kvm' id='3'>
  56. <name>srv01</name>
  57. <devices>
  58. <disk type='volume' device='disk'>
  59. <driver name='qemu' type='qcow2' cache='none' io='native'/>
  60. <source pool='default' volume='srv01_system'/>
  61. <backingStore/>
  62. <target dev='vda' bus='virtio'/>
  63. <alias name='virtio-disk0'/>
  64. <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
  65. </disk>
  66. <disk type='volume' device='disk'>
  67. <driver name='qemu' type='qcow2' cache='none' io='native'/>
  68. <source pool='default' volume='srv01_data'/>
  69. <backingStore type='file' index='1'>
  70. <format type='qcow2'/>
  71. <source file='/var/lib/libvirt/images/vol01'/>
  72. <backingStore/>
  73. </backingStore>
  74. <target dev='vdb' bus='virtio'/>
  75. <alias name='virtio-disk1'/>
  76. <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
  77. </disk>
  78. <disk type='volume' device='disk'>
  79. <driver name='qemu' type='qcow2' cache='none' io='native'/>
  80. <source pool='default' volume='vm05_system'/>
  81. <backingStore type='file' index='1'>
  82. <format type='qcow2'/>
  83. <source file='/var/lib/libvirt/images/vm04_system.qcow2'/>
  84. <backingStore type='file' index='2'>
  85. <format type='raw'/>
  86. <source file='/var/testsuite-data/disk-image-template.raw'/>
  87. <backingStore/>
  88. </backingStore>
  89. </backingStore>
  90. <target dev='vdc' bus='virtio'/>
  91. <alias name='virtio-disk0'/>
  92. <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
  93. </disk>
  94. <disk type='network' device='cdrom'>
  95. <driver name='qemu' type='raw' cache='none' io='native'/>
  96. <source protocol='http' name='/pub/iso/myimage.iso' query='foo=bar&amp;baz=flurb' index='1'>
  97. <host name='dev-srv.tf.local' port='80'/>
  98. </source>
  99. <target dev='hda' bus='ide'/>
  100. <readonly/>
  101. <alias name='ide0-0-0'/>
  102. <address type='drive' controller='0' bus='0' target='0' unit='0'/>
  103. </disk>
  104. </devices>
  105. </domain>
  106. """
  107. domain_mock = make_mock_vm(vm_def)
  108. pool_mock = make_mock_storage_pool(
  109. "default", "dir", ["srv01_system", "srv01_data", "vm05_system"]
  110. )
  111. # Append backing store to srv01_data volume XML description
  112. srv1data_mock = pool_mock.storageVolLookupByName("srv01_data")
  113. append_to_XMLDesc(
  114. srv1data_mock,
  115. """
  116. <backingStore>
  117. <path>/var/lib/libvirt/images/vol01</path>
  118. <format type="qcow2"/>
  119. </backingStore>""",
  120. )
  121. assert virt.get_disks("srv01") == {
  122. "vda": {
  123. "type": "disk",
  124. "file": "default/srv01_system",
  125. "file format": "qcow2",
  126. "disk size": 12345,
  127. "virtual size": 1234567,
  128. },
  129. "vdb": {
  130. "type": "disk",
  131. "file": "default/srv01_data",
  132. "file format": "qcow2",
  133. "disk size": 12345,
  134. "virtual size": 1234567,
  135. "backing file": {
  136. "file": "/var/lib/libvirt/images/vol01",
  137. "file format": "qcow2",
  138. },
  139. },
  140. "vdc": {
  141. "type": "disk",
  142. "file": "default/vm05_system",
  143. "file format": "qcow2",
  144. "disk size": 12345,
  145. "virtual size": 1234567,
  146. "backing file": {
  147. "file": "/var/lib/libvirt/images/vm04_system.qcow2",
  148. "file format": "qcow2",
  149. "backing file": {
  150. "file": "/var/testsuite-data/disk-image-template.raw",
  151. "file format": "raw",
  152. },
  153. },
  154. },
  155. "hda": {
  156. "type": "cdrom",
  157. "file format": "raw",
  158. "file": "http://dev-srv.tf.local:80/pub/iso/myimage.iso?foo=bar&baz=flurb",
  159. },
  160. }
  161. def test_get_disk_convert_volumes(make_mock_vm, make_mock_storage_pool):
  162. vm_def = """<domain type='kvm' id='3'>
  163. <name>srv01</name>
  164. <devices>
  165. <disk type='file' device='disk'>
  166. <driver name='qemu' type='qcow2' cache='none' io='native'/>
  167. <source file='/path/to/default/srv01_system'/>
  168. <target dev='vda' bus='virtio'/>
  169. <alias name='virtio-disk0'/>
  170. <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
  171. </disk>
  172. <disk type='block' device='disk'>
  173. <driver name='qemu' type='raw'/>
  174. <source dev='/path/to/default/srv01_data'/>
  175. <target dev='vdb' bus='virtio'/>
  176. <alias name='virtio-disk1'/>
  177. <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
  178. </disk>
  179. <disk type='file' device='disk'>
  180. <driver name='qemu' type='qcow2' cache='none' io='native'/>
  181. <source file='/path/to/srv01_extra'/>
  182. <target dev='vdc' bus='virtio'/>
  183. <alias name='virtio-disk1'/>
  184. <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
  185. </disk>
  186. </devices>
  187. </domain>
  188. """
  189. domain_mock = make_mock_vm(vm_def)
  190. pool_mock = make_mock_storage_pool("default", "dir", ["srv01_system", "srv01_data"])
  191. subprocess_mock = MagicMock()
  192. popen_mock = MagicMock(spec=virt.subprocess.Popen)
  193. popen_mock.return_value.communicate.return_value = [
  194. """[
  195. {
  196. "virtual-size": 214748364800,
  197. "filename": "/path/to/srv01_extra",
  198. "cluster-size": 65536,
  199. "format": "qcow2",
  200. "actual-size": 340525056,
  201. "format-specific": {
  202. "type": "qcow2",
  203. "data": {
  204. "compat": "1.1",
  205. "lazy-refcounts": false,
  206. "refcount-bits": 16,
  207. "corrupt": false
  208. }
  209. },
  210. "dirty-flag": false
  211. }
  212. ]
  213. """
  214. ]
  215. subprocess_mock.Popen = popen_mock
  216. with patch.dict(virt.__dict__, {"subprocess": subprocess_mock}):
  217. assert {
  218. "vda": {
  219. "type": "disk",
  220. "file": "default/srv01_system",
  221. "file format": "qcow2",
  222. "disk size": 12345,
  223. "virtual size": 1234567,
  224. },
  225. "vdb": {
  226. "type": "disk",
  227. "file": "default/srv01_data",
  228. "file format": "raw",
  229. "disk size": 12345,
  230. "virtual size": 1234567,
  231. },
  232. "vdc": {
  233. "type": "disk",
  234. "file": "/path/to/srv01_extra",
  235. "file format": "qcow2",
  236. "cluster size": 65536,
  237. "disk size": 340525056,
  238. "virtual size": 214748364800,
  239. },
  240. } == virt.get_disks("srv01")
  241. def test_update_approx_mem(make_mock_vm):
  242. """
  243. test virt.update with memory parameter unchanged thought not exactly equals to the current value.
  244. This may happen since libvirt sometimes rounds the memory value.
  245. """
  246. xml_def = """
  247. <domain type="kvm">
  248. <name>my_vm</name>
  249. <memory unit='KiB'>3177680</memory>
  250. <currentMemory unit='KiB'>3177680</currentMemory>
  251. <vcpu placement='static'>1</vcpu>
  252. <os>
  253. <type arch='x86_64'>hvm</type>
  254. </os>
  255. <on_reboot>restart</on_reboot>
  256. </domain>
  257. """
  258. domain_mock = make_mock_vm(xml_def)
  259. ret = virt.update("my_vm", mem={"boot": "3253941043B", "current": "3253941043B"})
  260. assert not ret["definition"]
  261. def test_gen_hypervisor_features():
  262. """
  263. Test the virt._gen_xml hypervisor_features handling
  264. """
  265. xml_data = virt._gen_xml(
  266. virt.libvirt.openAuth.return_value,
  267. "hello",
  268. 1,
  269. 512,
  270. {},
  271. {},
  272. "kvm",
  273. "hvm",
  274. "x86_64",
  275. hypervisor_features={"kvm-hint-dedicated": True},
  276. )
  277. root = ET.fromstring(xml_data)
  278. assert "on" == root.find("features/kvm/hint-dedicated").attrib["state"]
  279. def test_update_hypervisor_features(make_mock_vm):
  280. """
  281. Test changing the hypervisor features of a guest
  282. """
  283. xml_def = """
  284. <domain type="kvm">
  285. <name>my_vm</name>
  286. <memory unit='KiB'>524288</memory>
  287. <currentMemory unit='KiB'>524288</currentMemory>
  288. <vcpu placement='static'>1</vcpu>
  289. <os>
  290. <type arch='x86_64'>linux</type>
  291. <kernel>/usr/lib/grub2/x86_64-xen/grub.xen</kernel>
  292. </os>
  293. <features>
  294. <kvm>
  295. <hint-dedicated state="on"/>
  296. </kvm>
  297. </features>
  298. <on_reboot>restart</on_reboot>
  299. </domain>
  300. """
  301. domain_mock = make_mock_vm(xml_def)
  302. # Update with no change to the features
  303. ret = virt.update("my_vm", hypervisor_features={"kvm-hint-dedicated": True})
  304. assert not ret["definition"]
  305. # Alter the features
  306. ret = virt.update("my_vm", hypervisor_features={"kvm-hint-dedicated": False})
  307. assert ret["definition"]
  308. setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0])
  309. assert "off" == setxml.find("features/kvm/hint-dedicated").get("state")
  310. # Add the features
  311. xml_def = """
  312. <domain type="kvm">
  313. <name>my_vm</name>
  314. <memory unit='KiB'>524288</memory>
  315. <currentMemory unit='KiB'>524288</currentMemory>
  316. <vcpu placement='static'>1</vcpu>
  317. <os>
  318. <type arch='x86_64'>linux</type>
  319. <kernel>/usr/lib/grub2/x86_64-xen/grub.xen</kernel>
  320. </os>
  321. </domain>
  322. """
  323. domain_mock = make_mock_vm(xml_def)
  324. ret = virt.update("my_vm", hypervisor_features={"kvm-hint-dedicated": True})
  325. assert ret["definition"]
  326. setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0])
  327. assert "on" == setxml.find("features/kvm/hint-dedicated").get("state")
  328. def test_gen_clock():
  329. """
  330. Test the virt._gen_xml clock property
  331. """
  332. # Localtime with adjustment
  333. xml_data = virt._gen_xml(
  334. virt.libvirt.openAuth.return_value,
  335. "hello",
  336. 1,
  337. 512,
  338. {},
  339. {},
  340. "kvm",
  341. "hvm",
  342. "x86_64",
  343. clock={"adjustment": 3600, "utc": False},
  344. )
  345. root = ET.fromstring(xml_data)
  346. assert "localtime" == root.find("clock").get("offset")
  347. assert "3600" == root.find("clock").get("adjustment")
  348. # Specific timezone
  349. xml_data = virt._gen_xml(
  350. virt.libvirt.openAuth.return_value,
  351. "hello",
  352. 1,
  353. 512,
  354. {},
  355. {},
  356. "kvm",
  357. "hvm",
  358. "x86_64",
  359. clock={"timezone": "CEST"},
  360. )
  361. root = ET.fromstring(xml_data)
  362. assert "timezone" == root.find("clock").get("offset")
  363. assert "CEST" == root.find("clock").get("timezone")
  364. # UTC
  365. xml_data = virt._gen_xml(
  366. virt.libvirt.openAuth.return_value,
  367. "hello",
  368. 1,
  369. 512,
  370. {},
  371. {},
  372. "kvm",
  373. "hvm",
  374. "x86_64",
  375. clock={"utc": True},
  376. )
  377. root = ET.fromstring(xml_data)
  378. assert "utc" == root.find("clock").get("offset")
  379. # Timers
  380. xml_data = virt._gen_xml(
  381. virt.libvirt.openAuth.return_value,
  382. "hello",
  383. 1,
  384. 512,
  385. {},
  386. {},
  387. "kvm",
  388. "hvm",
  389. "x86_64",
  390. clock={
  391. "timers": {
  392. "tsc": {"frequency": 3504000000, "mode": "native"},
  393. "rtc": {
  394. "tickpolicy": "catchup",
  395. "slew": 4636,
  396. "threshold": 123,
  397. "limit": 2342,
  398. },
  399. "hpet": {"present": False},
  400. },
  401. },
  402. )
  403. root = ET.fromstring(xml_data)
  404. assert "utc" == root.find("clock").get("offset")
  405. assert "3504000000" == root.find("clock/timer[@name='tsc']").get("frequency")
  406. assert "native" == root.find("clock/timer[@name='tsc']").get("mode")
  407. assert "catchup" == root.find("clock/timer[@name='rtc']").get("tickpolicy")
  408. assert {"slew": "4636", "threshold": "123", "limit": "2342"} == root.find(
  409. "clock/timer[@name='rtc']/catchup"
  410. ).attrib
  411. assert "no" == root.find("clock/timer[@name='hpet']").get("present")
  412. def test_update_clock(make_mock_vm):
  413. """
  414. test virt.update with clock parameter
  415. """
  416. xml_def = """
  417. <domain type="kvm">
  418. <name>my_vm</name>
  419. <memory unit='KiB'>524288</memory>
  420. <currentMemory unit='KiB'>524288</currentMemory>
  421. <vcpu placement='static'>1</vcpu>
  422. <os>
  423. <type arch='x86_64'>linux</type>
  424. <kernel>/usr/lib/grub2/x86_64-xen/grub.xen</kernel>
  425. </os>
  426. <clock offset="localtime" adjustment="-3600">
  427. <timer name="tsc" frequency="3504000000" mode="native" />
  428. <timer name="kvmclock" present="no" />
  429. </clock>
  430. <on_reboot>restart</on_reboot>
  431. </domain>
  432. """
  433. domain_mock = make_mock_vm(xml_def)
  434. # Update with no change to the features
  435. ret = virt.update(
  436. "my_vm",
  437. clock={
  438. "utc": False,
  439. "adjustment": -3600,
  440. "timers": {
  441. "tsc": {"frequency": 3504000000, "mode": "native"},
  442. "kvmclock": {"present": False},
  443. },
  444. },
  445. )
  446. assert not ret["definition"]
  447. # Update
  448. ret = virt.update(
  449. "my_vm",
  450. clock={
  451. "timezone": "CEST",
  452. "timers": {
  453. "rtc": {
  454. "track": "wall",
  455. "tickpolicy": "catchup",
  456. "slew": 4636,
  457. "threshold": 123,
  458. "limit": 2342,
  459. },
  460. "hpet": {"present": True},
  461. },
  462. },
  463. )
  464. assert ret["definition"]
  465. setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0])
  466. assert "timezone" == setxml.find("clock").get("offset")
  467. assert "CEST" == setxml.find("clock").get("timezone")
  468. assert {"rtc", "hpet"} == {t.get("name") for t in setxml.findall("clock/timer")}
  469. assert "catchup" == setxml.find("clock/timer[@name='rtc']").get("tickpolicy")
  470. assert "wall" == setxml.find("clock/timer[@name='rtc']").get("track")
  471. assert {"slew": "4636", "threshold": "123", "limit": "2342"} == setxml.find(
  472. "clock/timer[@name='rtc']/catchup"
  473. ).attrib
  474. assert "yes" == setxml.find("clock/timer[@name='hpet']").get("present")
  475. # Revert to UTC
  476. ret = virt.update("my_vm", clock={"utc": True, "adjustment": None, "timers": None})
  477. assert ret["definition"]
  478. setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0])
  479. assert {"offset": "utc"} == setxml.find("clock").attrib
  480. assert setxml.find("clock/timer") is None
  481. def test_update_stop_on_reboot_reset(make_mock_vm):
  482. """
  483. Test virt.update to remove the on_reboot=destroy flag
  484. """
  485. xml_def = """
  486. <domain type='kvm'>
  487. <name>my_vm</name>
  488. <memory unit='KiB'>524288</memory>
  489. <currentMemory unit='KiB'>524288</currentMemory>
  490. <vcpu placement='static'>1</vcpu>
  491. <on_reboot>destroy</on_reboot>
  492. <os>
  493. <type arch='x86_64'>hvm</type>
  494. </os>
  495. </domain>"""
  496. domain_mock = make_mock_vm(xml_def)
  497. ret = virt.update("my_vm")
  498. assert ret["definition"]
  499. define_mock = virt.libvirt.openAuth().defineXML
  500. setxml = ET.fromstring(define_mock.call_args[0][0])
  501. assert "restart" == setxml.find("./on_reboot").text
  502. def test_update_stop_on_reboot(make_mock_vm):
  503. """
  504. Test virt.update to add the on_reboot=destroy flag
  505. """
  506. xml_def = """
  507. <domain type='kvm'>
  508. <name>my_vm</name>
  509. <memory unit='KiB'>524288</memory>
  510. <currentMemory unit='KiB'>524288</currentMemory>
  511. <vcpu placement='static'>1</vcpu>
  512. <os>
  513. <type arch='x86_64'>hvm</type>
  514. </os>
  515. </domain>"""
  516. domain_mock = make_mock_vm(xml_def)
  517. ret = virt.update("my_vm", stop_on_reboot=True)
  518. assert ret["definition"]
  519. define_mock = virt.libvirt.openAuth().defineXML
  520. setxml = ET.fromstring(define_mock.call_args[0][0])
  521. assert "destroy" == setxml.find("./on_reboot").text
  522. def test_init_no_stop_on_reboot(make_capabilities):
  523. """
  524. Test virt.init to add the on_reboot=restart flag
  525. """
  526. make_capabilities()
  527. with patch.dict(virt.os.__dict__, {"chmod": MagicMock(), "makedirs": MagicMock()}):
  528. with patch.dict(virt.__salt__, {"cmd.run": MagicMock()}):
  529. virt.init("test_vm", 2, 2048, start=False)
  530. define_mock = virt.libvirt.openAuth().defineXML
  531. setxml = ET.fromstring(define_mock.call_args[0][0])
  532. assert "restart" == setxml.find("./on_reboot").text
  533. def test_init_stop_on_reboot(make_capabilities):
  534. """
  535. Test virt.init to add the on_reboot=destroy flag
  536. """
  537. make_capabilities()
  538. with patch.dict(virt.os.__dict__, {"chmod": MagicMock(), "makedirs": MagicMock()}):
  539. with patch.dict(virt.__salt__, {"cmd.run": MagicMock()}):
  540. virt.init("test_vm", 2, 2048, stop_on_reboot=True, start=False)
  541. define_mock = virt.libvirt.openAuth().defineXML
  542. setxml = ET.fromstring(define_mock.call_args[0][0])
  543. assert "destroy" == setxml.find("./on_reboot").text