diff --git a/roles/make_lv/README.md b/roles/make_lv/README.md new file mode 100644 index 0000000..28af76a --- /dev/null +++ b/roles/make_lv/README.md @@ -0,0 +1,68 @@ +ansible-make-lv +========= + +Role for creating simple lv disks with 1 group and 1 volume + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +All variables +```yaml +roles: + - role: genlab.ansible_make_lv + virtual_group: "group" + logical_volume: "volume" + lvm_dev: "/dev/sda" + size: 100%FREE + fs_type: ext4 + storage_mountpoint: "/mnt" + storage_mountpoint_mode: "0775" + mountpoint_owner: root + mountpoint_group: root + is_container: false # disables udev in LVM if true +``` + +Minimal variables +```yaml +roles: + - role: genlab.ansible_make_lv + virtual_group: "group" + logical_volume: "volume" + lvm_dev: "/dev/sda" +``` + +Defaults +```yaml +size: 100%FREE +fs_type: ext4 +storage_mountpoint: "/mnt" +storage_mountpoint_mode: "0775" +mountpoint_owner: root +mountpoint_group: root +is_container: false # disables udev in LVM if true +``` + +License +------- + +BSD + +Author Information +------------------ + +malyuk.ss@genlab.llc diff --git a/roles/make_lv/defaults/main.yml b/roles/make_lv/defaults/main.yml new file mode 100644 index 0000000..46d48fb --- /dev/null +++ b/roles/make_lv/defaults/main.yml @@ -0,0 +1,8 @@ +--- +size: 100%FREE +fs_type: ext4 +storage_mountpoint: "/mnt" +storage_mountpoint_mode: "0775" +mountpoint_owner: root +mountpoint_group: root +is_container: false diff --git a/roles/make_lv/handlers/main.yml b/roles/make_lv/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/make_lv/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/make_lv/meta/main.yml b/roles/make_lv/meta/main.yml new file mode 100644 index 0000000..52c881b --- /dev/null +++ b/roles/make_lv/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "make_lv" + namespace: genlab + author: "Sergey Malyuk" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["focal", "jammy", "noble"] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/make_lv/molecule/default/converge.yml b/roles/make_lv/molecule/default/converge.yml new file mode 100644 index 0000000..ee8c9aa --- /dev/null +++ b/roles/make_lv/molecule/default/converge.yml @@ -0,0 +1,60 @@ +--- +- name: Converge + hosts: all + pre_tasks: + - name: Get /root/image.img stats + ansible.builtin.stat: + path: /root/image.img + register: img_stat + + - name: Create image file + when: not img_stat.stat.exists + ansible.builtin.command: dd if=/dev/zero of=/root/image.img bs=1M count=128 + register: dd_result + changed_when: dd_result.rc == 0 + + - name: Get linked loop devices + ansible.builtin.command: losetup -a + changed_when: false + register: loop_devs + + - name: Link image to loop device + when: "'/root/image.img' not in loop_devs.stdout" + ansible.builtin.command: losetup --find --show -P /root/image.img + register: loop_device + changed_when: "'/dev/loop' in loop_device.stdout" + + - name: Get all loop devices + ansible.builtin.command: losetup -a + register: losetup_out + changed_when: false + + - name: Find line with /root/image.img + ansible.builtin.set_fact: + matched_line: "{{ losetup_out.stdout_lines + | select('search', '/root/image.img') + | list + | first + | default('') }}" + + - name: Extract loop device name (/dev/loopN) + ansible.builtin.set_fact: + loop_dev: "{{ matched_line | regex_search('(/dev/loop[0-9]+)', '\\1') | default('') }}" + + - name: Debug result + ansible.builtin.debug: + msg: "/root/image.img is linked to {{ loop_dev[0] }}" + when: loop_dev != '' + + roles: + - role: genlab.make_lv + virtual_group: "group" + logical_volume: "volume" + lvm_dev: "{{ loop_dev[0] }}" + size: 100%FREE + fs_type: ext4 + storage_mountpoint: "/mnt" + storage_mountpoint_mode: "0755" + mountpoint_owner: root + mountpoint_group: root + is_container: true diff --git a/roles/make_lv/molecule/default/molecule.yml b/roles/make_lv/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/make_lv/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/make_lv/molecule/default/verify.yml b/roles/make_lv/molecule/default/verify.yml new file mode 100644 index 0000000..ab99d42 --- /dev/null +++ b/roles/make_lv/molecule/default/verify.yml @@ -0,0 +1,65 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars: + virtual_group: "group" + logical_volume: "volume" + lvm_dev: "/dev/loop0" + fs_type: ext4 + storage_mountpoint: "/mnt" + + tasks: + - name: Get volume group list + ansible.builtin.shell: + cmd: "set -o pipefail ; pvs | grep {{ lvm_dev }}" + executable: /bin/bash + changed_when: false + failed_when: false + register: vg_list + + - name: Assert that volume group exists + ansible.builtin.assert: + that: virtual_group in vg_list.stdout + success_msg: "{{ lvm_dev }} has {{ virtual_group }} group" + fail_msg: "{{ lvm_dev }} DOES NOT have {{ virtual_group }} group" + + - name: Get logical volume list + ansible.builtin.shell: + cmd: "set -o pipefail ; lvs | grep {{ virtual_group }} | grep {{ logical_volume }}" + executable: /bin/bash + changed_when: false + failed_when: false + register: lv_list + + - name: Assert that logical volume exists + ansible.builtin.assert: + that: logical_volume in lv_list.stdout + success_msg: "{{ virtual_group }} has {{ logical_volume }} volume" + fail_msg: "{{ virtual_group }} DOES NOT have {{ logical_volume }} volume" + + - name: Get filesystem type + ansible.builtin.command: "blkid /dev/mapper/{{ virtual_group }}-{{ logical_volume }} -o value -s TYPE" + changed_when: false + failed_when: false + register: current_fs + + - name: Assert that current filesystem is the required one + ansible.builtin.assert: + that: current_fs.stdout == fs_type + success_msg: "{{ virtual_group }} - {{ logical_volume }} has {{ fs_type }} filesystem" + fail_msg: "{{ virtual_group }} - {{ logical_volume }} DOES NOT have {{ fs_type }} filesystem" + + - name: Get logical volume mount info # noqa: command-instead-of-module — tells ansible-lint to ignore the error, because you can't actually + # do it with ansible.builtin.mount as the linter begs me to do + ansible.builtin.shell: "mount | grep -w '{{ virtual_group }}-{{ logical_volume }}'" + register: mount_check + ignore_errors: true + changed_when: false + + - name: Assert that volume is mounted + ansible.builtin.assert: + that: mount_check.rc == 0 + success_msg: "{{ virtual_group }}-{{ logical_volume }} is mounted at {{ storage_mountpoint }}" + fail_msg: "{{ virtual_group }}-{{ logical_volume }} IS NOT mounted at {{ storage_mountpoint }}" diff --git a/roles/make_lv/tasks/main.yml b/roles/make_lv/tasks/main.yml new file mode 100644 index 0000000..ac2537b --- /dev/null +++ b/roles/make_lv/tasks/main.yml @@ -0,0 +1,102 @@ +--- +- name: Install LVM tools + ansible.builtin.apt: + name: lvm2 + state: present + update_cache: true + cache_valid_time: 3600 +- name: Check lvm storage exists + block: + - name: Get volume group list + ansible.builtin.shell: + cmd: "set -o pipefail ; pvs | grep {{ lvm_dev }}" + executable: /bin/bash + changed_when: false + failed_when: false + register: vg_list + + - name: Assert that volume group exists + ansible.builtin.assert: + that: virtual_group in vg_list.stdout + success_msg: "{{ lvm_dev }} has {{ virtual_group }} group" + fail_msg: "{{ lvm_dev }} DOES NOT have {{ virtual_group }} group" + + - name: Get logical volume list + ansible.builtin.shell: + cmd: "set -o pipefail ; lvs | grep {{ virtual_group }} | grep {{ logical_volume }}" + executable: /bin/bash + changed_when: false + failed_when: false + register: lv_list + + - name: Assert that logical volume exists + ansible.builtin.assert: + that: logical_volume in lv_list.stdout + success_msg: "{{ virtual_group }} has {{ logical_volume }} volume" + fail_msg: "{{ virtual_group }} DOES NOT have {{ logical_volume }} volume" + + - name: Get filesystem type + ansible.builtin.command: "blkid /dev/mapper/{{ virtual_group }}-{{ logical_volume }} -o value -s TYPE" + changed_when: false + failed_when: false + register: current_fs + + - name: Assert that current filesystem is the required one + ansible.builtin.assert: + that: current_fs.stdout == fs_type + success_msg: "{{ virtual_group }} - {{ logical_volume }} has {{ fs_type }} filesystem" + fail_msg: "{{ virtual_group }} - {{ logical_volume }} DOES NOT have {{ fs_type }} filesystem" + + rescue: + - name: Unmount the main storage + ansible.posix.mount: + src: "/dev/{{ virtual_group }}/{{ logical_volume }}" + path: "{{ storage_mountpoint }}" + state: absent + + - name: Wipe disk signatures + ansible.builtin.command: "wipefs -af {{ lvm_dev }}" + register: wipefs_result + changed_when: wipefs_result.rc == 0 + + - name: Create a volume group for the storage + community.general.lvg: + pvs: "{{ lvm_dev }}" + vg: "{{ virtual_group }}" + force: true + + - name: Create a logical volume for the storage + when: is_container + community.general.lvol: + vg: "{{ virtual_group }}" + lv: "{{ logical_volume }}" + size: "{{ size }}" + opts: "--config 'activation/udev_rules=0'" + + - name: Create a logical volume for the storage + when: not is_container + community.general.lvol: + vg: "{{ virtual_group }}" + lv: "{{ logical_volume }}" + size: "{{ size }}" + opts: "--config 'activation/udev_rules=1'" + + - name: Create a filesystem for the storage + community.general.filesystem: + dev: "/dev/{{ virtual_group }}/{{ logical_volume }}" + fstype: "{{ fs_type }}" + +- name: Create a mountpoint for the storage + ansible.builtin.file: + path: "{{ storage_mountpoint }}" + state: directory + owner: "{{ mountpoint_owner }}" + group: "{{ mountpoint_group }}" + mode: "{{ storage_mountpoint_mode }}" + +- name: Mount the main storage + ansible.posix.mount: + src: "/dev/{{ virtual_group }}/{{ logical_volume }}" + path: "{{ storage_mountpoint }}" + fstype: "{{ fs_type }}" + state: mounted diff --git a/roles/make_lv/vars/main.yml b/roles/make_lv/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/make_lv/vars/main.yml @@ -0,0 +1 @@ +---