From 158d3ffe7656df6deef5756f6258f3ea7701cd36 Mon Sep 17 00:00:00 2001 From: David Schroeder Date: Tue, 22 Sep 2026 22:01:53 -0500 Subject: [PATCH] search for orphan disks --- defaults.inc | 2 +- inc/orphan-disks.py | 16 +++++++++++++++- tests/test-orphan-disks.py | 7 +++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/defaults.inc b/defaults.inc index ef42129..3774bd8 100755 --- a/defaults.inc +++ b/defaults.inc @@ -3,7 +3,7 @@ action="${1:-}" FOLDER='/opt/idssys/ta-proxmenu' -VERS='2026.9.22-1' +VERS='2026.9.22-2' noupdate=' ' diff --git a/inc/orphan-disks.py b/inc/orphan-disks.py index 3b0eaab..bfc35e7 100644 --- a/inc/orphan-disks.py +++ b/inc/orphan-disks.py @@ -30,6 +30,8 @@ def enabled(value): def references(root, nodes, guests): """Read pmxcfs on this host, including offline guests and all config sections.""" refs, errors, seen, contents = {}, [], set(), {} + guest_names = {(guest['type'], str(guest['vmid'])): guest.get('name') + for guest in guests} for node in nodes: for kind, directory in [('qemu', 'qemu-server'), ('lxc', 'lxc')]: folder = root / node['node'] / directory @@ -48,6 +50,18 @@ def references(root, nodes, guests): continue contents[str(path)] = content seen.add((node['node'], kind, path.stem)) + name = guest_names.get((kind, path.stem)) + name_key = 'name' if kind == 'qemu' else 'hostname' + for line in content.splitlines(): + line = line.strip() + if line.startswith('['): + break + key, sep, value = line.partition(':') + if sep and key == name_key and value.strip(): + name = value.strip() + break + guest_id = f"{'VM' if kind == 'qemu' else 'LXC'} {path.stem}" + guest_label = f'{name} ({guest_id})' if name else f'{guest_id} (name unavailable)' section = 'current' for line in content.splitlines(): line = line.strip() @@ -61,7 +75,7 @@ def references(root, nodes, guests): continue for volume in VOLUME.findall(value): refs.setdefault(volume, []).append({ - 'node': node['node'], 'guest': f'{kind} {path.stem}', + 'node': node['node'], 'guest': guest_label, 'unused': bool(re.fullmatch(r'unused\d+', key)) and section == 'current', 'section': section, }) diff --git a/tests/test-orphan-disks.py b/tests/test-orphan-disks.py index ff4d72c..15cc907 100644 --- a/tests/test-orphan-disks.py +++ b/tests/test-orphan-disks.py @@ -20,13 +20,14 @@ class ReportTests(unittest.TestCase): for kind in ('qemu-server', 'lxc'): (self.root / node / kind).mkdir(parents=True) (self.root / 'a/qemu-server/100.conf').write_text( + 'name: app-server\n' 'scsi0: local:vm-100-disk-0,size=10G\n' 'unused0: local:vm-100-disk-1\n' 'efidisk0: shared:vm-100-efi\n' 'tpmstate0: shared:vm-100-tpm\n' '[snap]\nscsi0: local:vm-100-old\n' '[PENDING]\nscsi1: local:vm-100-pending\n') - (self.root / 'b/lxc/200.conf').write_text('rootfs: local:subvol-200-disk-0\nmp0: shared:subvol-200-disk-1,mp=/data\n') + (self.root / 'b/lxc/200.conf').write_text('hostname: fileserver\nrootfs: local:subvol-200-disk-0\nmp0: shared:subvol-200-disk-1,mp=/data\n') self.nodes = [{'node': n, 'status': 'online'} for n in ('a', 'b')] self.guests = [{'node': 'a', 'type': 'qemu', 'vmid': 100}, {'node': 'b', 'type': 'lxc', 'vmid': 200}] self.refs, self.errors, _ = disks.references(self.root, self.nodes, self.guests) @@ -44,7 +45,9 @@ class ReportTests(unittest.TestCase): def test_candidates_unused_replication_and_backing(self): self.assertEqual(self.result('local:vm-999-disk-0')[0], 'Orphan candidate') - self.assertEqual(self.result('local:vm-100-disk-1')[0], 'Unused disk') + self.assertEqual(self.result('local:vm-100-disk-1'), ('Unused disk', 'app-server (VM 100)')) + self.assertIn('a/app-server (VM 100)', self.result('local:vm-100-disk-0', node='b')[1]) + self.assertIn('b/fileserver (LXC 200)', self.result('local:subvol-200-disk-0')[1]) self.assertEqual(self.result('local:vm-100-disk-0', node='b')[0], 'Needs review') self.assertEqual(self.result('local:base-900-disk-0')[0], 'Needs review') self.assertEqual(self.result('local:999/base-999-disk-0.qcow2')[0], 'Needs review')