@cryptotaxi247 / netdata / commits / 35e8d07e5

feat(go.d/framework/chartengine): apply context_namespace to autogen chart contexts (#22642)

Ilya Mashchenko committed Jun 6, 2026 at 17:35 UTC 35e8d07e5e56754ef3790d5cb9aed851b4311515
11 files changed +315 -147
src/go/plugin/framework/chartengine/README.md
+1
@@ -168,6 +168,7 @@ Default lifecycle policy when template omits lifecycle:
168 | Topic | Behavior |
169 |-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
170 | Trigger | Unmatched series only when autogen is enabled |
171 +| Context namespace | Autogen context = top-level `context_namespace` + the full metric name (which includes any `SnapshotMeter` prefix); empty namespace leaves the bare name. A non-empty meter prefix stacks after `context_namespace`, so pair `context_namespace` with `SnapshotMeter("")` to avoid a doubled prefix |
172 | Structured families | Autogen has dedicated source builders for flattened `Histogram`, `Summary`, `StateSet`, and `MeasureSet` families |
173 | Metric metadata usage | Uses `metrix.MetricMeta` hints for title/family/unit where allowed |
174 | Type ID budget | Enforced via `AutogenPolicy.MaxTypeIDLen` + effective emit type-id prefix (`WithEmitTypeIDBudgetPrefix(...)`) |
src/go/plugin/framework/chartengine/autogen.go
+11 -3
@@ -79,6 +79,8 @@ func (e *Engine) resolveAutogenRoute(
79 return nil, false, nil
80 }
81
82 + namespace := e.state.cfg.autogenContextNamespace
83 +
84 route, ok, err := buildAutogenRoute(metricName, labels, meta, policy, e.state.cfg.autogenTypeID)
85 if err != nil {
86 return nil, false, err
@@ -112,7 +114,7 @@ func (e *Engine) resolveAutogenRoute(
114 Meta: program.ChartMeta{
115 Title: title,
116 Family: route.family,
115 - Context: getAutogenChartContext(route.contextName),
117 + Context: getAutogenChartContext(namespace, route.contextName),
118 Units: route.units,
119 Algorithm: route.algorithm,
120 Type: route.chartType,
@@ -601,10 +603,16 @@ func getAutogenChartTitle(metricName string) string {
603 return fmt.Sprintf("Metric \"%s\"", metricName)
604 }
605
604 -func getAutogenChartContext(metricName string) string {
606 +// getAutogenChartContext builds an autogen chart context, prefixed by the spec's root
607 +// context_namespace when set ("prometheus" + "foo" -> "prometheus.foo"), matching the
608 +// template compiler's "." join. Empty namespace -> the bare metric name (unchanged).
609 +func getAutogenChartContext(namespace, metricName string) string {
610 metricName = strings.TrimSpace(metricName)
611 if metricName == "" {
607 - return "metric"
612 + metricName = "metric"
613 + }
614 + if namespace = strings.TrimSpace(namespace); namespace != "" {
615 + return namespace + "." + metricName
616 }
617 return metricName
618 }
src/go/plugin/framework/chartengine/autogen_test.go
+21
@@ -372,6 +372,27 @@ func TestFitsTypeIDBudget(t *testing.T) {
372 }
373 }
374
375 +func TestGetAutogenChartContext(t *testing.T) {
376 + tests := map[string]struct {
377 + namespace string
378 + metric string
379 + want string
380 + }{
381 + "empty namespace returns bare metric": {namespace: "", metric: "foo", want: "foo"},
382 + "single-segment namespace is prefixed": {namespace: "prometheus", metric: "foo", want: "prometheus.foo"},
383 + "multi-segment namespace composes with dot": {namespace: "prometheus.app", metric: "foo", want: "prometheus.app.foo"},
384 + "whitespace-only namespace is treated empty": {namespace: " ", metric: "foo", want: "foo"},
385 + "empty metric falls back to metric": {namespace: "", metric: "", want: "metric"},
386 + "namespace with empty metric": {namespace: "prometheus", metric: "", want: "prometheus.metric"},
387 + }
388 +
389 + for name, tc := range tests {
390 + t.Run(name, func(t *testing.T) {
391 + assert.Equal(t, tc.want, getAutogenChartContext(tc.namespace, tc.metric))
392 + })
393 + }
394 +}
395 +
396 func sortedLabelView(labels map[string]string) metrix.LabelView {
397 items := make([]metrix.Label, 0, len(labels))
398 for key, value := range labels {
src/go/plugin/framework/chartengine/engine.go
+1
@@ -72,6 +72,7 @@ func (e *Engine) Load(spec *charttpl.Spec, revision uint64) error {
72 e.mu.Lock()
73 e.state.cfg.autogen = autogenPolicy
74 e.state.cfg.selector = selectorPolicy
75 + e.state.cfg.autogenContextNamespace = spec.ContextNamespace
76 e.state.program = compiled
77 e.state.matchIndex = buildMatchIndex(compiled.Charts())
78 // Template revision change resets routing/materialization internals.
src/go/plugin/framework/chartengine/options.go
+14 -11
@@ -12,17 +12,20 @@ import (
12 )
13
14 type engineConfig struct {
15 - autogen AutogenPolicy
16 - autogenTypeID string
17 - selector metrixselector.Selector
18 - autogenOverride policyOverride[AutogenPolicy]
19 - selectorOverride policyOverride[metrixselector.Selector]
20 - runtimeStore metrix.RuntimeStore
21 - runtimeStoreSet bool
22 - runtimeObserver func(PlanRuntimeSample)
23 - log *logger.Logger
24 - seriesSelection seriesSelectionMode
25 - runtimePlanner bool
15 + autogen AutogenPolicy
16 + autogenTypeID string
17 + // autogenContextNamespace prefixes autogen chart contexts (the spec's root
18 + // context_namespace), so autogen and template charts share one namespace.
19 + autogenContextNamespace string
20 + selector metrixselector.Selector
21 + autogenOverride policyOverride[AutogenPolicy]
22 + selectorOverride policyOverride[metrixselector.Selector]
23 + runtimeStore metrix.RuntimeStore
24 + runtimeStoreSet bool
25 + runtimeObserver func(PlanRuntimeSample)
26 + log *logger.Logger
27 + seriesSelection seriesSelectionMode
28 + runtimePlanner bool
29 }
30
31 type policyOverride[T any] struct {
src/go/plugin/framework/chartengine/planner_test.go
+76
@@ -233,6 +233,8 @@ func TestBuildPlanLegacySingleScenarioCases(t *testing.T) {
233 "BuildPlanAutogenRemovalLifecycleExpiry": {run: runTestBuildPlanAutogenRemovalLifecycleExpiry},
234 "BuildPlanFirstWriterWinsAndAccumulatesRepeatedRoutes": {run: runTestBuildPlanFirstWriterWinsAndAccumulatesRepeatedRoutes},
235 "BuildPlanEmptyEmissionAndScratchReusePruneAcrossCycles": {run: runTestBuildPlanEmptyEmissionAndScratchReusePruneAcrossCycles},
236 + "BuildPlanAutogenContextNamespacePrefixesContext": {run: runTestBuildPlanAutogenContextNamespacePrefixesContext},
237 + "BuildPlanAutogenContextNamespaceStubGroupOnly": {run: runTestBuildPlanAutogenContextNamespaceStubGroupOnly},
238 }
239
240 for name, tc := range tests {
@@ -999,6 +1001,80 @@ groups:
1001 assert.Equal(t, float64(10), update.Values[0].Float64)
1002 }
1003
1004 +func runTestBuildPlanAutogenContextNamespacePrefixesContext(t *testing.T) {
1005 + e, err := New(WithEnginePolicy(EnginePolicy{Autogen: &AutogenPolicy{Enabled: true}}))
1006 + require.NoError(t, err)
1007 +
1008 + // Root context_namespace must prefix autogen (unmatched-series) chart contexts,
1009 + // joined with "." like the template compiler ("prometheus" + "svc.errors_total").
1010 + yaml := `
1011 +version: v1
1012 +context_namespace: prometheus
1013 +groups:
1014 + - family: Service
1015 + metrics:
1016 + - svc.requests_total
1017 + charts:
1018 + - title: Requests
1019 + context: requests
1020 + units: requests/s
1021 + dimensions:
1022 + - selector: svc.requests_total
1023 + name: total
1024 +`
1025 + require.NoError(t, e.LoadYAML([]byte(yaml), 1))
1026 +
1027 + store := metrix.NewCollectorStore()
1028 + cc := mustCycleController(t, store)
1029 + sm := store.Write().SnapshotMeter("svc")
1030 + unmatched := sm.Counter("errors_total")
1031 + methodGET := sm.LabelSet(metrix.Label{Key: "method", Value: "GET"})
1032 +
1033 + cc.BeginCycle()
1034 + unmatched.ObserveTotal(10, methodGET)
1035 + cc.CommitCycleSuccess()
1036 +
1037 + plan, err := buildPlan(e, store.Read(metrix.ReadFlatten()))
1038 + require.NoError(t, err)
1039 +
1040 + create := findCreateChartAction(plan)
1041 + require.NotNil(t, create)
1042 + assert.Equal(t, "prometheus.svc.errors_total", create.Meta.Context)
1043 +}
1044 +
1045 +// Mirrors the autogen-only collector shape: a stub group satisfies the
1046 +// required groups[] but declares no charts, so every series is unmatched and handled by autogen,
1047 +// with contexts prefixed by the top-level context_namespace.
1048 +func runTestBuildPlanAutogenContextNamespaceStubGroupOnly(t *testing.T) {
1049 + e, err := New(WithEnginePolicy(EnginePolicy{Autogen: &AutogenPolicy{Enabled: true}}))
1050 + require.NoError(t, err)
1051 +
1052 + yaml := `
1053 +version: v1
1054 +context_namespace: prometheus
1055 +groups:
1056 + - family: Prometheus
1057 +`
1058 + require.NoError(t, e.LoadYAML([]byte(yaml), 1))
1059 +
1060 + store := metrix.NewCollectorStore()
1061 + cc := mustCycleController(t, store)
1062 + sm := store.Write().SnapshotMeter("")
1063 + unmatched := sm.Counter("requests_total")
1064 + methodGET := sm.LabelSet(metrix.Label{Key: "method", Value: "GET"})
1065 +
1066 + cc.BeginCycle()
1067 + unmatched.ObserveTotal(10, methodGET)
1068 + cc.CommitCycleSuccess()
1069 +
1070 + plan, err := buildPlan(e, store.Read(metrix.ReadFlatten()))
1071 + require.NoError(t, err)
1072 +
1073 + create := findCreateChartAction(plan)
1074 + require.NotNil(t, create)
1075 + assert.Equal(t, "prometheus.requests_total", create.Meta.Context)
1076 +}
1077 +
1078 func runTestBuildPlanAutogenUsesMetricMetadataForScalar(t *testing.T) {
1079 e, err := New(WithEnginePolicy(EnginePolicy{Autogen: &AutogenPolicy{Enabled: true}}))
1080 require.NoError(t, err)
src/go/plugin/framework/charttpl/README.md
+12
@@ -268,6 +268,18 @@ For example:
268 | Chart `context` | `queries` |
269 | **Resulting context** | **`mysql.queries`** |
270
271 +**Autogen charts** — the **top-level** `context_namespace` also prefixes the contexts of charts
272 +created by `engine.autogen` (metrics not matched by any template dimension), joined with the same
273 +`.`. Group-level `context_namespace` does not apply to autogen, since unmatched series belong to
274 +no group. For example, with top-level `context_namespace: nagios`, an unmatched metric
275 +`check_load` autogenerates the context `nagios.check_load`.
276 +
277 +The autogen context is `context_namespace` joined with the **full metric name**, and a metric's name
278 +includes any `SnapshotMeter("<prefix>")` prefix (`<prefix>.<instrument>`). So a non-empty meter prefix
279 +**stacks after** `context_namespace` — e.g. `context_namespace: app` with `SnapshotMeter("app")` and
280 +instrument `foo` yields `app.app.foo`. When you set `context_namespace`, write metrics with
281 +`SnapshotMeter("")` so the namespace has a single source; do not also encode it in the meter prefix.
282 +
283 ### 3. engine
284
285 Template-level policy that controls metric filtering and autogeneration.
src/go/plugin/scripts.d/collector/nagios/charts.yaml
+10 -10
@@ -10,11 +10,11 @@ groups:
10 groups:
11 - family: Execution
12 metrics:
13 - - nagios.job.execution_state
14 - - nagios.job.perfdata.threshold_state
15 - - nagios.job.execution_duration
16 - - nagios.job.execution_cpu_total
17 - - nagios.job.execution_max_rss
13 + - job.execution_state
14 + - job.perfdata.threshold_state
15 + - job.execution_duration
16 + - job.execution_cpu_total
17 + - job.execution_max_rss
18 charts:
19 - id: job_execution_state
20 title: Job Execution State
@@ -24,7 +24,7 @@ groups:
24 instances:
25 by_labels: [nagios_job]
26 dimensions:
27 - - selector: nagios.job.execution_state
27 + - selector: job.execution_state
28 - id: job_perfdata_threshold_state
29 title: Job Perfdata Threshold State
30 context: perfdata_threshold_state
@@ -33,7 +33,7 @@ groups:
33 instances:
34 by_labels: [nagios_job, perfdata_value]
35 dimensions:
36 - - selector: nagios.job.perfdata.threshold_state
36 + - selector: job.perfdata.threshold_state
37 - id: job_execution_duration
38 title: Execution Duration
39 context: execution_duration
@@ -42,7 +42,7 @@ groups:
42 instances:
43 by_labels: [nagios_job]
44 dimensions:
45 - - selector: nagios.job.execution_duration
45 + - selector: job.execution_duration
46 name: duration
47 options:
48 float: true
@@ -54,7 +54,7 @@ groups:
54 instances:
55 by_labels: [nagios_job]
56 dimensions:
57 - - selector: nagios.job.execution_cpu_total
57 + - selector: job.execution_cpu_total
58 name: total
59 options:
60 float: true
@@ -66,5 +66,5 @@ groups:
66 instances:
67 by_labels: [nagios_job]
68 dimensions:
69 - - selector: nagios.job.execution_max_rss
69 + - selector: job.execution_max_rss
70 name: rss
src/go/plugin/scripts.d/collector/nagios/collect.go
+3 -1
@@ -69,7 +69,9 @@ func (c *Collector) completeDueCheck(now time.Time, res checkRunResult) {
69 }
70
71 func (c *Collector) emitMetrics(execMetrics executionMetrics) {
72 - sm := c.store.Write().SnapshotMeter("nagios")
72 + // Empty meter prefix: the namespace comes from charts.yaml context_namespace (nagios),
73 + // which also prefixes autogen perfdata contexts. A "nagios" meter prefix would double it.
74 + sm := c.store.Write().SnapshotMeter("")
75
76 jobName := c.job.config.Name
77 if jobName == "" {
src/go/plugin/scripts.d/collector/nagios/collector_test.go
+89 -89
@@ -43,17 +43,17 @@ func TestCollector_ChartTemplateYAML(t *testing.T) {
43 }{
44 "execution duration dimension is float": {
45 context: "execution_duration",
46 - selector: "nagios.job.execution_duration",
46 + selector: "job.execution_duration",
47 wantFloat: true,
48 },
49 "execution cpu dimension is float": {
50 context: "execution_cpu",
51 - selector: "nagios.job.execution_cpu_total",
51 + selector: "job.execution_cpu_total",
52 wantFloat: true,
53 },
54 "execution memory dimension is integer": {
55 context: "execution_memory",
56 - selector: "nagios.job.execution_max_rss",
56 + selector: "job.execution_max_rss",
57 wantFloat: false,
58 },
59 }
@@ -360,22 +360,22 @@ func TestCollector_Collect(t *testing.T) {
360
361 read := coll.MetricStore().Read(metrix.ReadRaw())
362 flat := coll.MetricStore().Read(metrix.ReadFlatten())
363 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "check_disk", "nagios.job.execution_state": "ok"}, 1)
364 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "check_disk", "nagios.perfdata.true.job.execution_state": "ok"}, 1)
365 - assertMetricChartFamily(t, flat, "nagios.perfdata.true.job.execution_state", "Perfdata/true")
366 - assertMetricValue(t, flat, "nagios.job.execution_duration", metrix.Labels{"nagios_job": "check_disk"}, 2.5)
367 - assertMetricMeta(t, flat, "nagios.job.execution_duration", "seconds", true)
363 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "check_disk", "job.execution_state": "ok"}, 1)
364 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "check_disk", "perfdata.true.job.execution_state": "ok"}, 1)
365 + assertMetricChartFamily(t, flat, "perfdata.true.job.execution_state", "Perfdata/true")
366 + assertMetricValue(t, flat, "job.execution_duration", metrix.Labels{"nagios_job": "check_disk"}, 2.5)
367 + assertMetricMeta(t, flat, "job.execution_duration", "seconds", true)
368 if runtime.GOOS != "windows" {
369 - assertMetricValue(t, flat, "nagios.job.execution_cpu_total", metrix.Labels{"nagios_job": "check_disk"}, 0.5)
370 - assertMetricValue(t, flat, "nagios.job.execution_max_rss", metrix.Labels{"nagios_job": "check_disk"}, 12345)
371 - assertMetricMeta(t, flat, "nagios.job.execution_cpu_total", "seconds", true)
372 - assertMetricMeta(t, flat, "nagios.job.execution_max_rss", "bytes", false)
369 + assertMetricValue(t, flat, "job.execution_cpu_total", metrix.Labels{"nagios_job": "check_disk"}, 0.5)
370 + assertMetricValue(t, flat, "job.execution_max_rss", metrix.Labels{"nagios_job": "check_disk"}, 12345)
371 + assertMetricMeta(t, flat, "job.execution_cpu_total", "seconds", true)
372 + assertMetricMeta(t, flat, "job.execution_max_rss", "bytes", false)
373 } else {
374 - assertMetricMissing(t, flat, "nagios.job.execution_cpu_total", metrix.Labels{"nagios_job": "check_disk"})
375 - assertMetricMissing(t, flat, "nagios.job.execution_max_rss", metrix.Labels{"nagios_job": "check_disk"})
374 + assertMetricMissing(t, flat, "job.execution_cpu_total", metrix.Labels{"nagios_job": "check_disk"})
375 + assertMetricMissing(t, flat, "job.execution_max_rss", metrix.Labels{"nagios_job": "check_disk"})
376 }
377 - assertMetricValue(t, flat, "nagios.perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "check_disk", metrix.MeasureSetFieldLabel: "value"}, 30000)
378 - point, ok := read.MeasureSet("nagios.perfdata.true.bytes_used", metrix.Labels{"nagios_job": "check_disk"})
377 + assertMetricValue(t, flat, "perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "check_disk", metrix.MeasureSetFieldLabel: "value"}, 30000)
378 + point, ok := read.MeasureSet("perfdata.true.bytes_used", metrix.Labels{"nagios_job": "check_disk"})
379 require.True(t, ok)
380 assert.Equal(t, 30000.0, point.Values[0])
381
@@ -384,15 +384,15 @@ func TestCollector_Collect(t *testing.T) {
384 assert.Equal(t, 1, runner.calls)
385
386 flat = coll.MetricStore().Read(metrix.ReadFlatten())
387 - assertMetricValue(t, flat, "nagios.job.execution_duration", metrix.Labels{"nagios_job": "check_disk"}, 0)
387 + assertMetricValue(t, flat, "job.execution_duration", metrix.Labels{"nagios_job": "check_disk"}, 0)
388 if runtime.GOOS != "windows" {
389 - assertMetricValue(t, flat, "nagios.job.execution_cpu_total", metrix.Labels{"nagios_job": "check_disk"}, 0)
390 - assertMetricValue(t, flat, "nagios.job.execution_max_rss", metrix.Labels{"nagios_job": "check_disk"}, 0)
389 + assertMetricValue(t, flat, "job.execution_cpu_total", metrix.Labels{"nagios_job": "check_disk"}, 0)
390 + assertMetricValue(t, flat, "job.execution_max_rss", metrix.Labels{"nagios_job": "check_disk"}, 0)
391 } else {
392 - assertMetricMissing(t, flat, "nagios.job.execution_cpu_total", metrix.Labels{"nagios_job": "check_disk"})
393 - assertMetricMissing(t, flat, "nagios.job.execution_max_rss", metrix.Labels{"nagios_job": "check_disk"})
392 + assertMetricMissing(t, flat, "job.execution_cpu_total", metrix.Labels{"nagios_job": "check_disk"})
393 + assertMetricMissing(t, flat, "job.execution_max_rss", metrix.Labels{"nagios_job": "check_disk"})
394 }
395 - assertMetricValue(t, flat, "nagios.perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "check_disk", metrix.MeasureSetFieldLabel: "value"}, 30000)
395 + assertMetricValue(t, flat, "perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "check_disk", metrix.MeasureSetFieldLabel: "value"}, 30000)
396 },
397 },
398 "uses explicit check name for perfdata namespace": {
@@ -426,11 +426,11 @@ func TestCollector_Collect(t *testing.T) {
426 assert.Equal(t, 1, runner.calls)
427
428 flat := coll.MetricStore().Read(metrix.ReadFlatten())
429 - assertMetricValue(t, flat, "nagios.perfdata.check_service.job.execution_state", metrix.Labels{"nagios_job": "check_service_job", "nagios.perfdata.check_service.job.execution_state": "ok"}, 1)
430 - assertMetricChartFamily(t, flat, "nagios.perfdata.check_service.job.execution_state", "Perfdata/check_service")
431 - assertMetricValue(t, flat, "nagios.perfdata.check_service.bytes_used_value", metrix.Labels{"nagios_job": "check_service_job", metrix.MeasureSetFieldLabel: "value"}, 30000)
432 - assertMetricMissing(t, flat, "nagios.perfdata.pwsh.job.execution_state", metrix.Labels{"nagios_job": "check_service_job", "nagios.perfdata.pwsh.job.execution_state": "ok"})
433 - assertMetricMissing(t, flat, "nagios.perfdata.pwsh.bytes_used_value", metrix.Labels{"nagios_job": "check_service_job", metrix.MeasureSetFieldLabel: "value"})
429 + assertMetricValue(t, flat, "perfdata.check_service.job.execution_state", metrix.Labels{"nagios_job": "check_service_job", "perfdata.check_service.job.execution_state": "ok"}, 1)
430 + assertMetricChartFamily(t, flat, "perfdata.check_service.job.execution_state", "Perfdata/check_service")
431 + assertMetricValue(t, flat, "perfdata.check_service.bytes_used_value", metrix.Labels{"nagios_job": "check_service_job", metrix.MeasureSetFieldLabel: "value"}, 30000)
432 + assertMetricMissing(t, flat, "perfdata.pwsh.job.execution_state", metrix.Labels{"nagios_job": "check_service_job", "perfdata.pwsh.job.execution_state": "ok"})
433 + assertMetricMissing(t, flat, "perfdata.pwsh.bytes_used_value", metrix.Labels{"nagios_job": "check_service_job", metrix.MeasureSetFieldLabel: "value"})
434
435 *now = now.Add(1 * time.Second)
436 },
@@ -506,22 +506,22 @@ func TestCollector_Collect(t *testing.T) {
506 assert.Equal(t, 1, runner.calls)
507
508 flat := coll.MetricStore().Read(metrix.ReadFlatten())
509 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "period_job", "nagios.job.execution_state": "ok"}, 1)
510 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "period_job", "nagios.perfdata.true.job.execution_state": "ok"}, 1)
511 - assertMetricValue(t, flat, "nagios.perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "period_job", metrix.MeasureSetFieldLabel: "value"}, 30000)
512 - assertMetricValue(t, flat, "nagios.job.perfdata.threshold_state", metrix.Labels{
513 - "nagios_job": "period_job",
514 - perfdataValueLabelKey: "bytes_used",
515 - "nagios.job.perfdata.threshold_state": perfThresholdStateWarning,
509 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "period_job", "job.execution_state": "ok"}, 1)
510 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "period_job", "perfdata.true.job.execution_state": "ok"}, 1)
511 + assertMetricValue(t, flat, "perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "period_job", metrix.MeasureSetFieldLabel: "value"}, 30000)
512 + assertMetricValue(t, flat, "job.perfdata.threshold_state", metrix.Labels{
513 + "nagios_job": "period_job",
514 + perfdataValueLabelKey: "bytes_used",
515 + "job.perfdata.threshold_state": perfThresholdStateWarning,
516 }, 1)
517
518 raw := coll.MetricStore().Read()
519 - thresholdMetric := "nagios.perfdata.true.bytes_used_threshold_state"
519 + thresholdMetric := "perfdata.true.bytes_used_threshold_state"
520 thresholdLabels := metrix.Labels{"nagios_job": "period_job"}
521 point, ok := raw.StateSet(thresholdMetric, thresholdLabels)
522 require.True(t, ok)
523 assert.True(t, point.States[perfThresholdStateWarning])
524 - alertThresholdMetric := "nagios.job.perfdata.threshold_state"
524 + alertThresholdMetric := "job.perfdata.threshold_state"
525 alertThresholdLabels := metrix.Labels{"nagios_job": "period_job", perfdataValueLabelKey: "bytes_used"}
526 alertPoint, ok := raw.StateSet(alertThresholdMetric, alertThresholdLabels)
527 require.True(t, ok)
@@ -532,11 +532,11 @@ func TestCollector_Collect(t *testing.T) {
532 assert.Equal(t, 1, runner.calls)
533
534 flat = coll.MetricStore().Read(metrix.ReadFlatten())
535 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "period_job", "nagios.job.execution_state": "paused"}, 1)
536 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "period_job", "nagios.job.execution_state": "retry"}, 0)
537 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "period_job", "nagios.perfdata.true.job.execution_state": "paused"}, 1)
538 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "period_job", "nagios.perfdata.true.job.execution_state": "retry"}, 0)
539 - assertMetricValue(t, flat, "nagios.perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "period_job", metrix.MeasureSetFieldLabel: "value"}, 30000)
535 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "period_job", "job.execution_state": "paused"}, 1)
536 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "period_job", "job.execution_state": "retry"}, 0)
537 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "period_job", "perfdata.true.job.execution_state": "paused"}, 1)
538 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "period_job", "perfdata.true.job.execution_state": "retry"}, 0)
539 + assertMetricValue(t, flat, "perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "period_job", metrix.MeasureSetFieldLabel: "value"}, 30000)
540 assertMetricValue(t, flat, thresholdMetric, metrix.Labels{"nagios_job": "period_job", thresholdMetric: perfThresholdStateWarning}, 0)
541 assertMetricValue(t, flat, thresholdMetric, metrix.Labels{"nagios_job": "period_job", thresholdMetric: perfThresholdStateOK}, 0)
542 assertMetricValue(t, flat, thresholdMetric, metrix.Labels{"nagios_job": "period_job", thresholdMetric: perfThresholdStateCritical}, 0)
@@ -567,9 +567,9 @@ func TestCollector_Collect(t *testing.T) {
567 assert.Equal(t, 2, runner.calls)
568
569 flat = coll.MetricStore().Read(metrix.ReadFlatten())
570 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "period_job", "nagios.job.execution_state": "ok"}, 1)
571 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "period_job", "nagios.perfdata.true.job.execution_state": "ok"}, 1)
572 - assertMetricValue(t, flat, "nagios.perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "period_job", metrix.MeasureSetFieldLabel: "value"}, 10000)
570 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "period_job", "job.execution_state": "ok"}, 1)
571 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "period_job", "perfdata.true.job.execution_state": "ok"}, 1)
572 + assertMetricValue(t, flat, "perfdata.true.bytes_used_value", metrix.Labels{"nagios_job": "period_job", metrix.MeasureSetFieldLabel: "value"}, 10000)
573
574 raw = coll.MetricStore().Read()
575 point, ok = raw.StateSet(thresholdMetric, thresholdLabels)
@@ -647,19 +647,19 @@ func TestCollector_Collect(t *testing.T) {
647 assert.Equal(t, 1, runner.calls)
648 assert.Equal(t, 2, coll.state.currentAttempt())
649 flat := coll.MetricStore().Read(metrix.ReadFlatten())
650 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "retry_job", "nagios.job.execution_state": "warning"}, 1)
651 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "retry_job", "nagios.job.execution_state": "retry"}, 1)
652 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "retry_job", "nagios.perfdata.true.job.execution_state": "warning"}, 1)
653 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "retry_job", "nagios.perfdata.true.job.execution_state": "retry"}, 1)
654 - assertMetricValue(t, flat, "nagios.job.perfdata.threshold_state", metrix.Labels{
655 - "nagios_job": "retry_job",
656 - perfdataValueLabelKey: "bytes_used",
657 - "nagios.job.perfdata.threshold_state": perfThresholdStateWarning,
650 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "retry_job", "job.execution_state": "warning"}, 1)
651 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "retry_job", "job.execution_state": "retry"}, 1)
652 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "retry_job", "perfdata.true.job.execution_state": "warning"}, 1)
653 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "retry_job", "perfdata.true.job.execution_state": "retry"}, 1)
654 + assertMetricValue(t, flat, "job.perfdata.threshold_state", metrix.Labels{
655 + "nagios_job": "retry_job",
656 + perfdataValueLabelKey: "bytes_used",
657 + "job.perfdata.threshold_state": perfThresholdStateWarning,
658 }, 1)
659 - assertMetricValue(t, flat, "nagios.job.perfdata.threshold_state", metrix.Labels{
660 - "nagios_job": "retry_job",
661 - perfdataValueLabelKey: "bytes_used",
662 - "nagios.job.perfdata.threshold_state": perfThresholdStateRetry,
659 + assertMetricValue(t, flat, "job.perfdata.threshold_state", metrix.Labels{
660 + "nagios_job": "retry_job",
661 + perfdataValueLabelKey: "bytes_used",
662 + "job.perfdata.threshold_state": perfThresholdStateRetry,
663 }, 1)
664
665 *now = now.Add(9 * time.Second)
@@ -671,17 +671,17 @@ func TestCollector_Collect(t *testing.T) {
671 assert.Equal(t, 2, runner.calls)
672 assert.Equal(t, 1, coll.state.currentAttempt())
673 flat = coll.MetricStore().Read(metrix.ReadFlatten())
674 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "retry_job", "nagios.job.execution_state": "ok"}, 1)
675 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "retry_job", "nagios.job.execution_state": "retry"}, 0)
676 - assertMetricValue(t, flat, "nagios.job.perfdata.threshold_state", metrix.Labels{
677 - "nagios_job": "retry_job",
678 - perfdataValueLabelKey: "bytes_used",
679 - "nagios.job.perfdata.threshold_state": perfThresholdStateOK,
674 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "retry_job", "job.execution_state": "ok"}, 1)
675 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "retry_job", "job.execution_state": "retry"}, 0)
676 + assertMetricValue(t, flat, "job.perfdata.threshold_state", metrix.Labels{
677 + "nagios_job": "retry_job",
678 + perfdataValueLabelKey: "bytes_used",
679 + "job.perfdata.threshold_state": perfThresholdStateOK,
680 }, 1)
681 - assertMetricValue(t, flat, "nagios.job.perfdata.threshold_state", metrix.Labels{
682 - "nagios_job": "retry_job",
683 - perfdataValueLabelKey: "bytes_used",
684 - "nagios.job.perfdata.threshold_state": perfThresholdStateRetry,
681 + assertMetricValue(t, flat, "job.perfdata.threshold_state", metrix.Labels{
682 + "nagios_job": "retry_job",
683 + perfdataValueLabelKey: "bytes_used",
684 + "job.perfdata.threshold_state": perfThresholdStateRetry,
685 }, 0)
686 },
687 },
@@ -739,17 +739,17 @@ func TestCollector_Collect(t *testing.T) {
739 assert.Equal(t, 1, runner.calls)
740
741 flat := coll.MetricStore().Read(metrix.ReadFlatten())
742 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "nagios.job.execution_state": "warning"}, 1)
743 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "nagios.job.execution_state": "retry"}, 1)
744 - assertMetricValue(t, flat, "nagios.job.perfdata.threshold_state", metrix.Labels{
745 - "nagios_job": "paused_retry_job",
746 - perfdataValueLabelKey: "bytes_used",
747 - "nagios.job.perfdata.threshold_state": perfThresholdStateWarning,
742 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "job.execution_state": "warning"}, 1)
743 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "job.execution_state": "retry"}, 1)
744 + assertMetricValue(t, flat, "job.perfdata.threshold_state", metrix.Labels{
745 + "nagios_job": "paused_retry_job",
746 + perfdataValueLabelKey: "bytes_used",
747 + "job.perfdata.threshold_state": perfThresholdStateWarning,
748 }, 1)
749 - assertMetricValue(t, flat, "nagios.job.perfdata.threshold_state", metrix.Labels{
750 - "nagios_job": "paused_retry_job",
751 - perfdataValueLabelKey: "bytes_used",
752 - "nagios.job.perfdata.threshold_state": perfThresholdStateRetry,
749 + assertMetricValue(t, flat, "job.perfdata.threshold_state", metrix.Labels{
750 + "nagios_job": "paused_retry_job",
751 + perfdataValueLabelKey: "bytes_used",
752 + "job.perfdata.threshold_state": perfThresholdStateRetry,
753 }, 1)
754
755 *now = time.Date(2026, 3, 23, 20, 0, 0, 0, time.UTC)
@@ -757,20 +757,20 @@ func TestCollector_Collect(t *testing.T) {
757 assert.Equal(t, 1, runner.calls)
758
759 flat = coll.MetricStore().Read(metrix.ReadFlatten())
760 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "nagios.job.execution_state": "paused"}, 1)
761 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "nagios.job.execution_state": "retry"}, 0)
762 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "nagios.perfdata.true.job.execution_state": "paused"}, 1)
763 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "nagios.perfdata.true.job.execution_state": "retry"}, 0)
760 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "job.execution_state": "paused"}, 1)
761 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "job.execution_state": "retry"}, 0)
762 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "perfdata.true.job.execution_state": "paused"}, 1)
763 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "paused_retry_job", "perfdata.true.job.execution_state": "retry"}, 0)
764 for _, state := range perfThresholdAlertStateNames {
765 - assertMetricValue(t, flat, "nagios.job.perfdata.threshold_state", metrix.Labels{
766 - "nagios_job": "paused_retry_job",
767 - perfdataValueLabelKey: "bytes_used",
768 - "nagios.job.perfdata.threshold_state": state,
765 + assertMetricValue(t, flat, "job.perfdata.threshold_state", metrix.Labels{
766 + "nagios_job": "paused_retry_job",
767 + perfdataValueLabelKey: "bytes_used",
768 + "job.perfdata.threshold_state": state,
769 }, 0)
770 }
771
772 raw := coll.MetricStore().Read()
773 - alertPoint, ok := raw.StateSet("nagios.job.perfdata.threshold_state", metrix.Labels{
773 + alertPoint, ok := raw.StateSet("job.perfdata.threshold_state", metrix.Labels{
774 "nagios_job": "paused_retry_job",
775 perfdataValueLabelKey: "bytes_used",
776 })
@@ -808,10 +808,10 @@ func TestCollector_Collect(t *testing.T) {
808 assert.Equal(t, jobStateTimeout, coll.state.currentJobState())
809
810 flat := coll.MetricStore().Read(metrix.ReadFlatten())
811 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "timeout_job", "nagios.job.execution_state": "timeout"}, 1)
812 - assertMetricValue(t, flat, "nagios.job.execution_state", metrix.Labels{"nagios_job": "timeout_job", "nagios.job.execution_state": "retry"}, 1)
813 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "timeout_job", "nagios.perfdata.true.job.execution_state": "timeout"}, 1)
814 - assertMetricValue(t, flat, "nagios.perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "timeout_job", "nagios.perfdata.true.job.execution_state": "retry"}, 1)
811 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "timeout_job", "job.execution_state": "timeout"}, 1)
812 + assertMetricValue(t, flat, "job.execution_state", metrix.Labels{"nagios_job": "timeout_job", "job.execution_state": "retry"}, 1)
813 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "timeout_job", "perfdata.true.job.execution_state": "timeout"}, 1)
814 + assertMetricValue(t, flat, "perfdata.true.job.execution_state", metrix.Labels{"nagios_job": "timeout_job", "perfdata.true.job.execution_state": "retry"}, 1)
815
816 *now = now.Add(11 * time.Second)
817 runCollectCycle(t, coll)
src/go/plugin/scripts.d/collector/nagios/v2_gate_test.go
+77 -33
@@ -79,7 +79,7 @@ func TestV2Gate_G2_PerfdataRouting(t *testing.T) {
79 store := metrix.NewCollectorStore()
80 cc := gateCycleController(t, store)
81 cc.BeginCycle()
82 - sm := store.Write().SnapshotMeter("nagios")
82 + sm := store.Write().SnapshotMeter("")
83 labels := sm.LabelSet(
84 metrix.Label{Key: "nagios_job", Value: "gate_job"},
85 )
@@ -121,35 +121,35 @@ func TestV2Gate_G2_PerfdataRouting(t *testing.T) {
121 cc.CommitCycleSuccess()
122
123 reader := store.Read(metrix.ReadFlatten())
124 - assertMetricMeta(t, reader, "nagios.perfdata.check_gate.time_latency_value", "seconds", true)
125 - assertMetricMeta(t, reader, "nagios.perfdata.check_gate.bytes_throughput_value", "bytes", true)
126 - assertMetricMeta(t, reader, "nagios.perfdata.check_gate.bits_wire_rate_value", "bits", true)
127 - assertMetricMeta(t, reader, "nagios.perfdata.check_gate.percent_free_pct_value", "%", true)
128 - assertMetricMeta(t, reader, "nagios.perfdata.check_gate.counter_requests_value", "c", true)
129 - assertMetricMeta(t, reader, "nagios.perfdata.check_gate.generic_custom_value", "generic", true)
130 - assertMetricMeta(t, reader, "nagios.perfdata.check_gate.time_latency_threshold_state", "state", false)
131 - assertMetricMeta(t, reader, "nagios.job.perfdata.threshold_state", "state", false)
132 - assertMetricChartFamily(t, reader, "nagios.perfdata.check_gate.time_latency_value", "Perfdata/check_gate")
133 - assertMetricChartFamily(t, reader, "nagios.perfdata.check_gate.time_latency_threshold_state", "Perfdata/check_gate")
134 - assertMetricValue(t, reader, "nagios.perfdata.check_gate.time_latency_threshold_state", metrix.Labels{
124 + assertMetricMeta(t, reader, "perfdata.check_gate.time_latency_value", "seconds", true)
125 + assertMetricMeta(t, reader, "perfdata.check_gate.bytes_throughput_value", "bytes", true)
126 + assertMetricMeta(t, reader, "perfdata.check_gate.bits_wire_rate_value", "bits", true)
127 + assertMetricMeta(t, reader, "perfdata.check_gate.percent_free_pct_value", "%", true)
128 + assertMetricMeta(t, reader, "perfdata.check_gate.counter_requests_value", "c", true)
129 + assertMetricMeta(t, reader, "perfdata.check_gate.generic_custom_value", "generic", true)
130 + assertMetricMeta(t, reader, "perfdata.check_gate.time_latency_threshold_state", "state", false)
131 + assertMetricMeta(t, reader, "job.perfdata.threshold_state", "state", false)
132 + assertMetricChartFamily(t, reader, "perfdata.check_gate.time_latency_value", "Perfdata/check_gate")
133 + assertMetricChartFamily(t, reader, "perfdata.check_gate.time_latency_threshold_state", "Perfdata/check_gate")
134 + assertMetricValue(t, reader, "perfdata.check_gate.time_latency_threshold_state", metrix.Labels{
135 "nagios_job": "gate_job",
136 - "nagios.perfdata.check_gate.time_latency_threshold_state": perfThresholdStateWarning,
136 + "perfdata.check_gate.time_latency_threshold_state": perfThresholdStateWarning,
137 }, 1)
138 - assertMetricValue(t, reader, "nagios.job.perfdata.threshold_state", metrix.Labels{
139 - "nagios_job": "gate_job",
140 - perfdataValueLabelKey: "time_latency",
141 - "nagios.job.perfdata.threshold_state": perfThresholdStateWarning,
138 + assertMetricValue(t, reader, "job.perfdata.threshold_state", metrix.Labels{
139 + "nagios_job": "gate_job",
140 + perfdataValueLabelKey: "time_latency",
141 + "job.perfdata.threshold_state": perfThresholdStateWarning,
142 }, 1)
143 - assertMetricValue(t, reader, "nagios.job.perfdata.threshold_state", metrix.Labels{
144 - "nagios_job": "gate_job",
145 - perfdataValueLabelKey: "time_latency",
146 - "nagios.job.perfdata.threshold_state": perfThresholdStateRetry,
143 + assertMetricValue(t, reader, "job.perfdata.threshold_state", metrix.Labels{
144 + "nagios_job": "gate_job",
145 + perfdataValueLabelKey: "time_latency",
146 + "job.perfdata.threshold_state": perfThresholdStateRetry,
147 }, 0)
148 - assertSeriesKind(t, reader, "nagios.perfdata.check_gate.time_latency_value", metrix.Labels{
148 + assertSeriesKind(t, reader, "perfdata.check_gate.time_latency_value", metrix.Labels{
149 "nagios_job": "gate_job",
150 metrix.MeasureSetFieldLabel: perfFieldValue,
151 }, metrix.MetricKindGauge)
152 - assertSeriesKind(t, reader, "nagios.perfdata.check_gate.counter_requests_value", metrix.Labels{
152 + assertSeriesKind(t, reader, "perfdata.check_gate.counter_requests_value", metrix.Labels{
153 "nagios_job": "gate_job",
154 metrix.MeasureSetFieldLabel: perfFieldValue,
155 }, metrix.MetricKindCounter)
@@ -172,7 +172,7 @@ func TestV2Gate_G3_ChartLifecycleChurn(t *testing.T) {
172 emit := func(includeB bool) chartengine.Plan {
173 cc := gateCycleController(t, store)
174 cc.BeginCycle()
175 - sm := store.Write().SnapshotMeter("nagios")
175 + sm := store.Write().SnapshotMeter("")
176 ls := sm.LabelSet(
177 metrix.Label{Key: "nagios_job", Value: "gate_job"},
178 )
@@ -211,7 +211,7 @@ func TestV2Gate_G3_ChartLifecycleChurn(t *testing.T) {
211
212 cc := gateCycleController(t, store)
213 cc.BeginCycle()
214 - sm := store.Write().SnapshotMeter("nagios")
214 + sm := store.Write().SnapshotMeter("")
215 ls := sm.LabelSet(
216 metrix.Label{Key: "nagios_job", Value: "gate_job"},
217 )
@@ -238,12 +238,12 @@ func TestV2Gate_G3_ChartLifecycleChurn(t *testing.T) {
238 assert.NotZero(t, countActions[chartengine.CreateChartAction](plan1.Actions))
239 plan2 := emit(false)
240 assert.Zero(t, removeActionsCount(plan2.Actions))
241 - assertPlanHasUpdateForTarget(t, plan2, "nagios.perfdata.check_gate.bytes_a")
242 - assertPlanHasNoRemoveForTarget(t, plan2, "nagios.perfdata.check_gate.bytes_b")
241 + assertPlanHasUpdateForTarget(t, plan2, "perfdata.check_gate.bytes_a")
242 + assertPlanHasNoRemoveForTarget(t, plan2, "perfdata.check_gate.bytes_b")
243
244 cc := gateCycleController(t, store)
245 cc.BeginCycle()
246 - sm := store.Write().SnapshotMeter("nagios")
246 + sm := store.Write().SnapshotMeter("")
247 ls := sm.LabelSet(
248 metrix.Label{Key: "nagios_job", Value: "gate_job"},
249 )
@@ -260,8 +260,8 @@ func TestV2Gate_G3_ChartLifecycleChurn(t *testing.T) {
260
261 plan3 := emit(false)
262 assert.Zero(t, removeActionsCount(plan3.Actions))
263 - assertPlanHasUpdateForTarget(t, plan3, "nagios.perfdata.check_gate.bytes_a")
264 - assertPlanHasNoRemoveForTarget(t, plan3, "nagios.perfdata.check_gate.bytes_b")
263 + assertPlanHasUpdateForTarget(t, plan3, "perfdata.check_gate.bytes_a")
264 + assertPlanHasNoRemoveForTarget(t, plan3, "perfdata.check_gate.bytes_b")
265 plan4 := emit(false)
266 assert.Zero(t, removeActionsCount(plan4.Actions))
267 })
@@ -318,7 +318,7 @@ func TestV2Gate_G5_ScalingPrecisionEquivalence(t *testing.T) {
318 store := metrix.NewCollectorStore()
319 cc := gateCycleController(t, store)
320 cc.BeginCycle()
321 - sm := store.Write().SnapshotMeter("nagios")
321 + sm := store.Write().SnapshotMeter("")
322 for _, measureSet := range samples.values {
323 fields := perfMeasureSetValues(measureSet.value)
324 if measureSet.counter {
@@ -339,12 +339,56 @@ func TestV2Gate_G5_ScalingPrecisionEquivalence(t *testing.T) {
339 }
340 cc.CommitCycleSuccess()
341 flat := store.Read(metrix.ReadFlatten())
342 - assertMetricMeta(t, flat, "nagios."+candidateKey, tc.expectedUnit, true)
343 - assertMetricChartFamily(t, flat, "nagios."+candidateKey, "Perfdata/check_gate")
342 + assertMetricMeta(t, flat, candidateKey, tc.expectedUnit, true)
343 + assertMetricChartFamily(t, flat, candidateKey, "Perfdata/check_gate")
344 })
345 }
346 }
347
348 +// With an empty meter prefix and context_namespace: nagios, an autogen perfdata chart's context
349 +// is single-namespaced (nagios.perfdata.*) — never doubled (nagios.nagios.*) and never bare
350 +// (perfdata.*). The chart ID intentionally drops the redundant nagios. prefix; the collector
351 +// identity is already in the chart type (the job full name).
352 +func TestV2Gate_AutogenPerfdataContextSingleNamespaced(t *testing.T) {
353 + engine, err := chartengine.New()
354 + require.NoError(t, err)
355 + require.NoError(t, engine.LoadYAML([]byte(New().ChartTemplateYAML()), 1))
356 +
357 + store := metrix.NewCollectorStore()
358 + cc := gateCycleController(t, store)
359 + cc.BeginCycle()
360 + sm := store.Write().SnapshotMeter("")
361 + ls := sm.LabelSet(metrix.Label{Key: "nagios_job", Value: "check_mem"})
362 + fields := defaultPerfMeasureSetValues()
363 + fields[perfFieldValue] = 30000
364 + sm.MeasureSetGauge(
365 + "perfdata.check_memory.bytes_used",
366 + metrix.WithMeasureSetFields(perfMeasureSetFieldSpecs()...),
367 + metrix.WithChartFamily(perfdataFamily("check_memory")),
368 + metrix.WithUnit("bytes"),
369 + ).ObserveFields(fields, ls)
370 + cc.CommitCycleSuccess()
371 +
372 + plan, err := prepareCommittedPlan(engine, store.Read(metrix.ReadFlatten()))
373 + require.NoError(t, err)
374 +
375 + var found bool
376 + for _, action := range plan.Actions {
377 + create, ok := action.(chartengine.CreateChartAction)
378 + if !ok || !strings.Contains(create.ChartID, "perfdata.check_memory") {
379 + continue
380 + }
381 + found = true
382 + assert.Equal(t, "nagios.perfdata.check_memory.bytes_used", create.Meta.Context,
383 + "autogen perfdata context must be single-namespaced via context_namespace")
384 + assert.NotContains(t, create.Meta.Context, "nagios.nagios.",
385 + "autogen context must not double-prefix the namespace")
386 + assert.Falsef(t, strings.HasPrefix(create.ChartID, "nagios."),
387 + "autogen chart ID should drop the redundant nagios. prefix, got %q", create.ChartID)
388 + }
389 + require.True(t, found, "expected an autogen create-chart action for the perfdata measureset")
390 +}
391 +
392 func countActions[T any](actions []chartengine.EngineAction) int {
393 n := 0
394 for _, action := range actions {