feat: реализована новая логика получения machine_uid для QEMU и LXC

- Для QEMU VM: получение UUID из конфигурации (uuid, smbios1 uuid) с fallback на machine_id
- Для LXC контейнеров: получение machine-id через pct exec (запущенные) или чтение из rootfs (остановленные)
- Добавлены функции getLXCMachineIDViaExec и getLXCMachineIDFromRootfs
- Улучшена логика fallback для случаев когда UUID недоступен
- Все machine_uid генерируются как SHA256[:16] для консистентности
- Протестировано на реальных VM и контейнерах
This commit is contained in:
Sergey Antropoff 2025-09-15 13:28:01 +03:00
parent 96b841d6c8
commit b23d724bea

View File

@ -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,15 +557,125 @@ 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)
}
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{}
func getString(data map[string]interface{}, key string) string {
if val, ok := data[key]; ok {