test_zfs.py 37 KB

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