test_zfs.py 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  1. """
  2. Tests for the zfs utils library
  3. :codeauthor: Jorge Schrauwen <sjorge@blackdot.be>
  4. :maintainer: Jorge Schrauwen <sjorge@blackdot.be>
  5. :maturity: new
  6. :platform: illumos,freebsd,linux
  7. .. versionadded:: 2018.3.1
  8. """
  9. import salt.utils.zfs as zfs
  10. from salt.utils.odict import OrderedDict
  11. from tests.support.mock import MagicMock, patch
  12. from tests.support.unit import TestCase
  13. from tests.support.zfs import ZFSMockData
  14. class ZfsUtilsTestCase(TestCase):
  15. """
  16. This class contains a set of functions that test salt.utils.zfs utils
  17. """
  18. def setUp(self):
  19. # property_map mocks
  20. mock_data = ZFSMockData()
  21. self.pmap_zfs = mock_data.pmap_zfs
  22. self.pmap_zpool = mock_data.pmap_zpool
  23. self.pmap_exec_zfs = mock_data.pmap_exec_zfs
  24. self.pmap_exec_zpool = mock_data.pmap_exec_zpool
  25. for name in ("pmap_zfs", "pmap_zpool", "pmap_exec_zfs", "pmap_exec_zpool"):
  26. self.addCleanup(delattr, self, name)
  27. # NOTE: test parameter parsing
  28. def test_is_supported(self):
  29. """
  30. Test zfs.is_supported method
  31. """
  32. for value in [False, True]:
  33. with patch("salt.utils.path.which", MagicMock(return_value=value)):
  34. with patch(
  35. "salt.utils.platform.is_linux", MagicMock(return_value=value)
  36. ):
  37. self.assertEqual(value, zfs.is_supported())
  38. def test_property_data_zpool(self):
  39. """
  40. Test parsing of zpool get output
  41. """
  42. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  43. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  44. with patch.object(
  45. zfs, "_exec", MagicMock(return_value=self.pmap_exec_zpool)
  46. ):
  47. self.assertEqual(zfs.property_data_zpool(), self.pmap_zpool)
  48. def test_property_data_zfs(self):
  49. """
  50. Test parsing of zfs get output
  51. """
  52. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  53. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  54. with patch.object(
  55. zfs, "_exec", MagicMock(return_value=self.pmap_exec_zfs)
  56. ):
  57. self.assertEqual(zfs.property_data_zfs(), self.pmap_zfs)
  58. # NOTE: testing from_bool results
  59. def test_from_bool_on(self):
  60. """
  61. Test from_bool with 'on'
  62. """
  63. self.assertTrue(zfs.from_bool("on"))
  64. self.assertTrue(zfs.from_bool(zfs.from_bool("on")))
  65. def test_from_bool_off(self):
  66. """
  67. Test from_bool with 'off'
  68. """
  69. self.assertFalse(zfs.from_bool("off"))
  70. self.assertFalse(zfs.from_bool(zfs.from_bool("off")))
  71. def test_from_bool_none(self):
  72. """
  73. Test from_bool with 'none'
  74. """
  75. self.assertEqual(zfs.from_bool("none"), None)
  76. self.assertEqual(zfs.from_bool(zfs.from_bool("none")), None)
  77. def test_from_bool_passthrough(self):
  78. """
  79. Test from_bool with 'passthrough'
  80. """
  81. self.assertEqual(zfs.from_bool("passthrough"), "passthrough")
  82. self.assertEqual(zfs.from_bool(zfs.from_bool("passthrough")), "passthrough")
  83. def test_from_bool_alt_yes(self):
  84. """
  85. Test from_bool_alt with 'yes'
  86. """
  87. self.assertTrue(zfs.from_bool_alt("yes"))
  88. self.assertTrue(zfs.from_bool_alt(zfs.from_bool_alt("yes")))
  89. def test_from_bool_alt_no(self):
  90. """
  91. Test from_bool_alt with 'no'
  92. """
  93. self.assertFalse(zfs.from_bool_alt("no"))
  94. self.assertFalse(zfs.from_bool_alt(zfs.from_bool_alt("no")))
  95. def test_from_bool_alt_none(self):
  96. """
  97. Test from_bool_alt with 'none'
  98. """
  99. self.assertEqual(zfs.from_bool_alt("none"), None)
  100. self.assertEqual(zfs.from_bool_alt(zfs.from_bool_alt("none")), None)
  101. def test_from_bool_alt_passthrough(self):
  102. """
  103. Test from_bool_alt with 'passthrough'
  104. """
  105. self.assertEqual(zfs.from_bool_alt("passthrough"), "passthrough")
  106. self.assertEqual(
  107. zfs.from_bool_alt(zfs.from_bool_alt("passthrough")), "passthrough"
  108. )
  109. # NOTE: testing to_bool results
  110. def test_to_bool_true(self):
  111. """
  112. Test to_bool with True
  113. """
  114. self.assertEqual(zfs.to_bool(True), "on")
  115. self.assertEqual(zfs.to_bool(zfs.to_bool(True)), "on")
  116. def test_to_bool_false(self):
  117. """
  118. Test to_bool with False
  119. """
  120. self.assertEqual(zfs.to_bool(False), "off")
  121. self.assertEqual(zfs.to_bool(zfs.to_bool(False)), "off")
  122. def test_to_bool_none(self):
  123. """
  124. Test to_bool with None
  125. """
  126. self.assertEqual(zfs.to_bool(None), "none")
  127. self.assertEqual(zfs.to_bool(zfs.to_bool(None)), "none")
  128. def test_to_bool_passthrough(self):
  129. """
  130. Test to_bool with 'passthrough'
  131. """
  132. self.assertEqual(zfs.to_bool("passthrough"), "passthrough")
  133. self.assertEqual(zfs.to_bool(zfs.to_bool("passthrough")), "passthrough")
  134. def test_to_bool_alt_true(self):
  135. """
  136. Test to_bool_alt with True
  137. """
  138. self.assertEqual(zfs.to_bool_alt(True), "yes")
  139. self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(True)), "yes")
  140. def test_to_bool_alt_false(self):
  141. """
  142. Test to_bool_alt with False
  143. """
  144. self.assertEqual(zfs.to_bool_alt(False), "no")
  145. self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(False)), "no")
  146. def test_to_bool_alt_none(self):
  147. """
  148. Test to_bool_alt with None
  149. """
  150. self.assertEqual(zfs.to_bool_alt(None), "none")
  151. self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(None)), "none")
  152. def test_to_bool_alt_passthrough(self):
  153. """
  154. Test to_bool_alt with 'passthrough'
  155. """
  156. self.assertEqual(zfs.to_bool_alt("passthrough"), "passthrough")
  157. self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt("passthrough")), "passthrough")
  158. # NOTE: testing from_numeric results
  159. def test_from_numeric_str(self):
  160. """
  161. Test from_numeric with '42'
  162. """
  163. self.assertEqual(zfs.from_numeric("42"), 42)
  164. self.assertEqual(zfs.from_numeric(zfs.from_numeric("42")), 42)
  165. def test_from_numeric_int(self):
  166. """
  167. Test from_numeric with 42
  168. """
  169. self.assertEqual(zfs.from_numeric(42), 42)
  170. self.assertEqual(zfs.from_numeric(zfs.from_numeric(42)), 42)
  171. def test_from_numeric_none(self):
  172. """
  173. Test from_numeric with 'none'
  174. """
  175. self.assertEqual(zfs.from_numeric("none"), None)
  176. self.assertEqual(zfs.from_numeric(zfs.from_numeric("none")), None)
  177. def test_from_numeric_passthrough(self):
  178. """
  179. Test from_numeric with 'passthrough'
  180. """
  181. self.assertEqual(zfs.from_numeric("passthrough"), "passthrough")
  182. self.assertEqual(
  183. zfs.from_numeric(zfs.from_numeric("passthrough")), "passthrough"
  184. )
  185. # NOTE: testing to_numeric results
  186. def test_to_numeric_str(self):
  187. """
  188. Test to_numeric with '42'
  189. """
  190. self.assertEqual(zfs.to_numeric("42"), 42)
  191. self.assertEqual(zfs.to_numeric(zfs.to_numeric("42")), 42)
  192. def test_to_numeric_int(self):
  193. """
  194. Test to_numeric with 42
  195. """
  196. self.assertEqual(zfs.to_numeric(42), 42)
  197. self.assertEqual(zfs.to_numeric(zfs.to_numeric(42)), 42)
  198. def test_to_numeric_none(self):
  199. """
  200. Test to_numeric with 'none'
  201. """
  202. self.assertEqual(zfs.to_numeric(None), "none")
  203. self.assertEqual(zfs.to_numeric(zfs.to_numeric(None)), "none")
  204. def test_to_numeric_passthrough(self):
  205. """
  206. Test to_numeric with 'passthrough'
  207. """
  208. self.assertEqual(zfs.to_numeric("passthrough"), "passthrough")
  209. self.assertEqual(zfs.to_numeric(zfs.to_numeric("passthrough")), "passthrough")
  210. # NOTE: testing from_size results
  211. def test_from_size_absolute(self):
  212. """
  213. Test from_size with '5G'
  214. """
  215. self.assertEqual(zfs.from_size("5G"), 5368709120)
  216. self.assertEqual(zfs.from_size(zfs.from_size("5G")), 5368709120)
  217. def test_from_size_decimal(self):
  218. """
  219. Test from_size with '4.20M'
  220. """
  221. self.assertEqual(zfs.from_size("4.20M"), 4404019)
  222. self.assertEqual(zfs.from_size(zfs.from_size("4.20M")), 4404019)
  223. def test_from_size_none(self):
  224. """
  225. Test from_size with 'none'
  226. """
  227. self.assertEqual(zfs.from_size("none"), None)
  228. self.assertEqual(zfs.from_size(zfs.from_size("none")), None)
  229. def test_from_size_passthrough(self):
  230. """
  231. Test from_size with 'passthrough'
  232. """
  233. self.assertEqual(zfs.from_size("passthrough"), "passthrough")
  234. self.assertEqual(zfs.from_size(zfs.from_size("passthrough")), "passthrough")
  235. # NOTE: testing to_size results
  236. def test_to_size_str_absolute(self):
  237. """
  238. Test to_size with '5368709120'
  239. """
  240. self.assertEqual(zfs.to_size("5368709120"), "5G")
  241. self.assertEqual(zfs.to_size(zfs.to_size("5368709120")), "5G")
  242. def test_to_size_str_decimal(self):
  243. """
  244. Test to_size with '4404019'
  245. """
  246. self.assertEqual(zfs.to_size("4404019"), "4.20M")
  247. self.assertEqual(zfs.to_size(zfs.to_size("4404019")), "4.20M")
  248. def test_to_size_int_absolute(self):
  249. """
  250. Test to_size with 5368709120
  251. """
  252. self.assertEqual(zfs.to_size(5368709120), "5G")
  253. self.assertEqual(zfs.to_size(zfs.to_size(5368709120)), "5G")
  254. def test_to_size_int_decimal(self):
  255. """
  256. Test to_size with 4404019
  257. """
  258. self.assertEqual(zfs.to_size(4404019), "4.20M")
  259. self.assertEqual(zfs.to_size(zfs.to_size(4404019)), "4.20M")
  260. def test_to_size_none(self):
  261. """
  262. Test to_size with 'none'
  263. """
  264. self.assertEqual(zfs.to_size(None), "none")
  265. self.assertEqual(zfs.to_size(zfs.to_size(None)), "none")
  266. def test_to_size_passthrough(self):
  267. """
  268. Test to_size with 'passthrough'
  269. """
  270. self.assertEqual(zfs.to_size("passthrough"), "passthrough")
  271. self.assertEqual(zfs.to_size(zfs.to_size("passthrough")), "passthrough")
  272. # NOTE: testing from_str results
  273. def test_from_str_space(self):
  274. """
  275. Test from_str with "\"my pool/my dataset\"
  276. """
  277. self.assertEqual(zfs.from_str('"my pool/my dataset"'), "my pool/my dataset")
  278. self.assertEqual(
  279. zfs.from_str(zfs.from_str('"my pool/my dataset"')), "my pool/my dataset"
  280. )
  281. def test_from_str_squote_space(self):
  282. """
  283. Test from_str with "my pool/jorge's dataset"
  284. """
  285. self.assertEqual(
  286. zfs.from_str("my pool/jorge's dataset"), "my pool/jorge's dataset"
  287. )
  288. self.assertEqual(
  289. zfs.from_str(zfs.from_str("my pool/jorge's dataset")),
  290. "my pool/jorge's dataset",
  291. )
  292. def test_from_str_dquote_space(self):
  293. """
  294. Test from_str with "my pool/the \"good\" stuff"
  295. """
  296. self.assertEqual(
  297. zfs.from_str('my pool/the "good" stuff'), 'my pool/the "good" stuff'
  298. )
  299. self.assertEqual(
  300. zfs.from_str(zfs.from_str('my pool/the "good" stuff')),
  301. 'my pool/the "good" stuff',
  302. )
  303. def test_from_str_none(self):
  304. """
  305. Test from_str with 'none'
  306. """
  307. self.assertEqual(zfs.from_str("none"), None)
  308. self.assertEqual(zfs.from_str(zfs.from_str("none")), None)
  309. def test_from_str_passthrough(self):
  310. """
  311. Test from_str with 'passthrough'
  312. """
  313. self.assertEqual(zfs.from_str("passthrough"), "passthrough")
  314. self.assertEqual(zfs.from_str(zfs.from_str("passthrough")), "passthrough")
  315. # NOTE: testing to_str results
  316. def test_to_str_space(self):
  317. """
  318. Test to_str with 'my pool/my dataset'
  319. """
  320. # NOTE: for fun we use both the '"str"' and "\"str\"" way of getting the literal string: "str"
  321. self.assertEqual(zfs.to_str("my pool/my dataset"), '"my pool/my dataset"')
  322. self.assertEqual(
  323. zfs.to_str(zfs.to_str("my pool/my dataset")), '"my pool/my dataset"'
  324. )
  325. def test_to_str_squote_space(self):
  326. """
  327. Test to_str with "my pool/jorge's dataset"
  328. """
  329. self.assertEqual(
  330. zfs.to_str("my pool/jorge's dataset"), '"my pool/jorge\'s dataset"'
  331. )
  332. self.assertEqual(
  333. zfs.to_str(zfs.to_str("my pool/jorge's dataset")),
  334. '"my pool/jorge\'s dataset"',
  335. )
  336. def test_to_str_none(self):
  337. """
  338. Test to_str with 'none'
  339. """
  340. self.assertEqual(zfs.to_str(None), "none")
  341. self.assertEqual(zfs.to_str(zfs.to_str(None)), "none")
  342. def test_to_str_passthrough(self):
  343. """
  344. Test to_str with 'passthrough'
  345. """
  346. self.assertEqual(zfs.to_str("passthrough"), "passthrough")
  347. self.assertEqual(zfs.to_str(zfs.to_str("passthrough")), "passthrough")
  348. # NOTE: testing is_snapshot
  349. def test_is_snapshot_snapshot(self):
  350. """
  351. Test is_snapshot with a valid snapshot name
  352. """
  353. self.assertTrue(zfs.is_snapshot("zpool_name/dataset@backup"))
  354. def test_is_snapshot_bookmark(self):
  355. """
  356. Test is_snapshot with a valid bookmark name
  357. """
  358. self.assertFalse(zfs.is_snapshot("zpool_name/dataset#backup"))
  359. def test_is_snapshot_filesystem(self):
  360. """
  361. Test is_snapshot with a valid filesystem name
  362. """
  363. self.assertFalse(zfs.is_snapshot("zpool_name/dataset"))
  364. # NOTE: testing is_bookmark
  365. def test_is_bookmark_snapshot(self):
  366. """
  367. Test is_bookmark with a valid snapshot name
  368. """
  369. self.assertFalse(zfs.is_bookmark("zpool_name/dataset@backup"))
  370. def test_is_bookmark_bookmark(self):
  371. """
  372. Test is_bookmark with a valid bookmark name
  373. """
  374. self.assertTrue(zfs.is_bookmark("zpool_name/dataset#backup"))
  375. def test_is_bookmark_filesystem(self):
  376. """
  377. Test is_bookmark with a valid filesystem name
  378. """
  379. self.assertFalse(zfs.is_bookmark("zpool_name/dataset"))
  380. # NOTE: testing is_dataset
  381. def test_is_dataset_snapshot(self):
  382. """
  383. Test is_dataset with a valid snapshot name
  384. """
  385. self.assertFalse(zfs.is_dataset("zpool_name/dataset@backup"))
  386. def test_is_dataset_bookmark(self):
  387. """
  388. Test is_dataset with a valid bookmark name
  389. """
  390. self.assertFalse(zfs.is_dataset("zpool_name/dataset#backup"))
  391. def test_is_dataset_filesystem(self):
  392. """
  393. Test is_dataset with a valid filesystem/volume name
  394. """
  395. self.assertTrue(zfs.is_dataset("zpool_name/dataset"))
  396. # NOTE: testing zfs_command
  397. def test_zfs_command_simple(self):
  398. """
  399. Test if zfs_command builds the correct string
  400. """
  401. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  402. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  403. with patch.object(
  404. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  405. ):
  406. with patch.object(
  407. zfs,
  408. "property_data_zpool",
  409. MagicMock(return_value=self.pmap_zpool),
  410. ):
  411. self.assertEqual(zfs.zfs_command("list"), "/sbin/zfs list")
  412. def test_zfs_command_none_target(self):
  413. """
  414. Test if zfs_command builds the correct string with a target of None
  415. """
  416. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  417. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  418. with patch.object(
  419. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  420. ):
  421. with patch.object(
  422. zfs,
  423. "property_data_zpool",
  424. MagicMock(return_value=self.pmap_zpool),
  425. ):
  426. self.assertEqual(
  427. zfs.zfs_command("list", target=[None, "mypool", None]),
  428. "/sbin/zfs list mypool",
  429. )
  430. def test_zfs_command_flag(self):
  431. """
  432. Test if zfs_command builds the correct string
  433. """
  434. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  435. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  436. with patch.object(
  437. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  438. ):
  439. with patch.object(
  440. zfs,
  441. "property_data_zpool",
  442. MagicMock(return_value=self.pmap_zpool),
  443. ):
  444. my_flags = [
  445. "-r", # recursive
  446. ]
  447. self.assertEqual(
  448. zfs.zfs_command("list", flags=my_flags), "/sbin/zfs list -r"
  449. )
  450. def test_zfs_command_opt(self):
  451. """
  452. Test if zfs_command builds the correct string
  453. """
  454. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  455. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  456. with patch.object(
  457. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  458. ):
  459. with patch.object(
  460. zfs,
  461. "property_data_zpool",
  462. MagicMock(return_value=self.pmap_zpool),
  463. ):
  464. my_opts = {
  465. "-t": "snap", # only list snapshots
  466. }
  467. self.assertEqual(
  468. zfs.zfs_command("list", opts=my_opts),
  469. "/sbin/zfs list -t snap",
  470. )
  471. def test_zfs_command_flag_opt(self):
  472. """
  473. Test if zfs_command builds the correct string
  474. """
  475. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  476. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  477. with patch.object(
  478. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  479. ):
  480. with patch.object(
  481. zfs,
  482. "property_data_zpool",
  483. MagicMock(return_value=self.pmap_zpool),
  484. ):
  485. my_flags = [
  486. "-r", # recursive
  487. ]
  488. my_opts = {
  489. "-t": "snap", # only list snapshots
  490. }
  491. self.assertEqual(
  492. zfs.zfs_command("list", flags=my_flags, opts=my_opts),
  493. "/sbin/zfs list -r -t snap",
  494. )
  495. def test_zfs_command_target(self):
  496. """
  497. Test if zfs_command builds the correct string
  498. """
  499. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  500. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  501. with patch.object(
  502. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  503. ):
  504. with patch.object(
  505. zfs,
  506. "property_data_zpool",
  507. MagicMock(return_value=self.pmap_zpool),
  508. ):
  509. my_flags = [
  510. "-r", # recursive
  511. ]
  512. my_opts = {
  513. "-t": "snap", # only list snapshots
  514. }
  515. self.assertEqual(
  516. zfs.zfs_command(
  517. "list", flags=my_flags, opts=my_opts, target="mypool"
  518. ),
  519. "/sbin/zfs list -r -t snap mypool",
  520. )
  521. def test_zfs_command_target_with_space(self):
  522. """
  523. Test if zfs_command builds the correct string
  524. """
  525. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  526. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  527. with patch.object(
  528. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  529. ):
  530. with patch.object(
  531. zfs,
  532. "property_data_zpool",
  533. MagicMock(return_value=self.pmap_zpool),
  534. ):
  535. my_flags = [
  536. "-r", # recursive
  537. ]
  538. my_opts = {
  539. "-t": "snap", # only list snapshots
  540. }
  541. self.assertEqual(
  542. zfs.zfs_command(
  543. "list", flags=my_flags, opts=my_opts, target="my pool"
  544. ),
  545. '/sbin/zfs list -r -t snap "my pool"',
  546. )
  547. def test_zfs_command_property(self):
  548. """
  549. Test if zfs_command builds the correct string
  550. """
  551. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  552. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  553. with patch.object(
  554. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  555. ):
  556. with patch.object(
  557. zfs,
  558. "property_data_zpool",
  559. MagicMock(return_value=self.pmap_zpool),
  560. ):
  561. self.assertEqual(
  562. zfs.zfs_command(
  563. "get", property_name="quota", target="mypool"
  564. ),
  565. "/sbin/zfs get quota mypool",
  566. )
  567. def test_zfs_command_property_value(self):
  568. """
  569. Test if zfs_command builds the correct string
  570. """
  571. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  572. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  573. with patch.object(
  574. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  575. ):
  576. with patch.object(
  577. zfs,
  578. "property_data_zpool",
  579. MagicMock(return_value=self.pmap_zpool),
  580. ):
  581. my_flags = [
  582. "-r", # recursive
  583. ]
  584. self.assertEqual(
  585. zfs.zfs_command(
  586. "set",
  587. flags=my_flags,
  588. property_name="quota",
  589. property_value="5G",
  590. target="mypool",
  591. ),
  592. "/sbin/zfs set -r quota=5368709120 mypool",
  593. )
  594. def test_zfs_command_multi_property_value(self):
  595. """
  596. Test if zfs_command builds the correct string
  597. """
  598. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  599. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  600. with patch.object(
  601. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  602. ):
  603. with patch.object(
  604. zfs,
  605. "property_data_zpool",
  606. MagicMock(return_value=self.pmap_zpool),
  607. ):
  608. property_name = ["quota", "readonly"]
  609. property_value = ["5G", "no"]
  610. self.assertEqual(
  611. zfs.zfs_command(
  612. "set",
  613. property_name=property_name,
  614. property_value=property_value,
  615. target="mypool",
  616. ),
  617. "/sbin/zfs set quota=5368709120 readonly=off mypool",
  618. )
  619. def test_zfs_command_fs_props(self):
  620. """
  621. Test if zfs_command builds the correct string
  622. """
  623. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  624. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  625. with patch.object(
  626. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  627. ):
  628. with patch.object(
  629. zfs,
  630. "property_data_zpool",
  631. MagicMock(return_value=self.pmap_zpool),
  632. ):
  633. my_flags = [
  634. "-p", # create parent
  635. ]
  636. my_props = {
  637. "quota": "1G",
  638. "compression": "lz4",
  639. }
  640. self.assertEqual(
  641. zfs.zfs_command(
  642. "create",
  643. flags=my_flags,
  644. filesystem_properties=my_props,
  645. target="mypool/dataset",
  646. ),
  647. "/sbin/zfs create -p -o compression=lz4 -o quota=1073741824 mypool/dataset",
  648. )
  649. def test_zfs_command_fs_props_with_space(self):
  650. """
  651. Test if zfs_command builds the correct string
  652. """
  653. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  654. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  655. with patch.object(
  656. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  657. ):
  658. with patch.object(
  659. zfs,
  660. "property_data_zpool",
  661. MagicMock(return_value=self.pmap_zpool),
  662. ):
  663. my_props = {
  664. "quota": "4.2M",
  665. "compression": "lz4",
  666. }
  667. self.assertEqual(
  668. zfs.zfs_command(
  669. "create",
  670. filesystem_properties=my_props,
  671. target="my pool/jorge's dataset",
  672. ),
  673. '/sbin/zfs create -o compression=lz4 -o quota=4404019 "my pool/jorge\'s dataset"',
  674. )
  675. # NOTE: testing zpool_command
  676. def test_zpool_command_simple(self):
  677. """
  678. Test if zfs_command builds the correct string
  679. """
  680. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  681. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  682. with patch.object(
  683. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  684. ):
  685. with patch.object(
  686. zfs,
  687. "property_data_zpool",
  688. MagicMock(return_value=self.pmap_zpool),
  689. ):
  690. self.assertEqual(zfs.zpool_command("list"), "/sbin/zpool list")
  691. def test_zpool_command_opt(self):
  692. """
  693. Test if zpool_command builds the correct string
  694. """
  695. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  696. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  697. with patch.object(
  698. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  699. ):
  700. with patch.object(
  701. zfs,
  702. "property_data_zpool",
  703. MagicMock(return_value=self.pmap_zpool),
  704. ):
  705. my_opts = {
  706. "-o": "name,size", # show only name and size
  707. }
  708. self.assertEqual(
  709. zfs.zpool_command("list", opts=my_opts),
  710. "/sbin/zpool list -o name,size",
  711. )
  712. def test_zpool_command_opt_list(self):
  713. """
  714. Test if zpool_command builds the correct string
  715. """
  716. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  717. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  718. with patch.object(
  719. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  720. ):
  721. with patch.object(
  722. zfs,
  723. "property_data_zpool",
  724. MagicMock(return_value=self.pmap_zpool),
  725. ):
  726. my_opts = {
  727. "-d": ["/tmp", "/zvol"],
  728. }
  729. self.assertEqual(
  730. zfs.zpool_command("import", opts=my_opts, target="mypool"),
  731. "/sbin/zpool import -d /tmp -d /zvol mypool",
  732. )
  733. def test_zpool_command_flag_opt(self):
  734. """
  735. Test if zpool_command builds the correct string
  736. """
  737. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  738. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  739. with patch.object(
  740. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  741. ):
  742. with patch.object(
  743. zfs,
  744. "property_data_zpool",
  745. MagicMock(return_value=self.pmap_zpool),
  746. ):
  747. my_opts = {
  748. "-o": "name,size", # show only name and size
  749. }
  750. self.assertEqual(
  751. zfs.zpool_command("list", opts=my_opts),
  752. "/sbin/zpool list -o name,size",
  753. )
  754. def test_zpool_command_target(self):
  755. """
  756. Test if zpool_command builds the correct string
  757. """
  758. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  759. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  760. with patch.object(
  761. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  762. ):
  763. with patch.object(
  764. zfs,
  765. "property_data_zpool",
  766. MagicMock(return_value=self.pmap_zpool),
  767. ):
  768. my_opts = {
  769. "-o": "name,size", # show only name and size
  770. }
  771. self.assertEqual(
  772. zfs.zpool_command("list", opts=my_opts, target="mypool"),
  773. "/sbin/zpool list -o name,size mypool",
  774. )
  775. def test_zpool_command_target_with_space(self):
  776. """
  777. Test if zpool_command builds the correct string
  778. """
  779. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  780. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  781. with patch.object(
  782. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  783. ):
  784. with patch.object(
  785. zfs,
  786. "property_data_zpool",
  787. MagicMock(return_value=self.pmap_zpool),
  788. ):
  789. fs_props = {
  790. "quota": "100G",
  791. }
  792. pool_props = {
  793. "comment": "jorge's comment has a space",
  794. }
  795. self.assertEqual(
  796. zfs.zpool_command(
  797. "create",
  798. pool_properties=pool_props,
  799. filesystem_properties=fs_props,
  800. target="my pool",
  801. ),
  802. '/sbin/zpool create -O quota=107374182400 -o comment="jorge\'s comment has a space" "my pool"',
  803. )
  804. def test_zpool_command_property(self):
  805. """
  806. Test if zpool_command builds the correct string
  807. """
  808. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  809. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  810. with patch.object(
  811. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  812. ):
  813. with patch.object(
  814. zfs,
  815. "property_data_zpool",
  816. MagicMock(return_value=self.pmap_zpool),
  817. ):
  818. self.assertEqual(
  819. zfs.zpool_command(
  820. "get", property_name="comment", target="mypool"
  821. ),
  822. "/sbin/zpool get comment mypool",
  823. )
  824. def test_zpool_command_property_value(self):
  825. """
  826. Test if zpool_command builds the correct string
  827. """
  828. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  829. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  830. with patch.object(
  831. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  832. ):
  833. with patch.object(
  834. zfs,
  835. "property_data_zpool",
  836. MagicMock(return_value=self.pmap_zpool),
  837. ):
  838. my_flags = [
  839. "-v", # verbose
  840. ]
  841. self.assertEqual(
  842. zfs.zpool_command(
  843. "iostat", flags=my_flags, target=["mypool", 60, 1]
  844. ),
  845. "/sbin/zpool iostat -v mypool 60 1",
  846. )
  847. def test_parse_command_result_success(self):
  848. """
  849. Test if parse_command_result returns the expected result
  850. """
  851. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  852. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  853. with patch.object(
  854. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  855. ):
  856. with patch.object(
  857. zfs,
  858. "property_data_zpool",
  859. MagicMock(return_value=self.pmap_zpool),
  860. ):
  861. res = {}
  862. res["retcode"] = 0
  863. res["stderr"] = ""
  864. res["stdout"] = ""
  865. self.assertEqual(
  866. zfs.parse_command_result(res, "tested"),
  867. OrderedDict([("tested", True)]),
  868. )
  869. def test_parse_command_result_success_nolabel(self):
  870. """
  871. Test if parse_command_result returns the expected result
  872. """
  873. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  874. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  875. with patch.object(
  876. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  877. ):
  878. with patch.object(
  879. zfs,
  880. "property_data_zpool",
  881. MagicMock(return_value=self.pmap_zpool),
  882. ):
  883. res = {}
  884. res["retcode"] = 0
  885. res["stderr"] = ""
  886. res["stdout"] = ""
  887. self.assertEqual(
  888. zfs.parse_command_result(res), OrderedDict(),
  889. )
  890. def test_parse_command_result_fail(self):
  891. """
  892. Test if parse_command_result returns the expected result on failure
  893. """
  894. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  895. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  896. with patch.object(
  897. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  898. ):
  899. with patch.object(
  900. zfs,
  901. "property_data_zpool",
  902. MagicMock(return_value=self.pmap_zpool),
  903. ):
  904. res = {}
  905. res["retcode"] = 1
  906. res["stderr"] = ""
  907. res["stdout"] = ""
  908. self.assertEqual(
  909. zfs.parse_command_result(res, "tested"),
  910. OrderedDict([("tested", False)]),
  911. )
  912. def test_parse_command_result_nolabel(self):
  913. """
  914. Test if parse_command_result returns the expected result on failure
  915. """
  916. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  917. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  918. with patch.object(
  919. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  920. ):
  921. with patch.object(
  922. zfs,
  923. "property_data_zpool",
  924. MagicMock(return_value=self.pmap_zpool),
  925. ):
  926. res = {}
  927. res["retcode"] = 1
  928. res["stderr"] = ""
  929. res["stdout"] = ""
  930. self.assertEqual(
  931. zfs.parse_command_result(res), OrderedDict(),
  932. )
  933. def test_parse_command_result_fail_message(self):
  934. """
  935. Test if parse_command_result returns the expected result on failure with stderr
  936. """
  937. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  938. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  939. with patch.object(
  940. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  941. ):
  942. with patch.object(
  943. zfs,
  944. "property_data_zpool",
  945. MagicMock(return_value=self.pmap_zpool),
  946. ):
  947. res = {}
  948. res["retcode"] = 1
  949. res["stderr"] = "\n".join(
  950. ["ice is not hot", "usage:", "this should not be printed"]
  951. )
  952. res["stdout"] = ""
  953. self.assertEqual(
  954. zfs.parse_command_result(res, "tested"),
  955. OrderedDict(
  956. [("tested", False), ("error", "ice is not hot")]
  957. ),
  958. )
  959. def test_parse_command_result_fail_message_nolabel(self):
  960. """
  961. Test if parse_command_result returns the expected result on failure with stderr
  962. """
  963. with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")):
  964. with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")):
  965. with patch.object(
  966. zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs)
  967. ):
  968. with patch.object(
  969. zfs,
  970. "property_data_zpool",
  971. MagicMock(return_value=self.pmap_zpool),
  972. ):
  973. res = {}
  974. res["retcode"] = 1
  975. res["stderr"] = "\n".join(
  976. ["ice is not hot", "usage:", "this should not be printed"]
  977. )
  978. res["stdout"] = ""
  979. self.assertEqual(
  980. zfs.parse_command_result(res),
  981. OrderedDict([("error", "ice is not hot")]),
  982. )