diff --git a/cmd/poller/plugin/metricagent/metric_agent.go b/cmd/poller/plugin/metricagent/metric_agent.go index 4e3d3e20d..5680f4164 100644 --- a/cmd/poller/plugin/metricagent/metric_agent.go +++ b/cmd/poller/plugin/metricagent/metric_agent.go @@ -5,6 +5,7 @@ package metricagent import ( + "errors" "github.com/netapp/harvest/v2/cmd/poller/plugin" "github.com/netapp/harvest/v2/pkg/collector" "github.com/netapp/harvest/v2/pkg/conf" @@ -18,7 +19,7 @@ import ( type MetricAgent struct { *plugin.AbstractPlugin - actions []func(*matrix.Matrix) error + actions []func(map[string]*matrix.Matrix) error computeMetricRules []computeMetricRule } @@ -48,40 +49,54 @@ func (a *MetricAgent) Init(remote conf.Remote) error { func (a *MetricAgent) Run(dataMap map[string]*matrix.Matrix) ([]*matrix.Matrix, *collector.Metadata, error) { - var err error - data := dataMap[a.Object] + ee := make([]error, 0, len(a.actions)) for _, foo := range a.actions { - _ = foo(data) + err := foo(dataMap) + ee = append(ee, err) } - return nil, nil, err + return nil, nil, errors.Join(ee...) } -func (a *MetricAgent) computeMetrics(m *matrix.Matrix) error { +func (a *MetricAgent) computeMetrics(dataMap map[string]*matrix.Matrix) error { var ( metric *matrix.Metric metricVal, firstMetricVal *matrix.Metric + m []*matrix.Matrix err error metricNotFound []error ) + data := dataMap[a.Object] + // map values for compute_metric mapping rules for _, r := range a.computeMetricRules { - if metric = a.getMetric(m, r.metric); metric == nil { - if metric, err = m.NewMetricFloat64(r.metric); err != nil { + m = make([]*matrix.Matrix, len(r.metricNames)) + for i := range r.metricNames { + if data == nil { + m[i] = dataMap[r.metricNames[i]] + } else { + m[i] = data + } + } + if m[0] == nil { + return errs.New(errs.ErrMissingMetric, "matrix not found for metric "+r.metricNames[0]) + } + if metric = a.getMetric(m[0], r.metric); metric == nil { + if metric, err = m[0].NewMetricFloat64(r.metric); err != nil { a.SLogger.Error("Failed to create metric", slogx.Err(err), slog.String("metric", r.metric)) return err } metric.SetProperty("compute_metric mapping") } - for _, instance := range m.GetInstances() { + for iKey, instance := range m[0].GetInstances() { var result float64 // Parse first operand and store in result for further processing - if firstMetricVal = a.getMetric(m, r.metricNames[0]); firstMetricVal != nil { + if firstMetricVal = a.getMetric(m[0], r.metricNames[0]); firstMetricVal != nil { if val, ok := firstMetricVal.GetValueFloat64(instance); ok { result = val } else { @@ -97,9 +112,12 @@ func (a *MetricAgent) computeMetrics(m *matrix.Matrix) error { if value, err := strconv.Atoi(r.metricNames[i]); err == nil { v = float64(value) } else { - metricVal = a.getMetric(m, r.metricNames[i]) + if m[i] == nil || m[i].GetInstance(iKey) == nil { + return errs.New(errs.ErrMissingMetric, "matrix or instance not found for metric "+r.metricNames[i]) + } + metricVal = a.getMetric(m[i], r.metricNames[i]) if metricVal != nil { - v, _ = metricVal.GetValueFloat64(instance) + v, _ = metricVal.GetValueFloat64(m[i].GetInstance(iKey)) } else { metricNotFound = append(metricNotFound, err) break diff --git a/cmd/poller/plugin/metricagent/metric_agent_test.go b/cmd/poller/plugin/metricagent/metric_agent_test.go index b5e736c9b..0812f9614 100644 --- a/cmd/poller/plugin/metricagent/metric_agent_test.go +++ b/cmd/poller/plugin/metricagent/metric_agent_test.go @@ -41,7 +41,7 @@ func newAgent() *MetricAgent { return p } -func TestComputeMetricsRule(t *testing.T) { +func TestComputeMetricsRuleWithSingleMatrix(t *testing.T) { var ( instanceA, instanceB *matrix.Instance @@ -112,7 +112,9 @@ func TestComputeMetricsRule(t *testing.T) { assert.Nil(t, err) metricTotalDuration.SetValueFloat64(instanceB, 3600) - err = p.computeMetrics(m) + dataMap := make(map[string]*matrix.Matrix) + dataMap[p.Object] = m + _, _, err = p.Run(dataMap) assert.Nil(t, err) // check "space_total" for instanceA @@ -154,3 +156,51 @@ func TestComputeMetricsRule(t *testing.T) { assert.True(t, ok) assert.Equal(t, metricTransmissionRateVal, expected) } + +func TestComputeMetricsRuleWithMultiMatrix(t *testing.T) { + + var ( + instanceA, instanceB *matrix.Instance + metricDataBytes, metricTotalSpaceBytes, metricUsedPerc *matrix.Metric + expected float64 + err error + ) + + params := node.NewS("MetricAgent") + // create metric "storagegrid_storage_utilization_used_percent", which is percent of the metric value of storagegrid_storage_utilization_data_bytes by storagegrid_storage_utilization_total_space_bytes + params.NewChildS("compute_metric", "").NewChildS("", "storagegrid_storage_utilization_used_percent PERCENT storagegrid_storage_utilization_data_bytes storagegrid_storage_utilization_total_space_bytes") + abc := plugin.New("Test", nil, params, nil, "", nil) + p := &MetricAgent{AbstractPlugin: abc} + if err := p.Init(conf.Remote{}); err != nil { + panic(err) + } + + m1 := matrix.New("Matrix1", "", "Prometheus") + m2 := matrix.New("Matrix2", "", "Prometheus") + + instanceA, err = m1.NewInstance("A") + assert.Nil(t, err) + + instanceB, err = m2.NewInstance("A") + assert.Nil(t, err) + + metricDataBytes, err = m1.NewMetricFloat64("storagegrid_storage_utilization_data_bytes") + assert.Nil(t, err) + metricDataBytes.SetValueFloat64(instanceA, 9000000) + + metricTotalSpaceBytes, err = m2.NewMetricFloat64("storagegrid_storage_utilization_total_space_bytes") + assert.Nil(t, err) + metricTotalSpaceBytes.SetValueFloat64(instanceB, 36000000) + + dataMap := make(map[string]*matrix.Matrix) + dataMap["storagegrid_storage_utilization_data_bytes"] = m1 + dataMap["storagegrid_storage_utilization_total_space_bytes"] = m2 + _, _, err = p.Run(dataMap) + assert.Nil(t, err) + + expected = 25 + metricUsedPerc = m1.GetMetric("storagegrid_storage_utilization_used_percent") + metricUsedPercVal, ok := metricUsedPerc.GetValueFloat64(instanceA) + assert.True(t, ok) + assert.Equal(t, metricUsedPercVal, expected) +} diff --git a/cmd/poller/plugin/metricagent/parse_rules.go b/cmd/poller/plugin/metricagent/parse_rules.go index 893ccfcd8..cae0c1ed5 100644 --- a/cmd/poller/plugin/metricagent/parse_rules.go +++ b/cmd/poller/plugin/metricagent/parse_rules.go @@ -36,7 +36,7 @@ func (a *MetricAgent) parseRules() int { } } - a.actions = make([]func(matrix *matrix.Matrix) error, 0) + a.actions = make([]func(map[string]*matrix.Matrix) error, 0) count := 0 for _, c := range a.Params.GetChildren() {