test_lvm.py 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
  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. config_paths=('test.yaml',),
  201. borgmatic_runtime_directory='/run/borgmatic',
  202. patterns=patterns,
  203. dry_run=False,
  204. )
  205. == []
  206. )
  207. assert patterns == [
  208. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'),
  209. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'),
  210. ]
  211. def test_dump_data_sources_with_no_logical_volumes_skips_snapshots():
  212. config = {'lvm': {}}
  213. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  214. flexmock(module).should_receive('get_logical_volumes').and_return(())
  215. flexmock(module).should_receive('snapshot_logical_volume').never()
  216. flexmock(module).should_receive('mount_snapshot').never()
  217. assert (
  218. module.dump_data_sources(
  219. hook_config=config['lvm'],
  220. config=config,
  221. config_paths=('test.yaml',),
  222. borgmatic_runtime_directory='/run/borgmatic',
  223. patterns=patterns,
  224. dry_run=False,
  225. )
  226. == []
  227. )
  228. assert patterns == [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  229. def test_dump_data_sources_uses_snapshot_size_for_snapshot():
  230. config = {'lvm': {'snapshot_size': '1000PB'}}
  231. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  232. flexmock(module).should_receive('get_logical_volumes').and_return(
  233. (
  234. module.Logical_volume(
  235. name='lvolume1',
  236. device_path='/dev/lvolume1',
  237. mount_point='/mnt/lvolume1',
  238. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  239. ),
  240. module.Logical_volume(
  241. name='lvolume2',
  242. device_path='/dev/lvolume2',
  243. mount_point='/mnt/lvolume2',
  244. contained_patterns=(Pattern('/mnt/lvolume2'),),
  245. ),
  246. )
  247. )
  248. flexmock(module.os).should_receive('getpid').and_return(1234)
  249. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  250. 'lvcreate',
  251. 'lvolume1_borgmatic-1234',
  252. '/dev/lvolume1',
  253. '1000PB',
  254. ).once()
  255. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  256. 'lvcreate',
  257. 'lvolume2_borgmatic-1234',
  258. '/dev/lvolume2',
  259. '1000PB',
  260. ).once()
  261. flexmock(module).should_receive('get_snapshots').with_args(
  262. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  263. ).and_return(
  264. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  265. )
  266. flexmock(module).should_receive('get_snapshots').with_args(
  267. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  268. ).and_return(
  269. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  270. )
  271. flexmock(module).should_receive('mount_snapshot').with_args(
  272. 'mount', '/dev/lvolume1_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  273. ).once()
  274. flexmock(module).should_receive('mount_snapshot').with_args(
  275. 'mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  276. ).once()
  277. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  278. Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
  279. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
  280. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  281. Pattern('/mnt/lvolume2'), '/run/borgmatic'
  282. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
  283. assert (
  284. module.dump_data_sources(
  285. hook_config=config['lvm'],
  286. config=config,
  287. config_paths=('test.yaml',),
  288. borgmatic_runtime_directory='/run/borgmatic',
  289. patterns=patterns,
  290. dry_run=False,
  291. )
  292. == []
  293. )
  294. assert patterns == [
  295. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'),
  296. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'),
  297. ]
  298. def test_dump_data_sources_uses_custom_commands():
  299. config = {
  300. 'lvm': {
  301. 'lsblk_command': '/usr/local/bin/lsblk',
  302. 'lvcreate_command': '/usr/local/bin/lvcreate',
  303. 'lvs_command': '/usr/local/bin/lvs',
  304. 'mount_command': '/usr/local/bin/mount',
  305. },
  306. }
  307. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  308. flexmock(module).should_receive('get_logical_volumes').and_return(
  309. (
  310. module.Logical_volume(
  311. name='lvolume1',
  312. device_path='/dev/lvolume1',
  313. mount_point='/mnt/lvolume1',
  314. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  315. ),
  316. module.Logical_volume(
  317. name='lvolume2',
  318. device_path='/dev/lvolume2',
  319. mount_point='/mnt/lvolume2',
  320. contained_patterns=(Pattern('/mnt/lvolume2'),),
  321. ),
  322. )
  323. )
  324. flexmock(module.os).should_receive('getpid').and_return(1234)
  325. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  326. '/usr/local/bin/lvcreate',
  327. 'lvolume1_borgmatic-1234',
  328. '/dev/lvolume1',
  329. module.DEFAULT_SNAPSHOT_SIZE,
  330. ).once()
  331. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  332. '/usr/local/bin/lvcreate',
  333. 'lvolume2_borgmatic-1234',
  334. '/dev/lvolume2',
  335. module.DEFAULT_SNAPSHOT_SIZE,
  336. ).once()
  337. flexmock(module).should_receive('get_snapshots').with_args(
  338. '/usr/local/bin/lvs', snapshot_name='lvolume1_borgmatic-1234'
  339. ).and_return(
  340. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  341. )
  342. flexmock(module).should_receive('get_snapshots').with_args(
  343. '/usr/local/bin/lvs', snapshot_name='lvolume2_borgmatic-1234'
  344. ).and_return(
  345. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  346. )
  347. flexmock(module).should_receive('mount_snapshot').with_args(
  348. '/usr/local/bin/mount', '/dev/lvolume1_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  349. ).once()
  350. flexmock(module).should_receive('mount_snapshot').with_args(
  351. '/usr/local/bin/mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  352. ).once()
  353. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  354. Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
  355. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
  356. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  357. Pattern('/mnt/lvolume2'), '/run/borgmatic'
  358. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
  359. assert (
  360. module.dump_data_sources(
  361. hook_config=config['lvm'],
  362. config=config,
  363. config_paths=('test.yaml',),
  364. borgmatic_runtime_directory='/run/borgmatic',
  365. patterns=patterns,
  366. dry_run=False,
  367. )
  368. == []
  369. )
  370. assert patterns == [
  371. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'),
  372. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'),
  373. ]
  374. def test_dump_data_sources_with_dry_run_skips_snapshots_and_does_not_touch_patterns():
  375. config = {'lvm': {}}
  376. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  377. flexmock(module).should_receive('get_logical_volumes').and_return(
  378. (
  379. module.Logical_volume(
  380. name='lvolume1',
  381. device_path='/dev/lvolume1',
  382. mount_point='/mnt/lvolume1',
  383. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  384. ),
  385. module.Logical_volume(
  386. name='lvolume2',
  387. device_path='/dev/lvolume2',
  388. mount_point='/mnt/lvolume2',
  389. contained_patterns=(Pattern('/mnt/lvolume2'),),
  390. ),
  391. )
  392. )
  393. flexmock(module.os).should_receive('getpid').and_return(1234)
  394. flexmock(module).should_receive('snapshot_logical_volume').never()
  395. flexmock(module).should_receive('get_snapshots').with_args(
  396. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  397. ).and_return(
  398. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  399. )
  400. flexmock(module).should_receive('get_snapshots').with_args(
  401. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  402. ).and_return(
  403. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  404. )
  405. flexmock(module).should_receive('mount_snapshot').never()
  406. assert (
  407. module.dump_data_sources(
  408. hook_config=config['lvm'],
  409. config=config,
  410. config_paths=('test.yaml',),
  411. borgmatic_runtime_directory='/run/borgmatic',
  412. patterns=patterns,
  413. dry_run=True,
  414. )
  415. == []
  416. )
  417. assert patterns == [
  418. Pattern('/mnt/lvolume1/subdir'),
  419. Pattern('/mnt/lvolume2'),
  420. ]
  421. def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained_patterns():
  422. config = {'lvm': {}}
  423. patterns = [Pattern('/hmm')]
  424. flexmock(module).should_receive('get_logical_volumes').and_return(
  425. (
  426. module.Logical_volume(
  427. name='lvolume1',
  428. device_path='/dev/lvolume1',
  429. mount_point='/mnt/lvolume1',
  430. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  431. ),
  432. module.Logical_volume(
  433. name='lvolume2',
  434. device_path='/dev/lvolume2',
  435. mount_point='/mnt/lvolume2',
  436. contained_patterns=(Pattern('/mnt/lvolume2'),),
  437. ),
  438. )
  439. )
  440. flexmock(module.os).should_receive('getpid').and_return(1234)
  441. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  442. 'lvcreate', 'lvolume1_borgmatic-1234', '/dev/lvolume1', module.DEFAULT_SNAPSHOT_SIZE
  443. ).once()
  444. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  445. 'lvcreate', 'lvolume2_borgmatic-1234', '/dev/lvolume2', module.DEFAULT_SNAPSHOT_SIZE
  446. ).once()
  447. flexmock(module).should_receive('get_snapshots').with_args(
  448. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  449. ).and_return(
  450. (module.Snapshot(name='lvolume1_borgmatic-1234', device_path='/dev/lvolume1_snap'),)
  451. )
  452. flexmock(module).should_receive('get_snapshots').with_args(
  453. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  454. ).and_return(
  455. (module.Snapshot(name='lvolume2_borgmatic-1234', device_path='/dev/lvolume2_snap'),)
  456. )
  457. flexmock(module).should_receive('mount_snapshot').with_args(
  458. 'mount', '/dev/lvolume1_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  459. ).once()
  460. flexmock(module).should_receive('mount_snapshot').with_args(
  461. 'mount', '/dev/lvolume2_snap', '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  462. ).once()
  463. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  464. Pattern('/mnt/lvolume1/subdir'), '/run/borgmatic'
  465. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'))
  466. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  467. Pattern('/mnt/lvolume2'), '/run/borgmatic'
  468. ).and_return(Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'))
  469. assert (
  470. module.dump_data_sources(
  471. hook_config=config['lvm'],
  472. config=config,
  473. config_paths=('test.yaml',),
  474. borgmatic_runtime_directory='/run/borgmatic',
  475. patterns=patterns,
  476. dry_run=False,
  477. )
  478. == []
  479. )
  480. assert patterns == [
  481. Pattern('/hmm'),
  482. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume1/subdir'),
  483. Pattern('/run/borgmatic/lvm_snapshots/./mnt/lvolume2'),
  484. ]
  485. def test_dump_data_sources_with_missing_snapshot_errors():
  486. config = {'lvm': {}}
  487. patterns = [Pattern('/mnt/lvolume1/subdir'), Pattern('/mnt/lvolume2')]
  488. flexmock(module).should_receive('get_logical_volumes').and_return(
  489. (
  490. module.Logical_volume(
  491. name='lvolume1',
  492. device_path='/dev/lvolume1',
  493. mount_point='/mnt/lvolume1',
  494. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  495. ),
  496. module.Logical_volume(
  497. name='lvolume2',
  498. device_path='/dev/lvolume2',
  499. mount_point='/mnt/lvolume2',
  500. contained_patterns=(Pattern('/mnt/lvolume2'),),
  501. ),
  502. )
  503. )
  504. flexmock(module.os).should_receive('getpid').and_return(1234)
  505. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  506. 'lvcreate', 'lvolume1_borgmatic-1234', '/dev/lvolume1', module.DEFAULT_SNAPSHOT_SIZE
  507. ).once()
  508. flexmock(module).should_receive('snapshot_logical_volume').with_args(
  509. 'lvcreate', 'lvolume2_borgmatic-1234', '/dev/lvolume2', module.DEFAULT_SNAPSHOT_SIZE
  510. ).never()
  511. flexmock(module).should_receive('get_snapshots').with_args(
  512. 'lvs', snapshot_name='lvolume1_borgmatic-1234'
  513. ).and_return(())
  514. flexmock(module).should_receive('get_snapshots').with_args(
  515. 'lvs', snapshot_name='lvolume2_borgmatic-1234'
  516. ).never()
  517. flexmock(module).should_receive('mount_snapshot').never()
  518. with pytest.raises(ValueError):
  519. module.dump_data_sources(
  520. hook_config=config['lvm'],
  521. config=config,
  522. config_paths=('test.yaml',),
  523. borgmatic_runtime_directory='/run/borgmatic',
  524. patterns=patterns,
  525. dry_run=False,
  526. )
  527. def test_get_snapshots_lists_all_snapshots():
  528. flexmock(module.borgmatic.execute).should_receive(
  529. 'execute_command_and_capture_output'
  530. ).and_return(
  531. '''
  532. {
  533. "report": [
  534. {
  535. "lv": [
  536. {"lv_name": "snap1", "lv_path": "/dev/snap1"},
  537. {"lv_name": "snap2", "lv_path": "/dev/snap2"}
  538. ]
  539. }
  540. ],
  541. "log": [
  542. ]
  543. }
  544. '''
  545. )
  546. assert module.get_snapshots('lvs') == (
  547. module.Snapshot('snap1', '/dev/snap1'),
  548. module.Snapshot('snap2', '/dev/snap2'),
  549. )
  550. def test_get_snapshots_with_snapshot_name_lists_just_that_snapshot():
  551. flexmock(module.borgmatic.execute).should_receive(
  552. 'execute_command_and_capture_output'
  553. ).and_return(
  554. '''
  555. {
  556. "report": [
  557. {
  558. "lv": [
  559. {"lv_name": "snap1", "lv_path": "/dev/snap1"},
  560. {"lv_name": "snap2", "lv_path": "/dev/snap2"}
  561. ]
  562. }
  563. ],
  564. "log": [
  565. ]
  566. }
  567. '''
  568. )
  569. assert module.get_snapshots('lvs', snapshot_name='snap2') == (
  570. module.Snapshot('snap2', '/dev/snap2'),
  571. )
  572. def test_get_snapshots_with_invalid_lvs_json_errors():
  573. flexmock(module.borgmatic.execute).should_receive(
  574. 'execute_command_and_capture_output'
  575. ).and_return('{')
  576. with pytest.raises(ValueError):
  577. assert module.get_snapshots('lvs')
  578. def test_get_snapshots_with_lvs_json_missing_report_errors():
  579. flexmock(module.borgmatic.execute).should_receive(
  580. 'execute_command_and_capture_output'
  581. ).and_return(
  582. '''
  583. {
  584. "report": [],
  585. "log": [
  586. ]
  587. }
  588. '''
  589. )
  590. with pytest.raises(ValueError):
  591. assert module.get_snapshots('lvs')
  592. def test_get_snapshots_with_lvs_json_missing_keys_errors():
  593. flexmock(module.borgmatic.execute).should_receive(
  594. 'execute_command_and_capture_output'
  595. ).and_return(
  596. '''
  597. {
  598. "report": [
  599. {
  600. "lv": [
  601. {}
  602. ]
  603. }
  604. ],
  605. "log": [
  606. ]
  607. }
  608. '''
  609. )
  610. with pytest.raises(ValueError):
  611. assert module.get_snapshots('lvs')
  612. def test_remove_data_source_dumps_unmounts_and_remove_snapshots():
  613. config = {'lvm': {}}
  614. flexmock(module).should_receive('get_logical_volumes').and_return(
  615. (
  616. module.Logical_volume(
  617. name='lvolume1',
  618. device_path='/dev/lvolume1',
  619. mount_point='/mnt/lvolume1',
  620. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  621. ),
  622. module.Logical_volume(
  623. name='lvolume2',
  624. device_path='/dev/lvolume2',
  625. mount_point='/mnt/lvolume2',
  626. contained_patterns=(Pattern('/mnt/lvolume2'),),
  627. ),
  628. )
  629. )
  630. flexmock(module.borgmatic.config.paths).should_receive(
  631. 'replace_temporary_subdirectory_with_glob'
  632. ).and_return('/run/borgmatic')
  633. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  634. flexmock(module.os.path).should_receive('isdir').and_return(True)
  635. flexmock(module.shutil).should_receive('rmtree')
  636. flexmock(module).should_receive('unmount_snapshot').with_args(
  637. 'umount',
  638. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  639. ).once()
  640. flexmock(module).should_receive('unmount_snapshot').with_args(
  641. 'umount',
  642. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  643. ).once()
  644. flexmock(module).should_receive('get_snapshots').and_return(
  645. (
  646. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  647. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  648. module.Snapshot('nonborgmatic', '/dev/nonborgmatic'),
  649. ),
  650. )
  651. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume1').once()
  652. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume2').once()
  653. flexmock(module).should_receive('remove_snapshot').with_args(
  654. 'nonborgmatic', '/dev/nonborgmatic'
  655. ).never()
  656. module.remove_data_source_dumps(
  657. hook_config=config['lvm'],
  658. config=config,
  659. borgmatic_runtime_directory='/run/borgmatic',
  660. dry_run=False,
  661. )
  662. def test_remove_data_source_dumps_bails_for_missing_lvm_configuration():
  663. flexmock(module).should_receive('get_logical_volumes').never()
  664. flexmock(module.borgmatic.config.paths).should_receive(
  665. 'replace_temporary_subdirectory_with_glob'
  666. ).never()
  667. flexmock(module).should_receive('unmount_snapshot').never()
  668. flexmock(module).should_receive('remove_snapshot').never()
  669. module.remove_data_source_dumps(
  670. hook_config=None,
  671. config={'source_directories': '/mnt/lvolume'},
  672. borgmatic_runtime_directory='/run/borgmatic',
  673. dry_run=False,
  674. )
  675. def test_remove_data_source_dumps_bails_for_missing_lsblk_command():
  676. config = {'lvm': {}}
  677. flexmock(module).should_receive('get_logical_volumes').and_raise(FileNotFoundError)
  678. flexmock(module.borgmatic.config.paths).should_receive(
  679. 'replace_temporary_subdirectory_with_glob'
  680. ).never()
  681. flexmock(module).should_receive('unmount_snapshot').never()
  682. flexmock(module).should_receive('remove_snapshot').never()
  683. module.remove_data_source_dumps(
  684. hook_config=config['lvm'],
  685. config=config,
  686. borgmatic_runtime_directory='/run/borgmatic',
  687. dry_run=False,
  688. )
  689. def test_remove_data_source_dumps_bails_for_lsblk_command_error():
  690. config = {'lvm': {}}
  691. flexmock(module).should_receive('get_logical_volumes').and_raise(
  692. module.subprocess.CalledProcessError(1, 'wtf')
  693. )
  694. flexmock(module.borgmatic.config.paths).should_receive(
  695. 'replace_temporary_subdirectory_with_glob'
  696. ).never()
  697. flexmock(module).should_receive('unmount_snapshot').never()
  698. flexmock(module).should_receive('remove_snapshot').never()
  699. module.remove_data_source_dumps(
  700. hook_config=config['lvm'],
  701. config=config,
  702. borgmatic_runtime_directory='/run/borgmatic',
  703. dry_run=False,
  704. )
  705. def test_remove_data_source_dumps_with_missing_snapshot_directory_skips_unmount():
  706. config = {'lvm': {}}
  707. flexmock(module).should_receive('get_logical_volumes').and_return(
  708. (
  709. module.Logical_volume(
  710. name='lvolume1',
  711. device_path='/dev/lvolume1',
  712. mount_point='/mnt/lvolume1',
  713. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  714. ),
  715. module.Logical_volume(
  716. name='lvolume2',
  717. device_path='/dev/lvolume2',
  718. mount_point='/mnt/lvolume2',
  719. contained_patterns=(Pattern('/mnt/lvolume2'),),
  720. ),
  721. )
  722. )
  723. flexmock(module.borgmatic.config.paths).should_receive(
  724. 'replace_temporary_subdirectory_with_glob'
  725. ).and_return('/run/borgmatic')
  726. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  727. flexmock(module.os.path).should_receive('isdir').with_args(
  728. '/run/borgmatic/lvm_snapshots'
  729. ).and_return(False)
  730. flexmock(module.shutil).should_receive('rmtree').never()
  731. flexmock(module).should_receive('unmount_snapshot').never()
  732. flexmock(module).should_receive('get_snapshots').and_return(
  733. (
  734. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  735. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  736. ),
  737. )
  738. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume1').once()
  739. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume2').once()
  740. module.remove_data_source_dumps(
  741. hook_config=config['lvm'],
  742. config=config,
  743. borgmatic_runtime_directory='/run/borgmatic',
  744. dry_run=False,
  745. )
  746. def test_remove_data_source_dumps_with_missing_snapshot_mount_path_skips_unmount():
  747. config = {'lvm': {}}
  748. flexmock(module).should_receive('get_logical_volumes').and_return(
  749. (
  750. module.Logical_volume(
  751. name='lvolume1',
  752. device_path='/dev/lvolume1',
  753. mount_point='/mnt/lvolume1',
  754. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  755. ),
  756. module.Logical_volume(
  757. name='lvolume2',
  758. device_path='/dev/lvolume2',
  759. mount_point='/mnt/lvolume2',
  760. contained_patterns=(Pattern('/mnt/lvolume2'),),
  761. ),
  762. )
  763. )
  764. flexmock(module.borgmatic.config.paths).should_receive(
  765. 'replace_temporary_subdirectory_with_glob'
  766. ).and_return('/run/borgmatic')
  767. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  768. flexmock(module.os.path).should_receive('isdir').with_args(
  769. '/run/borgmatic/lvm_snapshots'
  770. ).and_return(True)
  771. flexmock(module.os.path).should_receive('isdir').with_args(
  772. '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  773. ).and_return(False)
  774. flexmock(module.os.path).should_receive('isdir').with_args(
  775. '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  776. ).and_return(True)
  777. flexmock(module.shutil).should_receive('rmtree')
  778. flexmock(module).should_receive('unmount_snapshot').with_args(
  779. 'umount',
  780. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  781. ).never()
  782. flexmock(module).should_receive('unmount_snapshot').with_args(
  783. 'umount',
  784. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  785. ).once()
  786. flexmock(module).should_receive('get_snapshots').and_return(
  787. (
  788. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  789. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  790. ),
  791. )
  792. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume1').once()
  793. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume2').once()
  794. module.remove_data_source_dumps(
  795. hook_config=config['lvm'],
  796. config=config,
  797. borgmatic_runtime_directory='/run/borgmatic',
  798. dry_run=False,
  799. )
  800. def test_remove_data_source_dumps_with_successful_mount_point_removal_skips_unmount():
  801. config = {'lvm': {}}
  802. flexmock(module).should_receive('get_logical_volumes').and_return(
  803. (
  804. module.Logical_volume(
  805. name='lvolume1',
  806. device_path='/dev/lvolume1',
  807. mount_point='/mnt/lvolume1',
  808. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  809. ),
  810. module.Logical_volume(
  811. name='lvolume2',
  812. device_path='/dev/lvolume2',
  813. mount_point='/mnt/lvolume2',
  814. contained_patterns=(Pattern('/mnt/lvolume2'),),
  815. ),
  816. )
  817. )
  818. flexmock(module.borgmatic.config.paths).should_receive(
  819. 'replace_temporary_subdirectory_with_glob'
  820. ).and_return('/run/borgmatic')
  821. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  822. flexmock(module.os.path).should_receive('isdir').with_args(
  823. '/run/borgmatic/lvm_snapshots'
  824. ).and_return(True)
  825. flexmock(module.os.path).should_receive('isdir').with_args(
  826. '/run/borgmatic/lvm_snapshots/mnt/lvolume1'
  827. ).and_return(True).and_return(False)
  828. flexmock(module.os.path).should_receive('isdir').with_args(
  829. '/run/borgmatic/lvm_snapshots/mnt/lvolume2'
  830. ).and_return(True).and_return(True)
  831. flexmock(module.shutil).should_receive('rmtree')
  832. flexmock(module).should_receive('unmount_snapshot').with_args(
  833. 'umount',
  834. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  835. ).never()
  836. flexmock(module).should_receive('unmount_snapshot').with_args(
  837. 'umount',
  838. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  839. ).once()
  840. flexmock(module).should_receive('get_snapshots').and_return(
  841. (
  842. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  843. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  844. ),
  845. )
  846. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume1').once()
  847. flexmock(module).should_receive('remove_snapshot').with_args('lvremove', '/dev/lvolume2').once()
  848. module.remove_data_source_dumps(
  849. hook_config=config['lvm'],
  850. config=config,
  851. borgmatic_runtime_directory='/run/borgmatic',
  852. dry_run=False,
  853. )
  854. def test_remove_data_source_dumps_bails_for_missing_umount_command():
  855. config = {'lvm': {}}
  856. flexmock(module).should_receive('get_logical_volumes').and_return(
  857. (
  858. module.Logical_volume(
  859. name='lvolume1',
  860. device_path='/dev/lvolume1',
  861. mount_point='/mnt/lvolume1',
  862. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  863. ),
  864. module.Logical_volume(
  865. name='lvolume2',
  866. device_path='/dev/lvolume2',
  867. mount_point='/mnt/lvolume2',
  868. contained_patterns=(Pattern('/mnt/lvolume2'),),
  869. ),
  870. )
  871. )
  872. flexmock(module.borgmatic.config.paths).should_receive(
  873. 'replace_temporary_subdirectory_with_glob'
  874. ).and_return('/run/borgmatic')
  875. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  876. flexmock(module.os.path).should_receive('isdir').and_return(True)
  877. flexmock(module.shutil).should_receive('rmtree')
  878. flexmock(module).should_receive('unmount_snapshot').with_args(
  879. 'umount',
  880. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  881. ).and_raise(FileNotFoundError)
  882. flexmock(module).should_receive('unmount_snapshot').with_args(
  883. 'umount',
  884. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  885. ).never()
  886. flexmock(module).should_receive('get_snapshots').never()
  887. flexmock(module).should_receive('remove_snapshot').never()
  888. module.remove_data_source_dumps(
  889. hook_config=config['lvm'],
  890. config=config,
  891. borgmatic_runtime_directory='/run/borgmatic',
  892. dry_run=False,
  893. )
  894. def test_remove_data_source_dumps_bails_for_umount_command_error():
  895. config = {'lvm': {}}
  896. flexmock(module).should_receive('get_logical_volumes').and_return(
  897. (
  898. module.Logical_volume(
  899. name='lvolume1',
  900. device_path='/dev/lvolume1',
  901. mount_point='/mnt/lvolume1',
  902. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  903. ),
  904. module.Logical_volume(
  905. name='lvolume2',
  906. device_path='/dev/lvolume2',
  907. mount_point='/mnt/lvolume2',
  908. contained_patterns=(Pattern('/mnt/lvolume2'),),
  909. ),
  910. )
  911. )
  912. flexmock(module.borgmatic.config.paths).should_receive(
  913. 'replace_temporary_subdirectory_with_glob'
  914. ).and_return('/run/borgmatic')
  915. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  916. flexmock(module.os.path).should_receive('isdir').and_return(True)
  917. flexmock(module.shutil).should_receive('rmtree')
  918. flexmock(module).should_receive('unmount_snapshot').with_args(
  919. 'umount',
  920. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  921. ).and_raise(module.subprocess.CalledProcessError(1, 'wtf'))
  922. flexmock(module).should_receive('unmount_snapshot').with_args(
  923. 'umount',
  924. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  925. ).never()
  926. flexmock(module).should_receive('get_snapshots').never()
  927. flexmock(module).should_receive('remove_snapshot').never()
  928. module.remove_data_source_dumps(
  929. hook_config=config['lvm'],
  930. config=config,
  931. borgmatic_runtime_directory='/run/borgmatic',
  932. dry_run=False,
  933. )
  934. def test_remove_data_source_dumps_bails_for_missing_lvs_command():
  935. config = {'lvm': {}}
  936. flexmock(module).should_receive('get_logical_volumes').and_return(
  937. (
  938. module.Logical_volume(
  939. name='lvolume1',
  940. device_path='/dev/lvolume1',
  941. mount_point='/mnt/lvolume1',
  942. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  943. ),
  944. module.Logical_volume(
  945. name='lvolume2',
  946. device_path='/dev/lvolume2',
  947. mount_point='/mnt/lvolume2',
  948. contained_patterns=(Pattern('/mnt/lvolume2'),),
  949. ),
  950. )
  951. )
  952. flexmock(module.borgmatic.config.paths).should_receive(
  953. 'replace_temporary_subdirectory_with_glob'
  954. ).and_return('/run/borgmatic')
  955. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  956. flexmock(module.os.path).should_receive('isdir').and_return(True)
  957. flexmock(module.shutil).should_receive('rmtree')
  958. flexmock(module).should_receive('unmount_snapshot').with_args(
  959. 'umount',
  960. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  961. ).once()
  962. flexmock(module).should_receive('unmount_snapshot').with_args(
  963. 'umount',
  964. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  965. ).once()
  966. flexmock(module).should_receive('get_snapshots').and_raise(FileNotFoundError)
  967. flexmock(module).should_receive('remove_snapshot').never()
  968. module.remove_data_source_dumps(
  969. hook_config=config['lvm'],
  970. config=config,
  971. borgmatic_runtime_directory='/run/borgmatic',
  972. dry_run=False,
  973. )
  974. def test_remove_data_source_dumps_bails_for_lvs_command_error():
  975. config = {'lvm': {}}
  976. flexmock(module).should_receive('get_logical_volumes').and_return(
  977. (
  978. module.Logical_volume(
  979. name='lvolume1',
  980. device_path='/dev/lvolume1',
  981. mount_point='/mnt/lvolume1',
  982. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  983. ),
  984. module.Logical_volume(
  985. name='lvolume2',
  986. device_path='/dev/lvolume2',
  987. mount_point='/mnt/lvolume2',
  988. contained_patterns=(Pattern('/mnt/lvolume2'),),
  989. ),
  990. )
  991. )
  992. flexmock(module.borgmatic.config.paths).should_receive(
  993. 'replace_temporary_subdirectory_with_glob'
  994. ).and_return('/run/borgmatic')
  995. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  996. flexmock(module.os.path).should_receive('isdir').and_return(True)
  997. flexmock(module.shutil).should_receive('rmtree')
  998. flexmock(module).should_receive('unmount_snapshot').with_args(
  999. 'umount',
  1000. '/run/borgmatic/lvm_snapshots/mnt/lvolume1',
  1001. ).once()
  1002. flexmock(module).should_receive('unmount_snapshot').with_args(
  1003. 'umount',
  1004. '/run/borgmatic/lvm_snapshots/mnt/lvolume2',
  1005. ).once()
  1006. flexmock(module).should_receive('get_snapshots').and_raise(
  1007. module.subprocess.CalledProcessError(1, 'wtf')
  1008. )
  1009. flexmock(module).should_receive('remove_snapshot').never()
  1010. module.remove_data_source_dumps(
  1011. hook_config=config['lvm'],
  1012. config=config,
  1013. borgmatic_runtime_directory='/run/borgmatic',
  1014. dry_run=False,
  1015. )
  1016. def test_remove_data_source_with_dry_run_skips_snapshot_unmount_and_delete():
  1017. config = {'lvm': {}}
  1018. flexmock(module).should_receive('get_logical_volumes').and_return(
  1019. (
  1020. module.Logical_volume(
  1021. name='lvolume1',
  1022. device_path='/dev/lvolume1',
  1023. mount_point='/mnt/lvolume1',
  1024. contained_patterns=(Pattern('/mnt/lvolume1/subdir'),),
  1025. ),
  1026. module.Logical_volume(
  1027. name='lvolume2',
  1028. device_path='/dev/lvolume2',
  1029. mount_point='/mnt/lvolume2',
  1030. contained_patterns=(Pattern('/mnt/lvolume2'),),
  1031. ),
  1032. )
  1033. )
  1034. flexmock(module.borgmatic.config.paths).should_receive(
  1035. 'replace_temporary_subdirectory_with_glob'
  1036. ).and_return('/run/borgmatic')
  1037. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  1038. flexmock(module.os.path).should_receive('isdir').and_return(True)
  1039. flexmock(module.shutil).should_receive('rmtree').never()
  1040. flexmock(module).should_receive('unmount_snapshot').never()
  1041. flexmock(module).should_receive('get_snapshots').and_return(
  1042. (
  1043. module.Snapshot('lvolume1_borgmatic-1234', '/dev/lvolume1'),
  1044. module.Snapshot('lvolume2_borgmatic-1234', '/dev/lvolume2'),
  1045. module.Snapshot('nonborgmatic', '/dev/nonborgmatic'),
  1046. ),
  1047. ).once()
  1048. flexmock(module).should_receive('remove_snapshot').never()
  1049. module.remove_data_source_dumps(
  1050. hook_config=config['lvm'],
  1051. config=config,
  1052. borgmatic_runtime_directory='/run/borgmatic',
  1053. dry_run=True,
  1054. )