KarelWintersky gist felülvizsgálása . Revízióhoz ugrás
Nincsenek változtatások
KarelWintersky gist felülvizsgálása . Revízióhoz ugrás
Nincsenek változtatások
KarelWintersky gist felülvizsgálása . Revízióhoz ugrás
1 file changed, 152 insertions
pve_summary.py(fájl létrehozva)
@@ -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() |
Újabb
Régebbi