1
0

test_virt.py 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Jayesh Kariya <jayeshk@saltstack.com>
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import tempfile
  8. import shutil
  9. # Import Salt Testing Libs
  10. from tests.support.runtests import RUNTIME_VARS
  11. from tests.support.mixins import LoaderModuleMockMixin
  12. from tests.support.unit import TestCase
  13. from tests.support.mock import (
  14. MagicMock,
  15. mock_open,
  16. patch)
  17. # Import Salt Libs
  18. import salt.states.virt as virt
  19. import salt.utils.files
  20. from salt.exceptions import CommandExecutionError
  21. # Import 3rd-party libs
  22. from salt.ext import six
  23. class LibvirtMock(MagicMock): # pylint: disable=too-many-ancestors
  24. '''
  25. libvirt library mockup
  26. '''
  27. class libvirtError(Exception): # pylint: disable=invalid-name
  28. '''
  29. libvirt error mockup
  30. '''
  31. def get_error_message(self):
  32. '''
  33. Fake function return error message
  34. '''
  35. return six.text_type(self)
  36. class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
  37. '''
  38. Test cases for salt.states.libvirt
  39. '''
  40. def setup_loader_modules(self):
  41. self.mock_libvirt = LibvirtMock() # pylint: disable=attribute-defined-outside-init
  42. self.addCleanup(delattr, self, 'mock_libvirt')
  43. loader_globals = {
  44. 'libvirt': self.mock_libvirt
  45. }
  46. return {virt: loader_globals}
  47. @classmethod
  48. def setUpClass(cls):
  49. cls.pki_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  50. @classmethod
  51. def tearDownClass(cls):
  52. shutil.rmtree(cls.pki_dir)
  53. del cls.pki_dir
  54. # 'keys' function tests: 1
  55. def test_keys(self):
  56. '''
  57. Test to manage libvirt keys.
  58. '''
  59. with patch('os.path.isfile', MagicMock(return_value=False)):
  60. name = 'sunrise'
  61. ret = {'name': name,
  62. 'result': True,
  63. 'comment': '',
  64. 'changes': {}}
  65. mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'],
  66. {'libvirt.servercert.pem': 'A'}])
  67. with patch.dict(virt.__salt__, {'pillar.ext': mock}):
  68. comt = ('All keys are correct')
  69. ret.update({'comment': comt})
  70. self.assertDictEqual(virt.keys(name, basepath=self.pki_dir), ret)
  71. with patch.dict(virt.__opts__, {'test': True}):
  72. comt = ('Libvirt keys are set to be updated')
  73. ret.update({'comment': comt, 'result': None})
  74. self.assertDictEqual(virt.keys(name, basepath=self.pki_dir), ret)
  75. with patch.dict(virt.__opts__, {'test': False}):
  76. with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())):
  77. comt = ('Updated libvirt certs and keys')
  78. ret.update({'comment': comt, 'result': True,
  79. 'changes': {'servercert': 'new'}})
  80. self.assertDictEqual(virt.keys(name, basepath=self.pki_dir), ret)
  81. def test_keys_with_expiration_days(self):
  82. '''
  83. Test to manage libvirt keys.
  84. '''
  85. with patch('os.path.isfile', MagicMock(return_value=False)):
  86. name = 'sunrise'
  87. ret = {'name': name,
  88. 'result': True,
  89. 'comment': '',
  90. 'changes': {}}
  91. mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'],
  92. {'libvirt.servercert.pem': 'A'}])
  93. with patch.dict(virt.__salt__, {'pillar.ext': mock}):
  94. comt = ('All keys are correct')
  95. ret.update({'comment': comt})
  96. self.assertDictEqual(virt.keys(name,
  97. basepath=self.pki_dir,
  98. expiration_days=700), ret)
  99. with patch.dict(virt.__opts__, {'test': True}):
  100. comt = ('Libvirt keys are set to be updated')
  101. ret.update({'comment': comt, 'result': None})
  102. self.assertDictEqual(virt.keys(name,
  103. basepath=self.pki_dir,
  104. expiration_days=700), ret)
  105. with patch.dict(virt.__opts__, {'test': False}):
  106. with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())):
  107. comt = ('Updated libvirt certs and keys')
  108. ret.update({'comment': comt, 'result': True,
  109. 'changes': {'servercert': 'new'}})
  110. self.assertDictEqual(virt.keys(name,
  111. basepath=self.pki_dir,
  112. expiration_days=700), ret)
  113. def test_keys_with_state(self):
  114. '''
  115. Test to manage libvirt keys.
  116. '''
  117. with patch('os.path.isfile', MagicMock(return_value=False)):
  118. name = 'sunrise'
  119. ret = {'name': name,
  120. 'result': True,
  121. 'comment': '',
  122. 'changes': {}}
  123. mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'],
  124. {'libvirt.servercert.pem': 'A'}])
  125. with patch.dict(virt.__salt__, {'pillar.ext': mock}):
  126. comt = ('All keys are correct')
  127. ret.update({'comment': comt})
  128. self.assertDictEqual(virt.keys(name,
  129. basepath=self.pki_dir,
  130. st='California'), ret)
  131. with patch.dict(virt.__opts__, {'test': True}):
  132. comt = ('Libvirt keys are set to be updated')
  133. ret.update({'comment': comt, 'result': None})
  134. self.assertDictEqual(virt.keys(name,
  135. basepath=self.pki_dir,
  136. st='California'), ret)
  137. with patch.dict(virt.__opts__, {'test': False}):
  138. with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())):
  139. comt = ('Updated libvirt certs and keys')
  140. ret.update({'comment': comt, 'result': True,
  141. 'changes': {'servercert': 'new'}})
  142. self.assertDictEqual(virt.keys(name,
  143. basepath=self.pki_dir,
  144. st='California'), ret)
  145. def test_keys_with_all_options(self):
  146. '''
  147. Test to manage libvirt keys.
  148. '''
  149. with patch('os.path.isfile', MagicMock(return_value=False)):
  150. name = 'sunrise'
  151. ret = {'name': name,
  152. 'result': True,
  153. 'comment': '',
  154. 'changes': {}}
  155. mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'],
  156. {'libvirt.servercert.pem': 'A'}])
  157. with patch.dict(virt.__salt__, {'pillar.ext': mock}):
  158. comt = ('All keys are correct')
  159. ret.update({'comment': comt})
  160. self.assertDictEqual(virt.keys(name,
  161. basepath=self.pki_dir,
  162. country='USA',
  163. st='California',
  164. locality='Los_Angeles',
  165. organization='SaltStack',
  166. expiration_days=700), ret)
  167. with patch.dict(virt.__opts__, {'test': True}):
  168. comt = ('Libvirt keys are set to be updated')
  169. ret.update({'comment': comt, 'result': None})
  170. self.assertDictEqual(virt.keys(name,
  171. basepath=self.pki_dir,
  172. country='USA',
  173. st='California',
  174. locality='Los_Angeles',
  175. organization='SaltStack',
  176. expiration_days=700), ret)
  177. with patch.dict(virt.__opts__, {'test': False}):
  178. with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())):
  179. comt = ('Updated libvirt certs and keys')
  180. ret.update({'comment': comt, 'result': True,
  181. 'changes': {'servercert': 'new'}})
  182. self.assertDictEqual(virt.keys(name,
  183. basepath=self.pki_dir,
  184. country='USA',
  185. st='California',
  186. locality='Los_Angeles',
  187. organization='SaltStack',
  188. expiration_days=700), ret)
  189. def test_running(self):
  190. '''
  191. running state test cases.
  192. '''
  193. ret = {'name': 'myvm',
  194. 'changes': {},
  195. 'result': True,
  196. 'comment': 'myvm is running'}
  197. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  198. 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
  199. 'virt.start': MagicMock(return_value=0),
  200. }):
  201. ret.update({'changes': {'myvm': 'Domain started'},
  202. 'comment': 'Domain myvm started'})
  203. self.assertDictEqual(virt.running('myvm'), ret)
  204. init_mock = MagicMock(return_value=True)
  205. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  206. 'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')),
  207. 'virt.init': init_mock,
  208. 'virt.start': MagicMock(return_value=0)
  209. }):
  210. ret.update({'changes': {'myvm': 'Domain defined and started'},
  211. 'comment': 'Domain myvm defined and started'})
  212. self.assertDictEqual(virt.running('myvm',
  213. cpu=2,
  214. mem=2048,
  215. image='/path/to/img.qcow2'), ret)
  216. init_mock.assert_called_with('myvm', cpu=2, mem=2048, image='/path/to/img.qcow2',
  217. os_type=None, arch=None, boot=None,
  218. disk=None, disks=None, nic=None, interfaces=None,
  219. graphics=None, hypervisor=None,
  220. seed=True, install=True, pub_key=None, priv_key=None,
  221. connection=None, username=None, password=None)
  222. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  223. 'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')),
  224. 'virt.init': init_mock,
  225. 'virt.start': MagicMock(return_value=0)
  226. }):
  227. ret.update({'changes': {'myvm': 'Domain defined and started'},
  228. 'comment': 'Domain myvm defined and started'})
  229. disks = [{
  230. 'name': 'system',
  231. 'size': 8192,
  232. 'overlay_image': True,
  233. 'pool': 'default',
  234. 'image': '/path/to/image.qcow2'
  235. },
  236. {
  237. 'name': 'data',
  238. 'size': 16834
  239. }]
  240. ifaces = [{
  241. 'name': 'eth0',
  242. 'mac': '01:23:45:67:89:AB'
  243. },
  244. {
  245. 'name': 'eth1',
  246. 'type': 'network',
  247. 'source': 'admin'
  248. }]
  249. graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}}
  250. self.assertDictEqual(virt.running('myvm',
  251. cpu=2,
  252. mem=2048,
  253. os_type='linux',
  254. arch='i686',
  255. vm_type='qemu',
  256. disk_profile='prod',
  257. disks=disks,
  258. nic_profile='prod',
  259. interfaces=ifaces,
  260. graphics=graphics,
  261. seed=False,
  262. install=False,
  263. pub_key='/path/to/key.pub',
  264. priv_key='/path/to/key',
  265. connection='someconnection',
  266. username='libvirtuser',
  267. password='supersecret'), ret)
  268. init_mock.assert_called_with('myvm',
  269. cpu=2,
  270. mem=2048,
  271. os_type='linux',
  272. arch='i686',
  273. image=None,
  274. disk='prod',
  275. disks=disks,
  276. nic='prod',
  277. interfaces=ifaces,
  278. graphics=graphics,
  279. hypervisor='qemu',
  280. seed=False,
  281. boot=None,
  282. install=False,
  283. pub_key='/path/to/key.pub',
  284. priv_key='/path/to/key',
  285. connection='someconnection',
  286. username='libvirtuser',
  287. password='supersecret')
  288. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  289. 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
  290. 'virt.start': MagicMock(side_effect=[self.mock_libvirt.libvirtError('libvirt error msg')])
  291. }):
  292. ret.update({'changes': {}, 'result': False, 'comment': 'libvirt error msg'})
  293. self.assertDictEqual(virt.running('myvm'), ret)
  294. # Working update case when running
  295. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  296. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  297. 'virt.update': MagicMock(return_value={'definition': True, 'cpu': True})
  298. }):
  299. ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
  300. 'result': True,
  301. 'comment': 'Domain myvm updated, restart to fully apply the changes'})
  302. self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
  303. # Working update case when running with boot params
  304. boot = {
  305. 'kernel': '/root/f8-i386-vmlinuz',
  306. 'initrd': '/root/f8-i386-initrd',
  307. 'cmdline': 'console=ttyS0 ks=http://example.com/f8-i386/os/'
  308. }
  309. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  310. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  311. 'virt.update': MagicMock(return_value={'definition': True, 'cpu': True})
  312. }):
  313. ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
  314. 'result': True,
  315. 'comment': 'Domain myvm updated, restart to fully apply the changes'})
  316. self.assertDictEqual(virt.running('myvm', update=True, boot=boot), ret)
  317. # Working update case when stopped
  318. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  319. 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}),
  320. 'virt.start': MagicMock(return_value=0),
  321. 'virt.update': MagicMock(return_value={'definition': True})
  322. }):
  323. ret.update({'changes': {'myvm': 'Domain updated and started'},
  324. 'result': True,
  325. 'comment': 'Domain myvm updated and started'})
  326. self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
  327. # Failed live update case
  328. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  329. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  330. 'virt.update': MagicMock(return_value={'definition': True, 'cpu': False, 'errors': ['some error']})
  331. }):
  332. ret.update({'changes': {'myvm': {'definition': True, 'cpu': False, 'errors': ['some error']}},
  333. 'result': True,
  334. 'comment': 'Domain myvm updated, but some live update(s) failed'})
  335. self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
  336. # Failed definition update case
  337. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  338. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  339. 'virt.update': MagicMock(side_effect=[self.mock_libvirt.libvirtError('error message')])
  340. }):
  341. ret.update({'changes': {},
  342. 'result': False,
  343. 'comment': 'error message'})
  344. self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
  345. def test_stopped(self):
  346. '''
  347. stopped state test cases.
  348. '''
  349. ret = {'name': 'myvm',
  350. 'changes': {},
  351. 'result': True}
  352. shutdown_mock = MagicMock(return_value=True)
  353. # Normal case
  354. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  355. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  356. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  357. 'virt.shutdown': shutdown_mock
  358. }):
  359. ret.update({'changes': {
  360. 'stopped': [{'domain': 'myvm', 'shutdown': True}]
  361. },
  362. 'comment': 'Machine has been shut down'})
  363. self.assertDictEqual(virt.stopped('myvm'), ret)
  364. shutdown_mock.assert_called_with('myvm', connection=None, username=None, password=None)
  365. # Normal case with user-provided connection parameters
  366. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  367. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  368. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  369. 'virt.shutdown': shutdown_mock,
  370. }):
  371. self.assertDictEqual(virt.stopped('myvm',
  372. connection='myconnection',
  373. username='user',
  374. password='secret'), ret)
  375. shutdown_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret')
  376. # Case where an error occurred during the shutdown
  377. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  378. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  379. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  380. 'virt.shutdown': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
  381. }):
  382. ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
  383. 'result': False,
  384. 'comment': 'No changes had happened'})
  385. self.assertDictEqual(virt.stopped('myvm'), ret)
  386. # Case there the domain doesn't exist
  387. with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
  388. ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
  389. self.assertDictEqual(virt.stopped('myvm'), ret)
  390. # Case where the domain is already stopped
  391. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  392. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  393. 'virt.vm_state': MagicMock(return_value={'myvm': 'shutdown'})
  394. }):
  395. ret.update({'changes': {},
  396. 'result': True,
  397. 'comment': 'No changes had happened'})
  398. self.assertDictEqual(virt.stopped('myvm'), ret)
  399. def test_powered_off(self):
  400. '''
  401. powered_off state test cases.
  402. '''
  403. ret = {'name': 'myvm',
  404. 'changes': {},
  405. 'result': True}
  406. stop_mock = MagicMock(return_value=True)
  407. # Normal case
  408. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  409. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  410. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  411. 'virt.stop': stop_mock
  412. }):
  413. ret.update({'changes': {
  414. 'unpowered': [{'domain': 'myvm', 'stop': True}]
  415. },
  416. 'comment': 'Machine has been powered off'})
  417. self.assertDictEqual(virt.powered_off('myvm'), ret)
  418. stop_mock.assert_called_with('myvm', connection=None, username=None, password=None)
  419. # Normal case with user-provided connection parameters
  420. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  421. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  422. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  423. 'virt.stop': stop_mock,
  424. }):
  425. self.assertDictEqual(virt.powered_off('myvm',
  426. connection='myconnection',
  427. username='user',
  428. password='secret'), ret)
  429. stop_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret')
  430. # Case where an error occurred during the poweroff
  431. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  432. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  433. 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}),
  434. 'virt.stop': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
  435. }):
  436. ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
  437. 'result': False,
  438. 'comment': 'No changes had happened'})
  439. self.assertDictEqual(virt.powered_off('myvm'), ret)
  440. # Case there the domain doesn't exist
  441. with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
  442. ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
  443. self.assertDictEqual(virt.powered_off('myvm'), ret)
  444. # Case where the domain is already stopped
  445. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  446. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  447. 'virt.vm_state': MagicMock(return_value={'myvm': 'shutdown'})
  448. }):
  449. ret.update({'changes': {},
  450. 'result': True,
  451. 'comment': 'No changes had happened'})
  452. self.assertDictEqual(virt.powered_off('myvm'), ret)
  453. def test_snapshot(self):
  454. '''
  455. snapshot state test cases.
  456. '''
  457. ret = {'name': 'myvm',
  458. 'changes': {},
  459. 'result': True}
  460. snapshot_mock = MagicMock(return_value=True)
  461. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  462. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  463. 'virt.snapshot': snapshot_mock
  464. }):
  465. ret.update({'changes': {
  466. 'saved': [{'domain': 'myvm', 'snapshot': True}]
  467. },
  468. 'comment': 'Snapshot has been taken'})
  469. self.assertDictEqual(virt.snapshot('myvm'), ret)
  470. snapshot_mock.assert_called_with('myvm', suffix=None, connection=None, username=None, password=None)
  471. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  472. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  473. 'virt.snapshot': snapshot_mock,
  474. }):
  475. self.assertDictEqual(virt.snapshot('myvm',
  476. suffix='snap',
  477. connection='myconnection',
  478. username='user',
  479. password='secret'), ret)
  480. snapshot_mock.assert_called_with('myvm',
  481. suffix='snap',
  482. connection='myconnection',
  483. username='user',
  484. password='secret')
  485. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  486. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  487. 'virt.snapshot': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
  488. }):
  489. ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
  490. 'result': False,
  491. 'comment': 'No changes had happened'})
  492. self.assertDictEqual(virt.snapshot('myvm'), ret)
  493. with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
  494. ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
  495. self.assertDictEqual(virt.snapshot('myvm'), ret)
  496. def test_rebooted(self):
  497. '''
  498. rebooted state test cases.
  499. '''
  500. ret = {'name': 'myvm',
  501. 'changes': {},
  502. 'result': True}
  503. reboot_mock = MagicMock(return_value=True)
  504. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  505. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  506. 'virt.reboot': reboot_mock
  507. }):
  508. ret.update({'changes': {
  509. 'rebooted': [{'domain': 'myvm', 'reboot': True}]
  510. },
  511. 'comment': 'Machine has been rebooted'})
  512. self.assertDictEqual(virt.rebooted('myvm'), ret)
  513. reboot_mock.assert_called_with('myvm', connection=None, username=None, password=None)
  514. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  515. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  516. 'virt.reboot': reboot_mock,
  517. }):
  518. self.assertDictEqual(virt.rebooted('myvm',
  519. connection='myconnection',
  520. username='user',
  521. password='secret'), ret)
  522. reboot_mock.assert_called_with('myvm',
  523. connection='myconnection',
  524. username='user',
  525. password='secret')
  526. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  527. 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
  528. 'virt.reboot': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
  529. }):
  530. ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
  531. 'result': False,
  532. 'comment': 'No changes had happened'})
  533. self.assertDictEqual(virt.rebooted('myvm'), ret)
  534. with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
  535. ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
  536. self.assertDictEqual(virt.rebooted('myvm'), ret)
  537. def test_network_running(self):
  538. '''
  539. network_running state test cases.
  540. '''
  541. ret = {'name': 'mynet', 'changes': {}, 'result': True, 'comment': ''}
  542. define_mock = MagicMock(return_value=True)
  543. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  544. 'virt.network_info': MagicMock(return_value={}),
  545. 'virt.network_define': define_mock
  546. }):
  547. ret.update({'changes': {'mynet': 'Network defined and started'},
  548. 'comment': 'Network mynet defined and started'})
  549. self.assertDictEqual(virt.network_running('mynet',
  550. 'br2',
  551. 'bridge',
  552. vport='openvswitch',
  553. tag=180,
  554. ipv4_config={
  555. 'cidr': '192.168.2.0/24',
  556. 'dhcp_ranges': [
  557. {'start': '192.168.2.10', 'end': '192.168.2.25'},
  558. {'start': '192.168.2.110', 'end': '192.168.2.125'},
  559. ]
  560. },
  561. ipv6_config={
  562. 'cidr': '2001:db8:ca2:2::1/64',
  563. 'dhcp_ranges': [
  564. {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
  565. ]
  566. },
  567. autostart=False,
  568. connection='myconnection',
  569. username='user',
  570. password='secret'), ret)
  571. define_mock.assert_called_with('mynet',
  572. 'br2',
  573. 'bridge',
  574. vport='openvswitch',
  575. tag=180,
  576. autostart=False,
  577. start=True,
  578. ipv4_config={
  579. 'cidr': '192.168.2.0/24',
  580. 'dhcp_ranges': [
  581. {'start': '192.168.2.10', 'end': '192.168.2.25'},
  582. {'start': '192.168.2.110', 'end': '192.168.2.125'},
  583. ]
  584. },
  585. ipv6_config={
  586. 'cidr': '2001:db8:ca2:2::1/64',
  587. 'dhcp_ranges': [
  588. {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'},
  589. ]
  590. },
  591. connection='myconnection',
  592. username='user',
  593. password='secret')
  594. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  595. 'virt.network_info': MagicMock(return_value={'mynet': {'active': True}}),
  596. 'virt.network_define': define_mock,
  597. }):
  598. ret.update({'changes': {}, 'comment': 'Network mynet exists and is running'})
  599. self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
  600. start_mock = MagicMock(return_value=True)
  601. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  602. 'virt.network_info': MagicMock(return_value={'mynet': {'active': False}}),
  603. 'virt.network_start': start_mock,
  604. 'virt.network_define': define_mock,
  605. }):
  606. ret.update({'changes': {'mynet': 'Network started'}, 'comment': 'Network mynet started'})
  607. self.assertDictEqual(virt.network_running('mynet',
  608. 'br2',
  609. 'bridge',
  610. connection='myconnection',
  611. username='user',
  612. password='secret'), ret)
  613. start_mock.assert_called_with('mynet', connection='myconnection', username='user', password='secret')
  614. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  615. 'virt.network_info': MagicMock(return_value={}),
  616. 'virt.network_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
  617. }):
  618. ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
  619. self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
  620. def test_pool_running(self):
  621. '''
  622. pool_running state test cases.
  623. '''
  624. ret = {'name': 'mypool', 'changes': {}, 'result': True, 'comment': ''}
  625. mocks = {mock: MagicMock(return_value=True) for mock in ['define', 'autostart', 'build', 'start', 'stop']}
  626. with patch.dict(virt.__opts__, {'test': False}):
  627. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  628. 'virt.pool_info': MagicMock(return_value={}),
  629. 'virt.pool_define': mocks['define'],
  630. 'virt.pool_build': mocks['build'],
  631. 'virt.pool_start': mocks['start'],
  632. 'virt.pool_set_autostart': mocks['autostart']
  633. }):
  634. ret.update({'changes': {'mypool': 'Pool defined, started and marked for autostart'},
  635. 'comment': 'Pool mypool defined, started and marked for autostart'})
  636. self.assertDictEqual(virt.pool_running('mypool',
  637. ptype='logical',
  638. target='/dev/base',
  639. permissions={'mode': '0770',
  640. 'owner': 1000,
  641. 'group': 100,
  642. 'label': 'seclabel'},
  643. source={'devices': [{'path': '/dev/sda'}]},
  644. transient=True,
  645. autostart=True,
  646. connection='myconnection',
  647. username='user',
  648. password='secret'), ret)
  649. mocks['define'].assert_called_with('mypool',
  650. ptype='logical',
  651. target='/dev/base',
  652. permissions={'mode': '0770',
  653. 'owner': 1000,
  654. 'group': 100,
  655. 'label': 'seclabel'},
  656. source_devices=[{'path': '/dev/sda'}],
  657. source_dir=None,
  658. source_adapter=None,
  659. source_hosts=None,
  660. source_auth=None,
  661. source_name=None,
  662. source_format=None,
  663. source_initiator=None,
  664. transient=True,
  665. start=False,
  666. connection='myconnection',
  667. username='user',
  668. password='secret')
  669. mocks['autostart'].assert_called_with('mypool',
  670. state='on',
  671. connection='myconnection',
  672. username='user',
  673. password='secret')
  674. mocks['build'].assert_called_with('mypool',
  675. connection='myconnection',
  676. username='user',
  677. password='secret')
  678. mocks['start'].assert_called_with('mypool',
  679. connection='myconnection',
  680. username='user',
  681. password='secret')
  682. mocks['update'] = MagicMock(return_value=False)
  683. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  684. 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': True}}),
  685. 'virt.pool_update': MagicMock(return_value=False),
  686. }):
  687. ret.update({'changes': {}, 'comment': 'Pool mypool unchanged and is running'})
  688. self.assertDictEqual(virt.pool_running('mypool',
  689. ptype='logical',
  690. target='/dev/base',
  691. source={'devices': [{'path': '/dev/sda'}]}), ret)
  692. for mock in mocks:
  693. mocks[mock].reset_mock()
  694. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  695. 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped', 'autostart': True}}),
  696. 'virt.pool_update': mocks['update'],
  697. 'virt.pool_build': mocks['build'],
  698. 'virt.pool_start': mocks['start']
  699. }):
  700. ret.update({'changes': {'mypool': 'Pool started'}, 'comment': 'Pool mypool started'})
  701. self.assertDictEqual(virt.pool_running('mypool',
  702. ptype='logical',
  703. target='/dev/base',
  704. source={'devices': [{'path': '/dev/sda'}]}), ret)
  705. mocks['start'].assert_called_with('mypool', connection=None, username=None, password=None)
  706. mocks['build'].assert_not_called()
  707. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  708. 'virt.pool_info': MagicMock(return_value={}),
  709. 'virt.pool_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
  710. }):
  711. ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
  712. self.assertDictEqual(virt.pool_running('mypool',
  713. ptype='logical',
  714. target='/dev/base',
  715. source={'devices': [{'path': '/dev/sda'}]}), ret)
  716. # Test case with update and autostart change on stopped pool
  717. for mock in mocks:
  718. mocks[mock].reset_mock()
  719. mocks['update'] = MagicMock(return_value=True)
  720. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  721. 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped', 'autostart': True}}),
  722. 'virt.pool_update': mocks['update'],
  723. 'virt.pool_set_autostart': mocks['autostart'],
  724. 'virt.pool_build': mocks['build'],
  725. 'virt.pool_start': mocks['start']
  726. }):
  727. ret.update({'changes': {'mypool': 'Pool updated, built, autostart flag changed and started'},
  728. 'comment': 'Pool mypool updated, built, autostart flag changed and started',
  729. 'result': True})
  730. self.assertDictEqual(virt.pool_running('mypool',
  731. ptype='logical',
  732. target='/dev/base',
  733. autostart=False,
  734. permissions={'mode': '0770',
  735. 'owner': 1000,
  736. 'group': 100,
  737. 'label': 'seclabel'},
  738. source={'devices': [{'path': '/dev/sda'}]}), ret)
  739. mocks['start'].assert_called_with('mypool', connection=None, username=None, password=None)
  740. mocks['build'].assert_called_with('mypool', connection=None, username=None, password=None)
  741. mocks['autostart'].assert_called_with('mypool', state='off',
  742. connection=None, username=None, password=None)
  743. mocks['update'].assert_called_with('mypool',
  744. ptype='logical',
  745. target='/dev/base',
  746. permissions={'mode': '0770',
  747. 'owner': 1000,
  748. 'group': 100,
  749. 'label': 'seclabel'},
  750. source_devices=[{'path': '/dev/sda'}],
  751. source_dir=None,
  752. source_adapter=None,
  753. source_hosts=None,
  754. source_auth=None,
  755. source_name=None,
  756. source_format=None,
  757. source_initiator=None,
  758. connection=None,
  759. username=None,
  760. password=None)
  761. # test case with update and no autostart change on running pool
  762. for mock in mocks:
  763. mocks[mock].reset_mock()
  764. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  765. 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': False}}),
  766. 'virt.pool_update': mocks['update'],
  767. 'virt.pool_build': mocks['build'],
  768. 'virt.pool_start': mocks['start'],
  769. 'virt.pool_stop': mocks['stop']
  770. }):
  771. ret.update({'changes': {'mypool': 'Pool updated, built and restarted'},
  772. 'comment': 'Pool mypool updated, built and restarted',
  773. 'result': True})
  774. self.assertDictEqual(virt.pool_running('mypool',
  775. ptype='logical',
  776. target='/dev/base',
  777. autostart=False,
  778. permissions={'mode': '0770',
  779. 'owner': 1000,
  780. 'group': 100,
  781. 'label': 'seclabel'},
  782. source={'devices': [{'path': '/dev/sda'}]}), ret)
  783. mocks['stop'].assert_called_with('mypool', connection=None, username=None, password=None)
  784. mocks['start'].assert_called_with('mypool', connection=None, username=None, password=None)
  785. mocks['build'].assert_called_with('mypool', connection=None, username=None, password=None)
  786. mocks['update'].assert_called_with('mypool',
  787. ptype='logical',
  788. target='/dev/base',
  789. permissions={'mode': '0770',
  790. 'owner': 1000,
  791. 'group': 100,
  792. 'label': 'seclabel'},
  793. source_devices=[{'path': '/dev/sda'}],
  794. source_dir=None,
  795. source_adapter=None,
  796. source_hosts=None,
  797. source_auth=None,
  798. source_name=None,
  799. source_format=None,
  800. source_initiator=None,
  801. connection=None,
  802. username=None,
  803. password=None)
  804. with patch.dict(virt.__opts__, {'test': True}):
  805. # test case with test=True and no change
  806. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  807. 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': True}}),
  808. 'virt.pool_update': MagicMock(return_value=False),
  809. }):
  810. ret.update({'changes': {}, 'comment': 'Pool mypool unchanged and is running',
  811. 'result': True})
  812. self.assertDictEqual(virt.pool_running('mypool',
  813. ptype='logical',
  814. target='/dev/base',
  815. source={'devices': [{'path': '/dev/sda'}]}), ret)
  816. # test case with test=True and started
  817. for mock in mocks:
  818. mocks[mock].reset_mock()
  819. mocks['update'] = MagicMock(return_value=False)
  820. with patch.dict(virt.__salt__, { # pylint: disable=no-member
  821. 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped', 'autostart': True}}),
  822. 'virt.pool_update': mocks['update']
  823. }):
  824. ret.update({'changes': {'mypool': 'Pool started'},
  825. 'comment': 'Pool mypool started',
  826. 'result': None})
  827. self.assertDictEqual(virt.pool_running('mypool',
  828. ptype='logical',
  829. target='/dev/base',
  830. source={'devices': [{'path': '/dev/sda'}]}), ret)
  831. def test_pool_deleted(self):
  832. '''
  833. Test the pool_deleted state
  834. '''
  835. # purge=False test case, stopped pool
  836. with patch.dict(virt.__salt__, {
  837. 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'stopped', 'type': 'dir'}}),
  838. 'virt.pool_undefine': MagicMock(return_value=True)
  839. }):
  840. expected = {
  841. 'name': 'test01',
  842. 'changes': {
  843. 'stopped': False,
  844. 'deleted_volumes': [],
  845. 'deleted': False,
  846. 'undefined': True,
  847. },
  848. 'result': True,
  849. 'comment': '',
  850. }
  851. with patch.dict(virt.__opts__, {'test': False}):
  852. self.assertDictEqual(expected, virt.pool_deleted('test01'))
  853. with patch.dict(virt.__opts__, {'test': True}):
  854. expected['result'] = None
  855. self.assertDictEqual(expected, virt.pool_deleted('test01'))
  856. # purge=False test case
  857. with patch.dict(virt.__salt__, {
  858. 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'dir'}}),
  859. 'virt.pool_undefine': MagicMock(return_value=True),
  860. 'virt.pool_stop': MagicMock(return_value=True)
  861. }):
  862. expected = {
  863. 'name': 'test01',
  864. 'changes': {
  865. 'stopped': True,
  866. 'deleted_volumes': [],
  867. 'deleted': False,
  868. 'undefined': True,
  869. },
  870. 'result': True,
  871. 'comment': '',
  872. }
  873. with patch.dict(virt.__opts__, {'test': False}):
  874. self.assertDictEqual(expected, virt.pool_deleted('test01'))
  875. with patch.dict(virt.__opts__, {'test': True}):
  876. expected['result'] = None
  877. self.assertDictEqual(expected, virt.pool_deleted('test01'))
  878. # purge=True test case
  879. with patch.dict(virt.__salt__, {
  880. 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'dir'}}),
  881. 'virt.pool_list_volumes': MagicMock(return_value=['vm01.qcow2', 'vm02.qcow2']),
  882. 'virt.pool_refresh': MagicMock(return_value=True),
  883. 'virt.volume_delete': MagicMock(return_value=True),
  884. 'virt.pool_stop': MagicMock(return_value=True),
  885. 'virt.pool_delete': MagicMock(return_value=True),
  886. 'virt.pool_undefine': MagicMock(return_value=True)
  887. }):
  888. expected = {
  889. 'name': 'test01',
  890. 'changes': {
  891. 'stopped': True,
  892. 'deleted_volumes': ['vm01.qcow2', 'vm02.qcow2'],
  893. 'deleted': True,
  894. 'undefined': True,
  895. },
  896. 'result': True,
  897. 'comment': '',
  898. }
  899. with patch.dict(virt.__opts__, {'test': False}):
  900. self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True))
  901. with patch.dict(virt.__opts__, {'test': True}):
  902. expected['result'] = None
  903. self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True))
  904. # Case of backend not unsupporting delete operations
  905. with patch.dict(virt.__salt__, {
  906. 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'iscsi'}}),
  907. 'virt.pool_stop': MagicMock(return_value=True),
  908. 'virt.pool_undefine': MagicMock(return_value=True)
  909. }):
  910. expected = {
  911. 'name': 'test01',
  912. 'changes': {
  913. 'stopped': True,
  914. 'deleted_volumes': [],
  915. 'deleted': False,
  916. 'undefined': True,
  917. },
  918. 'result': True,
  919. 'comment': 'Unsupported actions for pool of type "iscsi": deleting volume, deleting pool',
  920. }
  921. with patch.dict(virt.__opts__, {'test': False}):
  922. self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True))
  923. with patch.dict(virt.__opts__, {'test': True}):
  924. expected['result'] = None
  925. self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True))