Последняя активность 1749140288

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

Версия da25734d8fe8640ffa30c3c041f73e1d517c3089

pve_summary.py Исходник
1#!/usr/bin/env python3
2#
3# Скрипт выводит информацию об имеющихся LXC и VMs в проксе.
4# Сортировка по статусу (running|stopped) и ID.
5# Выводится: id, тип, название, статус, выделенная память и выделенные диски
6# (c) Karel Wintersky, 2025-05-15
7#
8import subprocess
9import re
10
11def 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
22def 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
27def 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
32def 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
41def format_size(mb):
42 """Форматирует размер в удобный вид."""
43 if mb >= 1024:
44 return f"{round(mb/1024)}G"
45 return f"{mb}M"
46
47def 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
69def 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
91def 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
101def 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
151if __name__ == "__main__":
152 main()
153