Skip to content

Commit 54f1f44

Browse files
committed
feat: add support for "bootable" snapshots
This feature is only support on platforms that support snapshot manager (snapm) The bootable flag, when used with the snapm create command, creates a boot menu entry for the snapshot set. This allows the user to select the boot menu entry on reboot that will use the newly created snapshot set. Signed-off-by: Todd Gill <tgill@redhat.com>
1 parent 4f0f4c2 commit 54f1f44

File tree

8 files changed

+234
-1
lines changed

8 files changed

+234
-1
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Sets are defined in the following format:
5454
```yaml
5555
snapshot_lvm_set:
5656
name: snapset1
57+
bootable: true
5758
volumes:
5859
- name: snapshot VG1 LV1
5960
vg: test_vg1
@@ -84,6 +85,7 @@ that it applies, for example:
8485
```yaml
8586
snapshot_lvm_set:
8687
name: snapset1
88+
bootable: false
8789
volumes:
8890
- name: snapshot VG1 LV1
8991
vg: test_vg1
@@ -243,6 +245,18 @@ the rest will be excluded. For example,
243245
`snapshot_lvm_vg_include: "^sql_db_"` will only operate on volume groups
244246
whose names start with `sql_db_`. This uses the Python `re.search`.
245247

248+
### snapshot_lvm_bootable
249+
250+
Boolean - default is unset. Only supported on operating systems that
251+
support snapshot manager (snapm). When set to true, and passed to the
252+
'snapshot' command, the snapshot created will have a corresponding boot
253+
entry. The boot entry will be removed when the snapset is removed.
254+
255+
### snapshot_use_copr (EXPERIMENTAL)
256+
257+
Boolean - default is unset - if you want to enable the copr repo to use the
258+
latest development version of snapm, use `snapshot_use_copr: true`
259+
246260
### Variables Exported by the Role
247261

248262
#### snapshot_facts

library/snapshot.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,21 @@
9595
to a regex pattern that matches the names of the volume groups you want to use and
9696
the rest will be excluded
9797
type: str
98+
snapshot_lvm_bootable:
99+
description: Only supported on operating systems that support snapshot manager (snapm).
100+
When set to true, and passed to the 'snapshot' command, the snapshot created will have
101+
a corresponding boot entry. The boot entry will be removed when the snapset is removed.
102+
type: bool
98103
snapshot_lvm_set:
99104
description: set of volumes
100105
type: dict
101106
suboptions:
102107
name:
103108
description: name of set
104109
type: str
110+
bootable:
111+
description: create snapshot with boot menu entry (requires snapm)
112+
type: bool
105113
volumes:
106114
description: list of volumes
107115
type: list
@@ -282,6 +290,7 @@ def run_module():
282290
snapshot_lvm_all_vgs=dict(type="bool"),
283291
snapshot_lvm_verify_only=dict(type="bool"),
284292
snapshot_lvm_mount_origin=dict(type="bool"),
293+
snapshot_lvm_bootable=dict(type="bool"),
285294
snapshot_lvm_mountpoint_create=dict(type="bool"),
286295
snapshot_lvm_unmount_all=dict(type="bool"),
287296
snapshot_lvm_percent_space_required=dict(type="str"),
@@ -296,6 +305,7 @@ def run_module():
296305
type="dict",
297306
options=dict(
298307
name=dict(type="str"),
308+
bootable=dict(type="bool"),
299309
volumes=dict(
300310
type="list",
301311
elements="dict",

module_utils/snapshot_lsr/lvm.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ def get_json_from_args(module, module_args, vg_include):
8181
if module_args["snapshot_lvm_snapset_name"]:
8282
args_dict["name"] = module_args["snapshot_lvm_snapset_name"]
8383

84+
if module_args["snapshot_lvm_bootable"]:
85+
args_dict["bootable"] = module_args["snapshot_lvm_bootable"]
86+
else:
87+
args_dict["bootable"] = False
88+
8489
for vg, lv_list in vgs_lvs_iterator(
8590
module,
8691
module_args["snapshot_lvm_vg"],

module_utils/snapshot_lsr/snapmgr.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ def mgr_snapshot_cmd(module, module_args, snapset_json):
200200

201201
snapset_name = snapset_json["name"]
202202
volume_list = snapset_json["volumes"]
203+
if "bootable" in snapset_json:
204+
bootable = snapset_json["bootable"]
205+
else:
206+
bootable = False
203207

204208
source_list = mgr_get_source_list_for_create(volume_list)
205209

@@ -210,7 +214,10 @@ def mgr_snapshot_cmd(module, module_args, snapset_json):
210214

211215
try:
212216
manager.create_snapshot_set(
213-
snapset_name, source_list, SNAPM_DEFAULT_SIZE_POLICY
217+
snapset_name,
218+
source_list,
219+
SNAPM_DEFAULT_SIZE_POLICY,
220+
boot=bootable,
214221
)
215222
changed = True
216223
except snapm.SnapmError as snap_err:

module_utils/snapshot_lsr/validate.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ def get_json_from_args(module, module_args, vg_include):
3333
if module_args["snapshot_lvm_snapset_name"]:
3434
args_dict["name"] = module_args["snapshot_lvm_snapset_name"]
3535

36+
if module_args["snapshot_lvm_bootable"]:
37+
args_dict["bootable"] = module_args["snapshot_lvm_bootable"]
38+
3639
for vg, lv_list in vgs_lvs_iterator(
3740
module,
3841
module_args["snapshot_lvm_vg"],

tasks/enable_copr.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
# Primarily for testing unreleased versions
3+
- name: Enable snapm copr on Fedora
4+
command: dnf -y copr enable packit/snapshotmanager-snapm-357
5+
when: ansible_facts['distribution'] == 'Fedora'
6+
changed_when: true

tasks/main.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,62 @@
33
- name: Set platform/version specific variables
44
include_tasks: tasks/set_vars.yml
55

6+
- name: Enable copr if requested
7+
include_tasks: enable_copr.yml
8+
when: snapshot_use_copr | d(false)
9+
610
- name: Ensure required packages are installed
711
package:
812
name: "{{ __snapshot_packages }}"
913
state: present
1014
use: "{{ (__snapshot_is_ostree | d(false)) |
1115
ternary('ansible.posix.rhel_rpm_ostree', omit) }}"
1216

17+
- name: Get snapm version
18+
check_mode: false
19+
command: snapm --version
20+
changed_when: false
21+
register: __snapshot_snapm_version_output
22+
# Ignore errors as snapm may not be available
23+
ignore_errors: true
24+
25+
- name: Set snapm availability fact
26+
set_fact:
27+
__snapshot_snapm_available: "{{ __snapshot_snapm_version_output is success }}"
28+
29+
- name: Set snapm version
30+
set_fact:
31+
__snapshot_snapm_version: "{{ (__snapshot_snapm_version_output.stdout) }}"
32+
when:
33+
- __snapshot_snapm_available
34+
35+
- name: Package snapm version must be 0.4 or later
36+
fail:
37+
msg: >
38+
Package snapm version {{ __snapshot_snapm_version }} is too old -
39+
must be 0.4 or later
40+
when:
41+
- __snapshot_snapm_available
42+
- __snapshot_snapm_version is version("0.4", "<")
43+
44+
- name: Package snapm required for bootable snapsets
45+
fail:
46+
msg: >
47+
Package snapm is required for bootable snapsets
48+
when:
49+
- not __snapshot_snapm_available | d(false)
50+
- snapshot_lvm_bootable | d(false)
51+
52+
- name: Package snapm version must be 0.5 or later bootable snapsets
53+
fail:
54+
msg: >
55+
Package snapm version {{ __snapshot_snapm_version }} is too old -
56+
must be 0.5 or later to use bootable snapsets
57+
when:
58+
- __snapshot_snapm_available
59+
- __snapshot_snapm_version is version("0.5", "<")
60+
- snapshot_lvm_bootable | d(false)
61+
1362
- name: Run snapshot module and handle errors
1463
block:
1564
- name: Run snapshot module {{ snapshot_lvm_action }}
@@ -19,6 +68,7 @@
1968
snapshot_lvm_all_vgs: "{{ snapshot_lvm_all_vgs | d(false) }}"
2069
snapshot_lvm_verify_only: "{{ snapshot_lvm_verify_only | d(false) }}"
2170
snapshot_lvm_mount_origin: "{{ snapshot_lvm_mount_origin | d(false) }}"
71+
snapshot_lvm_bootable: "{{ snapshot_lvm_bootable | d(false) }}"
2272
snapshot_lvm_mountpoint_create: "{{
2373
snapshot_lvm_mountpoint_create | d(false) }}"
2474
snapshot_lvm_unmount_all: "{{ snapshot_lvm_unmount_all | d(false) }}"

tests/tests_set_bootable.yml

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
- name: Snapshot a set of volumes with snapshot_lvm_bootable set to true
3+
hosts: all
4+
vars:
5+
test_disk_min_size: "1g"
6+
test_disk_count: 10
7+
test_storage_pools:
8+
- name: test_vg1
9+
disks: "{{ range(0, 3) | map('extract', unused_disks) | list }}"
10+
volumes:
11+
- name: lv1
12+
size: "15%"
13+
- name: lv2
14+
size: "50%"
15+
- name: test_vg2
16+
disks: "{{ range(3, 6) | map('extract', unused_disks) | list }}"
17+
volumes:
18+
- name: lv3
19+
size: "10%"
20+
- name: lv4
21+
size: "20%"
22+
- name: test_vg3
23+
disks: "{{ range(6, 10) | map('extract', unused_disks) | list }}"
24+
volumes:
25+
- name: lv5
26+
size: "30%"
27+
- name: lv6
28+
size: "25%"
29+
- name: lv7
30+
size: "10%"
31+
- name: lv8
32+
size: "10%"
33+
snapshot_test_set:
34+
name: snapset1
35+
volumes:
36+
- name: snapshot VG1 LV1
37+
vg: test_vg1
38+
lv: lv1
39+
percent_space_required: 20
40+
- name: snapshot VG2 LV3
41+
vg: test_vg2
42+
lv: lv3
43+
percent_space_required: 15
44+
- name: snapshot VG2 LV4
45+
vg: test_vg2
46+
lv: lv4
47+
percent_space_required: 15
48+
- name: snapshot VG3 LV7
49+
vg: test_vg3
50+
lv: lv7
51+
percent_space_required: 15
52+
tasks:
53+
- name: Load test variables
54+
include_vars:
55+
file: vars/rh_distros_vars.yml
56+
when: __snapshot_is_ostree is not defined
57+
58+
- name: Test is only supported on platforms with snapm support
59+
debug:
60+
msg: >
61+
This test is only supported on platforms with snapm support.
62+
when:
63+
- not __snapshot_snapm_available | d(false)
64+
65+
- name: End test
66+
meta: end_play
67+
when:
68+
- not __snapshot_snapm_available | d(false)
69+
70+
- name: Run tests
71+
block:
72+
- name: Setup
73+
include_tasks: tasks/setup.yml
74+
75+
- name: Run the snapshot role to create snapshot set of LVs
76+
include_role:
77+
name: linux-system-roles.snapshot
78+
vars:
79+
snapshot_lvm_action: snapshot
80+
snapshot_lvm_bootable: true
81+
snapshot_lvm_set: "{{ snapshot_test_set }}"
82+
83+
- name: Assert changes for create snapset
84+
assert:
85+
that: snapshot_cmd["changed"]
86+
87+
- name: Run the snapshot role to verify the set of snapshots for the LVs
88+
include_role:
89+
name: linux-system-roles.snapshot
90+
vars:
91+
snapshot_lvm_action: check
92+
snapshot_lvm_set: "{{ snapshot_test_set }}"
93+
snapshot_lvm_verify_only: true
94+
95+
- name: Create snapset again for idempotence
96+
include_role:
97+
name: linux-system-roles.snapshot
98+
vars:
99+
snapshot_lvm_action: snapshot
100+
snapshot_lvm_set: "{{ snapshot_test_set }}"
101+
102+
- name: Assert no changes for create snapset
103+
assert:
104+
that: not snapshot_cmd["changed"]
105+
106+
- name: Run the snapshot role remove the set
107+
include_role:
108+
name: linux-system-roles.snapshot
109+
vars:
110+
snapshot_lvm_action: remove
111+
snapshot_lvm_set: "{{ snapshot_test_set }}"
112+
113+
- name: Assert changes for remove snapset
114+
assert:
115+
that: snapshot_cmd["changed"]
116+
117+
- name: Run the snapshot role to verify the set is removed
118+
include_role:
119+
name: linux-system-roles.snapshot
120+
vars:
121+
snapshot_lvm_action: remove
122+
snapshot_lvm_set: "{{ snapshot_test_set }}"
123+
snapshot_lvm_verify_only: true
124+
125+
- name: Remove again to check idempotence
126+
include_role:
127+
name: linux-system-roles.snapshot
128+
vars:
129+
snapshot_lvm_action: remove
130+
snapshot_lvm_set: "{{ snapshot_test_set }}"
131+
132+
- name: Assert no changes for remove snapset
133+
assert:
134+
that: not snapshot_cmd["changed"]
135+
always:
136+
- name: Cleanup
137+
include_tasks: tasks/cleanup.yml
138+
tags: tests::cleanup

0 commit comments

Comments
 (0)