test_virt.py 99 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664
  1. # -*- coding: utf-8 -*-
  2. '''
  3. virt execution module unit tests
  4. '''
  5. # pylint: disable=3rd-party-module-not-gated
  6. # Import python libs
  7. from __future__ import absolute_import, print_function, unicode_literals
  8. import os
  9. import re
  10. import datetime
  11. # Import Salt Testing libs
  12. from tests.support.mixins import LoaderModuleMockMixin
  13. from tests.support.unit import TestCase
  14. from tests.support.mock import MagicMock, patch
  15. # Import salt libs
  16. import salt.utils.yaml
  17. import salt.modules.virt as virt
  18. import salt.modules.config as config
  19. from salt._compat import ElementTree as ET
  20. import salt.config
  21. import salt.syspaths
  22. from salt.exceptions import CommandExecutionError
  23. # Import third party libs
  24. from salt.ext import six
  25. # pylint: disable=import-error
  26. from salt.ext.six.moves import range # pylint: disable=redefined-builtin
  27. # pylint: disable=invalid-name,protected-access,attribute-defined-outside-init,too-many-public-methods,unused-argument
  28. class LibvirtMock(MagicMock): # pylint: disable=too-many-ancestors
  29. '''
  30. Libvirt library mock
  31. '''
  32. class virDomain(MagicMock):
  33. '''
  34. virDomain mock
  35. '''
  36. class libvirtError(Exception):
  37. '''
  38. libvirtError mock
  39. '''
  40. class VirtTestCase(TestCase, LoaderModuleMockMixin):
  41. '''
  42. Test cases for salt.module.virt
  43. '''
  44. def setup_loader_modules(self):
  45. self.mock_libvirt = LibvirtMock()
  46. self.mock_conn = MagicMock()
  47. self.mock_libvirt.openAuth.return_value = self.mock_conn
  48. self.mock_popen = MagicMock()
  49. self.addCleanup(delattr, self, 'mock_libvirt')
  50. self.addCleanup(delattr, self, 'mock_conn')
  51. self.addCleanup(delattr, self, 'mock_popen')
  52. mock_subprocess = MagicMock()
  53. mock_subprocess.Popen.return_value = self.mock_popen # pylint: disable=no-member
  54. loader_globals = {
  55. '__salt__': {
  56. 'config.get': config.get,
  57. 'config.option': config.option,
  58. },
  59. 'libvirt': self.mock_libvirt,
  60. 'subprocess': mock_subprocess
  61. }
  62. return {virt: loader_globals, config: loader_globals}
  63. def set_mock_vm(self, name, xml):
  64. '''
  65. Define VM to use in tests
  66. '''
  67. self.mock_conn.listDefinedDomains.return_value = [name] # pylint: disable=no-member
  68. mock_domain = self.mock_libvirt.virDomain()
  69. self.mock_conn.lookupByName.return_value = mock_domain # pylint: disable=no-member
  70. mock_domain.XMLDesc.return_value = xml # pylint: disable=no-member
  71. # Return state as shutdown
  72. mock_domain.info.return_value = [4, 0, 0, 0] # pylint: disable=no-member
  73. return mock_domain
  74. def test_disk_profile_merge(self):
  75. '''
  76. Test virt._disk_profile() when merging with user-defined disks
  77. '''
  78. root_dir = os.path.join(salt.syspaths.ROOT_DIR, 'srv', 'salt-images')
  79. userdisks = [{'name': 'data', 'size': 16384, 'format': 'raw'}]
  80. disks = virt._disk_profile('default', 'kvm', userdisks, 'myvm', image='/path/to/image')
  81. self.assertEqual(
  82. [{'name': 'system',
  83. 'device': 'disk',
  84. 'size': 8192,
  85. 'format': 'qcow2',
  86. 'model': 'virtio',
  87. 'filename': 'myvm_system.qcow2',
  88. 'image': '/path/to/image',
  89. 'source_file': '{0}{1}myvm_system.qcow2'.format(root_dir, os.sep)},
  90. {'name': 'data',
  91. 'device': 'disk',
  92. 'size': 16384,
  93. 'format': 'raw',
  94. 'model': 'virtio',
  95. 'filename': 'myvm_data.raw',
  96. 'source_file': '{0}{1}myvm_data.raw'.format(root_dir, os.sep)}],
  97. disks
  98. )
  99. def test_boot_default_dev(self):
  100. '''
  101. Test virt._gen_xml() default boot device
  102. '''
  103. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  104. nicp = virt._nic_profile('default', 'kvm')
  105. xml_data = virt._gen_xml(
  106. 'hello',
  107. 1,
  108. 512,
  109. diskp,
  110. nicp,
  111. 'kvm',
  112. 'hvm',
  113. 'x86_64'
  114. )
  115. root = ET.fromstring(xml_data)
  116. self.assertEqual(root.find('os/boot').attrib['dev'], 'hd')
  117. self.assertEqual(root.find('os/type').attrib['arch'], 'x86_64')
  118. self.assertEqual(root.find('os/type').text, 'hvm')
  119. def test_boot_custom_dev(self):
  120. '''
  121. Test virt._gen_xml() custom boot device
  122. '''
  123. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  124. nicp = virt._nic_profile('default', 'kvm')
  125. xml_data = virt._gen_xml(
  126. 'hello',
  127. 1,
  128. 512,
  129. diskp,
  130. nicp,
  131. 'kvm',
  132. 'hvm',
  133. 'x86_64',
  134. boot_dev='cdrom'
  135. )
  136. root = ET.fromstring(xml_data)
  137. self.assertEqual(root.find('os/boot').attrib['dev'], 'cdrom')
  138. def test_boot_multiple_devs(self):
  139. '''
  140. Test virt._gen_xml() multiple boot devices
  141. '''
  142. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  143. nicp = virt._nic_profile('default', 'kvm')
  144. xml_data = virt._gen_xml(
  145. 'hello',
  146. 1,
  147. 512,
  148. diskp,
  149. nicp,
  150. 'kvm',
  151. 'hvm',
  152. 'x86_64',
  153. boot_dev='cdrom network'
  154. )
  155. root = ET.fromstring(xml_data)
  156. devs = root.findall('.//boot')
  157. self.assertTrue(len(devs) == 2)
  158. def test_gen_xml_no_nic(self):
  159. '''
  160. Test virt._gen_xml() serial console
  161. '''
  162. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  163. nicp = virt._nic_profile('default', 'kvm')
  164. xml_data = virt._gen_xml(
  165. 'hello',
  166. 1,
  167. 512,
  168. diskp,
  169. nicp,
  170. 'kvm',
  171. 'hvm',
  172. 'x86_64',
  173. serial_type='pty',
  174. console=True
  175. )
  176. root = ET.fromstring(xml_data)
  177. self.assertEqual(root.find('devices/serial').attrib['type'], 'pty')
  178. self.assertEqual(root.find('devices/console').attrib['type'], 'pty')
  179. def test_gen_xml_for_serial_console(self):
  180. '''
  181. Test virt._gen_xml() serial console
  182. '''
  183. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  184. nicp = virt._nic_profile('default', 'kvm')
  185. xml_data = virt._gen_xml(
  186. 'hello',
  187. 1,
  188. 512,
  189. diskp,
  190. nicp,
  191. 'kvm',
  192. 'hvm',
  193. 'x86_64',
  194. serial_type='pty',
  195. console=True
  196. )
  197. root = ET.fromstring(xml_data)
  198. self.assertEqual(root.find('devices/serial').attrib['type'], 'pty')
  199. self.assertEqual(root.find('devices/console').attrib['type'], 'pty')
  200. def test_gen_xml_for_telnet_console(self):
  201. '''
  202. Test virt._gen_xml() telnet console
  203. '''
  204. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  205. nicp = virt._nic_profile('default', 'kvm')
  206. xml_data = virt._gen_xml(
  207. 'hello',
  208. 1,
  209. 512,
  210. diskp,
  211. nicp,
  212. 'kvm',
  213. 'hvm',
  214. 'x86_64',
  215. serial_type='tcp',
  216. console=True,
  217. telnet_port=22223
  218. )
  219. root = ET.fromstring(xml_data)
  220. self.assertEqual(root.find('devices/serial').attrib['type'], 'tcp')
  221. self.assertEqual(root.find('devices/console').attrib['type'], 'tcp')
  222. self.assertEqual(root.find('devices/console/source').attrib['service'], '22223')
  223. def test_gen_xml_for_telnet_console_unspecified_port(self):
  224. '''
  225. Test virt._gen_xml() telnet console without any specified port
  226. '''
  227. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  228. nicp = virt._nic_profile('default', 'kvm')
  229. xml_data = virt._gen_xml(
  230. 'hello',
  231. 1,
  232. 512,
  233. diskp,
  234. nicp,
  235. 'kvm',
  236. 'hvm',
  237. 'x86_64',
  238. serial_type='tcp',
  239. console=True
  240. )
  241. root = ET.fromstring(xml_data)
  242. self.assertEqual(root.find('devices/serial').attrib['type'], 'tcp')
  243. self.assertEqual(root.find('devices/console').attrib['type'], 'tcp')
  244. self.assertIsInstance(int(root.find('devices/console/source').attrib['service']), int)
  245. def test_gen_xml_for_serial_no_console(self):
  246. '''
  247. Test virt._gen_xml() with no serial console
  248. '''
  249. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  250. nicp = virt._nic_profile('default', 'kvm')
  251. xml_data = virt._gen_xml(
  252. 'hello',
  253. 1,
  254. 512,
  255. diskp,
  256. nicp,
  257. 'kvm',
  258. 'hvm',
  259. 'x86_64',
  260. serial_type='pty',
  261. console=False
  262. )
  263. root = ET.fromstring(xml_data)
  264. self.assertEqual(root.find('devices/serial').attrib['type'], 'pty')
  265. self.assertEqual(root.find('devices/console'), None)
  266. def test_gen_xml_for_telnet_no_console(self):
  267. '''
  268. Test virt._gen_xml() with no telnet console
  269. '''
  270. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  271. nicp = virt._nic_profile('default', 'kvm')
  272. xml_data = virt._gen_xml(
  273. 'hello',
  274. 1,
  275. 512,
  276. diskp,
  277. nicp,
  278. 'kvm',
  279. 'hvm',
  280. 'x86_64',
  281. serial_type='tcp',
  282. console=False,
  283. )
  284. root = ET.fromstring(xml_data)
  285. self.assertEqual(root.find('devices/serial').attrib['type'], 'tcp')
  286. self.assertEqual(root.find('devices/console'), None)
  287. def test_gen_xml_nographics_default(self):
  288. '''
  289. Test virt._gen_xml() with default no graphics device
  290. '''
  291. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  292. nicp = virt._nic_profile('default', 'kvm')
  293. xml_data = virt._gen_xml(
  294. 'hello',
  295. 1,
  296. 512,
  297. diskp,
  298. nicp,
  299. 'kvm',
  300. 'hvm',
  301. 'x86_64'
  302. )
  303. root = ET.fromstring(xml_data)
  304. self.assertIsNone(root.find('devices/graphics'))
  305. def test_gen_xml_vnc_default(self):
  306. '''
  307. Test virt._gen_xml() with default vnc graphics device
  308. '''
  309. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  310. nicp = virt._nic_profile('default', 'kvm')
  311. xml_data = virt._gen_xml(
  312. 'hello',
  313. 1,
  314. 512,
  315. diskp,
  316. nicp,
  317. 'kvm',
  318. 'hvm',
  319. 'x86_64',
  320. graphics={'type': 'vnc', 'port': 1234, 'tlsPort': 5678,
  321. 'listen': {'type': 'address', 'address': 'myhost'}},
  322. )
  323. root = ET.fromstring(xml_data)
  324. self.assertEqual(root.find('devices/graphics').attrib['type'], 'vnc')
  325. self.assertEqual(root.find('devices/graphics').attrib['autoport'], 'no')
  326. self.assertEqual(root.find('devices/graphics').attrib['port'], '1234')
  327. self.assertFalse('tlsPort' in root.find('devices/graphics').attrib)
  328. self.assertEqual(root.find('devices/graphics').attrib['listen'], 'myhost')
  329. self.assertEqual(root.find('devices/graphics/listen').attrib['type'], 'address')
  330. self.assertEqual(root.find('devices/graphics/listen').attrib['address'], 'myhost')
  331. def test_gen_xml_spice_default(self):
  332. '''
  333. Test virt._gen_xml() with default spice graphics device
  334. '''
  335. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  336. nicp = virt._nic_profile('default', 'kvm')
  337. xml_data = virt._gen_xml(
  338. 'hello',
  339. 1,
  340. 512,
  341. diskp,
  342. nicp,
  343. 'kvm',
  344. 'hvm',
  345. 'x86_64',
  346. graphics={'type': 'spice'},
  347. )
  348. root = ET.fromstring(xml_data)
  349. self.assertEqual(root.find('devices/graphics').attrib['type'], 'spice')
  350. self.assertEqual(root.find('devices/graphics').attrib['autoport'], 'yes')
  351. self.assertEqual(root.find('devices/graphics').attrib['listen'], '0.0.0.0')
  352. self.assertEqual(root.find('devices/graphics/listen').attrib['type'], 'address')
  353. self.assertEqual(root.find('devices/graphics/listen').attrib['address'], '0.0.0.0')
  354. def test_gen_xml_spice(self):
  355. '''
  356. Test virt._gen_xml() with spice graphics device
  357. '''
  358. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  359. nicp = virt._nic_profile('default', 'kvm')
  360. xml_data = virt._gen_xml(
  361. 'hello',
  362. 1,
  363. 512,
  364. diskp,
  365. nicp,
  366. 'kvm',
  367. 'hvm',
  368. 'x86_64',
  369. graphics={'type': 'spice', 'port': 1234, 'tls_port': 5678, 'listen': {'type': 'none'}},
  370. )
  371. root = ET.fromstring(xml_data)
  372. self.assertEqual(root.find('devices/graphics').attrib['type'], 'spice')
  373. self.assertEqual(root.find('devices/graphics').attrib['autoport'], 'no')
  374. self.assertEqual(root.find('devices/graphics').attrib['port'], '1234')
  375. self.assertEqual(root.find('devices/graphics').attrib['tlsPort'], '5678')
  376. self.assertFalse('listen' in root.find('devices/graphics').attrib)
  377. self.assertEqual(root.find('devices/graphics/listen').attrib['type'], 'none')
  378. self.assertFalse('address' in root.find('devices/graphics/listen').attrib)
  379. def test_default_disk_profile_hypervisor_esxi(self):
  380. '''
  381. Test virt._disk_profile() default ESXi profile
  382. '''
  383. mock = MagicMock(return_value={})
  384. with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member
  385. ret = virt._disk_profile('nonexistent', 'vmware')
  386. self.assertTrue(len(ret) == 1)
  387. found = [disk for disk in ret if disk['name'] == 'system']
  388. self.assertTrue(bool(found))
  389. system = found[0]
  390. self.assertEqual(system['format'], 'vmdk')
  391. self.assertEqual(system['model'], 'scsi')
  392. self.assertTrue(int(system['size']) >= 1)
  393. def test_default_disk_profile_hypervisor_kvm(self):
  394. '''
  395. Test virt._disk_profile() default KVM profile
  396. '''
  397. mock = MagicMock(return_value={})
  398. with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member
  399. ret = virt._disk_profile('nonexistent', 'kvm')
  400. self.assertTrue(len(ret) == 1)
  401. found = [disk for disk in ret if disk['name'] == 'system']
  402. self.assertTrue(bool(found))
  403. system = found[0]
  404. self.assertEqual(system['format'], 'qcow2')
  405. self.assertEqual(system['model'], 'virtio')
  406. self.assertTrue(int(system['size']) >= 1)
  407. def test_default_disk_profile_hypervisor_xen(self):
  408. '''
  409. Test virt._disk_profile() default XEN profile
  410. '''
  411. mock = MagicMock(return_value={})
  412. with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member
  413. ret = virt._disk_profile('nonexistent', 'xen')
  414. self.assertTrue(len(ret) == 1)
  415. found = [disk for disk in ret if disk['name'] == 'system']
  416. self.assertTrue(bool(found))
  417. system = found[0]
  418. self.assertEqual(system['format'], 'qcow2')
  419. self.assertEqual(system['model'], 'xen')
  420. self.assertTrue(int(system['size']) >= 1)
  421. def test_default_nic_profile_hypervisor_esxi(self):
  422. '''
  423. Test virt._nic_profile() default ESXi profile
  424. '''
  425. mock = MagicMock(return_value={})
  426. with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member
  427. ret = virt._nic_profile('nonexistent', 'vmware')
  428. self.assertTrue(len(ret) == 1)
  429. eth0 = ret[0]
  430. self.assertEqual(eth0['name'], 'eth0')
  431. self.assertEqual(eth0['type'], 'bridge')
  432. self.assertEqual(eth0['source'], 'DEFAULT')
  433. self.assertEqual(eth0['model'], 'e1000')
  434. def test_default_nic_profile_hypervisor_kvm(self):
  435. '''
  436. Test virt._nic_profile() default KVM profile
  437. '''
  438. mock = MagicMock(return_value={})
  439. with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member
  440. ret = virt._nic_profile('nonexistent', 'kvm')
  441. self.assertTrue(len(ret) == 1)
  442. eth0 = ret[0]
  443. self.assertEqual(eth0['name'], 'eth0')
  444. self.assertEqual(eth0['type'], 'bridge')
  445. self.assertEqual(eth0['source'], 'br0')
  446. self.assertEqual(eth0['model'], 'virtio')
  447. def test_default_nic_profile_hypervisor_xen(self):
  448. '''
  449. Test virt._nic_profile() default XEN profile
  450. '''
  451. mock = MagicMock(return_value={})
  452. with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member
  453. ret = virt._nic_profile('nonexistent', 'xen')
  454. self.assertTrue(len(ret) == 1)
  455. eth0 = ret[0]
  456. self.assertEqual(eth0['name'], 'eth0')
  457. self.assertEqual(eth0['type'], 'bridge')
  458. self.assertEqual(eth0['source'], 'br0')
  459. self.assertFalse(eth0['model'])
  460. def test_gen_vol_xml(self):
  461. '''
  462. Test virt._get_vol_xml()
  463. '''
  464. xml_data = virt._gen_vol_xml('vmname', 'system', 'qcow2', 8192, '/path/to/image/')
  465. root = ET.fromstring(xml_data)
  466. self.assertEqual(root.find('name').text, 'vmname/system.qcow2')
  467. self.assertEqual(root.find('key').text, 'vmname/system')
  468. self.assertEqual(root.find('capacity').attrib['unit'], 'KiB')
  469. self.assertEqual(root.find('capacity').text, six.text_type(8192 * 1024))
  470. def test_gen_xml_for_kvm_default_profile(self):
  471. '''
  472. Test virt._gen_xml(), KVM default profile case
  473. '''
  474. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  475. nicp = virt._nic_profile('default', 'kvm')
  476. xml_data = virt._gen_xml(
  477. 'hello',
  478. 1,
  479. 512,
  480. diskp,
  481. nicp,
  482. 'kvm',
  483. 'hvm',
  484. 'x86_64',
  485. )
  486. root = ET.fromstring(xml_data)
  487. self.assertEqual(root.attrib['type'], 'kvm')
  488. self.assertEqual(root.find('vcpu').text, '1')
  489. self.assertEqual(root.find('memory').text, six.text_type(512 * 1024))
  490. self.assertEqual(root.find('memory').attrib['unit'], 'KiB')
  491. disks = root.findall('.//disk')
  492. self.assertEqual(len(disks), 1)
  493. disk = disks[0]
  494. root_dir = salt.config.DEFAULT_MINION_OPTS.get('root_dir')
  495. self.assertTrue(disk.find('source').attrib['file'].startswith(root_dir))
  496. self.assertTrue('hello_system' in disk.find('source').attrib['file'])
  497. self.assertEqual(disk.find('target').attrib['dev'], 'vda')
  498. self.assertEqual(disk.find('target').attrib['bus'], 'virtio')
  499. self.assertEqual(disk.find('driver').attrib['name'], 'qemu')
  500. self.assertEqual(disk.find('driver').attrib['type'], 'qcow2')
  501. interfaces = root.findall('.//interface')
  502. self.assertEqual(len(interfaces), 1)
  503. iface = interfaces[0]
  504. self.assertEqual(iface.attrib['type'], 'bridge')
  505. self.assertEqual(iface.find('source').attrib['bridge'], 'br0')
  506. self.assertEqual(iface.find('model').attrib['type'], 'virtio')
  507. mac = iface.find('mac').attrib['address']
  508. self.assertTrue(
  509. re.match('^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$', mac, re.I))
  510. def test_gen_xml_for_esxi_default_profile(self):
  511. '''
  512. Test virt._gen_xml(), ESXi/vmware default profile case
  513. '''
  514. diskp = virt._disk_profile('default', 'vmware', [], 'hello')
  515. nicp = virt._nic_profile('default', 'vmware')
  516. xml_data = virt._gen_xml(
  517. 'hello',
  518. 1,
  519. 512,
  520. diskp,
  521. nicp,
  522. 'vmware',
  523. 'hvm',
  524. 'x86_64',
  525. )
  526. root = ET.fromstring(xml_data)
  527. self.assertEqual(root.attrib['type'], 'vmware')
  528. self.assertEqual(root.find('vcpu').text, '1')
  529. self.assertEqual(root.find('memory').text, six.text_type(512 * 1024))
  530. self.assertEqual(root.find('memory').attrib['unit'], 'KiB')
  531. disks = root.findall('.//disk')
  532. self.assertEqual(len(disks), 1)
  533. disk = disks[0]
  534. self.assertTrue('[0]' in disk.find('source').attrib['file'])
  535. self.assertTrue('hello_system' in disk.find('source').attrib['file'])
  536. self.assertEqual(disk.find('target').attrib['dev'], 'sda')
  537. self.assertEqual(disk.find('target').attrib['bus'], 'scsi')
  538. self.assertEqual(disk.find('address').attrib['unit'], '0')
  539. interfaces = root.findall('.//interface')
  540. self.assertEqual(len(interfaces), 1)
  541. iface = interfaces[0]
  542. self.assertEqual(iface.attrib['type'], 'bridge')
  543. self.assertEqual(iface.find('source').attrib['bridge'], 'DEFAULT')
  544. self.assertEqual(iface.find('model').attrib['type'], 'e1000')
  545. mac = iface.find('mac').attrib['address']
  546. self.assertTrue(
  547. re.match('^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$', mac, re.I))
  548. def test_gen_xml_for_xen_default_profile(self):
  549. '''
  550. Test virt._gen_xml(), XEN PV default profile case
  551. '''
  552. diskp = virt._disk_profile('default', 'xen', [], 'hello')
  553. nicp = virt._nic_profile('default', 'xen')
  554. with patch.dict(virt.__grains__, {'os_family': 'Suse'}):
  555. xml_data = virt._gen_xml(
  556. 'hello',
  557. 1,
  558. 512,
  559. diskp,
  560. nicp,
  561. 'xen',
  562. 'xen',
  563. 'x86_64',
  564. )
  565. root = ET.fromstring(xml_data)
  566. self.assertEqual(root.attrib['type'], 'xen')
  567. self.assertEqual(root.find('vcpu').text, '1')
  568. self.assertEqual(root.find('memory').text, six.text_type(512 * 1024))
  569. self.assertEqual(root.find('memory').attrib['unit'], 'KiB')
  570. self.assertEqual(root.find('.//kernel').text, '/usr/lib/grub2/x86_64-xen/grub.xen')
  571. disks = root.findall('.//disk')
  572. self.assertEqual(len(disks), 1)
  573. disk = disks[0]
  574. root_dir = salt.config.DEFAULT_MINION_OPTS.get('root_dir')
  575. self.assertTrue(disk.find('source').attrib['file'].startswith(root_dir))
  576. self.assertTrue('hello_system' in disk.find('source').attrib['file'])
  577. self.assertEqual(disk.find('target').attrib['dev'], 'xvda')
  578. self.assertEqual(disk.find('target').attrib['bus'], 'xen')
  579. self.assertEqual(disk.find('driver').attrib['name'], 'qemu')
  580. self.assertEqual(disk.find('driver').attrib['type'], 'qcow2')
  581. interfaces = root.findall('.//interface')
  582. self.assertEqual(len(interfaces), 1)
  583. iface = interfaces[0]
  584. self.assertEqual(iface.attrib['type'], 'bridge')
  585. self.assertEqual(iface.find('source').attrib['bridge'], 'br0')
  586. self.assertIsNone(iface.find('model'))
  587. def test_gen_xml_for_esxi_custom_profile(self):
  588. '''
  589. Test virt._gen_xml(), ESXi/vmware custom profile case
  590. '''
  591. disks = {
  592. 'noeffect': [
  593. {'first': {'size': 8192, 'pool': 'datastore1'}},
  594. {'second': {'size': 4096, 'pool': 'datastore2'}}
  595. ]
  596. }
  597. nics = {
  598. 'noeffect': [
  599. {'name': 'eth1', 'source': 'ONENET'},
  600. {'name': 'eth2', 'source': 'TWONET'}
  601. ]
  602. }
  603. with patch.dict(virt.__salt__, # pylint: disable=no-member
  604. {'config.get': MagicMock(side_effect=[disks, nics])}):
  605. diskp = virt._disk_profile('noeffect', 'vmware', [], 'hello')
  606. nicp = virt._nic_profile('noeffect', 'vmware')
  607. xml_data = virt._gen_xml(
  608. 'hello',
  609. 1,
  610. 512,
  611. diskp,
  612. nicp,
  613. 'vmware',
  614. 'hvm',
  615. 'x86_64',
  616. )
  617. root = ET.fromstring(xml_data)
  618. self.assertEqual(root.attrib['type'], 'vmware')
  619. self.assertEqual(root.find('vcpu').text, '1')
  620. self.assertEqual(root.find('memory').text, six.text_type(512 * 1024))
  621. self.assertEqual(root.find('memory').attrib['unit'], 'KiB')
  622. self.assertTrue(len(root.findall('.//disk')) == 2)
  623. self.assertTrue(len(root.findall('.//interface')) == 2)
  624. def test_gen_xml_for_kvm_custom_profile(self):
  625. '''
  626. Test virt._gen_xml(), KVM custom profile case
  627. '''
  628. disks = {
  629. 'noeffect': [
  630. {'first': {'size': 8192, 'pool': '/var/lib/images'}},
  631. {'second': {'size': 4096, 'pool': '/var/lib/images'}}
  632. ]
  633. }
  634. nics = {
  635. 'noeffect': [
  636. {'name': 'eth1', 'source': 'b2'},
  637. {'name': 'eth2', 'source': 'b2'}
  638. ]
  639. }
  640. with patch.dict(virt.__salt__, {'config.get': MagicMock(side_effect=[ # pylint: disable=no-member
  641. disks, nics])}):
  642. diskp = virt._disk_profile('noeffect', 'kvm', [], 'hello')
  643. nicp = virt._nic_profile('noeffect', 'kvm')
  644. xml_data = virt._gen_xml(
  645. 'hello',
  646. 1,
  647. 512,
  648. diskp,
  649. nicp,
  650. 'kvm',
  651. 'hvm',
  652. 'x86_64',
  653. )
  654. root = ET.fromstring(xml_data)
  655. self.assertEqual(root.attrib['type'], 'kvm')
  656. self.assertEqual(root.find('vcpu').text, '1')
  657. self.assertEqual(root.find('memory').text, six.text_type(512 * 1024))
  658. self.assertEqual(root.find('memory').attrib['unit'], 'KiB')
  659. self.assertTrue(len(root.findall('.//disk')) == 2)
  660. self.assertTrue(len(root.findall('.//interface')) == 2)
  661. @patch('salt.modules.virt.pool_info',
  662. return_value={'mypool': {'target_path': os.path.join(salt.syspaths.ROOT_DIR, 'pools', 'mypool')}})
  663. def test_disk_profile_kvm_disk_pool(self, mock_poolinfo):
  664. '''
  665. Test virt._gen_xml(), KVM case with pools defined.
  666. '''
  667. disks = {
  668. 'noeffect': [
  669. {'first': {'size': 8192, 'pool': 'mypool'}},
  670. {'second': {'size': 4096}}
  671. ]
  672. }
  673. # pylint: disable=no-member
  674. with patch.dict(virt.__salt__, {'config.get': MagicMock(side_effect=[
  675. disks,
  676. os.path.join(salt.syspaths.ROOT_DIR, 'default', 'path')])}):
  677. diskp = virt._disk_profile('noeffect', 'kvm', [], 'hello')
  678. pools_path = os.path.join(salt.syspaths.ROOT_DIR, 'pools', 'mypool') + os.sep
  679. default_path = os.path.join(salt.syspaths.ROOT_DIR, 'default', 'path') + os.sep
  680. self.assertEqual(len(diskp), 2)
  681. self.assertTrue(diskp[0]['source_file'].startswith(pools_path))
  682. self.assertTrue(diskp[1]['source_file'].startswith(default_path))
  683. # pylint: enable=no-member
  684. def test_disk_profile_kvm_disk_external_image(self):
  685. '''
  686. Test virt._gen_xml(), KVM case with an external image.
  687. '''
  688. diskp = virt._disk_profile(None, 'kvm', [
  689. {
  690. 'name': 'mydisk',
  691. 'source_file': '/path/to/my/image.qcow2'
  692. }], 'hello')
  693. self.assertEqual(len(diskp), 1)
  694. self.assertEqual(diskp[0]['source_file'], ('/path/to/my/image.qcow2'))
  695. @patch('salt.modules.virt.pool_info', return_value={})
  696. def test_disk_profile_kvm_disk_pool_notfound(self, mock_poolinfo):
  697. '''
  698. Test virt._gen_xml(), KVM case with pools defined.
  699. '''
  700. disks = {
  701. 'noeffect': [
  702. {'first': {'size': 8192, 'pool': 'default'}},
  703. ]
  704. }
  705. with patch.dict(virt.__salt__, {'config.get': MagicMock(side_effect=[ # pylint: disable=no-member
  706. disks, "/default/path/"])}):
  707. with self.assertRaises(CommandExecutionError):
  708. virt._disk_profile('noeffect', 'kvm', [], 'hello')
  709. @patch('salt.modules.virt.pool_info', return_value={'target_path': '/dev/disk/by-path'})
  710. def test_disk_profile_kvm_disk_pool_invalid(self, mock_poolinfo):
  711. '''
  712. Test virt._gen_xml(), KVM case with pools defined.
  713. '''
  714. disks = {
  715. 'noeffect': [
  716. {'first': {'size': 8192, 'pool': 'default'}},
  717. ]
  718. }
  719. with patch.dict(virt.__salt__, {'config.get': MagicMock(side_effect=[ # pylint: disable=no-member
  720. disks, "/default/path/"])}):
  721. with self.assertRaises(CommandExecutionError):
  722. virt._disk_profile('noeffect', 'kvm', [], 'hello')
  723. def test_gen_xml_cdrom(self):
  724. '''
  725. Test virt._gen_xml(), generating a cdrom device (different disk type, no source)
  726. '''
  727. diskp = virt._disk_profile(None, 'kvm', [{
  728. 'name': 'tested',
  729. 'device': 'cdrom',
  730. 'source_file': None,
  731. 'model': 'ide'}], 'hello')
  732. nicp = virt._nic_profile(None, 'kvm')
  733. xml_data = virt._gen_xml(
  734. 'hello',
  735. 1,
  736. 512,
  737. diskp,
  738. nicp,
  739. 'kvm',
  740. 'hvm',
  741. 'x86_64',
  742. )
  743. root = ET.fromstring(xml_data)
  744. disk = root.findall('.//disk')[0]
  745. self.assertEqual(disk.attrib['device'], 'cdrom')
  746. self.assertIsNone(disk.find('source'))
  747. def test_controller_for_esxi(self):
  748. '''
  749. Test virt._gen_xml() generated device controller for ESXi/vmware
  750. '''
  751. diskp = virt._disk_profile('default', 'vmware', [], 'hello')
  752. nicp = virt._nic_profile('default', 'vmware')
  753. xml_data = virt._gen_xml(
  754. 'hello',
  755. 1,
  756. 512,
  757. diskp,
  758. nicp,
  759. 'vmware',
  760. 'hvm',
  761. 'x86_64',
  762. )
  763. root = ET.fromstring(xml_data)
  764. controllers = root.findall('.//devices/controller')
  765. self.assertTrue(len(controllers) == 1)
  766. controller = controllers[0]
  767. self.assertEqual(controller.attrib['model'], 'lsilogic')
  768. def test_controller_for_kvm(self):
  769. '''
  770. Test virt._gen_xml() generated device controller for KVM
  771. '''
  772. diskp = virt._disk_profile('default', 'kvm', [], 'hello')
  773. nicp = virt._nic_profile('default', 'kvm')
  774. xml_data = virt._gen_xml(
  775. 'hello',
  776. 1,
  777. 512,
  778. diskp,
  779. nicp,
  780. 'kvm',
  781. 'hvm',
  782. 'x86_64',
  783. )
  784. root = ET.fromstring(xml_data)
  785. controllers = root.findall('.//devices/controller')
  786. # There should be no controller
  787. self.assertTrue(len(controllers) == 0)
  788. # kvm mac address shoud start with 52:54:00
  789. self.assertTrue("mac address='52:54:00" in xml_data)
  790. def test_diff_disks(self):
  791. '''
  792. Test virt._diff_disks()
  793. '''
  794. old_disks = ET.fromstring('''
  795. <devices>
  796. <disk type='file' device='disk'>
  797. <source file='/path/to/img0.qcow2'/>
  798. <target dev='vda' bus='virtio'/>
  799. </disk>
  800. <disk type='file' device='disk'>
  801. <source file='/path/to/img1.qcow2'/>
  802. <target dev='vdb' bus='virtio'/>
  803. </disk>
  804. <disk type='file' device='disk'>
  805. <source file='/path/to/img2.qcow2'/>
  806. <target dev='hda' bus='ide'/>
  807. </disk>
  808. <disk type='file' device='disk'>
  809. <source file='/path/to/img4.qcow2'/>
  810. <target dev='hdb' bus='ide'/>
  811. </disk>
  812. <disk type='file' device='cdrom'>
  813. <target dev='hdc' bus='ide'/>
  814. </disk>
  815. </devices>
  816. ''').findall('disk')
  817. new_disks = ET.fromstring('''
  818. <devices>
  819. <disk type='file' device='disk'>
  820. <source file='/path/to/img3.qcow2'/>
  821. <target dev='vda' bus='virtio'/>
  822. </disk>
  823. <disk type='file' device='disk' cache='default'>
  824. <source file='/path/to/img0.qcow2'/>
  825. <target dev='vda' bus='virtio'/>
  826. </disk>
  827. <disk type='file' device='disk'>
  828. <source file='/path/to/img4.qcow2'/>
  829. <target dev='vda' bus='virtio'/>
  830. </disk>
  831. <disk type='file' device='cdrom'>
  832. <target dev='hda' bus='ide'/>
  833. </disk>
  834. </devices>
  835. ''').findall('disk')
  836. ret = virt._diff_disk_lists(old_disks, new_disks)
  837. self.assertEqual([disk.find('source').get('file') if disk.find('source') is not None else None
  838. for disk in ret['unchanged']], [])
  839. self.assertEqual([disk.find('source').get('file') if disk.find('source') is not None else None
  840. for disk in ret['new']],
  841. ['/path/to/img3.qcow2', '/path/to/img0.qcow2', '/path/to/img4.qcow2', None])
  842. self.assertEqual([disk.find('target').get('dev') for disk in ret['sorted']],
  843. ['vda', 'vdb', 'vdc', 'hda'])
  844. self.assertEqual([disk.find('source').get('file') if disk.find('source') is not None else None
  845. for disk in ret['sorted']],
  846. ['/path/to/img3.qcow2',
  847. '/path/to/img0.qcow2',
  848. '/path/to/img4.qcow2',
  849. None])
  850. self.assertEqual(ret['new'][1].find('target').get('bus'), 'virtio')
  851. self.assertEqual([disk.find('source').get('file') if disk.find('source') is not None else None
  852. for disk in ret['deleted']],
  853. ['/path/to/img0.qcow2',
  854. '/path/to/img1.qcow2',
  855. '/path/to/img2.qcow2',
  856. '/path/to/img4.qcow2',
  857. None])
  858. def test_diff_nics(self):
  859. '''
  860. Test virt._diff_nics()
  861. '''
  862. old_nics = ET.fromstring('''
  863. <devices>
  864. <interface type='network'>
  865. <mac address='52:54:00:39:02:b1'/>
  866. <source network='default'/>
  867. <model type='virtio'/>
  868. <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
  869. </interface>
  870. <interface type='network'>
  871. <mac address='52:54:00:39:02:b2'/>
  872. <source network='admin'/>
  873. <model type='virtio'/>
  874. <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
  875. </interface>
  876. <interface type='network'>
  877. <mac address='52:54:00:39:02:b3'/>
  878. <source network='admin'/>
  879. <model type='virtio'/>
  880. <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
  881. </interface>
  882. </devices>
  883. ''').findall('interface')
  884. new_nics = ET.fromstring('''
  885. <devices>
  886. <interface type='network'>
  887. <mac address='52:54:00:39:02:b1'/>
  888. <source network='default'/>
  889. <model type='virtio'/>
  890. </interface>
  891. <interface type='network'>
  892. <mac address='52:54:00:39:02:b2'/>
  893. <source network='default'/>
  894. <model type='virtio'/>
  895. </interface>
  896. <interface type='network'>
  897. <mac address='52:54:00:39:02:b4'/>
  898. <source network='admin'/>
  899. <model type='virtio'/>
  900. </interface>
  901. </devices>
  902. ''').findall('interface')
  903. ret = virt._diff_interface_lists(old_nics, new_nics)
  904. self.assertEqual([nic.find('mac').get('address') for nic in ret['unchanged']],
  905. ['52:54:00:39:02:b1'])
  906. self.assertEqual([nic.find('mac').get('address') for nic in ret['new']],
  907. ['52:54:00:39:02:b2', '52:54:00:39:02:b4'])
  908. self.assertEqual([nic.find('mac').get('address') for nic in ret['deleted']],
  909. ['52:54:00:39:02:b2', '52:54:00:39:02:b3'])
  910. def test_init(self):
  911. '''
  912. Test init() function
  913. '''
  914. xml = '''
  915. <capabilities>
  916. <host>
  917. <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid>
  918. <cpu>
  919. <arch>x86_64</arch>
  920. <model>Nehalem</model>
  921. <vendor>Intel</vendor>
  922. <microcode version='25'/>
  923. <topology sockets='1' cores='4' threads='2'/>
  924. <feature name='vme'/>
  925. <feature name='ds'/>
  926. <feature name='acpi'/>
  927. <pages unit='KiB' size='4'/>
  928. <pages unit='KiB' size='2048'/>
  929. </cpu>
  930. <power_management>
  931. <suspend_mem/>
  932. <suspend_disk/>
  933. <suspend_hybrid/>
  934. </power_management>
  935. <migration_features>
  936. <live/>
  937. <uri_transports>
  938. <uri_transport>tcp</uri_transport>
  939. <uri_transport>rdma</uri_transport>
  940. </uri_transports>
  941. </migration_features>
  942. <topology>
  943. <cells num='1'>
  944. <cell id='0'>
  945. <memory unit='KiB'>12367120</memory>
  946. <pages unit='KiB' size='4'>3091780</pages>
  947. <pages unit='KiB' size='2048'>0</pages>
  948. <distances>
  949. <sibling id='0' value='10'/>
  950. </distances>
  951. <cpus num='8'>
  952. <cpu id='0' socket_id='0' core_id='0' siblings='0,4'/>
  953. <cpu id='1' socket_id='0' core_id='1' siblings='1,5'/>
  954. <cpu id='2' socket_id='0' core_id='2' siblings='2,6'/>
  955. <cpu id='3' socket_id='0' core_id='3' siblings='3,7'/>
  956. <cpu id='4' socket_id='0' core_id='0' siblings='0,4'/>
  957. <cpu id='5' socket_id='0' core_id='1' siblings='1,5'/>
  958. <cpu id='6' socket_id='0' core_id='2' siblings='2,6'/>
  959. <cpu id='7' socket_id='0' core_id='3' siblings='3,7'/>
  960. </cpus>
  961. </cell>
  962. </cells>
  963. </topology>
  964. <cache>
  965. <bank id='0' level='3' type='both' size='8' unit='MiB' cpus='0-7'/>
  966. </cache>
  967. <secmodel>
  968. <model>apparmor</model>
  969. <doi>0</doi>
  970. </secmodel>
  971. <secmodel>
  972. <model>dac</model>
  973. <doi>0</doi>
  974. <baselabel type='kvm'>+487:+486</baselabel>
  975. <baselabel type='qemu'>+487:+486</baselabel>
  976. </secmodel>
  977. </host>
  978. <guest>
  979. <os_type>hvm</os_type>
  980. <arch name='i686'>
  981. <wordsize>32</wordsize>
  982. <emulator>/usr/bin/qemu-system-i386</emulator>
  983. <machine maxCpus='255'>pc-i440fx-2.6</machine>
  984. <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
  985. <machine maxCpus='255'>pc-0.12</machine>
  986. <domain type='qemu'/>
  987. <domain type='kvm'>
  988. <emulator>/usr/bin/qemu-kvm</emulator>
  989. <machine maxCpus='255'>pc-i440fx-2.6</machine>
  990. <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
  991. <machine maxCpus='255'>pc-0.12</machine>
  992. </domain>
  993. </arch>
  994. <features>
  995. <cpuselection/>
  996. <deviceboot/>
  997. <disksnapshot default='on' toggle='no'/>
  998. <acpi default='on' toggle='yes'/>
  999. <apic default='on' toggle='no'/>
  1000. <pae/>
  1001. <nonpae/>
  1002. </features>
  1003. </guest>
  1004. <guest>
  1005. <os_type>hvm</os_type>
  1006. <arch name='x86_64'>
  1007. <wordsize>64</wordsize>
  1008. <emulator>/usr/bin/qemu-system-x86_64</emulator>
  1009. <machine maxCpus='255'>pc-i440fx-2.6</machine>
  1010. <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
  1011. <machine maxCpus='255'>pc-0.12</machine>
  1012. <domain type='qemu'/>
  1013. <domain type='kvm'>
  1014. <emulator>/usr/bin/qemu-kvm</emulator>
  1015. <machine maxCpus='255'>pc-i440fx-2.6</machine>
  1016. <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
  1017. <machine maxCpus='255'>pc-0.12</machine>
  1018. </domain>
  1019. </arch>
  1020. <features>
  1021. <cpuselection/>
  1022. <deviceboot/>
  1023. <disksnapshot default='on' toggle='no'/>
  1024. <acpi default='on' toggle='yes'/>
  1025. <apic default='on' toggle='no'/>
  1026. </features>
  1027. </guest>
  1028. </capabilities>
  1029. '''
  1030. self.mock_conn.getCapabilities.return_value = xml # pylint: disable=no-member
  1031. root_dir = os.path.join(salt.syspaths.ROOT_DIR, 'srv', 'salt-images')
  1032. defineMock = MagicMock(return_value=1)
  1033. self.mock_conn.defineXML = defineMock
  1034. mock_chmod = MagicMock()
  1035. mock_run = MagicMock()
  1036. with patch.dict(os.__dict__, {'chmod': mock_chmod, 'makedirs': MagicMock()}): # pylint: disable=no-member
  1037. with patch.dict(virt.__salt__, {'cmd.run': mock_run}): # pylint: disable=no-member
  1038. # Ensure the init() function allows creating VM without NIC and disk
  1039. virt.init('test vm',
  1040. 2,
  1041. 1234,
  1042. nic=None,
  1043. disk=None,
  1044. seed=False,
  1045. start=False)
  1046. definition = defineMock.call_args_list[0][0][0]
  1047. self.assertFalse('<interface' in definition)
  1048. self.assertFalse('<disk' in definition)
  1049. # Test case creating disks
  1050. defineMock.reset_mock()
  1051. mock_run.reset_mock()
  1052. virt.init('test vm',
  1053. 2,
  1054. 1234,
  1055. nic=None,
  1056. disk=None,
  1057. disks=[
  1058. {'name': 'system', 'size': 10240},
  1059. {'name': 'cddrive', 'device': 'cdrom', 'source_file': None, 'model': 'ide'}
  1060. ],
  1061. seed=False,
  1062. start=False)
  1063. definition = ET.fromstring(defineMock.call_args_list[0][0][0])
  1064. disk_sources = [disk.find('source').get('file') if disk.find('source') is not None else None
  1065. for disk in definition.findall('./devices/disk')]
  1066. expected_disk_path = os.path.join(root_dir, 'test vm_system.qcow2')
  1067. self.assertEqual(disk_sources, [expected_disk_path, None])
  1068. self.assertEqual(mock_run.call_args[0][0],
  1069. 'qemu-img create -f qcow2 "{0}" 10240M'.format(expected_disk_path))
  1070. self.assertEqual(mock_chmod.call_args[0][0], expected_disk_path)
  1071. def test_update(self):
  1072. '''
  1073. Test virt.update()
  1074. '''
  1075. root_dir = os.path.join(salt.syspaths.ROOT_DIR, 'srv', 'salt-images')
  1076. xml = '''
  1077. <domain type='kvm' id='7'>
  1078. <name>my vm</name>
  1079. <memory unit='KiB'>1048576</memory>
  1080. <currentMemory unit='KiB'>1048576</currentMemory>
  1081. <vcpu placement='auto'>1</vcpu>
  1082. <os>
  1083. <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type>
  1084. </os>
  1085. <devices>
  1086. <disk type='file' device='disk'>
  1087. <driver name='qemu' type='qcow2'/>
  1088. <source file='{0}{1}my vm_system.qcow2'/>
  1089. <backingStore/>
  1090. <target dev='vda' bus='virtio'/>
  1091. <alias name='virtio-disk0'/>
  1092. <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
  1093. </disk>
  1094. <disk type='file' device='disk'>
  1095. <driver name='qemu' type='qcow2'/>
  1096. <source file='{0}{1}my vm_data.qcow2'/>
  1097. <backingStore/>
  1098. <target dev='vdb' bus='virtio'/>
  1099. <alias name='virtio-disk1'/>
  1100. <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x1'/>
  1101. </disk>
  1102. <interface type='network'>
  1103. <mac address='52:54:00:39:02:b1'/>
  1104. <source network='default' bridge='virbr0'/>
  1105. <target dev='vnet0'/>
  1106. <model type='virtio'/>
  1107. <alias name='net0'/>
  1108. <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
  1109. </interface>
  1110. <interface type='network'>
  1111. <mac address='52:54:00:39:02:b2'/>
  1112. <source network='oldnet' bridge='virbr1'/>
  1113. <target dev='vnet1'/>
  1114. <model type='virtio'/>
  1115. <alias name='net1'/>
  1116. <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
  1117. </interface>
  1118. <graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'>
  1119. <listen type='address' address='127.0.0.1'/>
  1120. </graphics>
  1121. <video>
  1122. <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
  1123. <alias name='video0'/>
  1124. <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
  1125. </video>
  1126. </devices>
  1127. </domain>
  1128. '''.format(root_dir, os.sep)
  1129. domain_mock = self.set_mock_vm('my vm', xml)
  1130. domain_mock.OSType = MagicMock(return_value='hvm')
  1131. define_mock = MagicMock(return_value=True)
  1132. self.mock_conn.defineXML = define_mock
  1133. # Update vcpus case
  1134. setvcpus_mock = MagicMock(return_value=0)
  1135. domain_mock.setVcpusFlags = setvcpus_mock
  1136. self.assertEqual({
  1137. 'definition': True,
  1138. 'cpu': True,
  1139. 'disk': {'attached': [], 'detached': []},
  1140. 'interface': {'attached': [], 'detached': []}
  1141. }, virt.update('my vm', cpu=2))
  1142. setxml = ET.fromstring(define_mock.call_args[0][0])
  1143. self.assertEqual(setxml.find('vcpu').text, '2')
  1144. self.assertEqual(setvcpus_mock.call_args[0][0], 2)
  1145. # Update memory case
  1146. setmem_mock = MagicMock(return_value=0)
  1147. domain_mock.setMemoryFlags = setmem_mock
  1148. self.assertEqual({
  1149. 'definition': True,
  1150. 'mem': True,
  1151. 'disk': {'attached': [], 'detached': []},
  1152. 'interface': {'attached': [], 'detached': []}
  1153. }, virt.update('my vm', mem=2048))
  1154. setxml = ET.fromstring(define_mock.call_args[0][0])
  1155. self.assertEqual(setxml.find('memory').text, '2048')
  1156. self.assertEqual(setxml.find('memory').get('unit'), 'MiB')
  1157. self.assertEqual(setmem_mock.call_args[0][0], 2048 * 1024)
  1158. # Update disks case
  1159. devattach_mock = MagicMock(return_value=0)
  1160. devdetach_mock = MagicMock(return_value=0)
  1161. domain_mock.attachDevice = devattach_mock
  1162. domain_mock.detachDevice = devdetach_mock
  1163. mock_chmod = MagicMock()
  1164. mock_run = MagicMock()
  1165. with patch.dict(os.__dict__, {'chmod': mock_chmod, 'makedirs': MagicMock()}): # pylint: disable=no-member
  1166. with patch.dict(virt.__salt__, {'cmd.run': mock_run}): # pylint: disable=no-member
  1167. ret = virt.update('my vm', disk_profile='default', disks=[
  1168. {'name': 'cddrive', 'device': 'cdrom', 'source_file': None, 'model': 'ide'},
  1169. {'name': 'added', 'size': 2048}])
  1170. added_disk_path = os.path.join(
  1171. virt.__salt__['config.get']('virt:images'), 'my vm_added.qcow2') # pylint: disable=no-member
  1172. self.assertEqual(mock_run.call_args[0][0],
  1173. 'qemu-img create -f qcow2 "{0}" 2048M'.format(added_disk_path))
  1174. self.assertEqual(mock_chmod.call_args[0][0], added_disk_path)
  1175. self.assertListEqual(
  1176. [None, os.path.join(root_dir, 'my vm_added.qcow2')],
  1177. [ET.fromstring(disk).find('source').get('file') if str(disk).find('<source') > -1 else None
  1178. for disk in ret['disk']['attached']])
  1179. self.assertListEqual(
  1180. [os.path.join(root_dir, 'my vm_data.qcow2')],
  1181. [ET.fromstring(disk).find('source').get('file') for disk in ret['disk']['detached']])
  1182. self.assertEqual(devattach_mock.call_count, 2)
  1183. devdetach_mock.assert_called_once()
  1184. # Update nics case
  1185. yaml_config = '''
  1186. virt:
  1187. nic:
  1188. myprofile:
  1189. - network: default
  1190. name: eth0
  1191. '''
  1192. mock_config = salt.utils.yaml.safe_load(yaml_config)
  1193. devattach_mock.reset_mock()
  1194. devdetach_mock.reset_mock()
  1195. with patch.dict(salt.modules.config.__opts__, mock_config): # pylint: disable=no-member
  1196. ret = virt.update('my vm', nic_profile='myprofile',
  1197. interfaces=[{'name': 'eth0', 'type': 'network', 'source': 'default',
  1198. 'mac': '52:54:00:39:02:b1'},
  1199. {'name': 'eth1', 'type': 'network', 'source': 'newnet'}])
  1200. self.assertEqual(['newnet'],
  1201. [ET.fromstring(nic).find('source').get('network') for nic in ret['interface']['attached']])
  1202. self.assertEqual(['oldnet'],
  1203. [ET.fromstring(nic).find('source').get('network') for nic in ret['interface']['detached']])
  1204. devattach_mock.assert_called_once()
  1205. devdetach_mock.assert_called_once()
  1206. # Remove nics case
  1207. devattach_mock.reset_mock()
  1208. devdetach_mock.reset_mock()
  1209. ret = virt.update('my vm', nic_profile=None, interfaces=[])
  1210. self.assertEqual([], ret['interface']['attached'])
  1211. self.assertEqual(2, len(ret['interface']['detached']))
  1212. devattach_mock.assert_not_called()
  1213. devdetach_mock.assert_called()
  1214. # Remove disks case (yeah, it surely is silly)
  1215. devattach_mock.reset_mock()
  1216. devdetach_mock.reset_mock()
  1217. ret = virt.update('my vm', disk_profile=None, disks=[])
  1218. self.assertEqual([], ret['disk']['attached'])
  1219. self.assertEqual(2, len(ret['disk']['detached']))
  1220. devattach_mock.assert_not_called()
  1221. devdetach_mock.assert_called()
  1222. # Graphics change test case
  1223. self.assertEqual({
  1224. 'definition': True,
  1225. 'disk': {'attached': [], 'detached': []},
  1226. 'interface': {'attached': [], 'detached': []}
  1227. }, virt.update('my vm', graphics={'type': 'vnc'}))
  1228. setxml = ET.fromstring(define_mock.call_args[0][0])
  1229. self.assertEqual('vnc', setxml.find('devices/graphics').get('type'))
  1230. # Update with no diff case
  1231. self.assertEqual({
  1232. 'definition': False,
  1233. 'disk': {'attached': [], 'detached': []},
  1234. 'interface': {'attached': [], 'detached': []}
  1235. }, virt.update('my vm', cpu=1, mem=1024,
  1236. disk_profile='default', disks=[{'name': 'data', 'size': 2048}],
  1237. nic_profile='myprofile',
  1238. interfaces=[{'name': 'eth0', 'type': 'network', 'source': 'default',
  1239. 'mac': '52:54:00:39:02:b1'},
  1240. {'name': 'eth1', 'type': 'network', 'source': 'oldnet',
  1241. 'mac': '52:54:00:39:02:b2'}],
  1242. graphics={'type': 'spice',
  1243. 'listen': {'type': 'address', 'address': '127.0.0.1'}}))
  1244. # Failed XML description update case
  1245. self.mock_conn.defineXML.side_effect = self.mock_libvirt.libvirtError("Test error")
  1246. setmem_mock.reset_mock()
  1247. with self.assertRaises(self.mock_libvirt.libvirtError):
  1248. virt.update('my vm', mem=2048)
  1249. # Failed single update failure case
  1250. self.mock_conn.defineXML = MagicMock(return_value=True)
  1251. setmem_mock.side_effect = self.mock_libvirt.libvirtError("Failed to live change memory")
  1252. self.assertEqual({
  1253. 'definition': True,
  1254. 'errors': ['Failed to live change memory'],
  1255. 'disk': {'attached': [], 'detached': []},
  1256. 'interface': {'attached': [], 'detached': []}
  1257. }, virt.update('my vm', mem=2048))
  1258. # Failed multiple updates failure case
  1259. self.assertEqual({
  1260. 'definition': True,
  1261. 'errors': ['Failed to live change memory'],
  1262. 'cpu': True,
  1263. 'disk': {'attached': [], 'detached': []},
  1264. 'interface': {'attached': [], 'detached': []}
  1265. }, virt.update('my vm', cpu=4, mem=2048))
  1266. def test_mixed_dict_and_list_as_profile_objects(self):
  1267. '''
  1268. Test virt._nic_profile with mixed dictionaries and lists as input.
  1269. '''
  1270. yaml_config = '''
  1271. virt:
  1272. nic:
  1273. new-listonly-profile:
  1274. - bridge: br0
  1275. name: eth0
  1276. - model: virtio
  1277. name: eth1
  1278. source: test_network
  1279. type: network
  1280. new-list-with-legacy-names:
  1281. - eth0:
  1282. bridge: br0
  1283. - eth1:
  1284. bridge: br1
  1285. model: virtio
  1286. non-default-legacy-profile:
  1287. eth0:
  1288. bridge: br0
  1289. eth1:
  1290. bridge: br1
  1291. model: virtio
  1292. '''
  1293. mock_config = salt.utils.yaml.safe_load(yaml_config)
  1294. with patch.dict(salt.modules.config.__opts__, mock_config): # pylint: disable=no-member
  1295. for name in six.iterkeys(mock_config['virt']['nic']):
  1296. profile = salt.modules.virt._nic_profile(name, 'kvm')
  1297. self.assertEqual(len(profile), 2)
  1298. interface_attrs = profile[0]
  1299. self.assertIn('source', interface_attrs)
  1300. self.assertIn('type', interface_attrs)
  1301. self.assertIn('name', interface_attrs)
  1302. self.assertIn('model', interface_attrs)
  1303. self.assertEqual(interface_attrs['model'], 'virtio')
  1304. self.assertIn('mac', interface_attrs)
  1305. self.assertTrue(
  1306. re.match('^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$',
  1307. interface_attrs['mac'], re.I))
  1308. def test_get_graphics(self):
  1309. '''
  1310. Test virt.get_graphics()
  1311. '''
  1312. xml = '''<domain type='kvm' id='7'>
  1313. <name>test-vm</name>
  1314. <devices>
  1315. <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
  1316. <listen type='address' address='0.0.0.0'/>
  1317. </graphics>
  1318. </devices>
  1319. </domain>
  1320. '''
  1321. self.set_mock_vm("test-vm", xml)
  1322. graphics = virt.get_graphics('test-vm')
  1323. self.assertEqual('vnc', graphics['type'])
  1324. self.assertEqual('5900', graphics['port'])
  1325. self.assertEqual('0.0.0.0', graphics['listen'])
  1326. def test_get_nics(self):
  1327. '''
  1328. Test virt.get_nics()
  1329. '''
  1330. xml = '''<domain type='kvm' id='7'>
  1331. <name>test-vm</name>
  1332. <devices>
  1333. <interface type='bridge'>
  1334. <mac address='ac:de:48:b6:8b:59'/>
  1335. <source bridge='br0'/>
  1336. <model type='virtio'/>
  1337. <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
  1338. </interface>
  1339. </devices>
  1340. </domain>
  1341. '''
  1342. self.set_mock_vm("test-vm", xml)
  1343. nics = virt.get_nics('test-vm')
  1344. nic = nics[list(nics)[0]]
  1345. self.assertEqual('bridge', nic['type'])
  1346. self.assertEqual('ac:de:48:b6:8b:59', nic['mac'])
  1347. def test_get_xml(self):
  1348. '''
  1349. Test virt.get_xml()
  1350. '''
  1351. xml = '''<domain type='kvm' id='7'>
  1352. <name>test-vm</name>
  1353. <devices>
  1354. <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
  1355. <listen type='address' address='0.0.0.0'/>
  1356. </graphics>
  1357. </devices>
  1358. </domain>
  1359. '''
  1360. domain = self.set_mock_vm("test-vm", xml)
  1361. self.assertEqual(xml, virt.get_xml('test-vm'))
  1362. self.assertEqual(xml, virt.get_xml(domain))
  1363. def test_parse_qemu_img_info(self):
  1364. '''
  1365. Make sure that qemu-img info output is properly parsed
  1366. '''
  1367. qemu_infos = '''[{
  1368. "snapshots": [
  1369. {
  1370. "vm-clock-nsec": 0,
  1371. "name": "first-snap",
  1372. "date-sec": 1528877587,
  1373. "date-nsec": 380589000,
  1374. "vm-clock-sec": 0,
  1375. "id": "1",
  1376. "vm-state-size": 1234
  1377. },
  1378. {
  1379. "vm-clock-nsec": 0,
  1380. "name": "second snap",
  1381. "date-sec": 1528877592,
  1382. "date-nsec": 933509000,
  1383. "vm-clock-sec": 0,
  1384. "id": "2",
  1385. "vm-state-size": 4567
  1386. }
  1387. ],
  1388. "virtual-size": 25769803776,
  1389. "filename": "/disks/test.qcow2",
  1390. "cluster-size": 65536,
  1391. "format": "qcow2",
  1392. "actual-size": 217088,
  1393. "format-specific": {
  1394. "type": "qcow2",
  1395. "data": {
  1396. "compat": "1.1",
  1397. "lazy-refcounts": false,
  1398. "refcount-bits": 16,
  1399. "corrupt": false
  1400. }
  1401. },
  1402. "full-backing-filename": "/disks/mybacking.qcow2",
  1403. "backing-filename": "mybacking.qcow2",
  1404. "dirty-flag": false
  1405. },
  1406. {
  1407. "virtual-size": 25769803776,
  1408. "filename": "/disks/mybacking.qcow2",
  1409. "cluster-size": 65536,
  1410. "format": "qcow2",
  1411. "actual-size": 393744384,
  1412. "format-specific": {
  1413. "type": "qcow2",
  1414. "data": {
  1415. "compat": "1.1",
  1416. "lazy-refcounts": false,
  1417. "refcount-bits": 16,
  1418. "corrupt": false
  1419. }
  1420. },
  1421. "full-backing-filename": "/disks/root.qcow2",
  1422. "backing-filename": "root.qcow2",
  1423. "dirty-flag": false
  1424. },
  1425. {
  1426. "virtual-size": 25769803776,
  1427. "filename": "/disks/root.qcow2",
  1428. "cluster-size": 65536,
  1429. "format": "qcow2",
  1430. "actual-size": 196872192,
  1431. "format-specific": {
  1432. "type": "qcow2",
  1433. "data": {
  1434. "compat": "1.1",
  1435. "lazy-refcounts": false,
  1436. "refcount-bits": 16,
  1437. "corrupt": false
  1438. }
  1439. },
  1440. "dirty-flag": false
  1441. }]'''
  1442. self.assertEqual(
  1443. {
  1444. 'file': '/disks/test.qcow2',
  1445. 'file format': 'qcow2',
  1446. 'backing file': {
  1447. 'file': '/disks/mybacking.qcow2',
  1448. 'file format': 'qcow2',
  1449. 'disk size': 393744384,
  1450. 'virtual size': 25769803776,
  1451. 'cluster size': 65536,
  1452. 'backing file': {
  1453. 'file': '/disks/root.qcow2',
  1454. 'file format': 'qcow2',
  1455. 'disk size': 196872192,
  1456. 'virtual size': 25769803776,
  1457. 'cluster size': 65536,
  1458. }
  1459. },
  1460. 'disk size': 217088,
  1461. 'virtual size': 25769803776,
  1462. 'cluster size': 65536,
  1463. 'snapshots': [
  1464. {
  1465. 'id': '1',
  1466. 'tag': 'first-snap',
  1467. 'vmsize': 1234,
  1468. 'date': datetime.datetime.fromtimestamp(
  1469. float("{}.{}".format(1528877587, 380589000))).isoformat(),
  1470. 'vmclock': '00:00:00'
  1471. },
  1472. {
  1473. 'id': '2',
  1474. 'tag': 'second snap',
  1475. 'vmsize': 4567,
  1476. 'date': datetime.datetime.fromtimestamp(
  1477. float("{}.{}".format(1528877592, 933509000))).isoformat(),
  1478. 'vmclock': '00:00:00'
  1479. }
  1480. ],
  1481. }, virt._parse_qemu_img_info(qemu_infos))
  1482. def test_get_disks(self):
  1483. '''
  1484. Test virt.get_disks()
  1485. '''
  1486. xml = '''<domain type='kvm' id='7'>
  1487. <name>test-vm</name>
  1488. <devices>
  1489. <disk type='file' device='disk'>
  1490. <driver name='qemu' type='qcow2'/>
  1491. <source file='/disks/test.qcow2'/>
  1492. <target dev='vda' bus='virtio'/>
  1493. </disk>
  1494. <disk type='file' device='cdrom'>
  1495. <driver name='qemu' type='raw'/>
  1496. <source file='/disks/test-cdrom.iso'/>
  1497. <target dev='hda' bus='ide'/>
  1498. <readonly/>
  1499. </disk>
  1500. </devices>
  1501. </domain>
  1502. '''
  1503. self.set_mock_vm("test-vm", xml)
  1504. qemu_infos = '''[{
  1505. "virtual-size": 25769803776,
  1506. "filename": "/disks/test.qcow2",
  1507. "cluster-size": 65536,
  1508. "format": "qcow2",
  1509. "actual-size": 217088,
  1510. "format-specific": {
  1511. "type": "qcow2",
  1512. "data": {
  1513. "compat": "1.1",
  1514. "lazy-refcounts": false,
  1515. "refcount-bits": 16,
  1516. "corrupt": false
  1517. }
  1518. },
  1519. "full-backing-filename": "/disks/mybacking.qcow2",
  1520. "backing-filename": "mybacking.qcow2",
  1521. "dirty-flag": false
  1522. },
  1523. {
  1524. "virtual-size": 25769803776,
  1525. "filename": "/disks/mybacking.qcow2",
  1526. "cluster-size": 65536,
  1527. "format": "qcow2",
  1528. "actual-size": 393744384,
  1529. "format-specific": {
  1530. "type": "qcow2",
  1531. "data": {
  1532. "compat": "1.1",
  1533. "lazy-refcounts": false,
  1534. "refcount-bits": 16,
  1535. "corrupt": false
  1536. }
  1537. },
  1538. "dirty-flag": false
  1539. }]'''
  1540. self.mock_popen.communicate.return_value = [qemu_infos] # pylint: disable=no-member
  1541. disks = virt.get_disks('test-vm')
  1542. disk = disks.get('vda')
  1543. self.assertEqual('/disks/test.qcow2', disk['file'])
  1544. self.assertEqual('disk', disk['type'])
  1545. self.assertEqual('/disks/mybacking.qcow2', disk['backing file']['file'])
  1546. cdrom = disks.get('hda')
  1547. self.assertEqual('/disks/test-cdrom.iso', cdrom['file'])
  1548. self.assertEqual('cdrom', cdrom['type'])
  1549. self.assertFalse('backing file' in cdrom.keys())
  1550. @patch('salt.modules.virt.stop', return_value=True)
  1551. @patch('salt.modules.virt.undefine')
  1552. @patch('os.remove')
  1553. def test_purge_default(self, mock_remove, mock_undefine, mock_stop):
  1554. '''
  1555. Test virt.purge() with default parameters
  1556. '''
  1557. xml = '''<domain type='kvm' id='7'>
  1558. <name>test-vm</name>
  1559. <devices>
  1560. <disk type='file' device='disk'>
  1561. <driver name='qemu' type='qcow2'/>
  1562. <source file='/disks/test.qcow2'/>
  1563. <target dev='vda' bus='virtio'/>
  1564. </disk>
  1565. <disk type='file' device='cdrom'>
  1566. <driver name='qemu' type='raw'/>
  1567. <source file='/disks/test-cdrom.iso'/>
  1568. <target dev='hda' bus='ide'/>
  1569. <readonly/>
  1570. </disk>
  1571. </devices>
  1572. </domain>
  1573. '''
  1574. self.set_mock_vm("test-vm", xml)
  1575. qemu_infos = '''[{
  1576. "virtual-size": 25769803776,
  1577. "filename": "/disks/test.qcow2",
  1578. "cluster-size": 65536,
  1579. "format": "qcow2",
  1580. "actual-size": 217088,
  1581. "format-specific": {
  1582. "type": "qcow2",
  1583. "data": {
  1584. "compat": "1.1",
  1585. "lazy-refcounts": false,
  1586. "refcount-bits": 16,
  1587. "corrupt": false
  1588. }
  1589. },
  1590. "dirty-flag": false
  1591. }]'''
  1592. self.mock_popen.communicate.return_value = [qemu_infos] # pylint: disable=no-member
  1593. res = virt.purge('test-vm')
  1594. self.assertTrue(res)
  1595. mock_remove.assert_any_call('/disks/test.qcow2')
  1596. mock_remove.assert_any_call('/disks/test-cdrom.iso')
  1597. @patch('salt.modules.virt.stop', return_value=True)
  1598. @patch('salt.modules.virt.undefine')
  1599. @patch('os.remove')
  1600. def test_purge_noremovable(self, mock_remove, mock_undefine, mock_stop):
  1601. '''
  1602. Test virt.purge(removables=False)
  1603. '''
  1604. xml = '''<domain type='kvm' id='7'>
  1605. <name>test-vm</name>
  1606. <devices>
  1607. <disk type='file' device='disk'>
  1608. <driver name='qemu' type='qcow2'/>
  1609. <source file='/disks/test.qcow2'/>
  1610. <target dev='vda' bus='virtio'/>
  1611. </disk>
  1612. <disk type='file' device='cdrom'>
  1613. <driver name='qemu' type='raw'/>
  1614. <source file='/disks/test-cdrom.iso'/>
  1615. <target dev='hda' bus='ide'/>
  1616. <readonly/>
  1617. </disk>
  1618. <disk type='file' device='floppy'>
  1619. <driver name='qemu' type='raw'/>
  1620. <source file='/disks/test-floppy.iso'/>
  1621. <target dev='hdb' bus='ide'/>
  1622. <readonly/>
  1623. </disk>
  1624. </devices>
  1625. </domain>
  1626. '''
  1627. self.set_mock_vm("test-vm", xml)
  1628. qemu_infos = '''[{
  1629. "virtual-size": 25769803776,
  1630. "filename": "/disks/test.qcow2",
  1631. "cluster-size": 65536,
  1632. "format": "qcow2",
  1633. "actual-size": 217088,
  1634. "format-specific": {
  1635. "type": "qcow2",
  1636. "data": {
  1637. "compat": "1.1",
  1638. "lazy-refcounts": false,
  1639. "refcount-bits": 16,
  1640. "corrupt": false
  1641. }
  1642. },
  1643. "dirty-flag": false
  1644. }]'''
  1645. self.mock_popen.communicate.return_value = [qemu_infos] # pylint: disable=no-member
  1646. res = virt.purge('test-vm', removables=False)
  1647. self.assertTrue(res)
  1648. mock_remove.assert_called_once()
  1649. mock_remove.assert_any_call('/disks/test.qcow2')
  1650. def test_capabilities(self):
  1651. '''
  1652. Test the virt.capabilities parsing
  1653. '''
  1654. xml = '''
  1655. <capabilities>
  1656. <host>
  1657. <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid>
  1658. <cpu>
  1659. <arch>x86_64</arch>
  1660. <model>Nehalem</model>
  1661. <vendor>Intel</vendor>
  1662. <microcode version='25'/>
  1663. <topology sockets='1' cores='4' threads='2'/>
  1664. <feature name='vme'/>
  1665. <feature name='ds'/>
  1666. <feature name='acpi'/>
  1667. <pages unit='KiB' size='4'/>
  1668. <pages unit='KiB' size='2048'/>
  1669. </cpu>
  1670. <power_management>
  1671. <suspend_mem/>
  1672. <suspend_disk/>
  1673. <suspend_hybrid/>
  1674. </power_management>
  1675. <migration_features>
  1676. <live/>
  1677. <uri_transports>
  1678. <uri_transport>tcp</uri_transport>
  1679. <uri_transport>rdma</uri_transport>
  1680. </uri_transports>
  1681. </migration_features>
  1682. <topology>
  1683. <cells num='1'>
  1684. <cell id='0'>
  1685. <memory unit='KiB'>12367120</memory>
  1686. <pages unit='KiB' size='4'>3091780</pages>
  1687. <pages unit='KiB' size='2048'>0</pages>
  1688. <distances>
  1689. <sibling id='0' value='10'/>
  1690. </distances>
  1691. <cpus num='8'>
  1692. <cpu id='0' socket_id='0' core_id='0' siblings='0,4'/>
  1693. <cpu id='1' socket_id='0' core_id='1' siblings='1,5'/>
  1694. <cpu id='2' socket_id='0' core_id='2' siblings='2,6'/>
  1695. <cpu id='3' socket_id='0' core_id='3' siblings='3,7'/>
  1696. <cpu id='4' socket_id='0' core_id='0' siblings='0,4'/>
  1697. <cpu id='5' socket_id='0' core_id='1' siblings='1,5'/>
  1698. <cpu id='6' socket_id='0' core_id='2' siblings='2,6'/>
  1699. <cpu id='7' socket_id='0' core_id='3' siblings='3,7'/>
  1700. </cpus>
  1701. </cell>
  1702. </cells>
  1703. </topology>
  1704. <cache>
  1705. <bank id='0' level='3' type='both' size='8' unit='MiB' cpus='0-7'/>
  1706. </cache>
  1707. <secmodel>
  1708. <model>apparmor</model>
  1709. <doi>0</doi>
  1710. </secmodel>
  1711. <secmodel>
  1712. <model>dac</model>
  1713. <doi>0</doi>
  1714. <baselabel type='kvm'>+487:+486</baselabel>
  1715. <baselabel type='qemu'>+487:+486</baselabel>
  1716. </secmodel>
  1717. </host>
  1718. <guest>
  1719. <os_type>hvm</os_type>
  1720. <arch name='i686'>
  1721. <wordsize>32</wordsize>
  1722. <emulator>/usr/bin/qemu-system-i386</emulator>
  1723. <machine maxCpus='255'>pc-i440fx-2.6</machine>
  1724. <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
  1725. <machine maxCpus='255'>pc-0.12</machine>
  1726. <domain type='qemu'/>
  1727. <domain type='kvm'>
  1728. <emulator>/usr/bin/qemu-kvm</emulator>
  1729. <machine maxCpus='255'>pc-i440fx-2.6</machine>
  1730. <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
  1731. <machine maxCpus='255'>pc-0.12</machine>
  1732. </domain>
  1733. </arch>
  1734. <features>
  1735. <cpuselection/>
  1736. <deviceboot/>
  1737. <disksnapshot default='on' toggle='no'/>
  1738. <acpi default='on' toggle='yes'/>
  1739. <apic default='on' toggle='no'/>
  1740. <pae/>
  1741. <nonpae/>
  1742. </features>
  1743. </guest>
  1744. <guest>
  1745. <os_type>hvm</os_type>
  1746. <arch name='x86_64'>
  1747. <wordsize>64</wordsize>
  1748. <emulator>/usr/bin/qemu-system-x86_64</emulator>
  1749. <machine maxCpus='255'>pc-i440fx-2.6</machine>
  1750. <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
  1751. <machine maxCpus='255'>pc-0.12</machine>
  1752. <domain type='qemu'/>
  1753. <domain type='kvm'>
  1754. <emulator>/usr/bin/qemu-kvm</emulator>
  1755. <machine maxCpus='255'>pc-i440fx-2.6</machine>
  1756. <machine canonical='pc-i440fx-2.6' maxCpus='255'>pc</machine>
  1757. <machine maxCpus='255'>pc-0.12</machine>
  1758. </domain>
  1759. </arch>
  1760. <features>
  1761. <cpuselection/>
  1762. <deviceboot/>
  1763. <disksnapshot default='on' toggle='no'/>
  1764. <acpi default='on' toggle='yes'/>
  1765. <apic default='on' toggle='no'/>
  1766. </features>
  1767. </guest>
  1768. <guest>
  1769. <os_type>xen</os_type>
  1770. <arch name='x86_64'>
  1771. <wordsize>64</wordsize>
  1772. <emulator>/usr/bin/qemu-system-x86_64</emulator>
  1773. <machine>xenpv</machine>
  1774. <domain type='xen'/>
  1775. </arch>
  1776. </guest>
  1777. </capabilities>
  1778. '''
  1779. self.mock_conn.getCapabilities.return_value = xml # pylint: disable=no-member
  1780. caps = virt.capabilities()
  1781. expected = {
  1782. 'host': {
  1783. 'uuid': '44454c4c-3400-105a-8033-b3c04f4b344a',
  1784. 'cpu': {
  1785. 'arch': 'x86_64',
  1786. 'model': 'Nehalem',
  1787. 'vendor': 'Intel',
  1788. 'microcode': '25',
  1789. 'sockets': 1,
  1790. 'cores': 4,
  1791. 'threads': 2,
  1792. 'features': ['vme', 'ds', 'acpi'],
  1793. 'pages': [{'size': '4 KiB'}, {'size': '2048 KiB'}]
  1794. },
  1795. 'power_management': ['suspend_mem', 'suspend_disk', 'suspend_hybrid'],
  1796. 'migration': {
  1797. 'live': True,
  1798. 'transports': ['tcp', 'rdma']
  1799. },
  1800. 'topology': {
  1801. 'cells': [
  1802. {
  1803. 'id': 0,
  1804. 'memory': '12367120 KiB',
  1805. 'pages': [
  1806. {'size': '4 KiB', 'available': 3091780},
  1807. {'size': '2048 KiB', 'available': 0}
  1808. ],
  1809. 'distances': {
  1810. 0: 10,
  1811. },
  1812. 'cpus': [
  1813. {'id': 0, 'socket_id': 0, 'core_id': 0, 'siblings': '0,4'},
  1814. {'id': 1, 'socket_id': 0, 'core_id': 1, 'siblings': '1,5'},
  1815. {'id': 2, 'socket_id': 0, 'core_id': 2, 'siblings': '2,6'},
  1816. {'id': 3, 'socket_id': 0, 'core_id': 3, 'siblings': '3,7'},
  1817. {'id': 4, 'socket_id': 0, 'core_id': 0, 'siblings': '0,4'},
  1818. {'id': 5, 'socket_id': 0, 'core_id': 1, 'siblings': '1,5'},
  1819. {'id': 6, 'socket_id': 0, 'core_id': 2, 'siblings': '2,6'},
  1820. {'id': 7, 'socket_id': 0, 'core_id': 3, 'siblings': '3,7'}
  1821. ]
  1822. }
  1823. ]
  1824. },
  1825. 'cache': {
  1826. 'banks': [
  1827. {'id': 0, 'level': 3, 'type': 'both', 'size': '8 MiB', 'cpus': '0-7'}
  1828. ]
  1829. },
  1830. 'security': [
  1831. {'model': 'apparmor', 'doi': '0', 'baselabels': []},
  1832. {'model': 'dac', 'doi': '0', 'baselabels': [
  1833. {'type': 'kvm', 'label': '+487:+486'},
  1834. {'type': 'qemu', 'label': '+487:+486'}
  1835. ]}
  1836. ]
  1837. },
  1838. 'guests': [
  1839. {
  1840. 'os_type': 'hvm',
  1841. 'arch': {
  1842. 'name': 'i686',
  1843. 'wordsize': 32,
  1844. 'emulator': '/usr/bin/qemu-system-i386',
  1845. 'machines': {
  1846. 'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']},
  1847. 'pc-0.12': {'maxcpus': 255, 'alternate_names': []}
  1848. },
  1849. 'domains': {
  1850. 'qemu': {
  1851. 'emulator': None,
  1852. 'machines': {}
  1853. },
  1854. 'kvm': {
  1855. 'emulator': '/usr/bin/qemu-kvm',
  1856. 'machines': {
  1857. 'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']},
  1858. 'pc-0.12': {'maxcpus': 255, 'alternate_names': []}
  1859. }
  1860. }
  1861. }
  1862. },
  1863. 'features': {
  1864. 'cpuselection': {'default': True, 'toggle': False},
  1865. 'deviceboot': {'default': True, 'toggle': False},
  1866. 'disksnapshot': {'default': True, 'toggle': False},
  1867. 'acpi': {'default': True, 'toggle': True},
  1868. 'apic': {'default': True, 'toggle': False},
  1869. 'pae': {'default': True, 'toggle': False},
  1870. 'nonpae': {'default': True, 'toggle': False}
  1871. }
  1872. },
  1873. {
  1874. 'os_type': 'hvm',
  1875. 'arch': {
  1876. 'name': 'x86_64',
  1877. 'wordsize': 64,
  1878. 'emulator': '/usr/bin/qemu-system-x86_64',
  1879. 'machines': {
  1880. 'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']},
  1881. 'pc-0.12': {'maxcpus': 255, 'alternate_names': []}
  1882. },
  1883. 'domains': {
  1884. 'qemu': {
  1885. 'emulator': None,
  1886. 'machines': {}
  1887. },
  1888. 'kvm': {
  1889. 'emulator': '/usr/bin/qemu-kvm',
  1890. 'machines': {
  1891. 'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']},
  1892. 'pc-0.12': {'maxcpus': 255, 'alternate_names': []}
  1893. }
  1894. }
  1895. }
  1896. },
  1897. 'features': {
  1898. 'cpuselection': {'default': True, 'toggle': False},
  1899. 'deviceboot': {'default': True, 'toggle': False},
  1900. 'disksnapshot': {'default': True, 'toggle': False},
  1901. 'acpi': {'default': True, 'toggle': True},
  1902. 'apic': {'default': True, 'toggle': False}
  1903. }
  1904. },
  1905. {
  1906. 'os_type': 'xen',
  1907. 'arch': {
  1908. 'name': 'x86_64',
  1909. 'wordsize': 64,
  1910. 'emulator': '/usr/bin/qemu-system-x86_64',
  1911. 'machines': {
  1912. 'xenpv': {'alternate_names': []}
  1913. },
  1914. 'domains': {
  1915. 'xen': {
  1916. 'emulator': None,
  1917. 'machines': {}
  1918. }
  1919. }
  1920. }
  1921. }
  1922. ]
  1923. }
  1924. self.assertEqual(expected, caps)
  1925. def test_network(self):
  1926. '''
  1927. Test virt._get_net_xml()
  1928. '''
  1929. xml_data = virt._gen_net_xml('network', 'main', 'bridge', 'openvswitch')
  1930. root = ET.fromstring(xml_data)
  1931. self.assertEqual(root.find('name').text, 'network')
  1932. self.assertEqual(root.find('bridge').attrib['name'], 'main')
  1933. self.assertEqual(root.find('forward').attrib['mode'], 'bridge')
  1934. self.assertEqual(root.find('virtualport').attrib['type'], 'openvswitch')
  1935. def test_domain_capabilities(self):
  1936. '''
  1937. Test the virt.domain_capabilities parsing
  1938. '''
  1939. xml = '''
  1940. <domainCapabilities>
  1941. <path>/usr/bin/qemu-system-aarch64</path>
  1942. <domain>kvm</domain>
  1943. <machine>virt-2.12</machine>
  1944. <arch>aarch64</arch>
  1945. <vcpu max='255'/>
  1946. <iothreads supported='yes'/>
  1947. <os supported='yes'>
  1948. <loader supported='yes'>
  1949. <value>/usr/share/AAVMF/AAVMF_CODE.fd</value>
  1950. <value>/usr/share/AAVMF/AAVMF32_CODE.fd</value>
  1951. <value>/usr/share/OVMF/OVMF_CODE.fd</value>
  1952. <enum name='type'>
  1953. <value>rom</value>
  1954. <value>pflash</value>
  1955. </enum>
  1956. <enum name='readonly'>
  1957. <value>yes</value>
  1958. <value>no</value>
  1959. </enum>
  1960. </loader>
  1961. </os>
  1962. <cpu>
  1963. <mode name='host-passthrough' supported='yes'/>
  1964. <mode name='host-model' supported='yes'>
  1965. <model fallback='forbid'>sample-cpu</model>
  1966. <vendor>ACME</vendor>
  1967. <feature policy='require' name='vme'/>
  1968. <feature policy='require' name='ss'/>
  1969. </mode>
  1970. <mode name='custom' supported='yes'>
  1971. <model usable='unknown'>pxa262</model>
  1972. <model usable='yes'>pxa270-a0</model>
  1973. <model usable='no'>arm1136</model>
  1974. </mode>
  1975. </cpu>
  1976. <devices>
  1977. <disk supported='yes'>
  1978. <enum name='diskDevice'>
  1979. <value>disk</value>
  1980. <value>cdrom</value>
  1981. <value>floppy</value>
  1982. <value>lun</value>
  1983. </enum>
  1984. <enum name='bus'>
  1985. <value>fdc</value>
  1986. <value>scsi</value>
  1987. <value>virtio</value>
  1988. <value>usb</value>
  1989. <value>sata</value>
  1990. </enum>
  1991. </disk>
  1992. <graphics supported='yes'>
  1993. <enum name='type'>
  1994. <value>sdl</value>
  1995. <value>vnc</value>
  1996. </enum>
  1997. </graphics>
  1998. <video supported='yes'>
  1999. <enum name='modelType'>
  2000. <value>vga</value>
  2001. <value>virtio</value>
  2002. </enum>
  2003. </video>
  2004. <hostdev supported='yes'>
  2005. <enum name='mode'>
  2006. <value>subsystem</value>
  2007. </enum>
  2008. <enum name='startupPolicy'>
  2009. <value>default</value>
  2010. <value>mandatory</value>
  2011. <value>requisite</value>
  2012. <value>optional</value>
  2013. </enum>
  2014. <enum name='subsysType'>
  2015. <value>usb</value>
  2016. <value>pci</value>
  2017. <value>scsi</value>
  2018. </enum>
  2019. <enum name='capsType'/>
  2020. <enum name='pciBackend'>
  2021. <value>default</value>
  2022. <value>kvm</value>
  2023. <value>vfio</value>
  2024. </enum>
  2025. </hostdev>
  2026. </devices>
  2027. <features>
  2028. <gic supported='yes'>
  2029. <enum name='version'>
  2030. <value>3</value>
  2031. </enum>
  2032. </gic>
  2033. <vmcoreinfo supported='yes'/>
  2034. </features>
  2035. </domainCapabilities>
  2036. '''
  2037. self.mock_conn.getDomainCapabilities.return_value = xml # pylint: disable=no-member
  2038. caps = virt.domain_capabilities()
  2039. expected = {
  2040. 'emulator': '/usr/bin/qemu-system-aarch64',
  2041. 'domain': 'kvm',
  2042. 'machine': 'virt-2.12',
  2043. 'arch': 'aarch64',
  2044. 'max_vcpus': 255,
  2045. 'iothreads': True,
  2046. 'os': {
  2047. 'loader': {
  2048. 'type': ['rom', 'pflash'],
  2049. 'readonly': ['yes', 'no'],
  2050. 'values': [
  2051. '/usr/share/AAVMF/AAVMF_CODE.fd',
  2052. '/usr/share/AAVMF/AAVMF32_CODE.fd',
  2053. '/usr/share/OVMF/OVMF_CODE.fd'
  2054. ]
  2055. }
  2056. },
  2057. 'cpu': {
  2058. 'host-passthrough': True,
  2059. 'host-model': {
  2060. 'model': {
  2061. 'name': 'sample-cpu',
  2062. 'fallback': 'forbid'
  2063. },
  2064. 'vendor': 'ACME',
  2065. 'features': {
  2066. 'vme': 'require',
  2067. 'ss': 'require'
  2068. }
  2069. },
  2070. 'custom': {
  2071. 'models': {
  2072. 'pxa262': 'unknown',
  2073. 'pxa270-a0': 'yes',
  2074. 'arm1136': 'no'
  2075. }
  2076. }
  2077. },
  2078. 'devices': {
  2079. 'disk': {
  2080. 'diskDevice': ['disk', 'cdrom', 'floppy', 'lun'],
  2081. 'bus': ['fdc', 'scsi', 'virtio', 'usb', 'sata'],
  2082. },
  2083. 'graphics': {
  2084. 'type': ['sdl', 'vnc']
  2085. },
  2086. 'video': {
  2087. 'modelType': ['vga', 'virtio']
  2088. },
  2089. 'hostdev': {
  2090. 'mode': ['subsystem'],
  2091. 'startupPolicy': ['default', 'mandatory', 'requisite', 'optional'],
  2092. 'subsysType': ['usb', 'pci', 'scsi'],
  2093. 'capsType': [],
  2094. 'pciBackend': ['default', 'kvm', 'vfio']
  2095. }
  2096. },
  2097. 'features': {
  2098. 'gic': {
  2099. 'version': ['3']
  2100. },
  2101. 'vmcoreinfo': {}
  2102. }
  2103. }
  2104. self.assertEqual(expected, caps)
  2105. def test_network_tag(self):
  2106. '''
  2107. Test virt._get_net_xml() with VLAN tag
  2108. '''
  2109. xml_data = virt._gen_net_xml('network', 'main', 'bridge', 'openvswitch', 1001)
  2110. root = ET.fromstring(xml_data)
  2111. self.assertEqual(root.find('name').text, 'network')
  2112. self.assertEqual(root.find('bridge').attrib['name'], 'main')
  2113. self.assertEqual(root.find('forward').attrib['mode'], 'bridge')
  2114. self.assertEqual(root.find('virtualport').attrib['type'], 'openvswitch')
  2115. self.assertEqual(root.find('vlan/tag').attrib['id'], '1001')
  2116. def test_list_networks(self):
  2117. '''
  2118. Test virt.list_networks()
  2119. '''
  2120. names = ['net1', 'default', 'net2']
  2121. net_mocks = [MagicMock(), MagicMock(), MagicMock()]
  2122. for i, value in enumerate(names):
  2123. net_mocks[i].name.return_value = value
  2124. self.mock_conn.listAllNetworks.return_value = net_mocks # pylint: disable=no-member
  2125. actual = virt.list_networks()
  2126. self.assertEqual(names, actual)
  2127. def test_network_info(self):
  2128. '''
  2129. Test virt.network_info()
  2130. '''
  2131. self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV4 = 0
  2132. self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV6 = 1
  2133. net_mock = MagicMock()
  2134. # pylint: disable=no-member
  2135. net_mock.name.return_value = 'foo'
  2136. net_mock.UUIDString.return_value = 'some-uuid'
  2137. net_mock.bridgeName.return_value = 'br0'
  2138. net_mock.autostart.return_value = True
  2139. net_mock.isActive.return_value = False
  2140. net_mock.isPersistent.return_value = True
  2141. net_mock.DHCPLeases.return_value = [
  2142. {
  2143. 'iface': 'virbr0',
  2144. 'expirytime': 1527757552,
  2145. 'type': 0,
  2146. 'mac': '52:54:00:01:71:bd',
  2147. 'ipaddr': '192.168.122.45',
  2148. 'prefix': 24,
  2149. 'hostname': 'py3-test',
  2150. 'clientid': '01:52:54:00:01:71:bd',
  2151. 'iaid': None
  2152. }
  2153. ]
  2154. self.mock_conn.listAllNetworks.return_value = [net_mock]
  2155. # pylint: enable=no-member
  2156. net = virt.network_info('foo')
  2157. self.assertEqual({'foo': {
  2158. 'uuid': 'some-uuid',
  2159. 'bridge': 'br0',
  2160. 'autostart': True,
  2161. 'active': False,
  2162. 'persistent': True,
  2163. 'leases': [
  2164. {
  2165. 'iface': 'virbr0',
  2166. 'expirytime': 1527757552,
  2167. 'type': 'ipv4',
  2168. 'mac': '52:54:00:01:71:bd',
  2169. 'ipaddr': '192.168.122.45',
  2170. 'prefix': 24,
  2171. 'hostname': 'py3-test',
  2172. 'clientid': '01:52:54:00:01:71:bd',
  2173. 'iaid': None
  2174. }
  2175. ]}}, net)
  2176. def test_network_info_all(self):
  2177. '''
  2178. Test virt.network_info()
  2179. '''
  2180. self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV4 = 0
  2181. self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV6 = 1
  2182. net_mocks = []
  2183. # pylint: disable=no-member
  2184. for i in range(2):
  2185. net_mock = MagicMock()
  2186. net_mock.name.return_value = 'net{0}'.format(i)
  2187. net_mock.UUIDString.return_value = 'some-uuid'
  2188. net_mock.bridgeName.return_value = 'br{0}'.format(i)
  2189. net_mock.autostart.return_value = True
  2190. net_mock.isActive.return_value = False
  2191. net_mock.isPersistent.return_value = True
  2192. net_mock.DHCPLeases.return_value = []
  2193. net_mocks.append(net_mock)
  2194. self.mock_conn.listAllNetworks.return_value = net_mocks
  2195. # pylint: enable=no-member
  2196. net = virt.network_info()
  2197. self.assertEqual({
  2198. 'net0':
  2199. {
  2200. 'uuid': 'some-uuid',
  2201. 'bridge': 'br0',
  2202. 'autostart': True,
  2203. 'active': False,
  2204. 'persistent': True,
  2205. 'leases': []
  2206. }, 'net1':
  2207. {
  2208. 'uuid': 'some-uuid',
  2209. 'bridge': 'br1',
  2210. 'autostart': True,
  2211. 'active': False,
  2212. 'persistent': True,
  2213. 'leases': []
  2214. }
  2215. }, net)
  2216. def test_network_info_notfound(self):
  2217. '''
  2218. Test virt.network_info() when the network can't be found
  2219. '''
  2220. # pylint: disable=no-member
  2221. self.mock_conn.listAllNetworks.return_value = []
  2222. # pylint: enable=no-member
  2223. net = virt.network_info('foo')
  2224. self.assertEqual({}, net)
  2225. def test_pool(self):
  2226. '''
  2227. Test virt._gen_pool_xml()
  2228. '''
  2229. xml_data = virt._gen_pool_xml('pool', 'logical', '/dev/base')
  2230. root = ET.fromstring(xml_data)
  2231. self.assertEqual(root.find('name').text, 'pool')
  2232. self.assertEqual(root.attrib['type'], 'logical')
  2233. self.assertEqual(root.find('target/path').text, '/dev/base')
  2234. def test_pool_with_source(self):
  2235. '''
  2236. Test virt._gen_pool_xml() with a source device
  2237. '''
  2238. xml_data = virt._gen_pool_xml('pool', 'logical', '/dev/base', source_devices=[{'path': '/dev/sda'}])
  2239. root = ET.fromstring(xml_data)
  2240. self.assertEqual(root.find('name').text, 'pool')
  2241. self.assertEqual(root.attrib['type'], 'logical')
  2242. self.assertEqual(root.find('target/path').text, '/dev/base')
  2243. self.assertEqual(root.find('source/device').attrib['path'], '/dev/sda')
  2244. def test_pool_with_scsi(self):
  2245. '''
  2246. Test virt._gen_pool_xml() with a SCSI source
  2247. '''
  2248. xml_data = virt._gen_pool_xml('pool',
  2249. 'scsi',
  2250. '/dev/disk/by-path',
  2251. source_devices=[{'path': '/dev/sda'}],
  2252. source_adapter={
  2253. 'type': 'scsi_host',
  2254. 'parent_address': {
  2255. 'unique_id': 5,
  2256. 'address': {
  2257. 'domain': '0x0000',
  2258. 'bus': '0x00',
  2259. 'slot': '0x1f',
  2260. 'function': '0x2'
  2261. }
  2262. }
  2263. },
  2264. source_name='srcname')
  2265. root = ET.fromstring(xml_data)
  2266. self.assertEqual(root.find('name').text, 'pool')
  2267. self.assertEqual(root.attrib['type'], 'scsi')
  2268. self.assertEqual(root.find('target/path').text, '/dev/disk/by-path')
  2269. self.assertEqual(root.find('source/device'), None)
  2270. self.assertEqual(root.find('source/name'), None)
  2271. self.assertEqual(root.find('source/adapter').attrib['type'], 'scsi_host')
  2272. self.assertEqual(root.find('source/adapter/parentaddr').attrib['unique_id'], '5')
  2273. self.assertEqual(root.find('source/adapter/parentaddr/address').attrib['domain'], '0x0000')
  2274. self.assertEqual(root.find('source/adapter/parentaddr/address').attrib['bus'], '0x00')
  2275. self.assertEqual(root.find('source/adapter/parentaddr/address').attrib['slot'], '0x1f')
  2276. self.assertEqual(root.find('source/adapter/parentaddr/address').attrib['function'], '0x2')
  2277. def test_pool_with_rbd(self):
  2278. '''
  2279. Test virt._gen_pool_xml() with an RBD source
  2280. '''
  2281. xml_data = virt._gen_pool_xml('pool',
  2282. 'rbd',
  2283. source_devices=[{'path': '/dev/sda'}],
  2284. source_hosts=['1.2.3.4', 'my.ceph.monitor:69'],
  2285. source_auth={
  2286. 'type': 'ceph',
  2287. 'username': 'admin',
  2288. 'secret': {
  2289. 'type': 'uuid',
  2290. 'value': 'someuuid'
  2291. }
  2292. },
  2293. source_name='srcname',
  2294. source_adapter={'type': 'scsi_host', 'name': 'host0'},
  2295. source_dir='/some/dir',
  2296. source_format='fmt')
  2297. root = ET.fromstring(xml_data)
  2298. self.assertEqual(root.find('name').text, 'pool')
  2299. self.assertEqual(root.attrib['type'], 'rbd')
  2300. self.assertEqual(root.find('target'), None)
  2301. self.assertEqual(root.find('source/device'), None)
  2302. self.assertEqual(root.find('source/name').text, 'srcname')
  2303. self.assertEqual(root.find('source/adapter'), None)
  2304. self.assertEqual(root.find('source/dir'), None)
  2305. self.assertEqual(root.find('source/format'), None)
  2306. self.assertEqual(root.findall('source/host')[0].attrib['name'], '1.2.3.4')
  2307. self.assertTrue('port' not in root.findall('source/host')[0].attrib)
  2308. self.assertEqual(root.findall('source/host')[1].attrib['name'], 'my.ceph.monitor')
  2309. self.assertEqual(root.findall('source/host')[1].attrib['port'], '69')
  2310. self.assertEqual(root.find('source/auth').attrib['type'], 'ceph')
  2311. self.assertEqual(root.find('source/auth').attrib['username'], 'admin')
  2312. self.assertEqual(root.find('source/auth/secret').attrib['type'], 'uuid')
  2313. self.assertEqual(root.find('source/auth/secret').attrib['uuid'], 'someuuid')
  2314. def test_pool_with_netfs(self):
  2315. '''
  2316. Test virt._gen_pool_xml() with a netfs source
  2317. '''
  2318. xml_data = virt._gen_pool_xml('pool',
  2319. 'netfs',
  2320. target='/path/to/target',
  2321. permissions={
  2322. 'mode': '0770',
  2323. 'owner': 1000,
  2324. 'group': 100,
  2325. 'label': 'seclabel'
  2326. },
  2327. source_devices=[{'path': '/dev/sda'}],
  2328. source_hosts=['nfs.host'],
  2329. source_name='srcname',
  2330. source_adapter={'type': 'scsi_host', 'name': 'host0'},
  2331. source_dir='/some/dir',
  2332. source_format='nfs')
  2333. root = ET.fromstring(xml_data)
  2334. self.assertEqual(root.find('name').text, 'pool')
  2335. self.assertEqual(root.attrib['type'], 'netfs')
  2336. self.assertEqual(root.find('target/path').text, '/path/to/target')
  2337. self.assertEqual(root.find('target/permissions/mode').text, '0770')
  2338. self.assertEqual(root.find('target/permissions/owner').text, '1000')
  2339. self.assertEqual(root.find('target/permissions/group').text, '100')
  2340. self.assertEqual(root.find('target/permissions/label').text, 'seclabel')
  2341. self.assertEqual(root.find('source/device'), None)
  2342. self.assertEqual(root.find('source/name'), None)
  2343. self.assertEqual(root.find('source/adapter'), None)
  2344. self.assertEqual(root.find('source/dir').attrib['path'], '/some/dir')
  2345. self.assertEqual(root.find('source/format').attrib['type'], 'nfs')
  2346. self.assertEqual(root.find('source/host').attrib['name'], 'nfs.host')
  2347. self.assertEqual(root.find('source/auth'), None)
  2348. def test_list_pools(self):
  2349. '''
  2350. Test virt.list_pools()
  2351. '''
  2352. names = ['pool1', 'default', 'pool2']
  2353. pool_mocks = [MagicMock(), MagicMock(), MagicMock()]
  2354. for i, value in enumerate(names):
  2355. pool_mocks[i].name.return_value = value
  2356. self.mock_conn.listAllStoragePools.return_value = pool_mocks # pylint: disable=no-member
  2357. actual = virt.list_pools()
  2358. self.assertEqual(names, actual)
  2359. def test_pool_info(self):
  2360. '''
  2361. Test virt.pool_info()
  2362. '''
  2363. # pylint: disable=no-member
  2364. pool_mock = MagicMock()
  2365. pool_mock.name.return_value = 'foo'
  2366. pool_mock.UUIDString.return_value = 'some-uuid'
  2367. pool_mock.info.return_value = [0, 1234, 5678, 123]
  2368. pool_mock.autostart.return_value = True
  2369. pool_mock.isPersistent.return_value = True
  2370. pool_mock.XMLDesc.return_value = '''<pool type='dir'>
  2371. <name>default</name>
  2372. <uuid>d92682d0-33cf-4e10-9837-a216c463e158</uuid>
  2373. <capacity unit='bytes'>854374301696</capacity>
  2374. <allocation unit='bytes'>596275986432</allocation>
  2375. <available unit='bytes'>258098315264</available>
  2376. <source>
  2377. </source>
  2378. <target>
  2379. <path>/srv/vms</path>
  2380. <permissions>
  2381. <mode>0755</mode>
  2382. <owner>0</owner>
  2383. <group>0</group>
  2384. </permissions>
  2385. </target>
  2386. </pool>'''
  2387. self.mock_conn.listAllStoragePools.return_value = [pool_mock]
  2388. # pylint: enable=no-member
  2389. pool = virt.pool_info('foo')
  2390. self.assertEqual({'foo': {
  2391. 'uuid': 'some-uuid',
  2392. 'state': 'inactive',
  2393. 'capacity': 1234,
  2394. 'allocation': 5678,
  2395. 'free': 123,
  2396. 'autostart': True,
  2397. 'persistent': True,
  2398. 'type': 'dir',
  2399. 'target_path': '/srv/vms'}}, pool)
  2400. def test_pool_info_notarget(self):
  2401. '''
  2402. Test virt.pool_info()
  2403. '''
  2404. # pylint: disable=no-member
  2405. pool_mock = MagicMock()
  2406. pool_mock.name.return_value = 'ceph'
  2407. pool_mock.UUIDString.return_value = 'some-uuid'
  2408. pool_mock.info.return_value = [0, 0, 0, 0]
  2409. pool_mock.autostart.return_value = True
  2410. pool_mock.isPersistent.return_value = True
  2411. pool_mock.XMLDesc.return_value = '''<pool type='rbd'>
  2412. <name>ceph</name>
  2413. <uuid>some-uuid</uuid>
  2414. <capacity unit='bytes'>0</capacity>
  2415. <allocation unit='bytes'>0</allocation>
  2416. <available unit='bytes'>0</available>
  2417. <source>
  2418. <host name='localhost' port='6789'/>
  2419. <host name='localhost' port='6790'/>
  2420. <name>rbd</name>
  2421. <auth type='ceph' username='admin'>
  2422. <secret uuid='2ec115d7-3a88-3ceb-bc12-0ac909a6fd87'/>
  2423. </auth>
  2424. </source>
  2425. </pool>'''
  2426. self.mock_conn.listAllStoragePools.return_value = [pool_mock]
  2427. # pylint: enable=no-member
  2428. pool = virt.pool_info('ceph')
  2429. self.assertEqual({'ceph': {
  2430. 'uuid': 'some-uuid',
  2431. 'state': 'inactive',
  2432. 'capacity': 0,
  2433. 'allocation': 0,
  2434. 'free': 0,
  2435. 'autostart': True,
  2436. 'persistent': True,
  2437. 'type': 'rbd',
  2438. 'target_path': None}}, pool)
  2439. def test_pool_info_notfound(self):
  2440. '''
  2441. Test virt.pool_info() when the pool can't be found
  2442. '''
  2443. # pylint: disable=no-member
  2444. self.mock_conn.listAllStoragePools.return_value = []
  2445. # pylint: enable=no-member
  2446. pool = virt.pool_info('foo')
  2447. self.assertEqual({}, pool)
  2448. def test_pool_info_all(self):
  2449. '''
  2450. Test virt.pool_info()
  2451. '''
  2452. # pylint: disable=no-member
  2453. pool_mocks = []
  2454. for i in range(2):
  2455. pool_mock = MagicMock()
  2456. pool_mock.name.return_value = 'pool{0}'.format(i)
  2457. pool_mock.UUIDString.return_value = 'some-uuid-{0}'.format(i)
  2458. pool_mock.info.return_value = [0, 1234, 5678, 123]
  2459. pool_mock.autostart.return_value = True
  2460. pool_mock.isPersistent.return_value = True
  2461. pool_mock.XMLDesc.return_value = '''<pool type='dir'>
  2462. <name>default</name>
  2463. <uuid>d92682d0-33cf-4e10-9837-a216c463e158</uuid>
  2464. <capacity unit='bytes'>854374301696</capacity>
  2465. <allocation unit='bytes'>596275986432</allocation>
  2466. <available unit='bytes'>258098315264</available>
  2467. <source>
  2468. </source>
  2469. <target>
  2470. <path>/srv/vms</path>
  2471. <permissions>
  2472. <mode>0755</mode>
  2473. <owner>0</owner>
  2474. <group>0</group>
  2475. </permissions>
  2476. </target>
  2477. </pool>'''
  2478. pool_mocks.append(pool_mock)
  2479. self.mock_conn.listAllStoragePools.return_value = pool_mocks
  2480. # pylint: enable=no-member
  2481. pool = virt.pool_info()
  2482. self.assertEqual({
  2483. 'pool0':
  2484. {
  2485. 'uuid': 'some-uuid-0',
  2486. 'state': 'inactive',
  2487. 'capacity': 1234,
  2488. 'allocation': 5678,
  2489. 'free': 123,
  2490. 'autostart': True,
  2491. 'persistent': True,
  2492. 'type': 'dir',
  2493. 'target_path': '/srv/vms'
  2494. }, 'pool1': {
  2495. 'uuid': 'some-uuid-1',
  2496. 'state': 'inactive',
  2497. 'capacity': 1234,
  2498. 'allocation': 5678,
  2499. 'free': 123,
  2500. 'autostart': True,
  2501. 'persistent': True,
  2502. 'type': 'dir',
  2503. 'target_path': '/srv/vms'
  2504. }
  2505. }, pool)
  2506. def test_pool_list_volumes(self):
  2507. '''
  2508. Test virt.pool_list_volumes
  2509. '''
  2510. names = ['volume1', 'volume2']
  2511. mock_pool = MagicMock()
  2512. # pylint: disable=no-member
  2513. mock_pool.listVolumes.return_value = names
  2514. self.mock_conn.storagePoolLookupByName.return_value = mock_pool
  2515. # pylint: enable=no-member
  2516. self.assertEqual(names, virt.pool_list_volumes('default'))