Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Sets are defined in the following format:
```yaml
snapshot_lvm_set:
name: snapset1
bootable: true
volumes:
- name: snapshot VG1 LV1
vg: test_vg1
Expand Down Expand Up @@ -84,6 +85,7 @@ that it applies, for example:
```yaml
snapshot_lvm_set:
name: snapset1
bootable: false
volumes:
- name: snapshot VG1 LV1
vg: test_vg1
Expand Down Expand Up @@ -243,6 +245,18 @@ the rest will be excluded. For example,
`snapshot_lvm_vg_include: "^sql_db_"` will only operate on volume groups
whose names start with `sql_db_`. This uses the Python `re.search`.

### snapshot_lvm_bootable

Boolean - default is false. Only supported on operating systems that
support snapshot manager (snapm). When set to true, and passed to the
'snapshot' command, the snapshot created will have a corresponding boot
entry. The boot entry will be removed when the snapset is removed.

### snapshot_use_copr (EXPERIMENTAL)

Boolean - default is unset - if you want to enable the copr repo to use the
latest development version of snapm, use `snapshot_use_copr: true`

### Variables Exported by the Role

#### snapshot_facts
Expand Down
1 change: 1 addition & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ snapshot_lvm_mountpoint: ''
snapshot_lvm_mount_options: ''
snapshot_lvm_vg_include: ''
snapshot_lvm_fstype: ''
snapshot_lvm_bootable: false
10 changes: 10 additions & 0 deletions library/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,21 @@
to a regex pattern that matches the names of the volume groups you want to use and
the rest will be excluded
type: str
snapshot_lvm_bootable:
description: Only supported on operating systems that support snapshot manager (snapm).
When set to true, and passed to the 'snapshot' command, the snapshot created will have
a corresponding boot entry. The boot entry will be removed when the snapset is removed.
type: bool
snapshot_lvm_set:
description: set of volumes
type: dict
suboptions:
name:
description: name of set
type: str
bootable:
description: create snapshot with boot menu entry (requires snapm)
type: bool
volumes:
description: list of volumes
type: list
Expand Down Expand Up @@ -282,6 +290,7 @@ def run_module():
snapshot_lvm_all_vgs=dict(type="bool"),
snapshot_lvm_verify_only=dict(type="bool"),
snapshot_lvm_mount_origin=dict(type="bool"),
snapshot_lvm_bootable=dict(type="bool"),
snapshot_lvm_mountpoint_create=dict(type="bool"),
snapshot_lvm_unmount_all=dict(type="bool"),
snapshot_lvm_percent_space_required=dict(type="str"),
Expand All @@ -296,6 +305,7 @@ def run_module():
type="dict",
options=dict(
name=dict(type="str"),
bootable=dict(type="bool"),
volumes=dict(
type="list",
elements="dict",
Expand Down
5 changes: 5 additions & 0 deletions module_utils/snapshot_lsr/lvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ def get_json_from_args(module, module_args, vg_include):
if module_args["snapshot_lvm_snapset_name"]:
args_dict["name"] = module_args["snapshot_lvm_snapset_name"]

if module_args["snapshot_lvm_bootable"]:
args_dict["bootable"] = module_args["snapshot_lvm_bootable"]
else:
args_dict["bootable"] = False

for vg, lv_list in vgs_lvs_iterator(
module,
module_args["snapshot_lvm_vg"],
Expand Down
9 changes: 8 additions & 1 deletion module_utils/snapshot_lsr/snapmgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ def mgr_snapshot_cmd(module, module_args, snapset_json):

snapset_name = snapset_json["name"]
volume_list = snapset_json["volumes"]
if "bootable" in snapset_json:
bootable = snapset_json["bootable"]
else:
bootable = False

source_list = mgr_get_source_list_for_create(volume_list)

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

try:
manager.create_snapshot_set(
snapset_name, source_list, SNAPM_DEFAULT_SIZE_POLICY
snapset_name,
source_list,
SNAPM_DEFAULT_SIZE_POLICY,
boot=bootable,
)
changed = True
except snapm.SnapmError as snap_err:
Expand Down
3 changes: 3 additions & 0 deletions module_utils/snapshot_lsr/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ def get_json_from_args(module, module_args, vg_include):
if module_args["snapshot_lvm_snapset_name"]:
args_dict["name"] = module_args["snapshot_lvm_snapset_name"]

if module_args["snapshot_lvm_bootable"]:
args_dict["bootable"] = module_args["snapshot_lvm_bootable"]

for vg, lv_list in vgs_lvs_iterator(
module,
module_args["snapshot_lvm_vg"],
Expand Down
6 changes: 6 additions & 0 deletions tasks/enable_copr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
# Primarily for testing unreleased versions
- name: Enable snapm copr on Fedora
command: dnf -y copr enable packit/snapshotmanager-snapm-357
when: ansible_facts['distribution'] == 'Fedora'
changed_when: true
68 changes: 66 additions & 2 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,75 @@
- name: Set platform/version specific variables
include_tasks: tasks/set_vars.yml

- name: Enable copr if requested
include_tasks: enable_copr.yml
when: snapshot_use_copr | d(false)

- name: Ensure required packages are installed
package:
name: "{{ __snapshot_packages }}"
state: present
use: "{{ (__snapshot_is_ostree | d(false)) |
ternary('ansible.posix.rhel_rpm_ostree', omit) }}"

Copy link
Contributor

@richm richm Sep 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a task here to check the snapm version, and fail, or exit, if snapshot_lvm_bootable is true - for example, in the podman role, we do something similar to check if the user wants to use advanced features not available on some platforms - https://github.com/linux-system-roles/podman/blob/main/tasks/main.yml#L47-L79

  • install packages
  • get the snapm version - set the version in a global variable e.g. snapshot_snapm_version
  • if snapshot_lvm_bootable | d(false) and snapshot_snapm_version is version("<", "0.12") and snapshot_fail_if_too_old | d(true) - then use the fail: module to report failure
  • if snapshot_lvm_bootable | d(false) and snapshot_snapm_version is version("<", "0.12") and not snapshot_fail_if_too_old | d(true) - then use end_host: to skip the role without a failure

This:

  • lets the user of the role know that they are using an unsupported feature
  • allows the tests to pass when testing with an older version of snapm (by having the test set snapshot_fail_if_too_old: false)

- name: Get snapm version
check_mode: false
command: snapm --version
changed_when: false
register: __snapshot_snapm_version_output
# Ignore errors as snapm may not be available
ignore_errors: true

- name: Set snapm availability fact
set_fact:
__snapshot_snapm_available: "{{ __snapshot_snapm_version_output is success }}"

- name: Set snapm version
set_fact:
__snapshot_snapm_version: "{{ __snapshot_snapm_version_output.stdout }}"
when: __snapshot_snapm_available

# Determine if bootable support is needed
# If snapshot_lvm_bootable is set to true, or any of the volumes in snapshot_lvm_set
# have bootable set to true, then bootable support is needed.
- name: Set needs bootable support
set_fact:
__snapshot_needs_bootable_support: "{{ snapshot_lvm_bootable or (snapshot_lvm_set |
selectattr('bootable', 'defined') |
selectattr('bootable') | list | length > 0) }}"

- name: Package snapm version must be 0.4 or later
fail:
msg: >-
Package snapm version {{ __snapshot_snapm_version }} is too old -
must be 0.4 or later
when:
- __snapshot_snapm_available
- __snapshot_snapm_version is version("0.4", "<")

- name: Package snapm must available for bootable snapsets
fail:
msg: >-
Package snapm version 0.5 or later is required to use bootable snapsets
when:
- not __snapshot_snapm_available
- __snapshot_needs_bootable_support

- name: Package snapm must be version 0.5 or later for bootable snapsets
fail:
msg: >-
Package snapm version {{ __snapshot_snapm_version }} is too old -
version 0.5 or later is required to use bootable snapsets
when:
- not __snapshot_snapm_available or
__snapshot_snapm_version is version("0.5", "<")
- __snapshot_needs_bootable_support

- name: Run snapshot module and handle errors
when:
- snapshot_lvm_action is defined
- not __snapshot_needs_bootable_support or (__snapshot_snapm_available and
__snapshot_snapm_version is version("0.5", ">="))
block:
- name: Run snapshot module {{ snapshot_lvm_action }}
snapshot:
Expand All @@ -19,6 +80,7 @@
snapshot_lvm_all_vgs: "{{ snapshot_lvm_all_vgs | d(false) }}"
snapshot_lvm_verify_only: "{{ snapshot_lvm_verify_only | d(false) }}"
snapshot_lvm_mount_origin: "{{ snapshot_lvm_mount_origin | d(false) }}"
snapshot_lvm_bootable: "{{ snapshot_lvm_bootable | d(false) }}"
snapshot_lvm_mountpoint_create: "{{
snapshot_lvm_mountpoint_create | d(false) }}"
snapshot_lvm_unmount_all: "{{ snapshot_lvm_unmount_all | d(false) }}"
Expand All @@ -44,17 +106,19 @@
debug:
var: snapshot_cmd
verbosity: 2
when: snapshot_cmd is defined

- name: Set result
set_fact:
snapshot_cmd: "{{ snapshot_cmd }}"
when: snapshot_cmd is defined

- name: Set snapshot_facts to the JSON results
set_fact:
snapshot_facts: "{{ snapshot_cmd['data'] }}"
when: snapshot_lvm_action == "list"
when: snapshot_cmd is defined and snapshot_lvm_action == "list"

- name: Show errors
debug:
var: snapshot_cmd["errors"]
when: snapshot_cmd["return_code"] != 0
when: snapshot_cmd is defined and snapshot_cmd["return_code"] != 0
133 changes: 133 additions & 0 deletions tests/tests_set_bootable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
- name: Snapshot a set of volumes with snapshot_lvm_bootable set to true
hosts: all
vars:
test_disk_min_size: "1g"
test_disk_count: 10
test_storage_pools:
- name: test_vg1
disks: "{{ range(0, 3) | map('extract', unused_disks) | list }}"
volumes:
- name: lv1
size: "15%"
- name: lv2
size: "50%"
- name: test_vg2
disks: "{{ range(3, 6) | map('extract', unused_disks) | list }}"
volumes:
- name: lv3
size: "10%"
- name: lv4
size: "20%"
- name: test_vg3
disks: "{{ range(6, 10) | map('extract', unused_disks) | list }}"
volumes:
- name: lv5
size: "30%"
- name: lv6
size: "25%"
- name: lv7
size: "10%"
- name: lv8
size: "10%"
snapshot_test_set:
name: snapset1
volumes:
- name: snapshot VG1 LV1
vg: test_vg1
lv: lv1
percent_space_required: 20
- name: snapshot VG2 LV3
vg: test_vg2
lv: lv3
percent_space_required: 15
- name: snapshot VG2 LV4
vg: test_vg2
lv: lv4
percent_space_required: 15
- name: snapshot VG3 LV7
vg: test_vg3
lv: lv7
percent_space_required: 15
snapshot_lvm_bootable: true
tasks:
- name: Load test variables
include_vars:
file: vars/rh_distros_vars.yml
when: __snapshot_is_ostree is not defined

- name: Run tests
block:
- name: Setup
include_tasks: tasks/setup.yml

- name: Run the snapshot role to create snapshot set of LVs
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_action: snapshot
snapshot_lvm_set: "{{ snapshot_test_set }}"

- name: Assert changes for create snapset
assert:
that: snapshot_cmd["changed"]

- name: Run the snapshot role to verify the set of snapshots for the LVs
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_action: check
snapshot_lvm_set: "{{ snapshot_test_set }}"
snapshot_lvm_verify_only: true

- name: Create snapset again for idempotence
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_action: snapshot
snapshot_lvm_set: "{{ snapshot_test_set }}"

- name: Assert no changes for create snapset
assert:
that: not snapshot_cmd["changed"]

- name: Run the snapshot role remove the set
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_action: remove
snapshot_lvm_set: "{{ snapshot_test_set }}"

- name: Assert changes for remove snapset
assert:
that: snapshot_cmd["changed"]

- name: Run the snapshot role to verify the set is removed
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_action: remove
snapshot_lvm_set: "{{ snapshot_test_set }}"
snapshot_lvm_verify_only: true

- name: Remove again to check idempotence
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_action: remove
snapshot_lvm_set: "{{ snapshot_test_set }}"

- name: Assert no changes for remove snapset
assert:
that: not snapshot_cmd["changed"]
rescue:
- name: Check if error is due to snapm version too old
fail:
msg: Unexpected error occurred {{ ansible_failed_result | to_nice_json }}
when: not ansible_failed_result.msg is search(err_msg)
vars:
err_msg: Package snapm .*version 0.5 or later is required to use bootable snapsets
always:
- name: Cleanup
include_tasks: tasks/cleanup.yml
tags: tests::cleanup
Loading