Skip to content

Commit

Permalink
feat: add support for user to list snapshots that are part of a set
Browse files Browse the repository at this point in the history
return a json list of VGs that have include a list of the LVs that
are in the snapshot set.

Signed-off-by: Todd Gill <[email protected]>
  • Loading branch information
trgill committed Feb 4, 2024
1 parent 2723e1c commit 72f89d1
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 3 deletions.
97 changes: 94 additions & 3 deletions tasks/files/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class SnapshotCommand:
REMOVE = "remove"
REVERT = "revert"
EXTEND = "extend"

LIST = "list"

class SnapshotStatus:
SNAPSHOT_OK = 0
Expand Down Expand Up @@ -130,7 +130,6 @@ def make_handler(path, prefix, level):

def run_command(argv, stdin=None):
logger.info("Running... %s", " ".join(argv))

try:
proc = subprocess.Popen(
argv, stdin=stdin, stdout=subprocess.PIPE, close_fds=True
Expand Down Expand Up @@ -208,6 +207,50 @@ def lvm_full_report_json():
return lvm_json


def lvm_list_snapset_json(vg_name, lv_name, prefix, suffix):
lvm_json = lvm_full_report_json()
report = lvm_json["report"]
vg_dict = dict()
lv_list = list()

# Revert snapshots
for list_item in report:
# The list contains items that are not VGs
try:
list_item["vg"]
except KeyError:
continue
vg = list_item["vg"][0]["vg_name"]
if vg_name and vg != vg_name:
continue

for lv_item in list_item["lv"]:
lv_item_name = lv_item["lv_name"]
if lv_name and lv_name != lv_item_name:
continue

# Make sure the LV is part of the set
if not lvm_is_owned(lv_item_name, prefix, suffix):
continue

# Make sure the LV is a snapshot.
rc, is_snapshot = lvm_is_snapshot(vg, lv_item_name)

if rc != SnapshotStatus.SNAPSHOT_OK:
raise LvmBug("'lvs' failed '%d'" % rc)

if not is_snapshot:
continue

lv_list.append(json.dumps(lv_item))

if len(lv_list) > 0:
vg_dict[vg] = lv_list
lv_list = []

return json.dumps(vg_dict, indent=4)


def get_snapshot_name(lv_name, prefix, suffix):
if prefix:
prefix_str = prefix
Expand Down Expand Up @@ -680,6 +723,19 @@ def extend_lvs(vg_name, lv_name, prefix, suffix, required_space):
return SnapshotStatus.SNAPSHOT_OK, ""


def list_set(snapset_json):
snapset_name = snapset_json["name"]
lvm_json = lvm_list_snapset_json(None, None, None, snapset_name)
print(lvm_json)

return SnapshotStatus.SNAPSHOT_OK, ""


def list_lvs(vg_name, lv_name, prefix, suffix):
lvm_json = lvm_list_snapset_json(vg_name, lv_name, prefix, suffix)
print(lvm_json)
return SnapshotStatus.SNAPSHOT_OK, ""

def snapshot_lv(vg_name, lv_name, prefix, suffix, snap_size):
snapshot_name = get_snapshot_name(lv_name, prefix, suffix)

Expand Down Expand Up @@ -1590,6 +1646,8 @@ def validate_snapset_json(cmd, snapset, verify_only):
rc, message = validate_json_request(snapset_json, False)
elif cmd == SnapshotCommand.REMOVE:
rc, message = validate_json_request(snapset_json, False)
elif cmd == SnapshotCommand.LIST:
rc, message = validate_json_request(snapset_json, False)
else:
rc = SnapshotStatus.ERROR_UNKNOWN_FAILURE
message = "validate_snapset_json"
Expand Down Expand Up @@ -1813,9 +1871,34 @@ def extend_cmd(args):
return rc, message


def list_cmd(args):
logger.info(
"list_cmd: %s %s %s %s %s %s",
args.operation,
args.volume_group,
args.logical_volume,
args.suffix,
args.prefix,
args.set_json,
)

if args.set_json is None:
rc, message = list_lvs(
args.volume_group,
args.logical_volume,
args.prefix,
args.suffix,
)
else:
rc, message, snapset_json = validate_snapset_json(
SnapshotCommand.LIST, args.set_json, False
)
rc, message = list_set(snapset_json)

return rc, message

if __name__ == "__main__":
set_up_logging()

# Ensure that we get consistent output for parsing stdout/stderr and that we
# are using the lvmdbusd profile.
os.environ["LC_ALL"] = "C"
Expand Down Expand Up @@ -1954,6 +2037,14 @@ def extend_cmd(args):
)
extend_parser.set_defaults(func=extend_cmd)

# sub-parser for 'list'
list_parser = subparsers.add_parser(
SnapshotCommand.LIST,
help="List snapshots",
parents=[common_parser],
)
list_parser.set_defaults(func=list_cmd)

args = parser.parse_args()
return_code, display_message = args.func(args)
print_result(return_code, display_message)
Expand Down
12 changes: 12 additions & 0 deletions tasks/list.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: MIT
---
- name: List snapshots
ansible.builtin.script: "{{ __snapshot_cmd }}"
args:
executable: "{{ __snapshot_python }}"
body_format: json
register: snapshot_cmd

- name: Print out response
debug:
var: snapshot_cmd
4 changes: 4 additions & 0 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
ansible.builtin.include_tasks: revert.yml
when: snapshot_lvm_action == "revert"

- name: List Snapshot Attributes
ansible.builtin.include_tasks: list.yml
when: snapshot_lvm_action == "list"

- name: Extend Snapshots
ansible.builtin.include_tasks: extend.yml
when: snapshot_lvm_action == "extend"
130 changes: 130 additions & 0 deletions tests/tests_list.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
- name: Basic snapshot test
hosts: all
tasks:
- name: Run tests
block:
- name: Run the storage role to create test LVs
include_role:
name: fedora.linux_system_roles.storage

- name: Get unused disks
include_tasks: get_unused_disk.yml
vars:
min_size: "1g"
min_return: 10

- name: Set disk lists
set_fact:
disk_list_1: "{{ range(0, 3) | map('extract', unused_disks) |
list }}"
disk_list_2: "{{ range(3, 6) | map('extract', unused_disks) |
list }}"
disk_list_3: "{{ range(6, 10) | map('extract', unused_disks) |
list }}"

- name: Create LVM logical volumes under volume groups
include_role:
name: fedora.linux_system_roles.storage
vars:
storage_pools:
- name: test_vg1
disks: "{{ disk_list_1 }}"
volumes:
- name: lv1
size: "15%"
- name: lv2
size: "50%"
- name: test_vg2
disks: "{{ disk_list_2 }}"
volumes:
- name: lv3
size: "10%"
- name: lv4
size: "20%"
- name: test_vg3
disks: "{{ disk_list_3 }}"
volumes:
- name: lv5
size: "30%"
- name: lv6
size: "25%"
- name: lv7
size: "10%"
- name: lv8
size: "10%"

- name: Run the snapshot role to create snapshot LVs
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_percent_space_required: 15
snapshot_lvm_all_vgs: true
snapshot_lvm_snapset_name: snapset1
snapshot_lvm_action: snapshot

- name: Verify the snapshot LVs are created
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_all_vgs: true
snapshot_lvm_snapset_name: snapset1
snapshot_lvm_verify_only: true
snapshot_lvm_action: check

- name: List
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_snapset_name: snapset1
snapshot_lvm_action: list

- name: Run the snapshot role remove the snapshot LVs
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_snapset_name: snapset1
snapshot_lvm_action: remove

- name: Use the snapshot_lvm_verify option to make sure remove is done
include_role:
name: linux-system-roles.snapshot
vars:
snapshot_lvm_snapset_name: snapset1
snapshot_lvm_verify_only: true
snapshot_lvm_action: remove
always:
- name: Remove storage volumes
include_role:
name: fedora.linux_system_roles.storage
vars:
storage_safe_mode: false
storage_pools:
- name: test_vg1
disks: "{{ disk_list_1 }}"
state: absent
volumes:
- name: lv1
state: absent
- name: lv2
state: absent
- name: test_vg2
disks: "{{ disk_list_2 }}"
state: absent
volumes:
- name: lv3
state: absent
- name: lv4
state: absent
- name: test_vg3
disks: "{{ disk_list_3 }}"
state: absent
volumes:
- name: lv5
state: absent
- name: lv6
state: absent
- name: lv7
state: absent
- name: lv8
state: absent

0 comments on commit 72f89d1

Please sign in to comment.