From b23d724beaaad16723afe5f89a98d86b3c238d7a Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Mon, 15 Sep 2025 13:28:01 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=BD=D0=BE=D0=B2=D0=B0=D1=8F?= =?UTF-8?q?=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20machine=5Fuid=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20QEMU=20=D0=B8=20LXC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Для QEMU VM: получение UUID из конфигурации (uuid, smbios1 uuid) с fallback на machine_id - Для LXC контейнеров: получение machine-id через pct exec (запущенные) или чтение из rootfs (остановленные) - Добавлены функции getLXCMachineIDViaExec и getLXCMachineIDFromRootfs - Улучшена логика fallback для случаев когда UUID недоступен - Все machine_uid генерируются как SHA256[:16] для консистентности - Протестировано на реальных VM и контейнерах --- src/collectors/proxvms/proxvms_linux.go | 144 ++++++++++++++++++++---- 1 file changed, 119 insertions(+), 25 deletions(-) diff --git a/src/collectors/proxvms/proxvms_linux.go b/src/collectors/proxvms/proxvms_linux.go index 26925fd..60891e3 100644 --- a/src/collectors/proxvms/proxvms_linux.go +++ b/src/collectors/proxvms/proxvms_linux.go @@ -279,24 +279,8 @@ func processQEMUVM(vm VM, nodeName, clusterUID, nodeUID string) map[string]any { machineID = "unknown" } - // Для QEMU VM пытаемся получить UUID из smbios1 - var machineUID string - if smbiosUUID := getString(config, "smbios1"); smbiosUUID != "" { - // Извлекаем UUID из строки типа "uuid=5325693e-271b-47ed-95c2-3a2cf42f4886" - if strings.Contains(smbiosUUID, "uuid=") { - parts := strings.Split(smbiosUUID, "uuid=") - if len(parts) > 1 { - uuid := strings.TrimSpace(parts[1]) - machineUID = generateMachineUID(uuid) - } else { - machineUID = generateMachineUID(machineID) - } - } else { - machineUID = generateMachineUID(machineID) - } - } else { - machineUID = generateMachineUID(machineID) - } + // Генерируем machine_uid + machineUID := generateMachineUID("QEMU", vmidStr, config) // Создаем структуру VM vmData := map[string]any{ @@ -396,7 +380,7 @@ func processLXCContainer(ct VM, nodeName, clusterUID, nodeUID string) map[string } // Генерируем machine_uid - machineUID := generateMachineUID(machineID) + machineUID := generateMachineUID("LXC", vmidStr, config) // Создаем структуру контейнера ctData := map[string]any{ @@ -573,13 +557,123 @@ func generateNodeUID(clusterUUID, nodeID string) string { return hex.EncodeToString(hash[:])[:16] } -// generateMachineUID создает уникальный ID машины на основе machine_id -func generateMachineUID(machineID string) string { - if machineID == "" { - machineID = "unknown-machine-id" +// generateMachineUID генерирует уникальный ID машины для QEMU и LXC +func generateMachineUID(vmType, vmid string, config map[string]interface{}) string { + if vmType == "QEMU" { + return generateQEMUMachineUID(vmid, config) + } else if vmType == "LXC" { + return generateLXCMachineUID(vmid, config) } - hash := sha256.Sum256([]byte(machineID)) - return hex.EncodeToString(hash[:])[:16] + return "" +} + +// generateQEMUMachineUID генерирует machine_uid для QEMU VM +func generateQEMUMachineUID(vmid string, config map[string]interface{}) string { + // 1. Пытаемся получить uuid из конфигурации + if uuid, ok := config["uuid"].(string); ok && uuid != "" { + hash := sha256.Sum256([]byte(uuid)) + return hex.EncodeToString(hash[:])[:16] + } + + // 2. Пытаемся получить smbios1 uuid из конфигурации + if smbios1, ok := config["smbios1"].(string); ok && smbios1 != "" { + // Парсим smbios1 для поиска uuid + if parsed := parseKeyValueString(smbios1); parsed != nil { + if uuid, exists := parsed["uuid"]; exists { + if uuidStr, ok := uuid.(string); ok && uuidStr != "" { + hash := sha256.Sum256([]byte(uuidStr)) + return hex.EncodeToString(hash[:])[:16] + } + } + } + } + + // 3. Fallback: используем machine_id + if machineID, ok := config["machine_id"].(string); ok && machineID != "" { + hash := sha256.Sum256([]byte(machineID)) + return hex.EncodeToString(hash[:])[:16] + } + + return "" +} + +// generateLXCMachineUID генерирует machine_uid для LXC контейнера +func generateLXCMachineUID(vmid string, config map[string]interface{}) string { + // 1. Пытаемся получить machine-id из конфигурации + if machineID, ok := config["machine_id"].(string); ok && machineID != "" { + hash := sha256.Sum256([]byte(machineID)) + return hex.EncodeToString(hash[:])[:16] + } + + // 2. Пытаемся получить machine-id через pct exec (если контейнер запущен) + if machineID := getLXCMachineIDViaExec(vmid); machineID != "" { + hash := sha256.Sum256([]byte(machineID)) + return hex.EncodeToString(hash[:])[:16] + } + + // 3. Пытаемся прочитать machine-id из rootfs (если контейнер остановлен) + if machineID := getLXCMachineIDFromRootfs(vmid); machineID != "" { + hash := sha256.Sum256([]byte(machineID)) + return hex.EncodeToString(hash[:])[:16] + } + + return "" +} + +// getLXCMachineIDViaExec получает machine-id через pct exec (для запущенных контейнеров) +func getLXCMachineIDViaExec(vmid string) string { + cmd := exec.Command("pct", "exec", vmid, "--", "cat", "/etc/machine-id") + output, err := cmd.Output() + if err != nil { + debugLog("Failed to get machine-id via pct exec for LXC %s: %v", vmid, err) + return "" + } + + machineID := strings.TrimSpace(string(output)) + if machineID == "" { + debugLog("Empty machine-id from pct exec for LXC %s", vmid) + return "" + } + + debugLog("Successfully got machine-id via pct exec for LXC %s: %s", vmid, machineID) + return machineID +} + +// getLXCMachineIDFromRootfs читает machine-id из rootfs (для остановленных контейнеров) +func getLXCMachineIDFromRootfs(vmid string) string { + // Пробуем разные возможные пути + paths := []string{ + fmt.Sprintf("/var/lib/lxc/%s/rootfs/etc/machine-id", vmid), + fmt.Sprintf("/rpool/data/subvol-%s-disk-0/etc/machine-id", vmid), + fmt.Sprintf("/var/lib/lxc/%s/rootfs/var/lib/dbus/machine-id", vmid), + } + + for _, path := range paths { + if machineID := readMachineIDFromFile(path); machineID != "" { + debugLog("Successfully read machine-id from %s for LXC %s: %s", path, vmid, machineID) + return machineID + } + } + + debugLog("Failed to read machine-id from rootfs for LXC %s", vmid) + return "" +} + +// readMachineIDFromFile читает machine-id из файла +func readMachineIDFromFile(path string) string { + data, err := os.ReadFile(path) + if err != nil { + debugLog("Failed to read file %s: %v", path, err) + return "" + } + + machineID := strings.TrimSpace(string(data)) + if machineID == "" { + debugLog("Empty machine-id in file %s", path) + return "" + } + + return machineID } // getString извлекает строку из map[string]interface{}