Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for the "list" command #31

Merged
merged 2 commits into from
Feb 7, 2024

Conversation

trgill
Copy link
Collaborator

@trgill trgill commented Feb 4, 2024

Enhancement: add support for the "list" command

Reason: allowing the user to list snapshots that are part of a snapset is part of the MVP requirements

Result: users can list snapsets as needed.

Note:
the PR also includes a fix to make sure snapshot size allocation is at least the LVM minimum of 512MiB.
the naming of snapsets has been simplified to just use the snapset name as a suffix - the prefix parameter has been removed. the change will provide better compatibility with how the snapm API works.

Issue Tracker Tickets (Jira or BZ if any):

Use snapshot_lvm_snapset_name to identify a set of snapshots.  The snapset
name is appended to the LV name to indicate membership in the set.

Using a set name will map more easily to how snapm is being implemented.
If/when we decide to back the role with snapm, it will be easier to
keep the current interface if we use only a snapset name and drop the
prefix.

See : https://github.com/snapshotmanager/snapm/

Signed-off-by: Todd Gill <[email protected]>
@trgill
Copy link
Collaborator Author

trgill commented Feb 4, 2024

@richm just a draft right now. I need to clean up and provide some examples for how the user should traverse the JSON response from list.

tasks/files/snapshot.py Outdated Show resolved Hide resolved
tasks/list.yml Outdated Show resolved Hide resolved
@richm richm changed the title add support for the "list" command feat: add support for the "list" command Feb 4, 2024
@richm
Copy link
Contributor

richm commented Feb 4, 2024

Is the list command for the feature "Ability to gather facts ... and return the information as a Ansible variable that can easily be parsed by playbooks"? If so, then you should create and document (in the README.md in "Variables exported by the role") a variable like snapshot_facts e.g. https://github.com/linux-system-roles/bootloader?tab=readme-ov-file#variables-exported-by-the-role

In list.yml, you can parse the JSON output by using something like snapshot_facts: "{{ snapshot_cmd.stdout | from_json }}"

@trgill
Copy link
Collaborator Author

trgill commented Feb 4, 2024

Is the list command for the feature "Ability to gather facts ... and return the information as a Ansible variable that can easily be parsed by playbooks"? If so, then you should create and document (in the README.md in "Variables exported by the role") a variable like snapshot_facts e.g. https://github.com/linux-system-roles/bootloader?tab=readme-ov-file#variables-exported-by-the-role

yes, ok - will document. FYI - the json will look like:

I edited this response - I think if the user requests all, the response should include all LVM volumes (both snapshots and origins, along with mount point information).... updated JSON below. Will update PR

{
    "volumes": {
        "vg3": [
            {
                "lv_uuid": "VY7oRQ-zB1q-DzsP-1y7G-J3gL-ci1e-nQXwAy",
                "lv_name": "lv1_vg3",
                "lv_full_name": "vg3/lv1_vg3",
                "lv_path": "/dev/vg3/lv1_vg3",
                "lv_size": "1073741824",
                "origin": "",
                "origin_size": "1073741824",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "owi-a-s---",
                "vg_name": "vg3",
                "data_percent": "",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "Yhn7RG-k7pM-ylf9-NNt8-xuGI-WwrF-i0Pf6T",
                "lv_name": "lv1_vg3_snapset2",
                "lv_full_name": "vg3/lv1_vg3_snapset2",
                "lv_path": "/dev/vg3/lv1_vg3_snapset2",
                "lv_size": "322961408",
                "origin": "lv1_vg3",
                "origin_size": "1073741824",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "swi-a-s---",
                "vg_name": "vg3",
                "data_percent": "0.00",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "NlwbxX-NhwK-IHTj-sV9k-ldZY-Twvj-2SiCVe",
                "lv_name": "lv2_vg3",
                "lv_full_name": "vg3/lv2_vg3",
                "lv_path": "/dev/vg3/lv2_vg3",
                "lv_size": "1073741824",
                "origin": "",
                "origin_size": "1073741824",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "owi-a-s---",
                "vg_name": "vg3",
                "data_percent": "",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "j0RCzX-OVaA-MGDw-ejHO-Eu35-f4yG-VJL2Kr",
                "lv_name": "lv2_vg3_snapset2",
                "lv_full_name": "vg3/lv2_vg3_snapset2",
                "lv_path": "/dev/vg3/lv2_vg3_snapset2",
                "lv_size": "322961408",
                "origin": "lv2_vg3",
                "origin_size": "1073741824",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "swi-aos---",
                "vg_name": "vg3",
                "data_percent": "0.66",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "8kfTDY-22SL-4tC7-vTsR-1R63-zVzq-55qEL3",
                "lv_name": "lv3_vg3",
                "lv_full_name": "vg3/lv3_vg3",
                "lv_path": "/dev/vg3/lv3_vg3",
                "lv_size": "125829120",
                "origin": "",
                "origin_size": "125829120",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "owi-a-s---",
                "vg_name": "vg3",
                "data_percent": "",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "babChm-IzEN-Pf8q-1dxk-BJ9R-3kZb-u91utS",
                "lv_name": "lv3_vg3_snapset2",
                "lv_full_name": "vg3/lv3_vg3_snapset2",
                "lv_path": "/dev/vg3/lv3_vg3_snapset2",
                "lv_size": "41943040",
                "origin": "lv3_vg3",
                "origin_size": "125829120",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "swi-a-s---",
                "vg_name": "vg3",
                "data_percent": "0.00",
                "metadata_percent": ""
            }
        ],
        "vg2": [
            {
                "lv_uuid": "8uMuRW-1KCV-8FTJ-frhX-X39o-V15B-1uEC98",
                "lv_name": "lv1_vg2",
                "lv_full_name": "vg2/lv1_vg2",
                "lv_path": "/dev/vg2/lv1_vg2",
                "lv_size": "1073741824",
                "origin": "",
                "origin_size": "1073741824",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "owi-a-s---",
                "vg_name": "vg2",
                "data_percent": "",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "GGssIK-SHYI-to1m-MhVL-2BDk-PJ8X-dBnL7G",
                "lv_name": "lv1_vg2_snapset2",
                "lv_full_name": "vg2/lv1_vg2_snapset2",
                "lv_path": "/dev/vg2/lv1_vg2_snapset2",
                "lv_size": "322961408",
                "origin": "lv1_vg2",
                "origin_size": "1073741824",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "swi-aos---",
                "vg_name": "vg2",
                "data_percent": "20.97",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "83A9VM-kVEy-sc60-kF14-gKGb-5Ryj-7yDyEG",
                "lv_name": "lv2_vg2",
                "lv_full_name": "vg2/lv2_vg2",
                "lv_path": "/dev/vg2/lv2_vg2",
                "lv_size": "83886080",
                "origin": "",
                "origin_size": "83886080",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "owi-a-s---",
                "vg_name": "vg2",
                "data_percent": "",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "6tVL8A-U1x1-qqUt-WFVG-POsG-msFs-ZbbPq1",
                "lv_name": "lv2_vg2_snapset2",
                "lv_full_name": "vg2/lv2_vg2_snapset2",
                "lv_path": "/dev/vg2/lv2_vg2_snapset2",
                "lv_size": "29360128",
                "origin": "lv2_vg2",
                "origin_size": "83886080",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "swi-a-s---",
                "vg_name": "vg2",
                "data_percent": "0.00",
                "metadata_percent": ""
            }
        ],
        "vg1": [
            {
                "lv_uuid": "UnN0s0-TauJ-csnN-BgC1-3ocI-p8bE-jz0Hd8",
                "lv_name": "lv1_vg1",
                "lv_full_name": "vg1/lv1_vg1",
                "lv_path": "/dev/vg1/lv1_vg1",
                "lv_size": "1073741824",
                "origin": "",
                "origin_size": "1073741824",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "owi-aos---",
                "vg_name": "vg1",
                "data_percent": "",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "5Np7N9-H15x-Go96-fIwL-E0GR-4fVB-clLDW2",
                "lv_name": "lv1_vg1_snapset2",
                "lv_full_name": "vg1/lv1_vg1_snapset2",
                "lv_path": "/dev/vg1/lv1_vg1_snapset2",
                "lv_size": "322961408",
                "origin": "lv1_vg1",
                "origin_size": "1073741824",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "swi-a-s---",
                "vg_name": "vg1",
                "data_percent": "20.97",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "P0LPUQ-CljS-hOEm-U749-yyr9-USE7-1qDc2N",
                "lv_name": "lv2_vg1",
                "lv_full_name": "vg1/lv2_vg1",
                "lv_path": "/dev/vg1/lv2_vg1",
                "lv_size": "41943040",
                "origin": "",
                "origin_size": "41943040",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "owi-a-s---",
                "vg_name": "vg1",
                "data_percent": "",
                "metadata_percent": ""
            },
            {
                "lv_uuid": "FYIBRe-FDiW-PDUE-3l1y-mLzN-bLEg-qF12cz",
                "lv_name": "lv2_vg1_snapset2",
                "lv_full_name": "vg1/lv2_vg1_snapset2",
                "lv_path": "/dev/vg1/lv2_vg1_snapset2",
                "lv_size": "16777216",
                "origin": "lv2_vg1",
                "origin_size": "41943040",
                "pool_lv": "",
                "lv_tags": "",
                "lv_attr": "swi-a-s---",
                "vg_name": "vg1",
                "data_percent": "0.00",
                "metadata_percent": ""
            }
        ]
    },
    "mounts": {
        "/dev/vg3/lv1_vg3": null,
        "/dev/vg3/lv1_vg3_snapset2": null,
        "/dev/vg3/lv2_vg3": null,
        "/dev/vg3/lv2_vg3_snapset2": {
            "filesystems": [
                {
                    "target": "/tmp/mp1",
                    "source": "/dev/mapper/vg3-lv2_vg3_snapset2",
                    "fstype": "xfs",
                    "options": "rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota"
                }
            ]
        },
        "/dev/vg3/lv3_vg3": null,
        "/dev/vg3/lv3_vg3_snapset2": null,
        "/dev/vg2/lv1_vg2": null,
        "/dev/vg2/lv1_vg2_snapset2": {
            "filesystems": [
                {
                    "target": "/tmp/production_mnt",
                    "source": "/dev/mapper/vg2-lv1_vg2_snapset2",
                    "fstype": "xfs",
                    "options": "rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota"
                }
            ]
        },
        "/dev/vg2/lv2_vg2": null,
        "/dev/vg2/lv2_vg2_snapset2": null,
        "/dev/vg1/lv1_vg1": {
            "filesystems": [
                {
                    "target": "/mount",
                    "source": "/dev/mapper/vg1-lv1_vg1",
                    "fstype": "xfs",
                    "options": "rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota"
                },
                {
                    "target": "/tmp/mp3",
                    "source": "/dev/mapper/vg1-lv1_vg1",
                    "fstype": "xfs",
                    "options": "rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota"
                }
            ]
        },
        "/dev/vg1/lv1_vg1_snapset2": null,
        "/dev/vg1/lv2_vg1": null,
        "/dev/vg1/lv2_vg1_snapset2": null
    }
}

In list.yml, you can parse the JSON output by using something like snapshot_facts: "{{ snapshot_cmd.stdout | from_json }}"

Ok. Will change to:

# SPDX-License-Identifier: MIT
---
- name: List snapshots
  ansible.builtin.script: "{{ __snapshot_cmd }}"
  args:
    executable: "{{ __snapshot_python }}"
  register: snapshot_cmd

- name: Print out response
  debug:
    var: snapshot_cmd

- name: Set snapshot_facts to the JSON results
  set_fact:
    snapshot_facts: "{{ snapshot_cmd.stdout | from_json }}"

@trgill trgill marked this pull request as ready for review February 5, 2024 01:57
@trgill trgill requested a review from spetrosi as a code owner February 5, 2024 01:57
@trgill trgill force-pushed the snapset_names branch 2 times, most recently from 017fab2 to 8c70e8f Compare February 5, 2024 14:43
tasks/list.yml Show resolved Hide resolved
@richm
Copy link
Contributor

richm commented Feb 6, 2024

[citest]

@richm
Copy link
Contributor

richm commented Feb 6, 2024

[citest]

@trgill trgill force-pushed the snapset_names branch 2 times, most recently from 19b3352 to 1c26d4b Compare February 7, 2024 01:26
@trgill
Copy link
Collaborator Author

trgill commented Feb 7, 2024

[citest]

@richm
Copy link
Contributor

richm commented Feb 7, 2024

TASK [fedora.linux_system_roles.snapshot : Extend Snapshot] ********************
task path: /WORKDIR/git-snapset_namesookqktaz/.collection/ansible_collections/fedora/linux_system_roles/roles/snapshot/tasks/extend.yml:3
Wednesday 07 February 2024  01:53:59 +0000 (0:00:00.019)       0:00:32.313 **** 
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: NoneType: None
fatal: [sut]: FAILED! => {
    "changed": true,
    "rc": 28
}

STDOUT:

snapshot not found with name: test_vg1/lv1_

@trgill
Copy link
Collaborator Author

trgill commented Feb 7, 2024

[citest]

@trgill
Copy link
Collaborator Author

trgill commented Feb 7, 2024

the failure on centos-7 is related to the new list command - I'm investigating:

TASK [linux-system-roles.snapshot : Set snapshot_facts to the JSON results] *******************************************************************************************************************************************************************************
task path: /home/tgill/workspace/snapshot/tests/roles/linux-system-roles.snapshot/tasks/list.yml:9
Wednesday 07 February 2024 07:39:48 -0500 (0:00:00.292) 0:00:38.759 ****
fatal: [/home/tgill/.cache/linux-system-roles/centos-7.qcow2c]: FAILED! => {}

MSG:

the field 'args' has an invalid value ({'snapshot_facts': '{{ snapshot_cmd.stdout | from_json }}'}), and could not be converted to an dict.The error was: Expecting value: line 1 column 1 (char 0)

The error appears to be in '/home/tgill/workspace/snapshot/tests/roles/linux-system-roles.snapshot/tasks/list.yml': line 9, column 3, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  • name: Set snapshot_facts to the JSON results
    ^ here

@trgill
Copy link
Collaborator Author

trgill commented Feb 7, 2024

The problem is due to --json being added after centos7... will find work around.

findmnt: unrecognized option --json

@trgill trgill force-pushed the snapset_names branch 2 times, most recently from 93e4bcf to 213712b Compare February 7, 2024 14:18
tasks/files/snapshot.py Fixed Show fixed Hide fixed
tasks/files/snapshot.py Fixed Show fixed Hide fixed
tasks/files/snapshot.py Fixed Show fixed Hide fixed
@trgill
Copy link
Collaborator Author

trgill commented Feb 7, 2024

[citest]


output = output.replace('"', "")
output = output.replace("\n", "")
kv = dict(arg.split("=", 1) for arg in output.split(" ") if arg)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this function could just do

return dict(arg.split("=", 1) for arg in output.split(" ") if arg)

That is, return a dict instead of a string. I think the function that calls lvm_get_fs_mount_points will ultimately do the json string conversion to return to the caller

Copy link
Contributor

Choose a reason for hiding this comment

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

it isn't working properly for blockdevices that are mounted in multiple mount points

Ah - then I guess you could do the output.replace('"', ""), then instead of output.replace("\n", "") do lines = output.split("\n"), then iterate over lines and return a list of dict from this function

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've updated so the output for the case of multiple mounts looks like:

    "mounts": {
        "/dev/vg3/lv1_vg3": null,
        "/dev/vg3/lv1_vg3_snapset2": [
            {
                "TARGET": "/mnt/database",
                "SOURCE": "/dev/mapper/vg3-lv1_vg3_snapset2",
                "FSTYPE": "xfs",
                "OPTIONS": "rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota"
            }
        ],
        "/dev/vg3/lv2_vg3": null,
        "/dev/vg3/lv2_vg3_snapset2": null,
        "/dev/vg3/lv3_vg3": null,
        "/dev/vg3/lv3_vg3_snapset2": null,
        "/dev/vg2/lv1_vg2": null,
        "/dev/vg2/lv1_vg2_snapset2": [
            {
                "TARGET": "/mnt/production_mnt",
                "SOURCE": "/dev/mapper/vg2-lv1_vg2_snapset2",
                "FSTYPE": "xfs",
                "OPTIONS": "rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota"
            }
        ],
        "/dev/vg2/lv2_vg2": null,
        "/dev/vg2/lv2_vg2_snapset2": null,
        "/dev/vg1/lv1_vg1": null,
        "/dev/vg1/lv1_vg1_snapset2": [
            {
                "TARGET": "/mnt/new_mountpoint",
                "SOURCE": "/dev/mapper/vg1-lv1_vg1_snapset2",
                "FSTYPE": "xfs",
                "OPTIONS": "rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota"
            },
            {
                "TARGET": "/mnt/other_mp",
                "SOURCE": "/dev/mapper/vg1-lv1_vg1_snapset2",
                "FSTYPE": "xfs",
                "OPTIONS": "rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota"
            }
        ],
        "/dev/vg1/lv2_vg1": null,
        "/dev/vg1/lv2_vg1_snapset2": null
    }

I think I followed you suggestion and the result it:


def lvm_get_fs_mount_points(block_path):
    find_mnt_command = [
        "findmnt",
        block_path,
        "-P",
    ]
    mount_list = list()

    rc, output = run_command(find_mnt_command)
    if rc:
        return None

    output = output.replace('"', "")

    for line in output.split("\n"):
        if len(line):
            mount_list.append(dict(arg.split("=", 1) for arg in line.split(" ") if arg))

    return mount_list

if fs_mount_points:
fs_list.append(fs_mount_points)
fs_dict[block_path] = fs_mount_points
fs_list = list()
Copy link
Contributor

Choose a reason for hiding this comment

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

? the for loop builds fs_list, then you reset it here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is a problem. will fix. it isn't working properly for blockdevices that are mounted in multiple mount points.

@trgill
Copy link
Collaborator Author

trgill commented Feb 7, 2024

[citest]

tasks/list.yml Show resolved Hide resolved
@trgill
Copy link
Collaborator Author

trgill commented Feb 7, 2024

[citest bad]

@trgill
Copy link
Collaborator Author

trgill commented Feb 7, 2024

[citest]

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]>
Co-authored-by: Richard Megginson <[email protected]>
@richm richm merged commit 3c2518d into linux-system-roles:main Feb 7, 2024
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants