diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml
new file mode 100644
index 0000000..4ac0ec3
--- /dev/null
+++ b/.github/workflows/molecule.yml
@@ -0,0 +1,112 @@
+---
+
+# This is a basic workflow to help you get started with Actions
+
+name: Molecule
+
+# Controls when the action will run. Triggers the workflow on push or pull request
+# events but only for the master branch
+on:
+ push:
+ pull_request:
+ branches:
+ - master
+ - tags/*
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ # This workflow contains a single job called "build"
+ lint:
+ # The type of runner that the job will run on
+ runs-on: ubuntu-latest
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v2
+
+ - name: install lint prerequisite
+ run: |
+ sudo apt -y install python3-setuptools ansible-lint
+
+ - name: Install molecule
+ run: |
+ sudo apt update
+ sudo apt -y install python3-setuptools python3 python3-pip docker
+ sudo pip3 install wheel
+ sudo pip3 install docker molecule testinfra yamllint ansible-lint flake8
+
+ - name: molecule lint
+ run: |
+ molecule lint
+
+ requirements:
+ # The type of runner that the job will run on
+ runs-on: ubuntu-latest
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v2
+ - name: install prereq
+ run: |
+ ansible-galaxy collection install -r requirements.yml
+ ansible-galaxy role install -r requirements.yml
+
+ molecule:
+
+ runs-on: ubuntu-latest
+ if: "false"
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: configure keyboard layout
+ run: |
+ echo XKBLAYOUT='us' > /etc/default/keyboard
+
+ - name: install virtualization stack
+ env:
+ DEBIAN_FRONTEND: noninteractive
+ run: |
+ sudo apt-get update
+ sudo apt -y install cpu-checker
+ kvm-ok
+ # shit happens here, containerization sucks... that was worth the try
+
+ - name: install virtualization stack
+ env:
+ DEBIAN_FRONTEND: noninteractive
+ run: |
+ sudo apt -y install policycoreutils-python-utils qemu-kvm qemu-utils libvirt-clients libvirt-daemon-system bridge-utils python3-libvirt libnss-libvirt libguestfs-tools virtinst virt-top genisoimage libvirt-dev libvirt-bin
+
+ - name: install vagrant
+ env:
+ VAGRANT_DISABLE_STRICT_DEPENDENCY_ENFORCEMENT: 1
+ run: |
+ wget https://releases.hashicorp.com/vagrant/2.2.7/vagrant_2.2.7_x86_64.deb
+ sudo apt -y install ./vagrant_2.2.7_x86_64.deb
+ vagrant plugin install vagrant-persistent-storage
+ vagrant plugin install vagrant-libvirt
+ sudo groupadd --system libvirt
+ sudo usermod -a -G libvirt $(whoami)
+ sudo /etc/init.d/libvirtd restart
+
+ - name: Install molecule
+ run: |
+ sudo apt -y install python3-setuptools
+ pip3 install molecule-vagrant wheel
+
+ - name: create molecule vm
+ env:
+ LC_ALL: C.UTF-8
+ LANG: C.UTF-8
+ run: |
+ molecule create --scenario-name kvm
+
+ - name: verify
+ run: |
+ molecule verify
+
+ - name: teardown
+ run: |
+ molecule destroy
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..73f69e0
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/ansible-manage-lvm.iml b/.idea/ansible-manage-lvm.iml
new file mode 100644
index 0000000..d0876a7
--- /dev/null
+++ b/.idea/ansible-manage-lvm.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..8656114
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..900cbad
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/handlers/main.yml b/handlers/main.yml
index 2027eb2..db4600d 100644
--- a/handlers/main.yml
+++ b/handlers/main.yml
@@ -1,2 +1,44 @@
---
# handlers file for ansible-manage-lvm
+- name: manage_lvm | resizing swap
+ shell: "swapoff -a && mkswap /dev/{{ item[0]['vgname'] }}/{{ item[1]['lvname'] }} && swapon -va"
+ become: true
+ with_subelements:
+ - "{{ lvm_groups }}"
+ - lvnames
+ when: >
+ item[1]['filesystem'] is defined and
+ item[1]['filesystem'] == "swap"
+
+- name: manage_lvm | resizing btrfs
+ command: "btrfs filesystem resize max {{ item[1]['mntp'] }}"
+ become: true
+ with_subelements:
+ - "{{ lvm_groups }}"
+ - lvnames
+ when: >
+ item[1]['filesystem'] is defined and
+ item[1]['filesystem'] == "btrfs"
+
+- name: manage_lvm | resizing xfs
+ command: xfs_growfs -d {{ item[1]['mntp'] }}
+ become: true
+ with_subelements:
+ - "{{ lvm_groups }}"
+ - lvnames
+ when: >
+ item[1]['filesystem'] is defined and
+ item[1]['filesystem'] == "xfs"
+
+- name: manage_lvm | resizing filesystem
+ command: resize2fs /dev/{{ item[0]['vgname'] }}/{{ item[1]['lvname'] }}
+ become: true
+ with_subelements:
+ - "{{ lvm_groups }}"
+ - lvnames
+ when: >
+ item[1]['filesystem'] is defined and
+ item[1]['filesystem'] != "None" and
+ item[1]['filesystem'] != "swap" and
+ item[1]['filesystem'] != "xfs" and
+ item[1]['filesystem'] != "btrfs"
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml
new file mode 100644
index 0000000..9848cce
--- /dev/null
+++ b/molecule/default/converge.yml
@@ -0,0 +1,28 @@
+---
+- name: Converge
+ hosts: all
+ vars:
+ lvm_groups:
+ - vgname: my_vg
+ disks:
+ - /dev/sdb1
+ create: true
+ lvnames:
+ - lvname: my_lv
+ size: 40%VG
+ opts: "--wipesignatures y"
+ create: true
+ filesystem: ext4
+ mntp: "/var/lib/mountpoint"
+ - lvname: my_lv2
+ size: 10%VG
+ opts: "--wipesignatures y"
+ create: true
+ filesystem: ext4
+ mntp: "/var/lib/mountpoint2"
+ manage_lvm: true
+
+ tasks:
+ - name: "Include lvm"
+ include_role:
+ name: "ansible-manage-lvm"
diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml
new file mode 100644
index 0000000..967e97f
--- /dev/null
+++ b/molecule/default/molecule.yml
@@ -0,0 +1,27 @@
+---
+driver:
+ name: vagrant
+ provider:
+ name: virtualbox
+lint: yamllint . && flake8 && ansible-lint --exclude=meta
+platforms:
+ - name: Fedora-Molecule-LVM
+ box: bento/fedora-31
+ # box-url: ${platform_root}/packer/bento/builds/fedora-31-x86_64.virtualbox.box
+ provider_override_args:
+ - "persistent_storage.enabled = true"
+ - "persistent_storage.location = 'molecule-lvm.vdi'"
+ - "persistent_storage.size = 100"
+ - "persistent_storage.mount = false"
+ - "persistent_storage.diskdevice = '/dev/sdb'"
+provisioner:
+ name: ansible
+ inventory:
+ group_vars:
+ lvm:
+verifier:
+ name: testinfra
+ env:
+ PYTHONWARNINGS: "ignore:.*U.*mode is deprecated:DeprecationWarning"
+ options:
+ v: 1
diff --git a/molecule/default/tests/__pycache__/conftest.cpython-27-PYTEST.pyc b/molecule/default/tests/__pycache__/conftest.cpython-27-PYTEST.pyc
new file mode 100644
index 0000000..4cd4455
Binary files /dev/null and b/molecule/default/tests/__pycache__/conftest.cpython-27-PYTEST.pyc differ
diff --git a/molecule/default/tests/__pycache__/test_default.cpython-27-PYTEST.pyc b/molecule/default/tests/__pycache__/test_default.cpython-27-PYTEST.pyc
new file mode 100644
index 0000000..cb5ebdb
Binary files /dev/null and b/molecule/default/tests/__pycache__/test_default.cpython-27-PYTEST.pyc differ
diff --git a/molecule/default/tests/conftest.py b/molecule/default/tests/conftest.py
new file mode 100644
index 0000000..7bd2743
--- /dev/null
+++ b/molecule/default/tests/conftest.py
@@ -0,0 +1,21 @@
+"""PyTest Fixtures."""
+from __future__ import absolute_import
+import os
+import pytest
+
+
+def pytest_runtest_setup(item):
+ """Run tests only when under molecule with testinfra installed."""
+ try:
+ import testinfra
+ except ImportError:
+ pytest.skip("Test requires testinfra", allow_module_level=True)
+ if "MOLECULE_INVENTORY_FILE" in os.environ:
+ pytest.testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+ os.environ["MOLECULE_INVENTORY_FILE"]
+ ).get_hosts("all")
+ else:
+ pytest.skip(
+ "Test should run only from inside molecule.",
+ allow_module_level=True
+ )
diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py
new file mode 100644
index 0000000..ad9e53a
--- /dev/null
+++ b/molecule/default/tests/test_default.py
@@ -0,0 +1,22 @@
+"""Role testing files using testinfra."""
+
+
+def test_lvm_package_shall_be_installed(host):
+ assert host.package("lvm2").is_installed
+
+
+def test_non_persistent_volume_group_is_created(host):
+ command = """sudo vgdisplay | grep -c 'my_vg'"""
+ cmd = host.run(command)
+ assert '1' in cmd.stdout
+
+
+def test_mylv_logical_volume_is_created(host):
+ command = """sudo lvs -o lv_name my_vg --separator='|' --noheadings \
+ | grep -c 'my_lv'"""
+ cmd = host.run(command)
+ assert int(cmd.stdout.rstrip()) >= 1
+
+
+def test_volume_is_mounted(host):
+ host.file("/var/lib/mountpoint").mode == 0o731
diff --git a/tasks/centos.yml b/tasks/centos.yml
index 66cab6b..0d897f3 100644
--- a/tasks/centos.yml
+++ b/tasks/centos.yml
@@ -1,18 +1,38 @@
---
- name: centos | installing lvm2
- yum:
+ package:
name: "lvm2"
state: "present"
become: true
- name: centos | installing sg3_utils
- yum:
+ package:
name: "sg3_utils"
state: "present"
become: true
+- name: centos | install xfs tools
+ package:
+ name: "xfsprogs"
+ state: "present"
+ become: yes
+ loop: "{{ lvm_groups|subelements('lvnames') }}"
+ when: >
+ (
+ (item.1 is defined and item.1 != 'None') and
+ (
+ item.1.filesystem is defined and
+ item.1.filesystem == "xfs"
+ )
+ and
+ (
+ item.1.create is defined and
+ item.1.create
+ )
+ )
+
- name: centos | checking for scsi devices
- shell: "sg_scan"
+ command: "sg_scan"
become: true
register: "scsi_devices"
changed_when: False
diff --git a/tasks/create_lv.yml b/tasks/create_lv.yml
new file mode 100644
index 0000000..24f303f
--- /dev/null
+++ b/tasks/create_lv.yml
@@ -0,0 +1,86 @@
+---
+
+- name: manage_lvm | creating new LVM logical volume(s)
+ lvol:
+ vg: "{{ vg.item[0]['vgname'] }}"
+ lv: "{{ vg.item[1]['lvname'] }}"
+ size: "{{ vg.item[1]['size'] }}"
+ shrink: no
+ opts: "{{ vg.item[1]['opts'] | default('') }}"
+ state: "present"
+ become: true
+ notify:
+ - manage_lvm | resizing swap
+ - manage_lvm | resizing btrfs
+ - manage_lvm | resizing xfs
+ - manage_lvm | resizing filesystem
+ when: >
+ (
+ (vg.item[0]['create'] is defined
+ and vg.item[0]['create']) and
+ (vg.item[1] is defined and
+ vg.item[1] != 'None') and
+ (vg.item[1]['create'] is defined and
+ vg.item[1]['create'])
+ )
+
+- name: manage_lvm | creating new filesystem on new LVM logical volume(s)
+ filesystem:
+ fstype: "{{ vg.item[1]['filesystem'] }}"
+ dev: "/dev/{{ vg.item[0]['vgname'] }}/{{ vg.item[1]['lvname'] }}"
+ # resizefs: yes #coming in 2.0 which will replace the resizing filesystem task below
+ become: true
+ when: >
+ (
+ (vg.item[0]['create'] is defined and
+ vg.item[0]['create']) and
+ (vg.item[1] is defined and
+ vg.item[1] != 'None') and
+ (vg.item[1]['create'] is defined and
+ vg.item[1]['create']) and
+ (
+ vg.item[1]['filesystem'] is defined and
+ vg.item[1]['filesystem'] != 'None' and
+ vg.item[1]['filesystem'] != "swap" and
+ vg.item[1]['filesystem'] != "xfs"
+ )
+ )
+
+- name: lvm | check already converted
+ loop: "{{ lvm_groups|subelements('lvnames') }}"
+ shell: "xfs_info /dev/{{ vg.item.0.vgname }}/{{ vg.item.1.lvname }} | grep -c 'ftype=1'"
+ become: yes
+ register: alreadyformattedresult
+ ignore_errors: True
+ changed_when: no
+ when: >
+ (
+ (vg.item.1 is defined and vg.item.1 != 'None') and
+ (
+ vg.item.1.filesystem2 is defined and
+ vg.item.1.filesystem2 == "xfs"
+ )
+ and
+ (
+ vg.item.1.create is defined and
+ vg.item.1.create
+ )
+ )
+
+- name: manage_lvm | creating new filesystem on new LVM logical volume(s)
+ command: mkfs.xfs /dev/{{ vg.item.0.vgname }}/{{ vg.item.1.lvname }}
+ become: yes
+ when: >
+ (
+ alreadyformattedresult and
+ (vg.item[0]['create'] is defined and
+ vg.item[0]['create']) and
+ (vg.item[1] is defined and
+ vg.item[1] != 'None') and
+ (vg.item[1]['create'] is defined and
+ vg.item[1]['create']) and
+ (
+ vg.item[1]['filesystem'] is defined and
+ vg.item[1]['filesystem'] == "xfs"
+ )
+ )
diff --git a/tasks/create_vg.yml b/tasks/create_vg.yml
new file mode 100644
index 0000000..ba408ae
--- /dev/null
+++ b/tasks/create_vg.yml
@@ -0,0 +1,22 @@
+---
+- name: manage_lvm | check existing physical volume group(s)
+ shell: "vgdisplay | grep '{{ item['vgname'] }}'"
+ become: true
+ with_items: "{{ lvm_groups }}"
+ ignore_errors: True
+ changed_when: no
+ register: vg_exist
+
+- debug: var=vg_exist
+
+- name: manage_lvm | creating new LVM physical volume group(s)
+ lvg:
+ vg: "{{ item.item['vgname'] }}"
+ pvs: "{{ item.item['disks']|join(',') }}"
+ state: "present"
+ become: true
+ with_items: "{{ vg_exist.results }}"
+ when: >
+ item.rc != 0 and
+ item.item['create'] is defined and
+ item.item['create']
diff --git a/tasks/debian.yml b/tasks/debian.yml
index 3b38eaf..0ccccbf 100644
--- a/tasks/debian.yml
+++ b/tasks/debian.yml
@@ -1,22 +1,40 @@
---
- name: debian | installing pre-reqs
- apt:
+ package:
name:
- lvm2
- scsitools
- - xfsprogs
state: "present"
become: true
+- name: centos | install xfs tools
+ package:
+ name: "xfsprogs"
+ state: "present"
+ become: yes
+ loop: "{{ lvm_groups|subelements('lvnames') }}"
+ when: >
+ (
+ (item.1 is defined and item.1 != 'None') and
+ (
+ item.1.filesystem is defined and
+ item.1.filesystem == "xfs"
+ )
+ and
+ (
+ item.1.create is defined and
+ item.1.create
+ )
+ )
- name: debian | checking for scsi devices
- shell: "sg_scan"
+ command: "sg_scan"
become: true
register: "scsi_devices"
- changed_when: False
+ changed_when: false
- name: debian | rescanning for new disks added
command: "/sbin/rescan-scsi-bus"
become: true
- changed_when: False
- when: scsi_devices['stdout'] != ""
+ changed_when: false
+ when: scsi_devices['stdout'] | length
diff --git a/tasks/manage_lvm.yml b/tasks/manage_lvm.yml
index 872977b..c2f9ae1 100644
--- a/tasks/manage_lvm.yml
+++ b/tasks/manage_lvm.yml
@@ -1,123 +1,26 @@
---
-- name: manage_lvm | creating new LVM volume group(s)
- lvg:
- vg: "{{ item['vgname'] }}"
- pvs: "{{ item['disks']|join(',') }}"
- state: "present"
- become: true
- with_items: "{{ lvm_groups }}"
- when: >
- (item['create'] is defined and
- item['create'])
+- name: manage_lvm | manage physical volume group creation
+ include: create_vg.yml
-- name: manage_lvm | creating new LVM logical volume(s)
- lvol:
- vg: "{{ item[0]['vgname'] }}"
- lv: "{{ item[1]['lvname'] }}"
- size: "{{ item[1]['size'] }}"
- shrink: no
- opts: "{{ item[1]['opts'] | default('') }}"
- state: "present"
+- name: manage_lvm | check existing logical volume group(s)
+ shell: "lvs -o lv_name {{ item[0]['vgname'] }} --separator='|' --noheadings | grep {{ item[1]['lvname'] }}"
become: true
- register: lvm
+ ignore_errors: True
+ changed_when: no
with_subelements:
- "{{ lvm_groups }}"
- lvnames
- when: >
- ((item[0]['create'] is defined
- and item[0]['create']) and
- (item[1] is defined and
- item[1] != 'None') and
- (item[1]['create'] is defined and
- item[1]['create']))
+ register: lv_exist
-- name: manage_lvm | creating new filesystem on new LVM logical volume(s)
- filesystem:
- fstype: "{{ item[1]['filesystem'] }}"
- dev: "/dev/{{ item[0]['vgname'] }}/{{ item[1]['lvname'] }}"
- # resizefs: yes #coming in 2.0 which will replace the resizing filesystem task below
- become: true
- with_subelements:
- - "{{ lvm_groups }}"
- - lvnames
- when: >
- ((item[0]['create'] is defined and
- item[0]['create']) and
- (item[1] is defined and
- item[1] != 'None') and
- (item[1]['create'] is defined and
- item[1]['create']) and
- (item[1]['filesystem'] is defined and
- item[1]['filesystem'] != 'None') and
- item[1]['filesystem'] != "swap")
+- debug: var=lv_exist
-- name: manage_lvm | mounting new filesystem(s)
- mount:
- name: "{{ item[1]['mntp'] }}"
- src: "/dev/{{ item[0]['vgname'] }}/{{ item[1]['lvname'] }}"
- fstype: "{{ item[1]['filesystem'] }}"
- state: "mounted"
- opts: "{{ item[1]['mopts'] | default('defaults') }}"
- become: true
- with_subelements:
- - "{{ lvm_groups }}"
- - lvnames
+- name: manage_lvm | loop over logical volume(s)
+ include: create_lv.yml
+ loop: "{{ lv_exist.results }}"
+ loop_control:
+ loop_var: vg
when: >
- ((item[0]['create'] is defined and
- item[0]['create']) and
- (item[1] is defined and
- item[1] != 'None') and
- (item[1]['create'] is defined and
- item[1]['create']) and
- (item[1]['mount'] is defined and
- item[1]['mount']))
-
-- name: manage_lvm | resizing filesystem
- command: resize2fs /dev/{{ item[0]['vgname'] }}/{{ item[1]['lvname'] }}
- become: true
- with_subelements:
- - "{{ lvm_groups }}"
- - lvnames
- when: >
- lvm['changed'] and
- item[1]['filesystem'] is defined and
- item[1]['filesystem'] != "None" and
- item[1]['filesystem'] != "swap" and
- item[1]['filesystem'] != "xfs" and
- item[1]['filesystem'] != "btrfs"
-
-- name: manage_lvm | resizing xfs
- command: xfs_growfs -d {{ item[1]['mntp'] }}
- become: true
- with_subelements:
- - "{{ lvm_groups }}"
- - lvnames
- when: >
- lvm['changed'] and
- item[1]['filesystem'] is defined and
- item[1]['filesystem'] == "xfs"
-
-- name: manage_lvm | resizing swap
- shell: "swapoff -a && mkswap /dev/{{ item[0]['vgname'] }}/{{ item[1]['lvname'] }} && swapon -va"
- become: true
- with_subelements:
- - "{{ lvm_groups }}"
- - lvnames
- when: >
- lvm['changed'] and
- item[1]['filesystem'] is defined and
- item[1]['filesystem'] == "swap"
-
-- name: manage_lvm | resizing btrfs
- shell: "btrfs filesystem resize max {{ item[1]['mntp'] }}"
- become: true
- with_subelements:
- - "{{ lvm_groups }}"
- - lvnames
- when: >
- lvm['changed'] and
- item[1]['filesystem'] is defined and
- item[1]['filesystem'] == "btrfs"
+ vg.rc != 0
- name: manage_lvm | unmounting filesystem(s)
mount:
@@ -136,6 +39,29 @@
not item[1]['create'] and
item[1]['filesystem'] != "swap")
+- meta: flush_handlers
+
+- name: manage_lvm | mounting new filesystem(s)
+ mount:
+ name: "{{ item[1]['mntp'] }}"
+ src: "/dev/{{ item[0]['vgname'] }}/{{ item[1]['lvname'] }}"
+ fstype: "{{ item[1]['filesystem'] }}"
+ state: "mounted"
+ opts: "{{ item[1]['mopts'] | default('defaults') }}"
+ become: true
+ with_subelements:
+ - "{{ lvm_groups }}"
+ - lvnames
+ when: >
+ ((item[0]['create'] is defined and
+ item[0]['create']) and
+ (item[1] is defined and
+ item[1] != 'None') and
+ (item[1]['create'] is defined and
+ item[1]['create']) and
+ (item[1]['mount'] is defined and
+ item[1]['mount']))
+
- name: manage_lvm | Removing LVM logical volume(s)
lvol:
vg: "{{ item[0]['vgname'] }}"
@@ -152,7 +78,7 @@
(item[1]['create'] is defined and
not item[1]['create'])
-- name: manage_lvm | Removing LVM volume group(s)
+- name: manage_lvm | Removing LVM physical volume group(s)
lvg:
vg: "{{ item['vgname'] }}"
pvs: "{{ item['disks']|join(',') }}"