update
This commit is contained in:
Executable
+131
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Resize an LVM logical volume and its filesystem.
|
||||||
|
|
||||||
|
Author: Bart Sjerps
|
||||||
|
Date: 2026-07-17
|
||||||
|
Generated by opencode
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def die(msg: str) -> None:
|
||||||
|
"""Print an error message to stderr and exit with code 2."""
|
||||||
|
print(f"Error: {msg}", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd: list[str], dryrun: bool = False) -> str:
|
||||||
|
"""Execute a shell command, returning stdout. Exits on non-zero status."""
|
||||||
|
if dryrun:
|
||||||
|
print(f"[DRY-RUN] {' '.join(cmd)}")
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
err = (e.stderr or e.stdout or "").strip()
|
||||||
|
die(f"{' '.join(cmd)} failed: {err or f'exit code {e.returncode}'}")
|
||||||
|
return result.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_lv(mount: Optional[str] = None, lvname: Optional[str] = None) -> str:
|
||||||
|
"""Resolve a mountpoint or LV name to an absolute LVM device path."""
|
||||||
|
if mount:
|
||||||
|
dev = run(["findmnt", "-no", "SOURCE", "--target", mount])
|
||||||
|
if not dev:
|
||||||
|
die(f"No device mounted at {mount}")
|
||||||
|
else:
|
||||||
|
dev = lvname
|
||||||
|
|
||||||
|
if not dev.startswith("/dev/"):
|
||||||
|
dev = f"/dev/{dev}"
|
||||||
|
|
||||||
|
if not run(["lvs", dev]):
|
||||||
|
die(f"{dev} is not an LVM logical volume")
|
||||||
|
|
||||||
|
return dev
|
||||||
|
|
||||||
|
|
||||||
|
def detect_fs(dev: str) -> str:
|
||||||
|
"""Detect the filesystem type on a block device via blkid."""
|
||||||
|
fstype = run(["blkid", "-o", "value", "-s", "TYPE", dev])
|
||||||
|
if not fstype:
|
||||||
|
die(f"Cannot detect filesystem on {dev}")
|
||||||
|
return fstype
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_mount(dev: str, mount: Optional[str] = None) -> str:
|
||||||
|
"""Return a mountpoint for a device, using the provided one or looking it up."""
|
||||||
|
if mount:
|
||||||
|
return mount
|
||||||
|
|
||||||
|
mountpoint = run(["findmnt", "-no", "TARGET", "--source", dev])
|
||||||
|
if not mountpoint:
|
||||||
|
die(f"No mountpoint found for {dev}")
|
||||||
|
return mountpoint
|
||||||
|
|
||||||
|
|
||||||
|
def lvextend_cmd(size: str, dev: str) -> list[str]:
|
||||||
|
"""Build an lvextend command, using -l for extent/percent sizes."""
|
||||||
|
if "%" in size:
|
||||||
|
return ["lvextend", "-l", size, dev]
|
||||||
|
return ["lvextend", "-L", size, dev]
|
||||||
|
|
||||||
|
|
||||||
|
def btrfs_resize_size(size: str) -> str:
|
||||||
|
"""Map an LVM size spec to a btrfs filesystem resize argument."""
|
||||||
|
if "%" in size:
|
||||||
|
return "max"
|
||||||
|
return size
|
||||||
|
|
||||||
|
|
||||||
|
def validate_resize(dev: str, fstype: str, mount: Optional[str] = None) -> Optional[str]:
|
||||||
|
"""Validate resize is possible before mutating the LV. Returns mountpoint when needed."""
|
||||||
|
if fstype in ("ext2", "ext3", "ext4"):
|
||||||
|
return None
|
||||||
|
if fstype in ("xfs", "btrfs"):
|
||||||
|
return resolve_mount(dev, mount)
|
||||||
|
die(f"Unsupported filesystem: {fstype}")
|
||||||
|
|
||||||
|
|
||||||
|
def do_resize(dev: str, fstype: str, size: str, mount: Optional[str] = None, dryrun: bool = False) -> None:
|
||||||
|
"""Extend the LV with lvextend, then resize the filesystem accordingly."""
|
||||||
|
fs_mount = validate_resize(dev, fstype, mount)
|
||||||
|
|
||||||
|
run(lvextend_cmd(size, dev), dryrun=dryrun)
|
||||||
|
|
||||||
|
if fstype in ("ext2", "ext3", "ext4"):
|
||||||
|
run(["resize2fs", dev], dryrun=dryrun)
|
||||||
|
elif fstype == "xfs":
|
||||||
|
run(["xfs_growfs", fs_mount], dryrun=dryrun)
|
||||||
|
elif fstype == "btrfs":
|
||||||
|
run(["btrfs", "filesystem", "resize", btrfs_resize_size(size), fs_mount], dryrun=dryrun)
|
||||||
|
|
||||||
|
if not dryrun:
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Parse args, validate root, then resize LV and filesystem."""
|
||||||
|
parser = argparse.ArgumentParser(description="Resize an LVM logical volume and its filesystem")
|
||||||
|
parser.add_argument("-s", dest="size", required=True, help="New size (e.g. +10G, -5G, 20G, 100%FREE)")
|
||||||
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
group.add_argument("-m", dest="mount", metavar="MOUNTPOINT", help="Select LV by mountpoint")
|
||||||
|
group.add_argument("-l", dest="lvname", metavar="LVNAME", help="Select LV by name or path (e.g. vg0/data)")
|
||||||
|
parser.add_argument("-n", dest="dryrun", action="store_true", help="Dry-run – print what would be done without executing")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
die("This script must be run as root")
|
||||||
|
|
||||||
|
lvdev = resolve_lv(mount=args.mount, lvname=args.lvname)
|
||||||
|
fstype = detect_fs(lvdev)
|
||||||
|
do_resize(lvdev, fstype, args.size, mount=args.mount, dryrun=args.dryrun)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+17
-3
@@ -3,24 +3,28 @@ atc-docker01.dell-atc.lan
|
|||||||
atc-docker02.dell-atc.lan
|
atc-docker02.dell-atc.lan
|
||||||
atc-db01.dell-atc.lan
|
atc-db01.dell-atc.lan
|
||||||
atc-dns1.dell-atc.lan
|
atc-dns1.dell-atc.lan
|
||||||
# atc-db02.dell-atc.lan
|
|
||||||
atc-lake01.dell-atc.lan
|
atc-lake01.dell-atc.lan
|
||||||
atc-elastic01.dell-atc.lan
|
atc-elastic01.dell-atc.lan
|
||||||
atc-kafka01.dell-atc.lan
|
atc-kafka01.dell-atc.lan
|
||||||
atc-portal01.dell-atc.lan
|
atc-portal01.dell-atc.lan
|
||||||
atc-grafana.dell-atc.lan
|
atc-grafana.dell-atc.lan
|
||||||
# atc-test01.dell-atc.lan
|
|
||||||
atc-mgt01.dell-atc.lan
|
atc-mgt01.dell-atc.lan
|
||||||
atc-gpu-bart.dell-atc.lan
|
atc-gpu-bart.dell-atc.lan
|
||||||
atc-gpu-dev.dell-atc.lan
|
atc-gpu-dev.dell-atc.lan
|
||||||
atc-gpu-mo1.dell-atc.lan
|
atc-gpu-mo1.dell-atc.lan
|
||||||
atc-gpu-prod.dell-atc.lan
|
atc-gpu-prod.dell-atc.lan
|
||||||
atc-nas.dell-atc.lan
|
atc-nas.dell-atc.lan
|
||||||
|
dns1.dell-fde.lan
|
||||||
|
dns2.dell-fde.lan
|
||||||
|
db01.dell-fde.lan
|
||||||
|
haproxy.dell-fde.lan
|
||||||
|
|
||||||
[proxmox]
|
[proxmox]
|
||||||
dcp-pve01.dell-atc.lan
|
dcp-pve01.dell-atc.lan
|
||||||
DSS-GPU.dell-atc.lan
|
DSS-GPU.dell-atc.lan
|
||||||
pve2.dell-atc.lan
|
pve2.dell-atc.lan
|
||||||
|
proxmox01.dell-fde.lan
|
||||||
|
proxmox02.dell-fde.lan
|
||||||
|
|
||||||
[docker]
|
[docker]
|
||||||
atc-mgt01.dell-atc.lan
|
atc-mgt01.dell-atc.lan
|
||||||
@@ -32,7 +36,8 @@ atc-gpu-prod.dell-atc.lan
|
|||||||
atc-db02.dell-atc.lan
|
atc-db02.dell-atc.lan
|
||||||
atc-dns1.dell-atc.lan
|
atc-dns1.dell-atc.lan
|
||||||
atc-dns1.lan
|
atc-dns1.lan
|
||||||
# atc-dns2.dell-atc.lan
|
dns1.dell-fde.lan
|
||||||
|
dns2.dell-fde.lan
|
||||||
|
|
||||||
[rhel]
|
[rhel]
|
||||||
atc-docker01.dell-atc.lan
|
atc-docker01.dell-atc.lan
|
||||||
@@ -45,6 +50,10 @@ atc-gpu-prod.dell-atc.lan
|
|||||||
atc-nas.dell-atc.lan
|
atc-nas.dell-atc.lan
|
||||||
atc-backup.dell-atc.lan
|
atc-backup.dell-atc.lan
|
||||||
atc-db01.dell-atc.lan
|
atc-db01.dell-atc.lan
|
||||||
|
dns1.dell-fde.lan
|
||||||
|
haproxy.dell-fde.lan
|
||||||
|
dns2.dell-fde.lan
|
||||||
|
db01.dell-fde.lan
|
||||||
|
|
||||||
[borg_servers]
|
[borg_servers]
|
||||||
atc-backup.dell-atc.lan
|
atc-backup.dell-atc.lan
|
||||||
@@ -62,10 +71,15 @@ dcp-pve01.dell-atc.lan
|
|||||||
pve2.dell-atc.lan
|
pve2.dell-atc.lan
|
||||||
dss-gpu.dell-atc.lan
|
dss-gpu.dell-atc.lan
|
||||||
atc-dns1.lan
|
atc-dns1.lan
|
||||||
|
dns1.dell-fde.lan
|
||||||
|
haproxy.dell-fde.lan
|
||||||
|
dns2.dell-fde.lan
|
||||||
|
db01.dell-fde.lan
|
||||||
|
|
||||||
|
|
||||||
[postgres]
|
[postgres]
|
||||||
atc-db01.dell-atc.lan
|
atc-db01.dell-atc.lan
|
||||||
|
db01.dell-fde.lan
|
||||||
|
|
||||||
[nvidia]
|
[nvidia]
|
||||||
atc-gpu-bart.dell-atc.lan
|
atc-gpu-bart.dell-atc.lan
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
ansible.builtin.dnf:
|
ansible.builtin.dnf:
|
||||||
name: 'https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm'
|
name: 'https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm'
|
||||||
state: present
|
state: present
|
||||||
|
disable_gpg_check: true
|
||||||
|
|
||||||
- name: Postgres RPMs
|
- name: Postgres RPMs
|
||||||
ansible.builtin.dnf:
|
ansible.builtin.dnf:
|
||||||
@@ -15,6 +16,34 @@
|
|||||||
state: latest
|
state: latest
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
- name: Create Database Logical Volume
|
||||||
|
community.general.lvol:
|
||||||
|
vg: data
|
||||||
|
lv: pgdata
|
||||||
|
size: 32G
|
||||||
|
|
||||||
|
- name: Create Database Filesystem
|
||||||
|
community.general.filesystem:
|
||||||
|
fstype: xfs
|
||||||
|
dev: /dev/data/pgdata
|
||||||
|
|
||||||
|
- name: Mount filesystems
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: '/var/lib/pgsql'
|
||||||
|
src: '/dev/data/pgdata'
|
||||||
|
opts: 'noatime'
|
||||||
|
state: mounted
|
||||||
|
fstype: "xfs"
|
||||||
|
|
||||||
|
- name: Open Firewall port
|
||||||
|
firewalld:
|
||||||
|
# service: docker-registry
|
||||||
|
service: postgresql
|
||||||
|
permanent: true
|
||||||
|
state: enabled
|
||||||
|
immediate: yes
|
||||||
|
when: ansible_facts['os_family'] == "RedHat"
|
||||||
|
|
||||||
- name: PG Backup directory
|
- name: PG Backup directory
|
||||||
file:
|
file:
|
||||||
path: '/var/backups/postgres'
|
path: '/var/backups/postgres'
|
||||||
|
|||||||
Reference in New Issue
Block a user