feat: espace LVM — thin pool data_percent + ext4 offline via tune2fs

- lvs query enrichi : lv_dm_path, data_percent, lv_attr
- thin_pool_used_percent sur les thin pools (ex: Proxmox pve/data)
- LV ext4 non montés lus via tune2fs sur /dev/mapper path
- _format_lv() centralise la construction des entrées LV

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gilles Soulier
2026-05-28 20:54:32 +02:00
parent 325b5dc281
commit 3406edbab1
+38 -8
View File
@@ -314,7 +314,7 @@ def get_lvm_map():
return {} return {}
lvs_by_vg = {} lvs_by_vg = {}
lvs_out = run(["lvs", "--noheadings", "--reportformat", "json", lvs_out = run(["lvs", "--noheadings", "--reportformat", "json",
"-o", "lv_name,vg_name,lv_size,lv_path"]) "-o", "lv_name,vg_name,lv_size,lv_path,lv_dm_path,data_percent,lv_attr"])
if lvs_out: if lvs_out:
try: try:
for lv in json.loads(lvs_out)["report"][0]["lv"]: for lv in json.loads(lvs_out)["report"][0]["lv"]:
@@ -328,17 +328,47 @@ def get_lvm_map():
if pv_name and vg_name: if pv_name and vg_name:
result[pv_name] = { result[pv_name] = {
"vg_name": vg_name, "vg_name": vg_name,
"logical_volumes": [ "logical_volumes": [_format_lv(lv)
{"lv_name": lv.get("lv_name", ""), for lv in lvs_by_vg.get(vg_name, [])],
"size_human": _lv_size_human(lv.get("lv_size", "")),
"used_human": None, "free_human": None,
"used_percent": None, "fstype": None, "mountpoint": None}
for lv in lvs_by_vg.get(vg_name, [])
],
} }
dprint(f"LVM : {len(result)} volume(s) physique(s)") dprint(f"LVM : {len(result)} volume(s) physique(s)")
return result return result
def _format_lv(lv):
name = lv.get("lv_name", "")
dm_path = lv.get("lv_dm_path", "") or lv.get("lv_path", "") or ""
attr = lv.get("lv_attr", "")
entry = {
"lv_name": name,
"size_human": _lv_size_human(lv.get("lv_size", "")),
"used_human": None, "free_human": None, "used_percent": None,
"fstype": None, "mountpoint": None,
"thin_pool_used_percent": None,
}
# Thin pool : data_percent indique le remplissage du pool
dp = lv.get("data_percent", "")
if dp and dp.strip() not in ("", "0.00"):
try:
entry["thin_pool_used_percent"] = round(float(dp), 1)
dprint(f" LV {name} thin pool : {entry['thin_pool_used_percent']}% utilise")
except ValueError:
pass
# LV ext4 non monté → lecture offline via tune2fs
# (attr[0] 'V'/'v' = thin vol, 't'/'T' = thin pool; les deux ont data_percent)
is_pool = attr[:1].lower() in ("t",)
if not is_pool and dm_path:
space = _ext4_space(dm_path)
if space:
entry["used_human"] = bytes_human(space["used_bytes"])
entry["free_human"] = bytes_human(space["free_bytes"])
entry["used_percent"] = space["used_percent"]
dprint(f" LV {name} offline : {entry['used_human']} utilise ({entry['used_percent']}%)")
return entry
# -- /home users -------------------------------------------------------------- # -- /home users --------------------------------------------------------------