test_zpool.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Tests for salt.states.zpool
  4. :codeauthor: Jorge Schrauwen <sjorge@blackdot.be>
  5. :maintainer: Jorge Schrauwen <sjorge@blackdot.be>
  6. :maturity: new
  7. :depends: salt.utils.zfs, salt.modules.zpool
  8. :platform: illumos,freebsd,linux
  9. '''
  10. # Import Python libs
  11. from __future__ import absolute_import, unicode_literals, print_function
  12. # Import Salt Testing Libs
  13. from tests.support.zfs import ZFSMockData
  14. from tests.support.mixins import LoaderModuleMockMixin
  15. from tests.support.unit import TestCase
  16. from tests.support.mock import (
  17. MagicMock,
  18. patch)
  19. # Import Salt Execution module to test
  20. import salt.utils.zfs
  21. import salt.states.zpool as zpool
  22. # Import Salt Utils
  23. import salt.loader
  24. from salt.utils.odict import OrderedDict
  25. class ZpoolTestCase(TestCase, LoaderModuleMockMixin):
  26. '''
  27. Test cases for salt.states.zpool
  28. '''
  29. def setup_loader_modules(self):
  30. self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy()
  31. self.utils_patch = ZFSMockData().get_patched_utils()
  32. for key in ('opts', 'utils_patch'):
  33. self.addCleanup(delattr, self, key)
  34. utils = salt.loader.utils(opts, whitelist=['zfs'])
  35. zpool_obj = {
  36. zpool: {
  37. '__opts__': opts,
  38. '__grains__': {'kernel': 'SunOS'},
  39. '__utils__': utils,
  40. }
  41. }
  42. return zpool_obj
  43. def test_absent_without_pool(self):
  44. '''
  45. Test zpool absent without a pool
  46. '''
  47. ret = {'name': 'myzpool',
  48. 'result': True,
  49. 'comment': 'storage pool myzpool is absent',
  50. 'changes': {}}
  51. mock_exists = MagicMock(return_value=False)
  52. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  53. patch.dict(zpool.__utils__, self.utils_patch):
  54. self.assertEqual(zpool.absent('myzpool'), ret)
  55. def test_absent_destroy_pool(self):
  56. '''
  57. Test zpool absent destroying pool
  58. '''
  59. ret = {
  60. 'name': 'myzpool',
  61. 'result': True,
  62. 'comment': 'storage pool myzpool was destroyed',
  63. 'changes': {'myzpool': 'destroyed'},
  64. }
  65. mock_exists = MagicMock(return_value=True)
  66. mock_destroy = MagicMock(return_value=OrderedDict([
  67. ('destroyed', True),
  68. ]))
  69. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  70. patch.dict(zpool.__salt__, {'zpool.destroy': mock_destroy}), \
  71. patch.dict(zpool.__utils__, self.utils_patch):
  72. self.assertEqual(zpool.absent('myzpool'), ret)
  73. def test_absent_exporty_pool(self):
  74. '''
  75. Test zpool absent exporting pool
  76. '''
  77. ret = {
  78. 'name': 'myzpool',
  79. 'result': True,
  80. 'comment': 'storage pool myzpool was exported',
  81. 'changes': {'myzpool': 'exported'},
  82. }
  83. mock_exists = MagicMock(return_value=True)
  84. mock_destroy = MagicMock(return_value=OrderedDict([
  85. ('exported', True),
  86. ]))
  87. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  88. patch.dict(zpool.__salt__, {'zpool.export': mock_destroy}), \
  89. patch.dict(zpool.__utils__, self.utils_patch):
  90. self.assertEqual(zpool.absent('myzpool', export=True), ret)
  91. def test_absent_busy(self):
  92. '''
  93. Test zpool absent on a busy pool
  94. '''
  95. ret = {
  96. 'name': 'myzpool',
  97. 'result': False,
  98. 'comment': "\n".join([
  99. "cannot unmount '/myzpool': Device busy",
  100. "cannot export 'myzpool': pool is busy",
  101. ]),
  102. 'changes': {},
  103. }
  104. mock_exists = MagicMock(return_value=True)
  105. mock_destroy = MagicMock(return_value=OrderedDict([
  106. ('exported', False),
  107. ('error', "\n".join([
  108. "cannot unmount '/myzpool': Device busy",
  109. "cannot export 'myzpool': pool is busy",
  110. ])),
  111. ]))
  112. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  113. patch.dict(zpool.__salt__, {'zpool.export': mock_destroy}), \
  114. patch.dict(zpool.__utils__, self.utils_patch):
  115. self.assertEqual(zpool.absent('myzpool', export=True), ret)
  116. def test_present_import_success(self):
  117. '''
  118. Test zpool present with import allowed and unimported pool
  119. '''
  120. ret = {'name': 'myzpool',
  121. 'result': True,
  122. 'comment': 'storage pool myzpool was imported',
  123. 'changes': {'myzpool': 'imported'}}
  124. config = {
  125. 'import': True,
  126. }
  127. mock_exists = MagicMock(return_value=False)
  128. mock_import = MagicMock(return_value=OrderedDict([
  129. ('imported', True),
  130. ]))
  131. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  132. patch.dict(zpool.__salt__, {'zpool.import': mock_import}), \
  133. patch.dict(zpool.__utils__, self.utils_patch):
  134. self.assertEqual(zpool.present('myzpool', config=config), ret)
  135. def test_present_import_fail(self):
  136. '''
  137. Test zpool present with import allowed and no unimported pool or layout
  138. '''
  139. ret = {'name': 'myzpool',
  140. 'result': False,
  141. 'comment': 'storage pool myzpool was not imported, no (valid) layout specified for creation',
  142. 'changes': {}}
  143. config = {
  144. 'import': True,
  145. }
  146. mock_exists = MagicMock(return_value=False)
  147. mock_import = MagicMock(return_value=OrderedDict([
  148. ('imported', False),
  149. ]))
  150. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  151. patch.dict(zpool.__salt__, {'zpool.import': mock_import}), \
  152. patch.dict(zpool.__utils__, self.utils_patch):
  153. self.assertEqual(zpool.present('myzpool', config=config), ret)
  154. def test_present_create_success(self):
  155. '''
  156. Test zpool present with non existing pool
  157. '''
  158. ret = {'name': 'myzpool',
  159. 'result': True,
  160. 'comment': 'storage pool myzpool was created',
  161. 'changes': {'myzpool': 'created'}}
  162. config = {
  163. 'import': False,
  164. }
  165. layout = [
  166. OrderedDict([('mirror', ['disk0', 'disk1'])]),
  167. OrderedDict([('mirror', ['disk2', 'disk3'])]),
  168. ]
  169. properties = {
  170. 'autoexpand': True,
  171. }
  172. filesystem_properties = {
  173. 'quota': '5G',
  174. }
  175. mock_exists = MagicMock(return_value=False)
  176. mock_create = MagicMock(return_value=OrderedDict([
  177. ('created', True),
  178. ('vdevs', OrderedDict([
  179. ('mirror-0', ['/dev/dsk/disk0', '/dev/dsk/disk1']),
  180. ('mirror-1', ['/dev/dsk/disk2', '/dev/dsk/disk3']),
  181. ])),
  182. ]))
  183. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  184. patch.dict(zpool.__salt__, {'zpool.create': mock_create}), \
  185. patch.dict(zpool.__utils__, self.utils_patch):
  186. self.assertEqual(
  187. zpool.present(
  188. 'myzpool',
  189. config=config,
  190. layout=layout,
  191. properties=properties,
  192. filesystem_properties=filesystem_properties,
  193. ),
  194. ret,
  195. )
  196. def test_present_create_fail(self):
  197. '''
  198. Test zpool present with non existing pool (without a layout)
  199. '''
  200. ret = {'name': 'myzpool',
  201. 'result': False,
  202. 'comment': 'storage pool myzpool was not imported, no (valid) layout specified for creation',
  203. 'changes': {}}
  204. config = {
  205. 'import': False,
  206. }
  207. mock_exists = MagicMock(return_value=False)
  208. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  209. patch.dict(zpool.__utils__, self.utils_patch):
  210. self.assertEqual(zpool.present('myzpool', config=config), ret)
  211. def test_present_create_passthrough_fail(self):
  212. '''
  213. Test zpool present with non existing pool (without a layout)
  214. '''
  215. ret = {'name': 'myzpool',
  216. 'result': False,
  217. 'comment': "\n".join([
  218. "invalid vdev specification",
  219. "use 'force=True' to override the following errors:",
  220. "/data/salt/vdisk0 is part of exported pool 'zsalt'",
  221. "/data/salt/vdisk1 is part of exported pool 'zsalt'",
  222. ]),
  223. 'changes': {}}
  224. config = {
  225. 'force': False,
  226. 'import': False,
  227. }
  228. layout = [
  229. OrderedDict([('mirror', ['disk0', 'disk1'])]),
  230. OrderedDict([('mirror', ['disk2', 'disk3'])]),
  231. ]
  232. properties = {
  233. 'autoexpand': True,
  234. }
  235. filesystem_properties = {
  236. 'quota': '5G',
  237. }
  238. mock_exists = MagicMock(return_value=False)
  239. mock_create = MagicMock(return_value=OrderedDict([
  240. ('created', False),
  241. ('error', "\n".join([
  242. "invalid vdev specification",
  243. "use 'force=True' to override the following errors:",
  244. "/data/salt/vdisk0 is part of exported pool 'zsalt'",
  245. "/data/salt/vdisk1 is part of exported pool 'zsalt'",
  246. ])),
  247. ]))
  248. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  249. patch.dict(zpool.__salt__, {'zpool.create': mock_create}), \
  250. patch.dict(zpool.__utils__, self.utils_patch):
  251. self.assertEqual(
  252. zpool.present(
  253. 'myzpool',
  254. config=config,
  255. layout=layout,
  256. properties=properties,
  257. filesystem_properties=filesystem_properties,
  258. ),
  259. ret,
  260. )
  261. def test_present_update_success(self):
  262. '''
  263. Test zpool present with an existing pool that needs an update
  264. '''
  265. ret = {'name': 'myzpool',
  266. 'result': True,
  267. 'comment': 'properties updated',
  268. 'changes': {'myzpool': {'autoexpand': False}}}
  269. config = {
  270. 'import': False,
  271. }
  272. layout = [
  273. OrderedDict([('mirror', ['disk0', 'disk1'])]),
  274. OrderedDict([('mirror', ['disk2', 'disk3'])]),
  275. ]
  276. properties = {
  277. 'autoexpand': False,
  278. }
  279. mock_exists = MagicMock(return_value=True)
  280. mock_get = MagicMock(return_value=OrderedDict([
  281. ('comment', 'salt managed pool'),
  282. ('freeing', 0),
  283. ('listsnapshots', False),
  284. ('leaked', 0),
  285. ('feature@obsolete_counts', 'enabled'),
  286. ('feature@sha512', 'enabled'),
  287. ('delegation', True),
  288. ('dedupditto', '0'),
  289. ('dedupratio', '1.00x'),
  290. ('autoexpand', True),
  291. ('feature@bookmarks', 'enabled'),
  292. ('allocated', 115712),
  293. ('guid', 1591906802560842214),
  294. ('feature@large_blocks', 'enabled'),
  295. ('size', 2113929216),
  296. ('feature@enabled_txg', 'active'),
  297. ('feature@hole_birth', 'active'),
  298. ('capacity', 0),
  299. ('feature@multi_vdev_crash_dump', 'enabled'),
  300. ('feature@extensible_dataset', 'enabled'),
  301. ('cachefile', '-'),
  302. ('bootfs', '-'),
  303. ('autoreplace', True),
  304. ('readonly', False),
  305. ('version', '-'),
  306. ('health', 'ONLINE'),
  307. ('expandsize', '-'),
  308. ('feature@embedded_data', 'active'),
  309. ('feature@lz4_compress', 'active'),
  310. ('feature@async_destroy', 'enabled'),
  311. ('feature@skein', 'enabled'),
  312. ('feature@empty_bpobj', 'enabled'),
  313. ('feature@spacemap_histogram', 'active'),
  314. ('bootsize', '-'),
  315. ('free', 2113813504),
  316. ('feature@device_removal', 'enabled'),
  317. ('failmode', 'wait'),
  318. ('feature@filesystem_limits', 'enabled'),
  319. ('feature@edonr', 'enabled'),
  320. ('altroot', '-'),
  321. ('fragmentation', '0%'),
  322. ]))
  323. mock_set = MagicMock(return_value=OrderedDict([
  324. ('set', True),
  325. ]))
  326. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  327. patch.dict(zpool.__salt__, {'zpool.get': mock_get}), \
  328. patch.dict(zpool.__salt__, {'zpool.set': mock_set}), \
  329. patch.dict(zpool.__utils__, self.utils_patch):
  330. self.assertEqual(
  331. zpool.present(
  332. 'myzpool',
  333. config=config,
  334. layout=layout,
  335. properties=properties,
  336. ),
  337. ret,
  338. )
  339. def test_present_update_nochange_success(self):
  340. '''
  341. Test zpool present with non existing pool
  342. '''
  343. ret = {'name': 'myzpool',
  344. 'result': True,
  345. 'comment': 'no update needed',
  346. 'changes': {}}
  347. config = {
  348. 'import': False,
  349. }
  350. layout = [
  351. OrderedDict([('mirror', ['disk0', 'disk1'])]),
  352. OrderedDict([('mirror', ['disk2', 'disk3'])]),
  353. ]
  354. properties = {
  355. 'autoexpand': True,
  356. }
  357. mock_exists = MagicMock(return_value=True)
  358. mock_get = MagicMock(return_value=OrderedDict([
  359. ('comment', 'salt managed pool'),
  360. ('freeing', 0),
  361. ('listsnapshots', False),
  362. ('leaked', 0),
  363. ('feature@obsolete_counts', 'enabled'),
  364. ('feature@sha512', 'enabled'),
  365. ('delegation', True),
  366. ('dedupditto', '0'),
  367. ('dedupratio', '1.00x'),
  368. ('autoexpand', True),
  369. ('feature@bookmarks', 'enabled'),
  370. ('allocated', 115712),
  371. ('guid', 1591906802560842214),
  372. ('feature@large_blocks', 'enabled'),
  373. ('size', 2113929216),
  374. ('feature@enabled_txg', 'active'),
  375. ('feature@hole_birth', 'active'),
  376. ('capacity', 0),
  377. ('feature@multi_vdev_crash_dump', 'enabled'),
  378. ('feature@extensible_dataset', 'enabled'),
  379. ('cachefile', '-'),
  380. ('bootfs', '-'),
  381. ('autoreplace', True),
  382. ('readonly', False),
  383. ('version', '-'),
  384. ('health', 'ONLINE'),
  385. ('expandsize', '-'),
  386. ('feature@embedded_data', 'active'),
  387. ('feature@lz4_compress', 'active'),
  388. ('feature@async_destroy', 'enabled'),
  389. ('feature@skein', 'enabled'),
  390. ('feature@empty_bpobj', 'enabled'),
  391. ('feature@spacemap_histogram', 'active'),
  392. ('bootsize', '-'),
  393. ('free', 2113813504),
  394. ('feature@device_removal', 'enabled'),
  395. ('failmode', 'wait'),
  396. ('feature@filesystem_limits', 'enabled'),
  397. ('feature@edonr', 'enabled'),
  398. ('altroot', '-'),
  399. ('fragmentation', '0%'),
  400. ]))
  401. with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \
  402. patch.dict(zpool.__salt__, {'zpool.get': mock_get}), \
  403. patch.dict(zpool.__utils__, self.utils_patch):
  404. self.assertEqual(
  405. zpool.present(
  406. 'myzpool',
  407. config=config,
  408. layout=layout,
  409. properties=properties,
  410. ),
  411. ret,
  412. )