Skip to content

lvm: lvm_pv on RHEL 8, role fixes, Molecule scenario and Python 3.6 unit tests - #399

Merged
NavidSassan merged 12 commits into
mainfrom
fix/lvm
Sep 29, 2026
Merged

NavidSassan merged 12 commits into
mainfrom
fix/lvm

Conversation

@NavidSassan

@NavidSassan NavidSassan commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

On RHEL 8, pvcreate / pvresize in the lvm role failed with SyntaxError: future feature annotations is not defined. The vendored lvm_pv module starts with from __future__ import annotations, which the platform-python 3.6 that Ansible picks on RHEL 8 does not know. A Molecule scenario written to reproduce it turned up five more bugs in the role.

What changes

lvm_pv runs on RHEL 8. The module uses the preamble of the other in-house plugins, and the vendored-plugins list in CONTRIBUTING.md records the local patch.

The module tests also run on Python 3.6. The controller matrix (3.9+) cannot catch syntax that only 3.6 rejects. A new job in Linuxfabrik: Unit Tests runs the module and module_utils tests in UBI 8 on Python 3.6, against the module_utils of ansible-core 2.16 (the last series that supports Python 3.6 on the managed node, i.e. what the controller ships to a RHEL 8 host). Dependabot keeps it on the latest 2.16 release. tox -e py36-target does the same locally. ruff treats managed-node code and its tests as py37, so the formatter can no longer bring back syntax that 3.6 does not know. lvm_pv gets unit tests.

Role fixes.

  • lvm2 is installed; hosts deployed from a cloud image lacked pvs.
  • The tools for XFS and ext2/3/4 are installed; Debian cloud images lacked mkfs.xfs.
  • restorecon only runs where SELinux is enabled.
  • shrink: true never worked, because lvol refuses to shrink without force: true. Shrinking now needs both, like removing.
  • Removing a VG without pvs or an LV without size (state: absent) aborted the run.
  • mount_owner, mount_group and mount_mode were set on the directory underneath the mount and only took effect on the second run, which reported a change.

README. force is required to remove or shrink an LV, and N%FREE on an existing LV sets it to N% of the space that is free right now.

Testing

A new lvm scenario builds a starting layout on three additional disks and changes it: growpart and PV grow on a partition, pesize, vgextend, removing a VG that still holds an LV, lvextend with resizefs, 100%FREE and 100%VG, ext4 shrink, LV removal, mount options, owner, mode, SELinux labels and the rejection of relative sizes. Green on Debian 12/13, Rocky 8/9/10 and Ubuntu 22.04/24.04/26.04, including idempotence. Before the fix, the same scenario reproduced the SyntaxError on Rocky 8.

The Python 3.6 unit-test job was run locally with podman in UBI 8 (124 tests); this PR is its first run on GitHub. pre-commit is clean.

Not tested: NVMe devices, check mode.

… module runs on RHEL 8

The vendored upstream module starts with `from __future__ import
annotations`, which platform-python 3.6 on RHEL 8 rejects with
"SyntaxError: future feature annotations is not defined" before the
module runs. Use the preamble of the other in-house plugins instead
and record the local patch in the vendored-plugins list.
Drive main() with a fake run_command that keeps the PV state in
memory: create (with and without force), resize, remove, check mode,
and the sysfs rescan path of SCSI, whole-disk and NVMe devices.
Modules run on the managed node, where RHEL 8 uses platform-python
3.6. The controller matrix (3.9+) cannot catch syntax 3.6 does not
know, as lvm_pv showed. Enable the py36-target tox env and add a CI
job that runs the module and module_utils tests in UBI 8:

- the tests import the module_utils of ansible-core 2.16, the last
  series that supports Python 3.6 on the managed node, i.e. what the
  controller ships to a RHEL 8 host. ansible-core itself needs Python
  3.10, so it is installed without dependencies on the runner and put
  on PYTHONPATH in the container. Dependabot keeps it on the latest
  2.16 release.
- pytest comes from a hash-pinned lockfile compiled under Python 3.6
  (pytest 6.2.5, the last release for 3.6), which Dependabot does not
  watch, since it would pick releases without Python 3.6 support
- conftest.py skips the plugin loader when the controller part of
  ansible-core cannot be imported
- skipsdist for tox 3, since tox 4 does not run on Python 3.6
- ruff targets py37 for managed-node code and its tests, so the
  formatter no longer produces parenthesized context managers, and
  test_sqlite_query drops them
.github/unit-tests-py36/requirements.txt pins pytest 6.2.5 and, via
importlib-metadata, zipp 3.6.0. Dependency Review fails on
GHSA-6w46-j5rx-g56g (pytest) and GHSA-jfmj-5v4g-7637 (zipp). The
fixed releases require Python 3.10 and 3.8, while the test runner has
to run on the Python 3.6 of the modules under test. The pytest
advisory needs other local users on the machine and the zipp one a
crafted ZIP file opened with zipp.Path; the throwaway UBI 8 container
has neither. Allowlist both, as monitoring-plugins does for its py39
lockfile. The remaining pins have no active advisory; the one against
py 1.11.0 is withdrawn.
Hosts installed without LVM, e.g. from a cloud image, lack the LVM
tools, and lvm_pv failed with "No such file or directory: pvs".
lvol refuses to shrink an LV without force=true, and the
lvcreate/lvextend task never passed it, so `shrink: true` always
aborted. Pass the LV's `force` subkey; shrinking needs both, like
removing. In the present state lvol only uses force to allow
shrinking.
…without size

subelements("pvs") failed on a VG entry with `state: absent` and no
`pvs`, and the label of the size check read `size`, which an LV entry
with `state: absent` does not need.
Owner and mode were set on the directory before mounting, where the
mounted file system's root hides them. The second run then set them
on that root and reported a change. ansible.posix.mount creates a
missing mount point itself, so set owner and mode after mounting.
Debian cloud images have no mkfs.xfs, and XFS is the role's default,
so creating a file system failed there. Install e2fsprogs and
xfsprogs for the ext2/3/4 and xfs LVs in the inventory. The other
fstypes of community.general.filesystem are left to the admin, since
they are untested and not every supported distribution ships them.
On Debian and Ubuntu there is no restorecon, so mounting an LV
failed. Check for "enabled" rather than "not disabled": without the
SELinux Python bindings, the fact reads "Missing selinux Python
library".
…sion in the lvm_pv comment

For an existing LV, lvol takes N% of the space that is free in the VG
right now as the new absolute size, instead of adding it to the LV.

lvm_pv first shipped in community.general 11.0.0 (version_added and
the first tag containing 367b28d76), not 12.2.0, as CONTRIBUTING.md
already says. Also fix the typo in the second line of that comment.
prepare.yml builds a starting layout on three additional disks and
converge changes it: growpart and PV grow on a partition, pesize,
vgextend, removing a VG that holds an LV, lvextend with resizefs,
100%FREE and 100%VG, ext4 shrink, LV removal, mount options, owner,
mode and SELinux labels, and the rejection of relative sizes.

Runs on Debian 12/13, Rocky 8/9/10 and Ubuntu 22.04/24.04/26.04. The
mount group is daemon, since Debian and Ubuntu call the nobody group
nogroup, and the SELinux label check only runs where SELinux is
enabled. Green on all eight systems, so lvm is marked as proven on
all of them in COMPATIBILITY.md.
@NavidSassan
NavidSassan merged commit e242310 into main Sep 29, 2026
13 checks passed
@NavidSassan
NavidSassan deleted the fix/lvm branch September 29, 2026 13:47
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.

1 participant