chore: apply all changes
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# expand-root-lv role
|
||||
|
||||
Idempotent role that extends the root LVM logical volume to fill its
|
||||
volume group and grows the underlying filesystem (ext4 or xfs).
|
||||
Idempotent role that grows the root partition (via `growpart`), extends the
|
||||
root LVM logical volume to fill its volume group, and grows the underlying
|
||||
filesystem (ext4 or xfs).
|
||||
|
||||
## Where this runs in the lifecycle
|
||||
|
||||
@@ -24,6 +25,22 @@ size — a longstanding installer default that surprises every operator
|
||||
who hasn't been bitten by it before. ~90% of mk-labs VMs need this
|
||||
fix-up before they're fully useful.
|
||||
|
||||
After a Proxmox disk grow (increasing the VM disk size), the partition
|
||||
table, physical volume, logical volume, and filesystem all need to be
|
||||
extended in sequence. This role automates the full chain.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **growpart** — resizes the underlying partition to claim the newly
|
||||
provisioned disk space. Idempotent: no-op when the partition already
|
||||
fills the disk.
|
||||
2. **pvresize** — tells the kernel/LVM about the new partition size so
|
||||
the VG sees the additional free PEs.
|
||||
3. **lvextend** — extends the LV to claim all free PE in the VG
|
||||
(`+100%FREE`). No-op when there's nothing to grow.
|
||||
4. **fs grow** — `resize2fs` (ext4) or `xfs_growfs` (xfs), dispatched by
|
||||
detected filesystem type.
|
||||
|
||||
## Idempotency
|
||||
|
||||
- If `vg_free_count == 0`, the `lvextend` step is skipped and the
|
||||
@@ -49,22 +66,30 @@ expand_root_lv_skip: true
|
||||
|
||||
The day0 playbook checks this flag and skips the role cleanly.
|
||||
|
||||
To skip only the partition growstep while keeping LV/FS expansion
|
||||
(e.g. when the partition already covers the whole disk but the LV was
|
||||
provisioned small by the template), set:
|
||||
|
||||
```yaml
|
||||
expand_root_lv_pv_partition: undefined
|
||||
```
|
||||
|
||||
## Defaults
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|-------------------------------|---------------|-----------------------------------------------------|
|
||||
| `expand_root_lv_vg_name` | `ubuntu-vg` | LVM volume group name (Ubuntu installer default). |
|
||||
| `expand_root_lv_lv_name` | `ubuntu-lv` | LVM logical volume name (Ubuntu installer default). |
|
||||
| `expand_root_lv_pv_partition` | `/dev/sda3` | Partition backing the PV; grown via growpart. |
|
||||
| `expand_root_lv_mountpoint` | `/` | Mountpoint of the filesystem to grow. |
|
||||
|
||||
Override the VG/LV names in `host_vars/<host>.yml` for hosts that use a
|
||||
different LVM layout.
|
||||
Override the VG/LV/PV names in `host_vars/<host>.yml` for hosts that use a
|
||||
different layout.
|
||||
|
||||
## Limitations
|
||||
|
||||
- Does not extend the underlying partition. If the operator grows the
|
||||
Proxmox disk and the partition itself needs to grow before lvextend
|
||||
can claim the new space, run `growpart /dev/sda 3` (or equivalent)
|
||||
first. A future enhancement could automate this via `cloud-utils`'
|
||||
`growpart` package, but it's out of scope for the initial template
|
||||
fix-up case where the partition already covers the whole disk.
|
||||
- The `growpart` step requires the `cloud-guest-utils` package. The role
|
||||
installs it automatically on Debian/Ubuntu hosts when
|
||||
`expand_root_lv_pv_partition` is defined.
|
||||
- Only supports ext4 and xfs filesystems. Other filesystem types (btrfs,
|
||||
etc.) are left as a future enhancement.
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
expand_root_lv_vg_name: ubuntu-vg
|
||||
expand_root_lv_lv_name: ubuntu-lv
|
||||
|
||||
# The partition that backs the physical volume. After a Proxmox disk grow,
|
||||
# growpart must resize this partition before pvresize/lvextend can claim
|
||||
# the new space. This is the full device path (e.g. /dev/sda3).
|
||||
# If undefined, the growpart/pvresize steps are skipped.
|
||||
expand_root_lv_pv_partition: /dev/sda3
|
||||
|
||||
# Mount point we expect to be backed by the target LV. Used purely for
|
||||
# the resize2fs / xfs_growfs decision — the role inspects this path's
|
||||
# filesystem type and dispatches to the correct grow command.
|
||||
|
||||
@@ -2,44 +2,108 @@
|
||||
# ============================================================================
|
||||
# expand-root-lv / main
|
||||
# ----------------------------------------------------------------------------
|
||||
# 1. Confirm the target VG exists (skip role cleanly on non-LVM hosts).
|
||||
# 2. Read free physical-extent count for the VG.
|
||||
# 3. Extend the LV to +100%FREE only when free_pe > 0.
|
||||
# 4. Grow the filesystem on the mountpoint (ext4 -> resize2fs, xfs -> xfs_growfs).
|
||||
# 0. Ensure growpart is available (cloud-guest-utils provides the growpart binary)
|
||||
# 1. Grow the partition (growpart) if a PV partition device is defined
|
||||
# 2. Resize the physical volume (pvresize) to pick up the new partition size
|
||||
# 3. Confirm the target VG exists (skip role cleanly on non-LVM hosts).
|
||||
# 4. Read free physical-extent count for the VG.
|
||||
# 5. Extend the LV to +100%FREE only when free_pe > 0.
|
||||
# 6. Grow the filesystem on the mountpoint (ext4 -> resize2fs, xfs -> xfs_growfs).
|
||||
# Each step is idempotent and skips when there's nothing to do.
|
||||
# ============================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 0: Ensure growpart is available
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Ensure cloud-guest-utils (growpart) is installed
|
||||
ansible.builtin.package:
|
||||
name: cloud-guest-utils
|
||||
state: present
|
||||
when: expand_root_lv_pv_partition is defined
|
||||
tags: [growpart, always]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 1: Grow the partition that backs the PV
|
||||
# ---------------------------------------------------------------------------
|
||||
# growpart expects: growpart <device> <partition_number>
|
||||
# e.g. growpart /dev/sda 3 — NOT growpart /dev/sda3
|
||||
# We split expand_root_lv_pv_partition (e.g. /dev/sda3) into device and part_no.
|
||||
|
||||
- name: Derive device and partition number from PV partition path
|
||||
ansible.builtin.set_fact:
|
||||
expand_root_lv_pv_device: "{{ expand_root_lv_pv_partition | regex_replace('p?(\\d+)$', '') }}"
|
||||
expand_root_lv_pv_part_no: "{{ expand_root_lv_pv_partition | regex_replace('.*p?(\\d+)$', '\\1') }}"
|
||||
when: expand_root_lv_pv_partition is defined
|
||||
tags: [growpart, always]
|
||||
|
||||
- name: Grow partition to fill disk (growpart)
|
||||
ansible.builtin.command:
|
||||
cmd: "growpart {{ expand_root_lv_pv_device }} {{ expand_root_lv_pv_part_no }}"
|
||||
register: growpart_result
|
||||
when: expand_root_lv_pv_partition is defined
|
||||
changed_when: growpart_result.rc == 0 and "NO CHANGE" not in growpart_result.stdout
|
||||
tags: [growpart, always]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 2: Resize the physical volume to claim the new partition space
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Resize physical volume (pvresize)
|
||||
ansible.builtin.command:
|
||||
cmd: "pvresize {{ expand_root_lv_pv_partition }}"
|
||||
register: pvresize_result
|
||||
when: expand_root_lv_pv_partition is defined
|
||||
changed_when: pvresize_result.rc == 0
|
||||
tags: [pvresize, always]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 3: Confirm the target VG exists (skip role cleanly on non-LVM hosts).
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Gather LVM facts
|
||||
ansible.builtin.command:
|
||||
cmd: "vgs --noheadings --nosuffix --units b -o vg_name,vg_free_count {{ expand_root_lv_vg_name }}"
|
||||
register: vg_info
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: [lvm, always]
|
||||
|
||||
- name: Skip role when target VG is absent
|
||||
ansible.builtin.meta: end_play
|
||||
when: vg_info.rc != 0
|
||||
tags: [lvm, always]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 4: Parse free PE count for the VG
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Parse free PE count
|
||||
ansible.builtin.set_fact:
|
||||
expand_root_lv_free_pe: "{{ (vg_info.stdout.split() | last | int) if vg_info.stdout | length > 0 else 0 }}"
|
||||
tags: [lvm, always]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 5: Extend the LV to +100%FREE only when free_pe > 0
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Extend LV to fill VG (only if free PE > 0)
|
||||
ansible.builtin.command:
|
||||
cmd: "lvextend -l +100%FREE /dev/{{ expand_root_lv_vg_name }}/{{ expand_root_lv_lv_name }}"
|
||||
register: lvextend_result
|
||||
when: expand_root_lv_free_pe | int > 0
|
||||
changed_when: lvextend_result.rc == 0
|
||||
tags: [lvm, always]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 6: Detect filesystem type at mountpoint and grow
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Detect filesystem type at mountpoint
|
||||
ansible.builtin.command:
|
||||
cmd: "findmnt {{ expand_root_lv_mountpoint }} -no FSTYPE"
|
||||
register: fstype_result
|
||||
changed_when: false
|
||||
tags: [filesystem, always]
|
||||
|
||||
- name: Set filesystem type fact
|
||||
ansible.builtin.set_fact:
|
||||
expand_root_lv_fstype: "{{ fstype_result.stdout | trim }}"
|
||||
tags: [filesystem, always]
|
||||
|
||||
- name: Grow ext4 filesystem
|
||||
ansible.builtin.command:
|
||||
@@ -47,8 +111,12 @@
|
||||
register: resize_result
|
||||
when:
|
||||
- expand_root_lv_fstype == "ext4"
|
||||
- lvextend_result.changed | default(false)
|
||||
- (growpart_result is defined and growpart_result.changed) or
|
||||
(pvresize_result is defined and pvresize_result.changed) or
|
||||
(lvextend_result is defined and lvextend_result.changed) or
|
||||
(growpart_result is not defined)
|
||||
changed_when: resize_result.rc == 0
|
||||
tags: [filesystem, always]
|
||||
|
||||
- name: Grow xfs filesystem
|
||||
ansible.builtin.command:
|
||||
@@ -56,15 +124,24 @@
|
||||
register: xfs_result
|
||||
when:
|
||||
- expand_root_lv_fstype == "xfs"
|
||||
- lvextend_result.changed | default(false)
|
||||
- (growpart_result is defined and growpart_result.changed) or
|
||||
(pvresize_result is defined and pvresize_result.changed) or
|
||||
(lvextend_result is defined and lvextend_result.changed) or
|
||||
(growpart_result is not defined)
|
||||
changed_when: xfs_result.rc == 0
|
||||
tags: [filesystem, always]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 7: Report current root size
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Report current root size
|
||||
ansible.builtin.command:
|
||||
cmd: "df -h {{ expand_root_lv_mountpoint }}"
|
||||
register: df_result
|
||||
changed_when: false
|
||||
tags: [always]
|
||||
|
||||
- name: Show post-resize disk usage
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ df_result.stdout_lines }}"
|
||||
tags: [always]
|
||||
|
||||
Reference in New Issue
Block a user