diff --git a/src/collectors/proxvms/proxvms_linux.go b/src/collectors/proxvms/proxvms_linux.go index 973918b..26925fd 100644 --- a/src/collectors/proxvms/proxvms_linux.go +++ b/src/collectors/proxvms/proxvms_linux.go @@ -349,7 +349,7 @@ func processQEMUVM(vm VM, nodeName, clusterUID, nodeUID string) map[string]any { // Парсим JSON из поля description if description, ok := config["description"].(string); ok { if parsedDesc := parseDescriptionJSON(description); parsedDesc != nil { - config["description_parsed"] = parsedDesc + config["description"] = parsedDesc // Удаляем оригинальное поле description, если парсинг прошел успешно delete(config, "description") } @@ -442,7 +442,7 @@ func processLXCContainer(ct VM, nodeName, clusterUID, nodeUID string) map[string // Парсим JSON из поля description if description, ok := config["description"].(string); ok { if parsedDesc := parseDescriptionJSON(description); parsedDesc != nil { - config["description_parsed"] = parsedDesc + config["description"] = parsedDesc // Удаляем оригинальное поле description, если парсинг прошел успешно delete(config, "description") } @@ -501,6 +501,24 @@ func enrichConfigWithParsedData(config map[string]interface{}) { // Удаляем оригинальные mpX поля, если они были распарсены removeParsedFields(config, "mp") } + + // Парсим nameserver + if nameserver := parseNameserver(config); nameserver != nil { + config["nameserver"] = nameserver + delete(config, "nameserver") + } + + // Парсим searchdomain + if searchdomain := parseSearchdomain(config); searchdomain != nil { + config["searchdomain"] = searchdomain + delete(config, "searchdomain") + } + + // Парсим features + if features := parseFeatures(config); features != nil { + config["features"] = features + delete(config, "features") + } } // removeParsedFields удаляет оригинальные поля, которые были успешно распарсены @@ -784,9 +802,9 @@ func parseSCSIConfig(value interface{}) map[string]interface{} { val := strings.TrimSpace(part[equalIndex+1:]) if key != "" { - // Переименовываем size в pool_disk_size для SCSI устройств + // Переименовываем size в pool_disk_size_gb для SCSI устройств и конвертируем единицы if key == "size" { - result["pool_disk_size"] = val + result["pool_disk_size_gb"] = convertToGB(val) } else { result[key] = val } @@ -981,6 +999,131 @@ func parseMPDevices(config map[string]interface{}) map[string]interface{} { return mp } +// parseNameserver парсит nameserver в формат dns1: ip1, dns2: ip2 +func parseNameserver(config map[string]interface{}) map[string]interface{} { + if nameserver, ok := config["nameserver"].(string); ok { + if nameserver == "" { + return nil + } + + // Разбиваем по пробелам + servers := strings.Fields(nameserver) + result := make(map[string]interface{}) + + for i, server := range servers { + key := fmt.Sprintf("dns%d", i+1) + result[key] = server + } + + return result + } + return nil +} + +// parseSearchdomain парсит searchdomain в массив доменов +func parseSearchdomain(config map[string]interface{}) map[string]interface{} { + if searchdomain, ok := config["searchdomain"].(string); ok { + if searchdomain == "" { + return nil + } + + // Разбиваем по пробелам + domains := strings.Fields(searchdomain) + result := make(map[string]interface{}) + + for i, domain := range domains { + key := fmt.Sprintf("domain%d", i+1) + result[key] = domain + } + + return result + } + return nil +} + +// parseFeatures парсит features в структурированный формат +func parseFeatures(config map[string]interface{}) map[string]interface{} { + if features, ok := config["features"].(string); ok { + if features == "" { + return nil + } + + // Разбиваем по запятым + parts := strings.Split(features, ",") + result := make(map[string]interface{}) + + for _, part := range parts { + part = strings.TrimSpace(part) + if part == "" { + continue + } + + // Ищем знак равенства + equalIndex := strings.Index(part, "=") + if equalIndex == -1 { + // Если нет знака равенства, добавляем как ключ со значением true + result[part] = true + } else { + key := strings.TrimSpace(part[:equalIndex]) + val := strings.TrimSpace(part[equalIndex+1:]) + result[key] = val + } + } + + return result + } + return nil +} + +// convertToGB конвертирует размер в гигабайты +func convertToGB(sizeStr string) string { + if sizeStr == "" { + return "" + } + + // Убираем пробелы + sizeStr = strings.TrimSpace(sizeStr) + + // Определяем единицу измерения + var multiplier float64 + var size float64 + var err error + + if strings.HasSuffix(strings.ToUpper(sizeStr), "T") { + // Терабайты + sizeStr = strings.TrimSuffix(strings.ToUpper(sizeStr), "T") + multiplier = 1024 // 1TB = 1024GB + } else if strings.HasSuffix(strings.ToUpper(sizeStr), "G") { + // Гигабайты + sizeStr = strings.TrimSuffix(strings.ToUpper(sizeStr), "G") + multiplier = 1 + } else if strings.HasSuffix(strings.ToUpper(sizeStr), "M") { + // Мегабайты + sizeStr = strings.TrimSuffix(strings.ToUpper(sizeStr), "M") + multiplier = 1.0 / 1024 // 1MB = 1/1024GB + } else { + // По умолчанию считаем гигабайтами + multiplier = 1 + } + + size, err = strconv.ParseFloat(sizeStr, 64) + if err != nil { + return sizeStr // Возвращаем оригинальную строку, если не удалось распарсить + } + + // Конвертируем в гигабайты + result := size * multiplier + + // Форматируем результат + if result == float64(int64(result)) { + // Если целое число, возвращаем без дробной части + return fmt.Sprintf("%.0fG", result) + } else { + // Если дробное, возвращаем с 2 знаками после запятой + return fmt.Sprintf("%.2fG", result) + } +} + // debugLog выводит отладочную информацию (только если установлена переменная окружения DEBUG=1) func debugLog(format string, args ...interface{}) { if os.Getenv("DEBUG") == "1" {