diff --git a/README.md b/README.md index 9bc99fc..e1fce21 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,4 @@ - [users](roles/users/README.md) - [wg_hub](roles/wg_hub/README.md) - [wg_spoke](roles/wg_spoke/README.md) +- [xfs_project_quotas](roles/xfs_project_quotas/README.md) diff --git a/galaxy.yml b/galaxy.yml index 57f1177..2d76ce6 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.32.0 +version: 0.33.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) diff --git a/roles/xfs_project_quotas/README.md b/roles/xfs_project_quotas/README.md new file mode 100644 index 0000000..339386b --- /dev/null +++ b/roles/xfs_project_quotas/README.md @@ -0,0 +1,35 @@ +xfs_project_quotas +========= + +Sets up XFS quotas + +Requirements +------------ + +- `xfsprogs` package + +Role Variables +-------------- + +- `projects` — quota directories list +- `storage_mountpoint` — XFS mountpoint + + + +Example Playbook +---------------- + +See [converge.yml](molecule/default/converge.yml) + +License +------- + +BSD + +Author Information +------------------ + +Alexander Gorelyshev (corvus-migratorius@proton.me) diff --git a/roles/xfs_project_quotas/defaults/main.yml b/roles/xfs_project_quotas/defaults/main.yml new file mode 100644 index 0000000..52ca5d2 --- /dev/null +++ b/roles/xfs_project_quotas/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for xfs_project_quotas diff --git a/roles/xfs_project_quotas/handlers/main.yml b/roles/xfs_project_quotas/handlers/main.yml new file mode 100644 index 0000000..cf4176b --- /dev/null +++ b/roles/xfs_project_quotas/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for xfs_project_quotas diff --git a/roles/xfs_project_quotas/meta/main.yml b/roles/xfs_project_quotas/meta/main.yml new file mode 100644 index 0000000..616eb4e --- /dev/null +++ b/roles/xfs_project_quotas/meta/main.yml @@ -0,0 +1,16 @@ +galaxy_info: + role_name: xfs_project_quotas + namespace: genlab + author: Alexander Gorelyshev + company: Genlab, LLC + description: "" + license: GPL-2.0-or-later + min_ansible_version: "2.1" + + platforms: + - name: Ubuntu + versions: ["jammy", "noble"] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/xfs_project_quotas/molecule/default/configuration/example.yml b/roles/xfs_project_quotas/molecule/default/configuration/example.yml new file mode 100644 index 0000000..cbade34 --- /dev/null +++ b/roles/xfs_project_quotas/molecule/default/configuration/example.yml @@ -0,0 +1,16 @@ +- id: 100 + path: /mnt/test0 + name: test0 + bsoft: "100M" + bhard: "100M" + owner: root + group: root + mode: "0700" +- id: 101 + path: /mnt/test1 + name: test1 + bsoft: "100M" + bhard: "100M" + owner: root + group: root + mode: "0700" diff --git a/roles/xfs_project_quotas/molecule/default/converge.yml b/roles/xfs_project_quotas/molecule/default/converge.yml new file mode 100644 index 0000000..9e685ef --- /dev/null +++ b/roles/xfs_project_quotas/molecule/default/converge.yml @@ -0,0 +1,72 @@ +--- +- name: Converge + hosts: all + pre_tasks: + - name: Get /root/image.img stats + ansible.builtin.stat: + path: /root/image.img + register: xfs_project_quotas_img_stat + + - name: Create image file + when: not xfs_project_quotas_img_stat.stat.exists + ansible.builtin.command: dd if=/dev/zero of=/root/image.img bs=1M count=400 + register: xfs_project_quotas_dd_result + changed_when: xfs_project_quotas_dd_result.rc == 0 + + - name: Get linked loop devices + ansible.builtin.command: losetup -a + changed_when: false + register: xfs_project_quotas_loop_devs + + - name: Link image to loop device + when: "'/root/image.img' not in xfs_project_quotas_loop_devs.stdout" + ansible.builtin.command: losetup --find --show -P /root/image.img + register: xfs_project_quotas_loop_device + changed_when: "'/dev/loop' in xfs_project_quotas_loop_device.stdout" + + - name: Get all loop devices + ansible.builtin.command: losetup -a + register: xfs_project_quotas_losetup_out + changed_when: false + + - name: Find line with /root/image.img + ansible.builtin.set_fact: + xfs_project_quotas_matched_line: "{{ xfs_project_quotas_losetup_out.stdout_lines + | select('search', '/root/image.img') + | list + | first + | default('') }}" + + - name: Extract loop device name (/dev/loopN) + ansible.builtin.set_fact: + xfs_project_quotas_loop_dev: "{{ xfs_project_quotas_matched_line | regex_search('(/dev/loop[0-9]+)', '\\1') | default('') }}" + + - name: Debug result + ansible.builtin.debug: + msg: "/root/image.img is linked to {{ xfs_project_quotas_loop_dev[0] }}" + when: xfs_project_quotas_loop_dev != '' + + - name: "Quotas | Ensure xfsprogs package is installed" + ansible.builtin.apt: + name: xfsprogs + state: present + update_cache: true + cache_valid_time: 3600 + + - name: Make xfs filesystem + community.general.filesystem: + fstype: xfs + dev: "{{ xfs_project_quotas_loop_dev[0] }}" + + - name: Mount xfs virtual disk + ansible.posix.mount: + path: /mnt + src: /root/image.img + fstype: xfs + opts: defaults,prjquota + state: mounted + + roles: + - role: genlab.common.xfs_project_quotas + projects: "{{ lookup('file', 'configuration/example.yml') | from_yaml }}" + storage_mountpoint: "/mnt" diff --git a/roles/xfs_project_quotas/molecule/default/molecule.yml b/roles/xfs_project_quotas/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/xfs_project_quotas/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/xfs_project_quotas/molecule/default/verify.yml b/roles/xfs_project_quotas/molecule/default/verify.yml new file mode 100644 index 0000000..53b8fd6 --- /dev/null +++ b/roles/xfs_project_quotas/molecule/default/verify.yml @@ -0,0 +1,13 @@ +--- + +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + - name: Check XFS project quotas on /mnt + ansible.builtin.command: xfs_quota -x -c 'report -p' /mnt + register: xfs_project_quotas_quota_output + changed_when: false + failed_when: "'Project quota on /mnt' not in xfs_project_quotas_quota_output.stdout" diff --git a/roles/xfs_project_quotas/tasks/main.yml b/roles/xfs_project_quotas/tasks/main.yml new file mode 100644 index 0000000..b0e9c60 --- /dev/null +++ b/roles/xfs_project_quotas/tasks/main.yml @@ -0,0 +1,75 @@ +--- +- name: Check files + loop: + - /etc/projects + - /etc/projid + ansible.builtin.stat: + path: "{{ item }}" + register: xfs_project_quotas_proj_files + +- name: Create missing files + loop: "{{ xfs_project_quotas_proj_files.results }}" + when: not item.stat.exists + ansible.builtin.file: + path: "{{ item.invocation.module_args.path }}" + state: touch + owner: root + group: root + mode: "0644" + +- name: "Quotas | Ensure xfsprogs package is installed" + ansible.builtin.apt: + name: xfsprogs + state: present + update_cache: true + cache_valid_time: 3600 + +- name: "Quotas | Ensure all /etc/projects entries exist" + loop: "{{ projects }}" + ansible.builtin.lineinfile: + path: /etc/projects + state: present + backup: true + regexp: "^{{ item.id }}:" + line: "{{ item.id }}:{{ item.path }}" + +- name: "Quotas | Ensure all /etc/projects entries exist" + loop: "{{ projects }}" + ansible.builtin.lineinfile: + path: /etc/projid + state: present + backup: true + regexp: "^{{ item.name }}:" + line: "{{ item.name }}:{{ item.id }}" + +- name: "Quotas | Ensure all project paths exist" + loop: "{{ projects }}" + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + owner: "{{ item.owner }}" + group: "{{ item.group }}" + mode: "{{ item.mode }}" + +- name: "Quotas | Initialize project quotas" + changed_when: false + loop: "{{ projects }}" + ansible.builtin.command: + cmd: >- + xfs_quota + -x + -c + 'project -s {{ item.name }}' + {{ storage_mountpoint }} + +- name: "Quotas | Apply quota limits" + loop: "{{ projects }}" + community.general.xfs_quota: + type: project + name: "{{ item.name }}" + mountpoint: "{{ storage_mountpoint }}" + bsoft: "{{ item.bsoft | default('100G', false) }}" + bhard: "{{ item.bhard | default('100G', false) }}" + isoft: "{{ item.isoft | default(0, false) }}" + ihard: "{{ item.ihard | default(0, false) }}" + state: present diff --git a/roles/xfs_project_quotas/vars/main.yml b/roles/xfs_project_quotas/vars/main.yml new file mode 100644 index 0000000..0c60f9c --- /dev/null +++ b/roles/xfs_project_quotas/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for xfs_project_quotas