[IMP] script todo: QEMU « Lister les VM » — option infos avancées

Après « sudo virsh list --all », le menu propose désormais (o/N) un
tableau détaillé par VM : état, vCPU, RAM allouée (Max memory), taille
disque virtuelle et réelle (qemu-img info -U, lit même VM allumée), plus
l'espace total/libre/utilisé du stockage des images (shutil.disk_usage).

- _qemu_list_vms(ask_advanced=False) : seul le menu [4] prompte ;
  les appels internes (IP, console, resize, delete) restent inchangés.
- Helpers _qemu_dominfo (vcpu + Max memory) et _qemu_disk_sizes
  (virtuel + réel, -U). Validé en réel : 8 vCPU / 8.0G / 30.0G / 2.5G.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mathieu Benoit 2026-07-29 11:01:00 +00:00
parent edf066bb82
commit 4d2b7cea00
2 changed files with 108 additions and 2 deletions

View file

@ -1082,7 +1082,7 @@ class TODO:
elif status == "3":
self._qemu_download_image()
elif status == "4":
self._qemu_list_vms()
self._qemu_list_vms(ask_advanced=True)
elif status == "5":
self._qemu_show_ip()
elif status == "6":
@ -1130,10 +1130,100 @@ class TODO:
print(f"{t('Will execute:')} {cmd}")
self.execute.exec_command_live(cmd, source_erplibre=False)
def _qemu_list_vms(self):
def _qemu_list_vms(self, ask_advanced=False):
cmd = "sudo virsh list --all"
print(f"{t('Will execute:')} {cmd}")
self.execute.exec_command_live(cmd, source_erplibre=False)
if ask_advanced and self._is_yes(
input(t("Show advanced info (vCPU, RAM, disk)? (y/N): "))
):
self._qemu_list_vms_advanced()
@staticmethod
def _qemu_dominfo(name):
"""(vcpus, max_mem_kib) via « virsh dominfo », ou (0, 0)."""
try:
res = subprocess.run(
["sudo", "virsh", "dominfo", name],
capture_output=True,
text=True,
timeout=15,
)
except (OSError, subprocess.SubprocessError):
return 0, 0
vcpus, mem = 0, 0
for line in res.stdout.splitlines():
if line.startswith("CPU(s):"):
try:
vcpus = int(line.split(":", 1)[1].strip())
except ValueError:
pass
elif line.startswith("Max memory:"):
# « 4194304 KiB »
try:
mem = int(line.split(":", 1)[1].split()[0])
except (ValueError, IndexError):
pass
return vcpus, mem
@staticmethod
def _qemu_disk_sizes(disk):
"""(taille virtuelle, taille réelle sur disque) en octets, via
qemu-img info -U (lit même VM allumée). (0, 0) si échec."""
try:
res = subprocess.run(
["sudo", "qemu-img", "info", "-U", "--output=json", disk],
capture_output=True,
text=True,
timeout=20,
)
data = json.loads(res.stdout)
return (
int(data.get("virtual-size", 0)),
int(data.get("actual-size", 0)),
)
except (OSError, subprocess.SubprocessError, ValueError):
return 0, 0
def _qemu_list_vms_advanced(self):
"""Tableau détaillé par VM : état, vCPU, RAM allouée, disque (virtuel
+ réel), plus l'espace total disponible du stockage des images."""
names = self._qemu_list_domains()
if not names:
print(f"\n{t('No VM found.')}")
return
g = 1 << 30
header = (
f"\n{'VM':<28} {'État':<10} {'vCPU':>4} {'RAM':>8} "
f"{'Disque':>9} {'Réel':>9}"
)
print(header)
print("" * len(header.strip()))
disk_dirs = set()
for name in names:
state = self._qemu_domstate(name) or "?"
vcpus, mem_kib = self._qemu_dominfo(name)
disk = self._qemu_main_disk(name)
virt, actual = self._qemu_disk_sizes(disk) if disk else (0, 0)
if disk:
disk_dirs.add(os.path.dirname(disk))
ram_g = (mem_kib * 1024) / g if mem_kib else 0
print(
f"{name:<28.28} {state:<10.10} {vcpus:>4} "
f"{ram_g:>7.1f}G {virt / g:>8.1f}G {actual / g:>8.1f}G"
)
# Espace total disponible sur le(s) stockage(s) des disques.
for d in sorted(disk_dirs) or ["/var/lib/libvirt/images"]:
try:
usage = shutil.disk_usage(d)
except OSError:
continue
print(
f"\n{t('Storage')} {d} : "
f"{usage.free / g:.1f}G {t('free')} / "
f"{usage.total / g:.1f}G {t('total')} "
f"({usage.used / g:.1f}G {t('used')})"
)
def _qemu_show_ip(self):
# Affiche d'abord les VM (avec leur ID) pour que l'utilisateur sache

View file

@ -1420,6 +1420,22 @@ TRANSLATIONS = {
"fr": "Impossible de lire la taille actuelle du disque ; abandon.",
"en": "Could not read current disk size; aborting.",
},
"Show advanced info (vCPU, RAM, disk)? (y/N): ": {
"fr": "Afficher les infos avancées (vCPU, RAM, disque) ? (o/N) : ",
"en": "Show advanced info (vCPU, RAM, disk)? (y/N): ",
},
"Storage": {
"fr": "Stockage",
"en": "Storage",
},
"total": {
"fr": "au total",
"en": "total",
},
"used": {
"fr": "utilisés",
"en": "used",
},
"Enter +NG to grow, -NG to shrink, or NG for a target size "
"(e.g. +20G, -10G, 60G).": {
"fr": "Entrez +NG pour agrandir, -NG pour réduire, ou NG pour une "