test_lvm.py 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171
  1. import pytest
  2. from flexmock import flexmock
  3. from borgmatic.borg.pattern import Pattern, Pattern_style, Pattern_type
  4. from borgmatic.hooks.data_source import lvm as module
  5. def test_get_logical_volumes_filters_by_patterns():
  6. flexmock(module.borgmatic.execute).should_receive(
  7. 'execute_command_and_capture_output'
  8. ).and_return(
  9. '''
  10. {
  11. "blockdevices": [
  12. {
  13. "name": "vgroup-notmounted",
  14. "path": "/dev/mapper/vgroup-notmounted",
  15. "mountpoint": null,
  16. "type": "lvm"
  17. }, {
  18. "name": "vgroup-lvolume",
  19. "path": "/dev/mapper/vgroup-lvolume",
  20. "mountpoint": "/mnt/lvolume",
  21. "type": "lvm"
  22. }, {
  23. "name": "vgroup-other",
  24. "path": "/dev/mapper/vgroup-other",
  25. "mountpoint": "/mnt/other",
  26. "type": "lvm"
  27. }, {
  28. "name": "vgroup-notlvm",
  29. "path": "/dev/mapper/vgroup-notlvm",
  30. "mountpoint": "/mnt/notlvm",
  31. "type": "notlvm"
  32. }
  33. ]
  34. }
  35. '''
  36. )
  37. contained = {Pattern('/mnt/lvolume'), Pattern('/mnt/lvolume/subdir')}
  38. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  39. 'get_contained_patterns'
  40. ).with_args(None, contained).never()
  41. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  42. 'get_contained_patterns'
  43. ).with_args('/mnt/lvolume', contained).and_return(
  44. (Pattern('/mnt/lvolume'), Pattern('/mnt/lvolume/subdir'))
  45. )
  46. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  47. 'get_contained_patterns'
  48. ).with_args('/mnt/other', contained).and_return(())
  49. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  50. 'get_contained_patterns'
  51. ).with_args('/mnt/notlvm', contained).never()
  52. assert module.get_logical_volumes(
  53. 'lsblk', patterns=(Pattern('/mnt/lvolume'), Pattern('/mnt/lvolume/subdir'))
  54. ) == (
  55. module.Logical_volume(
  56. name='vgroup-lvolume',
  57. device_path='/dev/mapper/vgroup-lvolume',
  58. mount_point='/mnt/lvolume',
  59. contained_patterns=(Pattern('/mnt/lvolume'), Pattern('/mnt/lvolume/subdir')),
  60. ),
  61. )
  62. def test_get_logical_volumes_with_invalid_lsblk_json_errors():
  63. flexmock(module.borgmatic.execute).should_receive(
  64. 'execute_command_and_capture_output'
  65. ).and_return('{')
  66. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  67. 'get_contained_patterns'
  68. ).never()
  69. with pytest.raises(ValueError):
  70. module.get_logical_volumes(
  71. 'lsblk', patterns=(Pattern('/mnt/lvolume'), Pattern('/mnt/lvolume/subdir'))
  72. )
  73. def test_get_logical_volumes_with_lsblk_json_missing_keys_errors():
  74. flexmock(module.borgmatic.execute).should_receive(
  75. 'execute_command_and_capture_output'
  76. ).and_return('{"block_devices": [{}]}')
  77. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  78. 'get_contained_patterns'
  79. ).never()
  80. with pytest.raises(ValueError):
  81. module.get_logical_volumes(
  82. 'lsblk', patterns=(Pattern('/mnt/lvolume'), Pattern('/mnt/lvolume/subdir'))
  83. )
  84. def test_snapshot_logical_volume_with_percentage_snapshot_name_uses_lvcreate_extents_flag():
  85. flexmock(module.borgmatic.execute).should_receive('execute_command').with_args(
  86. (
  87. 'lvcreate',
  88. '--snapshot',
  89. '--extents',
  90. '10%ORIGIN',
  91. '--permission',
  92. 'r',
  93. '--name',
  94. 'snap',
  95. '/dev/snap',
  96. ),
  97. output_log_level=object,
  98. )
  99. module.snapshot_logical_volume('lvcreate', 'snap', '/dev/snap', '10%ORIGIN')
  100. def test_snapshot_logical_volume_with_non_percentage_snapshot_name_uses_lvcreate_size_flag():
  101. flexmock(module.borgmatic.execute).should_receive('execute_command').with_args(
  102. (
  103. 'lvcreate',
  104. '--snapshot',
  105. '--size',
  106. '10TB',
  107. '--permission',
  108. 'r',
  109. '--name',
  110. 'snap',
  111. '/dev/snap',
  112. ),
  113. output_log_level=object,
  114. )
  115. module.snapshot_logical_volume('lvcreate', 'snap', '/dev/snap', '10TB')
  116. @pytest.mark.parametrize(
  117. 'pattern,expected_pattern',
  118. (
  119. (
  120. Pattern('/foo/bar/baz'),
  121. Pattern('/run/borgmatic/lvm_snapshots/./foo/bar/baz'),
  122. ),
  123. (Pattern('/foo/bar'), Pattern('/run/borgmatic/lvm_snapshots/./foo/bar')),
  124. (
  125. Pattern('^/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
  126. Pattern(
  127. '^/run/borgmatic/lvm_snapshots/./foo/bar',
  128. Pattern_type.INCLUDE,
  129. Pattern_style.REGULAR_EXPRESSION,
  130. ),
  131. ),
  132. (
  133. Pattern('/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
  134. Pattern(
  135. '/run/borgmatic/lvm_snapshots/./foo/bar',
  136. Pattern_type.INCLUDE,
  137. Pattern_style.REGULAR_EXPRESSION,
  138. ),
  139. ),
  140. (Pattern('/foo'), Pattern('/run/borgmatic/lvm_snapshots/./foo')),
  141. (Pattern('/'), Pattern('/run/borgmatic/lvm_snapshots/./')),
  142. ),
  143. )
  144. def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_path(
  145. pattern, expected_pattern
  146. ):
  147. assert module.make_borg_snapshot_pattern(pattern, '/run/borgmatic') == expected_pattern
  148. def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
  149. config = {'lvm': {}}
  150. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  151. flexmock(module).should_receive('get_logical_volumes').and_return(
  152. (
  153. module.Logical_volume(
  154. name='lvolume1',
  155. device_path='/dev/lvolume1',
  156. mount_point='/mnt/lvolume1',
  157. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  158. ),
  159. module.Logical_volume(
  160. name='lvolume2',
  161. device_path='/dev/lvolume2',
  162. mount_point='/mnt/lvolume2',
  163. contained_patterns=(Pattern('/mnt/lvolume2'),),
  164. ),
  165. )
  166. )
  167. flexmock(module.os).should_receive('getpid').and_return(1234)
  168. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  169. 'lvcreate', 'lvolume1_borgmatic-1234', '/dev/lvolume1', module.DEFAULT_SNAPSHOT_SIZE
  170. ).once()
  171. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  172. 'lvcreate', 'lvolume2_borgmatic-1234', '/dev/lvolume2', module.DEFAULT_SNAPSHOT_SIZE
  173. ).once()
  174. flexmock(module).should_receive('get_snapshots').with_args(
  175. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  176. ).and_return(
  177. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  178. )
  179. flexmock(module).should_receive('get_snapshots').with_args(
  180. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  181. ).and_return(
  182. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  183. )
  184. flexmock(module).should_receive('mount_snapshot').with_args(
  185. 'mount', '/dev/lvolume1_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  186. ).once()
  187. flexmock(module).should_receive('mount_snapshot').with_args(
  188. 'mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  189. ).once()
  190. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  191. Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
  192. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
  193. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  194. Pattern('/mnt/lvolume2'), '/run/borgmatic'
  195. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
  196. assert (
  197. module.dump_data_sources(
  198. hook_config=config['lvm'],
  199. config=config,
  200. log_prefix='test',
  201. config_paths=('test.yaml',),
  202. borgmatic_runtime_directory='/run/borgmatic',
  203. patterns=patterns,
  204. dry_run=False,
  205. )
  206. == []
  207. )
  208. assert patterns == [
  209. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'),
  210. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'),
  211. ]
  212. def test_dump_data_sources_with_no_logical_volumes_skips_snapshots():
  213. config = {'lvm': {}}
  214. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  215. flexmock(module).should_receive('get_logical_volumes').and_return(())
  216. flexmock(module).should_receive('snapshot_logical_volume').never()
  217. flexmock(module).should_receive('mount_snapshot').never()
  218. assert (
  219. module.dump_data_sources(
  220. hook_config=config['lvm'],
  221. config=config,
  222. log_prefix='test',
  223. config_paths=('test.yaml',),
  224. borgmatic_runtime_directory='/run/borgmatic',
  225. patterns=patterns,
  226. dry_run=False,
  227. )
  228. == []
  229. )
  230. assert patterns == [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  231. def test_dump_data_sources_uses_snapshot_size_for_snapshot():
  232. config = {'lvm': {'snapshot_size': '1000PB'}}
  233. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  234. flexmock(module).should_receive('get_logical_volumes').and_return(
  235. (
  236. module.Logical_volume(
  237. name='lvolume1',
  238. device_path='/dev/lvolume1',
  239. mount_point='/mnt/lvolume1',
  240. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  241. ),
  242. module.Logical_volume(
  243. name='lvolume2',
  244. device_path='/dev/lvolume2',
  245. mount_point='/mnt/lvolume2',
  246. contained_patterns=(Pattern('/mnt/lvolume2'),),
  247. ),
  248. )
  249. )
  250. flexmock(module.os).should_receive('getpid').and_return(1234)
  251. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  252. 'lvcreate',
  253. 'lvolume1_borgmatic-1234',
  254. '/dev/lvolume1',
  255. '1000PB',
  256. ).once()
  257. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  258. 'lvcreate',
  259. 'lvolume2_borgmatic-1234',
  260. '/dev/lvolume2',
  261. '1000PB',
  262. ).once()
  263. flexmock(module).should_receive('get_snapshots').with_args(
  264. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  265. ).and_return(
  266. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  267. )
  268. flexmock(module).should_receive('get_snapshots').with_args(
  269. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  270. ).and_return(
  271. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  272. )
  273. flexmock(module).should_receive('mount_snapshot').with_args(
  274. 'mount', '/dev/lvolume1_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  275. ).once()
  276. flexmock(module).should_receive('mount_snapshot').with_args(
  277. 'mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  278. ).once()
  279. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  280. Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
  281. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
  282. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  283. Pattern('/mnt/lvolume2'), '/run/borgmatic'
  284. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
  285. assert (
  286. module.dump_data_sources(
  287. hook_config=config['lvm'],
  288. config=config,
  289. log_prefix='test',
  290. config_paths=('test.yaml',),
  291. borgmatic_runtime_directory='/run/borgmatic',
  292. patterns=patterns,
  293. dry_run=False,
  294. )
  295. == []
  296. )
  297. assert patterns == [
  298. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'),
  299. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'),
  300. ]
  301. def test_dump_data_sources_uses_custom_commands():
  302. config = {
  303. 'lvm': {
  304. 'lsblk_command': '/usr/local/bin/lsblk',
  305. 'lvcreate_command': '/usr/local/bin/lvcreate',
  306. 'lvs_command': '/usr/local/bin/lvs',
  307. 'mount_command': '/usr/local/bin/mount',
  308. },
  309. }
  310. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  311. flexmock(module).should_receive('get_logical_volumes').and_return(
  312. (
  313. module.Logical_volume(
  314. name='lvolume1',
  315. device_path='/dev/lvolume1',
  316. mount_point='/mnt/lvolume1',
  317. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  318. ),
  319. module.Logical_volume(
  320. name='lvolume2',
  321. device_path='/dev/lvolume2',
  322. mount_point='/mnt/lvolume2',
  323. contained_patterns=(Pattern('/mnt/lvolume2'),),
  324. ),
  325. )
  326. )
  327. flexmock(module.os).should_receive('getpid').and_return(1234)
  328. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  329. '/usr/local/bin/lvcreate',
  330. 'lvolume1_borgmatic-1234',
  331. '/dev/lvolume1',
  332. module.DEFAULT_SNAPSHOT_SIZE,
  333. ).once()
  334. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  335. '/usr/local/bin/lvcreate',
  336. 'lvolume2_borgmatic-1234',
  337. '/dev/lvolume2',
  338. module.DEFAULT_SNAPSHOT_SIZE,
  339. ).once()
  340. flexmock(module).should_receive('get_snapshots').with_args(
  341. '/usr/local/bin/lvs', snapshot_name='lvolume1_borgmatic-1234'
  342. ).and_return(
  343. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  344. )
  345. flexmock(module).should_receive('get_snapshots').with_args(
  346. '/usr/local/bin/lvs', snapshot_name='lvolume2_borgmatic-1234'
  347. ).and_return(
  348. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  349. )
  350. flexmock(module).should_receive('mount_snapshot').with_args(
  351. '/usr/local/bin/mount', '/dev/lvolume1_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  352. ).once()
  353. flexmock(module).should_receive('mount_snapshot').with_args(
  354. '/usr/local/bin/mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  355. ).once()
  356. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  357. Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
  358. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
  359. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  360. Pattern('/mnt/lvolume2'), '/run/borgmatic'
  361. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
  362. assert (
  363. module.dump_data_sources(
  364. hook_config=config['lvm'],
  365. config=config,
  366. log_prefix='test',
  367. config_paths=('test.yaml',),
  368. borgmatic_runtime_directory='/run/borgmatic',
  369. patterns=patterns,
  370. dry_run=False,
  371. )
  372. == []
  373. )
  374. assert patterns == [
  375. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'),
  376. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'),
  377. ]
  378. def test_dump_data_sources_with_dry_run_skips_snapshots_and_does_not_touch_patterns():
  379. config = {'lvm': {}}
  380. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  381. flexmock(module).should_receive('get_logical_volumes').and_return(
  382. (
  383. module.Logical_volume(
  384. name='lvolume1',
  385. device_path='/dev/lvolume1',
  386. mount_point='/mnt/lvolume1',
  387. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  388. ),
  389. module.Logical_volume(
  390. name='lvolume2',
  391. device_path='/dev/lvolume2',
  392. mount_point='/mnt/lvolume2',
  393. contained_patterns=(Pattern('/mnt/lvolume2'),),
  394. ),
  395. )
  396. )
  397. flexmock(module.os).should_receive('getpid').and_return(1234)
  398. flexmock(module).should_receive('snapshot_logical_volume').never()
  399. flexmock(module).should_receive('get_snapshots').with_args(
  400. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  401. ).and_return(
  402. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  403. )
  404. flexmock(module).should_receive('get_snapshots').with_args(
  405. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  406. ).and_return(
  407. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  408. )
  409. flexmock(module).should_receive('mount_snapshot').never()
  410. assert (
  411. module.dump_data_sources(
  412. hook_config=config['lvm'],
  413. config=config,
  414. log_prefix='test',
  415. config_paths=('test.yaml',),
  416. borgmatic_runtime_directory='/run/borgmatic',
  417. patterns=patterns,
  418. dry_run=True,
  419. )
  420. == []
  421. )
  422. assert patterns == [
  423. Pattern('/mnt/lvolume1/subdir'),
  424. Pattern('/mnt/lvolume2'),
  425. ]
  426. def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained_patterns():
  427. config = {'lvm': {}}
  428. patterns = [Pattern('/hmm')]
  429. flexmock(module).should_receive('get_logical_volumes').and_return(
  430. (
  431. module.Logical_volume(
  432. name='lvolume1',
  433. device_path='/dev/lvolume1',
  434. mount_point='/mnt/lvolume1',
  435. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  436. ),
  437. module.Logical_volume(
  438. name='lvolume2',
  439. device_path='/dev/lvolume2',
  440. mount_point='/mnt/lvolume2',
  441. contained_patterns=(Pattern('/mnt/lvolume2'),),
  442. ),
  443. )
  444. )
  445. flexmock(module.os).should_receive('getpid').and_return(1234)
  446. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  447. 'lvcreate', 'lvolume1_borgmatic-1234', '/dev/lvolume1', module.DEFAULT_SNAPSHOT_SIZE
  448. ).once()
  449. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  450. 'lvcreate', 'lvolume2_borgmatic-1234', '/dev/lvolume2', module.DEFAULT_SNAPSHOT_SIZE
  451. ).once()
  452. flexmock(module).should_receive('get_snapshots').with_args(
  453. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  454. ).and_return(
  455. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  456. )
  457. flexmock(module).should_receive('get_snapshots').with_args(
  458. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  459. ).and_return(
  460. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  461. )
  462. flexmock(module).should_receive('mount_snapshot').with_args(
  463. 'mount', '/dev/lvolume1_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  464. ).once()
  465. flexmock(module).should_receive('mount_snapshot').with_args(
  466. 'mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  467. ).once()
  468. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  469. Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
  470. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
  471. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  472. Pattern('/mnt/lvolume2'), '/run/borgmatic'
  473. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
  474. assert (
  475. module.dump_data_sources(
  476. hook_config=config['lvm'],
  477. config=config,
  478. log_prefix='test',
  479. config_paths=('test.yaml',),
  480. borgmatic_runtime_directory='/run/borgmatic',
  481. patterns=patterns,
  482. dry_run=False,
  483. )
  484. == []
  485. )
  486. assert patterns == [
  487. Pattern('/hmm'),
  488. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'),
  489. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'),
  490. ]
  491. def test_dump_data_sources_with_missing_snapshot_errors():
  492. config = {'lvm': {}}
  493. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  494. flexmock(module).should_receive('get_logical_volumes').and_return(
  495. (
  496. module.Logical_volume(
  497. name='lvolume1',
  498. device_path='/dev/lvolume1',
  499. mount_point='/mnt/lvolume1',
  500. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  501. ),
  502. module.Logical_volume(
  503. name='lvolume2',
  504. device_path='/dev/lvolume2',
  505. mount_point='/mnt/lvolume2',
  506. contained_patterns=(Pattern('/mnt/lvolume2'),),
  507. ),
  508. )
  509. )
  510. flexmock(module.os).should_receive('getpid').and_return(1234)
  511. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  512. 'lvcreate', 'lvolume1_borgmatic-1234', '/dev/lvolume1', module.DEFAULT_SNAPSHOT_SIZE
  513. ).once()
  514. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  515. 'lvcreate', 'lvolume2_borgmatic-1234', '/dev/lvolume2', module.DEFAULT_SNAPSHOT_SIZE
  516. ).never()
  517. flexmock(module).should_receive('get_snapshots').with_args(
  518. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  519. ).and_return(())
  520. flexmock(module).should_receive('get_snapshots').with_args(
  521. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  522. ).never()
  523. flexmock(module).should_receive('mount_snapshot').never()
  524. with pytest.raises(ValueError):
  525. module.dump_data_sources(
  526. hook_config=config['lvm'],
  527. config=config,
  528. log_prefix='test',
  529. config_paths=('test.yaml',),
  530. borgmatic_runtime_directory='/run/borgmatic',
  531. patterns=patterns,
  532. dry_run=False,
  533. )
  534. def test_get_snapshots_lists_all_snapshots():
  535. flexmock(module.borgmatic.execute).should_receive(
  536. 'execute_command_and_capture_output'
  537. ).and_return(
  538. '''
  539. {
  540. "report": [
  541. {
  542. "lv": [
  543. {"lv_name": "snap1", "lv_path": "/dev/snap1"},
  544. {"lv_name": "snap2", "lv_path": "/dev/snap2"}
  545. ]
  546. }
  547. ],
  548. "log": [
  549. ]
  550. }
  551. '''
  552. )
  553. assert module.get_snapshots('lvs') == (
  554. module.Snapshot('snap1', '/dev/snap1'),
  555. module.Snapshot('snap2', '/dev/snap2'),
  556. )
  557. def test_get_snapshots_with_snapshot_name_lists_just_that_snapshot():
  558. flexmock(module.borgmatic.execute).should_receive(
  559. 'execute_command_and_capture_output'
  560. ).and_return(
  561. '''
  562. {
  563. "report": [
  564. {
  565. "lv": [
  566. {"lv_name": "snap1", "lv_path": "/dev/snap1"},
  567. {"lv_name": "snap2", "lv_path": "/dev/snap2"}
  568. ]
  569. }
  570. ],
  571. "log": [
  572. ]
  573. }
  574. '''
  575. )
  576. assert module.get_snapshots('lvs', snapshot_name='snap2') == (
  577. module.Snapshot('snap2', '/dev/snap2'),
  578. )
  579. def test_get_snapshots_with_invalid_lvs_json_errors():
  580. flexmock(module.borgmatic.execute).should_receive(
  581. 'execute_command_and_capture_output'
  582. ).and_return('{')
  583. with pytest.raises(ValueError):
  584. assert module.get_snapshots('lvs')
  585. def test_get_snapshots_with_lvs_json_missing_report_errors():
  586. flexmock(module.borgmatic.execute).should_receive(
  587. 'execute_command_and_capture_output'
  588. ).and_return(
  589. '''
  590. {
  591. "report": [],
  592. "log": [
  593. ]
  594. }
  595. '''
  596. )
  597. with pytest.raises(ValueError):
  598. assert module.get_snapshots('lvs')
  599. def test_get_snapshots_with_lvs_json_missing_keys_errors():
  600. flexmock(module.borgmatic.execute).should_receive(
  601. 'execute_command_and_capture_output'
  602. ).and_return(
  603. '''
  604. {
  605. "report": [
  606. {
  607. "lv": [
  608. {}
  609. ]
  610. }
  611. ],
  612. "log": [
  613. ]
  614. }
  615. '''
  616. )
  617. with pytest.raises(ValueError):
  618. assert module.get_snapshots('lvs')
  619. def test_remove_data_source_dumps_unmounts_and_remove_snapshots():
  620. config = {'lvm': {}}
  621. flexmock(module).should_receive('get_logical_volumes').and_return(
  622. (
  623. module.Logical_volume(
  624. name='lvolume1',
  625. device_path='/dev/lvolume1',
  626. mount_point='/mnt/lvolume1',
  627. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  628. ),
  629. module.Logical_volume(
  630. name='lvolume2',
  631. device_path='/dev/lvolume2',
  632. mount_point='/mnt/lvolume2',
  633. contained_patterns=(Pattern('/mnt/lvolume2'),),
  634. ),
  635. )
  636. )
  637. flexmock(module.borgmatic.config.paths).should_receive(
  638. 'replace_temporary_subdirectory_with_glob'
  639. ).and_return('/run/borgmatic')
  640. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  641. flexmock(module.os.path).should_receive('isdir').and_return(True)
  642. flexmock(module.shutil).should_receive('rmtree')
  643. flexmock(module).should_receive('unmount_snapshot').with_args(
  644. 'umount',
  645. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  646. ).once()
  647. flexmock(module).should_receive('unmount_snapshot').with_args(
  648. 'umount',
  649. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  650. ).once()
  651. flexmock(module).should_receive('get_snapshots').and_return(
  652. (
  653. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  654. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  655. module.Snapshot('nonborgmatic', '/dev/nonborgmatic'),
  656. ),
  657. )
  658. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume1').once()
  659. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume2').once()
  660. flexmock(module).should_receive('remove_snapshot').with_args(
  661. 'nonborgmatic', '/dev/nonborgmatic'
  662. ).never()
  663. module.remove_data_source_dumps(
  664. hook_config=config['lvm'],
  665. config=config,
  666. log_prefix='test',
  667. borgmatic_runtime_directory='/run/borgmatic',
  668. dry_run=False,
  669. )
  670. def test_remove_data_source_dumps_bails_for_missing_lvm_configuration():
  671. flexmock(module).should_receive('get_logical_volumes').never()
  672. flexmock(module.borgmatic.config.paths).should_receive(
  673. 'replace_temporary_subdirectory_with_glob'
  674. ).never()
  675. flexmock(module).should_receive('unmount_snapshot').never()
  676. flexmock(module).should_receive('remove_snapshot').never()
  677. module.remove_data_source_dumps(
  678. hook_config=None,
  679. config={'source_directories': '/mnt/lvolume'},
  680. log_prefix='test',
  681. borgmatic_runtime_directory='/run/borgmatic',
  682. dry_run=False,
  683. )
  684. def test_remove_data_source_dumps_bails_for_missing_lsblk_command():
  685. config = {'lvm': {}}
  686. flexmock(module).should_receive('get_logical_volumes').and_raise(FileNotFoundError)
  687. flexmock(module.borgmatic.config.paths).should_receive(
  688. 'replace_temporary_subdirectory_with_glob'
  689. ).never()
  690. flexmock(module).should_receive('unmount_snapshot').never()
  691. flexmock(module).should_receive('remove_snapshot').never()
  692. module.remove_data_source_dumps(
  693. hook_config=config['lvm'],
  694. config=config,
  695. log_prefix='test',
  696. borgmatic_runtime_directory='/run/borgmatic',
  697. dry_run=False,
  698. )
  699. def test_remove_data_source_dumps_bails_for_lsblk_command_error():
  700. config = {'lvm': {}}
  701. flexmock(module).should_receive('get_logical_volumes').and_raise(
  702. module.subprocess.CalledProcessError(1, 'wtf')
  703. )
  704. flexmock(module.borgmatic.config.paths).should_receive(
  705. 'replace_temporary_subdirectory_with_glob'
  706. ).never()
  707. flexmock(module).should_receive('unmount_snapshot').never()
  708. flexmock(module).should_receive('remove_snapshot').never()
  709. module.remove_data_source_dumps(
  710. hook_config=config['lvm'],
  711. config=config,
  712. log_prefix='test',
  713. borgmatic_runtime_directory='/run/borgmatic',
  714. dry_run=False,
  715. )
  716. def test_remove_data_source_dumps_with_missing_snapshot_directory_skips_unmount():
  717. config = {'lvm': {}}
  718. flexmock(module).should_receive('get_logical_volumes').and_return(
  719. (
  720. module.Logical_volume(
  721. name='lvolume1',
  722. device_path='/dev/lvolume1',
  723. mount_point='/mnt/lvolume1',
  724. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  725. ),
  726. module.Logical_volume(
  727. name='lvolume2',
  728. device_path='/dev/lvolume2',
  729. mount_point='/mnt/lvolume2',
  730. contained_patterns=(Pattern('/mnt/lvolume2'),),
  731. ),
  732. )
  733. )
  734. flexmock(module.borgmatic.config.paths).should_receive(
  735. 'replace_temporary_subdirectory_with_glob'
  736. ).and_return('/run/borgmatic')
  737. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  738. flexmock(module.os.path).should_receive('isdir').with_args(
  739. '/run/borgmatic/lvm_snapshots'
  740. ).and_return(False)
  741. flexmock(module.shutil).should_receive('rmtree').never()
  742. flexmock(module).should_receive('unmount_snapshot').never()
  743. flexmock(module).should_receive('get_snapshots').and_return(
  744. (
  745. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  746. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  747. ),
  748. )
  749. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume1').once()
  750. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume2').once()
  751. module.remove_data_source_dumps(
  752. hook_config=config['lvm'],
  753. config=config,
  754. log_prefix='test',
  755. borgmatic_runtime_directory='/run/borgmatic',
  756. dry_run=False,
  757. )
  758. def test_remove_data_source_dumps_with_missing_snapshot_mount_path_skips_unmount():
  759. config = {'lvm': {}}
  760. flexmock(module).should_receive('get_logical_volumes').and_return(
  761. (
  762. module.Logical_volume(
  763. name='lvolume1',
  764. device_path='/dev/lvolume1',
  765. mount_point='/mnt/lvolume1',
  766. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  767. ),
  768. module.Logical_volume(
  769. name='lvolume2',
  770. device_path='/dev/lvolume2',
  771. mount_point='/mnt/lvolume2',
  772. contained_patterns=(Pattern('/mnt/lvolume2'),),
  773. ),
  774. )
  775. )
  776. flexmock(module.borgmatic.config.paths).should_receive(
  777. 'replace_temporary_subdirectory_with_glob'
  778. ).and_return('/run/borgmatic')
  779. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  780. flexmock(module.os.path).should_receive('isdir').with_args(
  781. '/run/borgmatic/lvm_snapshots'
  782. ).and_return(True)
  783. flexmock(module.os.path).should_receive('isdir').with_args(
  784. '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  785. ).and_return(False)
  786. flexmock(module.os.path).should_receive('isdir').with_args(
  787. '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  788. ).and_return(True)
  789. flexmock(module.shutil).should_receive('rmtree')
  790. flexmock(module).should_receive('unmount_snapshot').with_args(
  791. 'umount',
  792. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  793. ).never()
  794. flexmock(module).should_receive('unmount_snapshot').with_args(
  795. 'umount',
  796. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  797. ).once()
  798. flexmock(module).should_receive('get_snapshots').and_return(
  799. (
  800. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  801. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  802. ),
  803. )
  804. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume1').once()
  805. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume2').once()
  806. module.remove_data_source_dumps(
  807. hook_config=config['lvm'],
  808. config=config,
  809. log_prefix='test',
  810. borgmatic_runtime_directory='/run/borgmatic',
  811. dry_run=False,
  812. )
  813. def test_remove_data_source_dumps_with_successful_mount_point_removal_skips_unmount():
  814. config = {'lvm': {}}
  815. flexmock(module).should_receive('get_logical_volumes').and_return(
  816. (
  817. module.Logical_volume(
  818. name='lvolume1',
  819. device_path='/dev/lvolume1',
  820. mount_point='/mnt/lvolume1',
  821. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  822. ),
  823. module.Logical_volume(
  824. name='lvolume2',
  825. device_path='/dev/lvolume2',
  826. mount_point='/mnt/lvolume2',
  827. contained_patterns=(Pattern('/mnt/lvolume2'),),
  828. ),
  829. )
  830. )
  831. flexmock(module.borgmatic.config.paths).should_receive(
  832. 'replace_temporary_subdirectory_with_glob'
  833. ).and_return('/run/borgmatic')
  834. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  835. flexmock(module.os.path).should_receive('isdir').with_args(
  836. '/run/borgmatic/lvm_snapshots'
  837. ).and_return(True)
  838. flexmock(module.os.path).should_receive('isdir').with_args(
  839. '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  840. ).and_return(True).and_return(False)
  841. flexmock(module.os.path).should_receive('isdir').with_args(
  842. '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  843. ).and_return(True).and_return(True)
  844. flexmock(module.shutil).should_receive('rmtree')
  845. flexmock(module).should_receive('unmount_snapshot').with_args(
  846. 'umount',
  847. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  848. ).never()
  849. flexmock(module).should_receive('unmount_snapshot').with_args(
  850. 'umount',
  851. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  852. ).once()
  853. flexmock(module).should_receive('get_snapshots').and_return(
  854. (
  855. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  856. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  857. ),
  858. )
  859. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume1').once()
  860. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume2').once()
  861. module.remove_data_source_dumps(
  862. hook_config=config['lvm'],
  863. config=config,
  864. log_prefix='test',
  865. borgmatic_runtime_directory='/run/borgmatic',
  866. dry_run=False,
  867. )
  868. def test_remove_data_source_dumps_bails_for_missing_umount_command():
  869. config = {'lvm': {}}
  870. flexmock(module).should_receive('get_logical_volumes').and_return(
  871. (
  872. module.Logical_volume(
  873. name='lvolume1',
  874. device_path='/dev/lvolume1',
  875. mount_point='/mnt/lvolume1',
  876. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  877. ),
  878. module.Logical_volume(
  879. name='lvolume2',
  880. device_path='/dev/lvolume2',
  881. mount_point='/mnt/lvolume2',
  882. contained_patterns=(Pattern('/mnt/lvolume2'),),
  883. ),
  884. )
  885. )
  886. flexmock(module.borgmatic.config.paths).should_receive(
  887. 'replace_temporary_subdirectory_with_glob'
  888. ).and_return('/run/borgmatic')
  889. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  890. flexmock(module.os.path).should_receive('isdir').and_return(True)
  891. flexmock(module.shutil).should_receive('rmtree')
  892. flexmock(module).should_receive('unmount_snapshot').with_args(
  893. 'umount',
  894. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  895. ).and_raise(FileNotFoundError)
  896. flexmock(module).should_receive('unmount_snapshot').with_args(
  897. 'umount',
  898. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  899. ).never()
  900. flexmock(module).should_receive('get_snapshots').never()
  901. flexmock(module).should_receive('remove_snapshot').never()
  902. module.remove_data_source_dumps(
  903. hook_config=config['lvm'],
  904. config=config,
  905. log_prefix='test',
  906. borgmatic_runtime_directory='/run/borgmatic',
  907. dry_run=False,
  908. )
  909. def test_remove_data_source_dumps_bails_for_umount_command_error():
  910. config = {'lvm': {}}
  911. flexmock(module).should_receive('get_logical_volumes').and_return(
  912. (
  913. module.Logical_volume(
  914. name='lvolume1',
  915. device_path='/dev/lvolume1',
  916. mount_point='/mnt/lvolume1',
  917. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  918. ),
  919. module.Logical_volume(
  920. name='lvolume2',
  921. device_path='/dev/lvolume2',
  922. mount_point='/mnt/lvolume2',
  923. contained_patterns=(Pattern('/mnt/lvolume2'),),
  924. ),
  925. )
  926. )
  927. flexmock(module.borgmatic.config.paths).should_receive(
  928. 'replace_temporary_subdirectory_with_glob'
  929. ).and_return('/run/borgmatic')
  930. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  931. flexmock(module.os.path).should_receive('isdir').and_return(True)
  932. flexmock(module.shutil).should_receive('rmtree')
  933. flexmock(module).should_receive('unmount_snapshot').with_args(
  934. 'umount',
  935. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  936. ).and_raise(module.subprocess.CalledProcessError(1, 'wtf'))
  937. flexmock(module).should_receive('unmount_snapshot').with_args(
  938. 'umount',
  939. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  940. ).never()
  941. flexmock(module).should_receive('get_snapshots').never()
  942. flexmock(module).should_receive('remove_snapshot').never()
  943. module.remove_data_source_dumps(
  944. hook_config=config['lvm'],
  945. config=config,
  946. log_prefix='test',
  947. borgmatic_runtime_directory='/run/borgmatic',
  948. dry_run=False,
  949. )
  950. def test_remove_data_source_dumps_bails_for_missing_lvs_command():
  951. config = {'lvm': {}}
  952. flexmock(module).should_receive('get_logical_volumes').and_return(
  953. (
  954. module.Logical_volume(
  955. name='lvolume1',
  956. device_path='/dev/lvolume1',
  957. mount_point='/mnt/lvolume1',
  958. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  959. ),
  960. module.Logical_volume(
  961. name='lvolume2',
  962. device_path='/dev/lvolume2',
  963. mount_point='/mnt/lvolume2',
  964. contained_patterns=(Pattern('/mnt/lvolume2'),),
  965. ),
  966. )
  967. )
  968. flexmock(module.borgmatic.config.paths).should_receive(
  969. 'replace_temporary_subdirectory_with_glob'
  970. ).and_return('/run/borgmatic')
  971. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  972. flexmock(module.os.path).should_receive('isdir').and_return(True)
  973. flexmock(module.shutil).should_receive('rmtree')
  974. flexmock(module).should_receive('unmount_snapshot').with_args(
  975. 'umount',
  976. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  977. ).once()
  978. flexmock(module).should_receive('unmount_snapshot').with_args(
  979. 'umount',
  980. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  981. ).once()
  982. flexmock(module).should_receive('get_snapshots').and_raise(FileNotFoundError)
  983. flexmock(module).should_receive('remove_snapshot').never()
  984. module.remove_data_source_dumps(
  985. hook_config=config['lvm'],
  986. config=config,
  987. log_prefix='test',
  988. borgmatic_runtime_directory='/run/borgmatic',
  989. dry_run=False,
  990. )
  991. def test_remove_data_source_dumps_bails_for_lvs_command_error():
  992. config = {'lvm': {}}
  993. flexmock(module).should_receive('get_logical_volumes').and_return(
  994. (
  995. module.Logical_volume(
  996. name='lvolume1',
  997. device_path='/dev/lvolume1',
  998. mount_point='/mnt/lvolume1',
  999. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  1000. ),
  1001. module.Logical_volume(
  1002. name='lvolume2',
  1003. device_path='/dev/lvolume2',
  1004. mount_point='/mnt/lvolume2',
  1005. contained_patterns=(Pattern('/mnt/lvolume2'),),
  1006. ),
  1007. )
  1008. )
  1009. flexmock(module.borgmatic.config.paths).should_receive(
  1010. 'replace_temporary_subdirectory_with_glob'
  1011. ).and_return('/run/borgmatic')
  1012. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  1013. flexmock(module.os.path).should_receive('isdir').and_return(True)
  1014. flexmock(module.shutil).should_receive('rmtree')
  1015. flexmock(module).should_receive('unmount_snapshot').with_args(
  1016. 'umount',
  1017. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  1018. ).once()
  1019. flexmock(module).should_receive('unmount_snapshot').with_args(
  1020. 'umount',
  1021. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  1022. ).once()
  1023. flexmock(module).should_receive('get_snapshots').and_raise(
  1024. module.subprocess.CalledProcessError(1, 'wtf')
  1025. )
  1026. flexmock(module).should_receive('remove_snapshot').never()
  1027. module.remove_data_source_dumps(
  1028. hook_config=config['lvm'],
  1029. config=config,
  1030. log_prefix='test',
  1031. borgmatic_runtime_directory='/run/borgmatic',
  1032. dry_run=False,
  1033. )
  1034. def test_remove_data_source_with_dry_run_skips_snapshot_unmount_and_delete():
  1035. config = {'lvm': {}}
  1036. flexmock(module).should_receive('get_logical_volumes').and_return(
  1037. (
  1038. module.Logical_volume(
  1039. name='lvolume1',
  1040. device_path='/dev/lvolume1',
  1041. mount_point='/mnt/lvolume1',
  1042. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  1043. ),
  1044. module.Logical_volume(
  1045. name='lvolume2',
  1046. device_path='/dev/lvolume2',
  1047. mount_point='/mnt/lvolume2',
  1048. contained_patterns=(Pattern('/mnt/lvolume2'),),
  1049. ),
  1050. )
  1051. )
  1052. flexmock(module.borgmatic.config.paths).should_receive(
  1053. 'replace_temporary_subdirectory_with_glob'
  1054. ).and_return('/run/borgmatic')
  1055. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  1056. flexmock(module.os.path).should_receive('isdir').and_return(True)
  1057. flexmock(module.shutil).should_receive('rmtree').never()
  1058. flexmock(module).should_receive('unmount_snapshot').never()
  1059. flexmock(module).should_receive('get_snapshots').and_return(
  1060. (
  1061. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  1062. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  1063. module.Snapshot('nonborgmatic', '/dev/nonborgmatic'),
  1064. ),
  1065. ).once()
  1066. flexmock(module).should_receive('remove_snapshot').never()
  1067. module.remove_data_source_dumps(
  1068. hook_config=config['lvm'],
  1069. config=config,
  1070. log_prefix='test',
  1071. borgmatic_runtime_directory='/run/borgmatic',
  1072. dry_run=True,
  1073. )