Skip to content

Commit abd27c8

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 c2fa851 commit abd27c8

File tree

8 files changed

+232
-1
lines changed

8 files changed

+232
-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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ def mgr_snapshot_cmd(module, module_args, snapset_json):
200200

201201
snapset_name = snapset_json["name"]
202202
volume_list = snapset_json["volumes"]
203+
bootable = snapset_json["bootable"]
203204

204205
source_list = mgr_get_source_list_for_create(volume_list)
205206

@@ -210,7 +211,10 @@ def mgr_snapshot_cmd(module, module_args, snapset_json):
210211

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

0 commit comments

Comments
 (0)