package main import "log" // validateAndFixTemplate проверяет критичные настройки шаблона и исправляет их если нужно. // Вызывается после loadTemplate чтобы гарантировать корректность шаблона независимо от его источника. func validateAndFixTemplate(t *DashboardTemplate) { fixed := 0 // --- vl_interval для панелей с агрегацией --- vlIntervalPanels := []string{"rps", "rps_multi", "status_codes", "response_time", "traffic_combined", "traffic_combined_total"} for _, key := range vlIntervalPanels { p, ok := t.Panels[key] if !ok { continue } pm, ok := p.(map[string]interface{}) if !ok { continue } if v, _ := pm["vl_interval"].(string); v == "" { pm["vl_interval"] = "1m" log.Printf("[template-validator] Fixed: panels.%s.vl_interval = \"1m\"", key) fixed++ } } // --- spanNulls для status_codes --- if p, ok := t.Panels["status_codes"]; ok { if pm, ok := p.(map[string]interface{}); ok { if fc, ok := pm["fieldConfig"].(map[string]interface{}); ok { if defaults, ok := fc["defaults"].(map[string]interface{}); ok { if custom, ok := defaults["custom"].(map[string]interface{}); ok { if v, _ := custom["spanNulls"].(bool); !v { custom["spanNulls"] = true log.Printf("[template-validator] Fixed: panels.status_codes.fieldConfig.defaults.custom.spanNulls = true") fixed++ } } } } } } // --- vl_base_query для overview_tenant_per_nodes --- checkOverviewVLQuery(t, "overview_tenant_per_nodes", "log_type:access TENANT:in(${tenant:csv})", "log_type:access AND TENANT:${tenant:lucene}", &fixed, ) // --- vl_base_query для overview_tenant_per_node --- checkOverviewVLQuery(t, "overview_tenant_per_node", "log_type:access node_name:in(${node_name:csv})", "log_type:access AND node_name:${node_name:lucene}", &fixed, ) // --- layout: три столбца --- checkLayout(t, &fixed) if fixed > 0 { log.Printf("[template-validator] Total fixes applied: %d", fixed) } else { log.Printf("[template-validator] Template OK, no fixes needed") } } func checkOverviewVLQuery(t *DashboardTemplate, panelKey, expectedVL, expectedOS string, fixed *int) { p, ok := t.Panels[panelKey] if !ok { return } pm, ok := p.(map[string]interface{}) if !ok { return } qc, ok := pm["query_config"].(map[string]interface{}) if !ok { return } if v, _ := qc["vl_base_query"].(string); v == "" { qc["vl_base_query"] = expectedVL log.Printf("[template-validator] Fixed: panels.%s.query_config.vl_base_query", panelKey) (*fixed)++ } if v, _ := qc["base_query"].(string); v == "log_type:access" { qc["base_query"] = expectedOS log.Printf("[template-validator] Fixed: panels.%s.query_config.base_query", panelKey) (*fixed)++ } } func checkLayout(t *DashboardTemplate, fixed *int) { // Проверяем что tenant_panels имеет три столбца по 8 tenantPanels := t.Layout.TenantPanels hasTraffic := false wrongWidth := false for _, p := range tenantPanels { if p.PanelKey == "traffic_combined_total" { hasTraffic = true } if p.Width == 12 { wrongWidth = true } } if !hasTraffic || wrongWidth { log.Printf("[template-validator] Fixed: layout.tenant_panels restored to 3-column (8+8+8)") t.Layout.TenantPanels = []PanelLayout{ {PanelKey: "status_codes", TitleFormat: "Status Codes {client}", Width: 8, XOffset: 0}, {PanelKey: "rps", TitleFormat: "RPS {client}", Width: 8, XOffset: 8, Condition: &PanelCondition{Type: "max_domains", Value: 1}}, {PanelKey: "rps_multi", TitleFormat: "RPS {client}", Width: 8, XOffset: 8, Condition: &PanelCondition{Type: "min_domains", Value: 2}}, {PanelKey: "traffic_combined_total", TitleFormat: "Total Traffic {client}", Width: 8, XOffset: 16, Condition: &PanelCondition{Type: "min_domains", Value: 1}}, } (*fixed)++ } // Проверяем domain_panels domainPanels := t.Layout.DomainPanels hasDomainTraffic := false domainWrongWidth := false for _, p := range domainPanels { if p.PanelKey == "traffic_combined" { hasDomainTraffic = true } if p.Width == 12 { domainWrongWidth = true } } if !hasDomainTraffic || domainWrongWidth { log.Printf("[template-validator] Fixed: layout.domain_panels restored to 3-column (8+8+8)") t.Layout.DomainPanels = []PanelLayout{ {PanelKey: "status_codes", TitleSuffix: "{sid} Status Codes", Width: 8, XOffset: 0}, {PanelKey: "response_time", TitleSuffix: "{sid} Response Time", Width: 8, XOffset: 8}, {PanelKey: "traffic_combined", TitleFormat: "{domain} {sid} Traffic", Width: 8, XOffset: 16}, } (*fixed)++ } }