#!/usr/bin/env python3 # # Скрипт выводит информацию об имеющихся LXC и VMs в проксе. # Сортировка по статусу (running|stopped) и ID. # Выводится: id, тип, название, статус, выделенная память и выделенные диски # (c) Karel Wintersky, 2025-05-15 # import subprocess import re def run_command(cmd): """Выполняет команду и возвращает stdout.""" try: result = subprocess.run(cmd, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) return result.stdout except subprocess.CalledProcessError as e: print(f"Ошибка выполнения команды: {e}") return "" def get_lxc_list(): """Возвращает список ID LXC-контейнеров.""" output = run_command("pct list") return [line.split()[0] for line in output.split('\n')[1:] if line.strip()] def get_vm_list(): """Возвращает список ID виртуальных машин.""" output = run_command("qm list") return [line.split()[0] for line in output.split('\n')[1:] if line.strip()] def parse_size(size_str): """Конвертирует строку размера в мегабайты.""" if not size_str: return 0 size_str = size_str.upper() if 'G' in size_str: return int(float(size_str.replace('G', '')) * 1024) return int(float(size_str.replace('M', ''))) def format_size(mb): """Форматирует размер в удобный вид.""" if mb >= 1024: return f"{round(mb/1024)}G" return f"{mb}M" def get_lxc_data(lxc_id): """Собирает данные о LXC-контейнере.""" config = run_command(f"pct config {lxc_id}") status = run_command(f"pct status {lxc_id}") # Парсим данные с точным regex для size name = re.search(r"hostname:\s*(\S+)", config) memory = re.search(r"memory:\s*(\d+)", config) disks = re.findall(r"size=(\d+\w+)", config) total_disk = sum(parse_size(size) for size in disks) return { "id": lxc_id, "type": "LXC", "name": name.group(1) if name else "N/A", "status": "running" if "running" in status else "stopped", "memory": f"{memory.group(1)}M" if memory else "0M", "disks": format_size(total_disk), "sort_status": 0 if "running" in status else 1 # Для сортировки } def get_vm_data(vm_id): """Собирает данные о виртуальной машине.""" config = run_command(f"qm config {vm_id}") status = run_command(f"qm status {vm_id}") # Парсим данные с точным regex для size (исключая scsi-hw) name = re.search(r"name:\s*(\S+)", config) memory = re.search(r"memory:\s*(\d+)", config) disks = re.findall(r"scsi\d+:.*?size=(\d+\w+)", config) total_disk = sum(parse_size(size) for size in disks if "scsi-hw" not in size) return { "id": vm_id, "type": "KVM", "name": name.group(1) if name else "N/A", "status": "running" if "running" in status else "stopped", "memory": f"{memory.group(1)}M" if memory else "0M", "disks": format_size(total_disk), "sort_status": 0 if "running" in status else 1 # Для сортировки } def print_progress(current, total, prefix=""): """Простой прогресс-бар в консоли.""" bar_length = 30 progress = float(current) / total block = int(round(bar_length * progress)) text = f"{prefix} [{'#' * block}{'-' * (bar_length - block)}] {current}/{total}" print(text, end="\r") if current == total: print() def main(): print("Сбор данных о виртуальных машинах Proxmox...\n") # Получаем списки lxc_ids = get_lxc_list() vm_ids = get_vm_list() total = len(lxc_ids) + len(vm_ids) # Собираем данные all_data = [] # Обрабатываем LXC for i, lxc_id in enumerate(lxc_ids, 1): all_data.append(get_lxc_data(lxc_id)) print_progress(i, len(lxc_ids), "LXC ") # Обрабатываем KVM for i, vm_id in enumerate(vm_ids, 1): all_data.append(get_vm_data(vm_id)) print_progress(i, len(vm_ids), "KVM ") # Сортируем сначала по статусу, потом по ID all_data.sort(key=lambda x: (x["sort_status"], int(x["id"]))) # Вычисляем ширину столбца Name max_name_len = max(len(item["name"]) for item in all_data) + 2 # Вычисляем итоговые значения total_vms = sum(1 for item in all_data if item["type"] == "KVM") total_lxc = sum(1 for item in all_data if item["type"] == "LXC") total_disks = sum(parse_size(item["disks"].replace('G', '000').replace('M', '')) for item in all_data) total_disks_formatted = format_size(total_disks) # Выводим результаты print("\nРезультаты:") header = f"{'ID':<5} | {'Type':<5} | {'Name':<{max_name_len}} | {'Status':<8} | {'Memory':<8} | {'Disks':<10}" separator = "-" * len(header.expandtabs()) print(separator) print(header) print(separator) for item in all_data: print(f"{item['id']:<5} | {item['type']:<5} | {item['name']:<{max_name_len}} | {item['status']:<8} | {item['memory']:<8} | {item['disks']:<10}") print(separator) # Добавляем итоговую строку print(f"\nИТОГО: ВМ: {total_vms}, Контейнеры: {total_lxc}, Всего дисков: {total_disks_formatted}") if __name__ == "__main__": main()