test_zpool.py 18 KB

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