Zuletzt aktiv 1749140288

Скрипт выводит информацию об имеющихся LXC и VMs в проксе. Запускать с хоста.

KarelWintersky hat die Gist bearbeitet 1749140288. Zu Änderung gehen

Keine Änderungen

KarelWintersky hat die Gist bearbeitet 1748429349. Zu Änderung gehen

Keine Änderungen

KarelWintersky hat die Gist bearbeitet 1748429325. Zu Änderung gehen

1 file changed, 152 insertions

pve_summary.py(Datei erstellt)

@@ -0,0 +1,152 @@
1 + #!/usr/bin/env python3
2 + #
3 + # Скрипт выводит информацию об имеющихся LXC и VMs в проксе.
4 + # Сортировка по статусу (running|stopped) и ID.
5 + # Выводится: id, тип, название, статус, выделенная память и выделенные диски
6 + # (c) Karel Wintersky, 2025-05-15
7 + #
8 + import subprocess
9 + import re
10 +
11 + def run_command(cmd):
12 + """Выполняет команду и возвращает stdout."""
13 + try:
14 + result = subprocess.run(cmd, shell=True, check=True,
15 + stdout=subprocess.PIPE, stderr=subprocess.PIPE,
16 + text=True)
17 + return result.stdout
18 + except subprocess.CalledProcessError as e:
19 + print(f"Ошибка выполнения команды: {e}")
20 + return ""
21 +
22 + def get_lxc_list():
23 + """Возвращает список ID LXC-контейнеров."""
24 + output = run_command("pct list")
25 + return [line.split()[0] for line in output.split('\n')[1:] if line.strip()]
26 +
27 + def get_vm_list():
28 + """Возвращает список ID виртуальных машин."""
29 + output = run_command("qm list")
30 + return [line.split()[0] for line in output.split('\n')[1:] if line.strip()]
31 +
32 + def parse_size(size_str):
33 + """Конвертирует строку размера в мегабайты."""
34 + if not size_str:
35 + return 0
36 + size_str = size_str.upper()
37 + if 'G' in size_str:
38 + return int(float(size_str.replace('G', '')) * 1024)
39 + return int(float(size_str.replace('M', '')))
40 +
41 + def format_size(mb):
42 + """Форматирует размер в удобный вид."""
43 + if mb >= 1024:
44 + return f"{round(mb/1024)}G"
45 + return f"{mb}M"
46 +
47 + def get_lxc_data(lxc_id):
48 + """Собирает данные о LXC-контейнере."""
49 + config = run_command(f"pct config {lxc_id}")
50 + status = run_command(f"pct status {lxc_id}")
51 +
52 + # Парсим данные с точным regex для size
53 + name = re.search(r"hostname:\s*(\S+)", config)
54 + memory = re.search(r"memory:\s*(\d+)", config)
55 + disks = re.findall(r"size=(\d+\w+)", config)
56 +
57 + total_disk = sum(parse_size(size) for size in disks)
58 +
59 + return {
60 + "id": lxc_id,
61 + "type": "LXC",
62 + "name": name.group(1) if name else "N/A",
63 + "status": "running" if "running" in status else "stopped",
64 + "memory": f"{memory.group(1)}M" if memory else "0M",
65 + "disks": format_size(total_disk),
66 + "sort_status": 0 if "running" in status else 1 # Для сортировки
67 + }
68 +
69 + def get_vm_data(vm_id):
70 + """Собирает данные о виртуальной машине."""
71 + config = run_command(f"qm config {vm_id}")
72 + status = run_command(f"qm status {vm_id}")
73 +
74 + # Парсим данные с точным regex для size (исключая scsi-hw)
75 + name = re.search(r"name:\s*(\S+)", config)
76 + memory = re.search(r"memory:\s*(\d+)", config)
77 + disks = re.findall(r"scsi\d+:.*?size=(\d+\w+)", config)
78 +
79 + total_disk = sum(parse_size(size) for size in disks if "scsi-hw" not in size)
80 +
81 + return {
82 + "id": vm_id,
83 + "type": "KVM",
84 + "name": name.group(1) if name else "N/A",
85 + "status": "running" if "running" in status else "stopped",
86 + "memory": f"{memory.group(1)}M" if memory else "0M",
87 + "disks": format_size(total_disk),
88 + "sort_status": 0 if "running" in status else 1 # Для сортировки
89 + }
90 +
91 + def print_progress(current, total, prefix=""):
92 + """Простой прогресс-бар в консоли."""
93 + bar_length = 30
94 + progress = float(current) / total
95 + block = int(round(bar_length * progress))
96 + text = f"{prefix} [{'#' * block}{'-' * (bar_length - block)}] {current}/{total}"
97 + print(text, end="\r")
98 + if current == total:
99 + print()
100 +
101 + def main():
102 + print("Сбор данных о виртуальных машинах Proxmox...\n")
103 +
104 + # Получаем списки
105 + lxc_ids = get_lxc_list()
106 + vm_ids = get_vm_list()
107 + total = len(lxc_ids) + len(vm_ids)
108 +
109 + # Собираем данные
110 + all_data = []
111 +
112 + # Обрабатываем LXC
113 + for i, lxc_id in enumerate(lxc_ids, 1):
114 + all_data.append(get_lxc_data(lxc_id))
115 + print_progress(i, len(lxc_ids), "LXC ")
116 +
117 + # Обрабатываем KVM
118 + for i, vm_id in enumerate(vm_ids, 1):
119 + all_data.append(get_vm_data(vm_id))
120 + print_progress(i, len(vm_ids), "KVM ")
121 +
122 + # Сортируем сначала по статусу, потом по ID
123 + all_data.sort(key=lambda x: (x["sort_status"], int(x["id"])))
124 +
125 + # Вычисляем ширину столбца Name
126 + max_name_len = max(len(item["name"]) for item in all_data) + 2
127 +
128 + # Вычисляем итоговые значения
129 + total_vms = sum(1 for item in all_data if item["type"] == "KVM")
130 + total_lxc = sum(1 for item in all_data if item["type"] == "LXC")
131 + total_disks = sum(parse_size(item["disks"].replace('G', '000').replace('M', '')) for item in all_data)
132 + total_disks_formatted = format_size(total_disks)
133 +
134 +
135 + # Выводим результаты
136 + print("\nРезультаты:")
137 + header = f"{'ID':<5} | {'Type':<5} | {'Name':<{max_name_len}} | {'Status':<8} | {'Memory':<8} | {'Disks':<10}"
138 + separator = "-" * len(header.expandtabs())
139 + print(separator)
140 + print(header)
141 + print(separator)
142 +
143 + for item in all_data:
144 + print(f"{item['id']:<5} | {item['type']:<5} | {item['name']:<{max_name_len}} | {item['status']:<8} | {item['memory']:<8} | {item['disks']:<10}")
145 +
146 + print(separator)
147 +
148 + # Добавляем итоговую строку
149 + print(f"\nИТОГО: ВМ: {total_vms}, Контейнеры: {total_lxc}, Всего дисков: {total_disks_formatted}")
150 +
151 + if __name__ == "__main__":
152 + main()
Neuer Älter