From 6b01afc69837a9b5cc27ba5441fc16d3c6233b2d Mon Sep 17 00:00:00 2001 From: Chris Grindstaff Date: Thu, 1 Dec 2022 13:34:44 -0500 Subject: [PATCH] feat: plugins can use raw or display metric in calculations (#1567) * feat: plugins can use raw or display metric in calculations feat: add metric_agent percent operation docs: add metric_agent percent operation to docs fix: Histograms and arrays don't support display metrics yet --- cmd/collectors/rest/plugins/qtree/qtree.go | 3 +- cmd/collectors/rest/plugins/shelf/shelf.go | 3 +- cmd/collectors/rest/rest.go | 4 +- cmd/collectors/restperf/restperf.go | 15 ++-- cmd/collectors/simple/simple.go | 6 +- cmd/collectors/storagegrid/storagegrid.go | 4 +- cmd/collectors/unix/main.go | 6 +- cmd/collectors/zapi/collector/parsers.go | 12 ++- cmd/collectors/zapi/plugins/qtree/qtree.go | 3 +- cmd/collectors/zapi/plugins/sensor/sensor.go | 1 + cmd/collectors/zapi/plugins/shelf/shelf.go | 3 +- cmd/collectors/zapiperf/zapiperf.go | 12 +-- cmd/poller/plugin/metricagent/metric_agent.go | 32 +++++--- conf/rest/9.10.0/aggr.yaml | 3 +- conf/rest/9.12.0/aggr.yaml | 3 +- conf/rest/9.12.0/volume.yaml | 3 + conf/zapi/cdot/9.8.0/volume.yaml | 13 ++-- docs/plugins.md | 13 +++- pkg/matrix/matrix.go | 75 ++++++++++++------- pkg/matrix/metric.go | 5 -- 20 files changed, 115 insertions(+), 104 deletions(-) diff --git a/cmd/collectors/rest/plugins/qtree/qtree.go b/cmd/collectors/rest/plugins/qtree/qtree.go index 6d6738aa8..021cacca7 100644 --- a/cmd/collectors/rest/plugins/qtree/qtree.go +++ b/cmd/collectors/rest/plugins/qtree/qtree.go @@ -90,13 +90,12 @@ func (my *Qtree) Init() error { for _, obj := range quotaMetric { metricName, display, _, _ := util.ParseMetric(obj) - metric, err := my.data.NewMetricFloat64(metricName) + metric, err := my.data.NewMetricFloat64(metricName, display) if err != nil { my.Logger.Error().Stack().Err(err).Msg("add metric") return err } - metric.SetName(display) my.Logger.Trace().Msgf("added metric: (%s) [%s] %s", metricName, display, metric) } diff --git a/cmd/collectors/rest/plugins/shelf/shelf.go b/cmd/collectors/rest/plugins/shelf/shelf.go index 2b28b9b4b..bc97321bf 100644 --- a/cmd/collectors/rest/plugins/shelf/shelf.go +++ b/cmd/collectors/rest/plugins/shelf/shelf.go @@ -151,12 +151,11 @@ func (my *Shelf) Init() error { instanceLabels.NewChildS("", display) my.Logger.Debug().Msgf("added instance label: (%s) [%s]", attribute, display) case "float": - metric, err := my.data[attribute].NewMetricFloat64(metricName) + _, err := my.data[attribute].NewMetricFloat64(metricName, display) if err != nil { my.Logger.Error().Stack().Err(err).Msg("add metric") return err } - metric.SetName(display) my.Logger.Debug().Msgf("added metric: (%s) [%s]", attribute, display) } } diff --git a/cmd/collectors/rest/rest.go b/cmd/collectors/rest/rest.go index aec6425dc..82531d183 100644 --- a/cmd/collectors/rest/rest.go +++ b/cmd/collectors/rest/rest.go @@ -477,7 +477,7 @@ func (r *Rest) HandleResults(result []gjson.Result, prop *prop, isEndPoint bool) for _, metric := range prop.Metrics { metr, ok := mat.GetMetrics()[metric.Name] if !ok { - if metr, err = mat.NewMetricFloat64(metric.Name); err != nil { + if metr, err = mat.NewMetricFloat64(metric.Name, metric.Label); err != nil { r.Logger.Error().Err(err). Str("name", metric.Name). Msg("NewMetricFloat64") @@ -485,8 +485,6 @@ func (r *Rest) HandleResults(result []gjson.Result, prop *prop, isEndPoint bool) } f := instanceData.Get(metric.Name) if f.Exists() { - metr.SetName(metric.Label) - var floatValue float64 switch metric.MetricType { case "duration": diff --git a/cmd/collectors/restperf/restperf.go b/cmd/collectors/restperf/restperf.go index 4c77d05cf..7c7ccbd6f 100644 --- a/cmd/collectors/restperf/restperf.go +++ b/cmd/collectors/restperf/restperf.go @@ -390,13 +390,12 @@ func (r *RestPerf) processWorkLoadCounter() (map[string]*matrix.Matrix, error) { for name, metric := range r.Prop.Metrics { metr, ok := mat.GetMetrics()[name] if !ok { - if metr, err = mat.NewMetricFloat64(name); err != nil { + if metr, err = mat.NewMetricFloat64(name, metric.Label); err != nil { r.Logger.Error().Err(err). Str("name", name). Msg("NewMetricFloat64") } } - metr.SetName(metric.Label) metr.SetExportable(metric.Exportable) } @@ -445,7 +444,7 @@ func (r *RestPerf) processWorkLoadCounter() (map[string]*matrix.Matrix, error) { if m := mat.GetMetric(name); m != nil { continue } - if m, err := mat.NewMetricFloat64(name); err != nil { + if m, err := mat.NewMetricFloat64(name, "resource_latency"); err != nil { return nil, err } else { r.perfProp.counterInfo[name] = &counter{ @@ -455,7 +454,6 @@ func (r *RestPerf) processWorkLoadCounter() (map[string]*matrix.Matrix, error) { unit: r.perfProp.counterInfo[service.GetName()].unit, denominator: "ops", } - m.SetName("resource_latency") m.SetLabel("resource", resource) r.Logger.Debug().Str("name", name).Str("resource", resource).Msg("added workload latency metric") @@ -688,13 +686,12 @@ func (r *RestPerf) PollData() (map[string]*matrix.Matrix, error) { if histogramMetric != nil { r.Logger.Trace().Str("metric", key).Msg("Updating array metric attributes") } else { - histogramMetric, err = curMat.NewMetricFloat64(key) + histogramMetric, err = curMat.NewMetricFloat64(key, metric.Label) if err != nil { r.Logger.Error().Err(err).Str("key", key).Msg("unable to create histogram metric") continue } } - histogramMetric.SetName(metric.Label) histogramMetric.SetArray(true) histogramMetric.SetExportable(metric.Exportable) histogramMetric.SetBuckets(&labels) @@ -705,13 +702,12 @@ func (r *RestPerf) PollData() (map[string]*matrix.Matrix, error) { k := name + arrayKeyToken + label metr, ok := curMat.GetMetrics()[k] if !ok { - if metr, err = curMat.NewMetricFloat64(k); err != nil { + if metr, err = curMat.NewMetricFloat64(k, metric.Label); err != nil { r.Logger.Error().Err(err). Str("name", k). Msg("NewMetricFloat64") continue } - metr.SetName(metric.Label) if x := strings.Split(label, arrayKeyToken); len(x) == 2 { metr.SetLabel("metric", x[0]) metr.SetLabel("submetric", x[1]) @@ -751,14 +747,13 @@ func (r *RestPerf) PollData() (map[string]*matrix.Matrix, error) { } else { metr, ok := curMat.GetMetrics()[name] if !ok { - if metr, err = curMat.NewMetricFloat64(name); err != nil { + if metr, err = curMat.NewMetricFloat64(name, metric.Label); err != nil { r.Logger.Error().Err(err). Str("name", name). Int("instIndex", instIndex). Msg("NewMetricFloat64") } } - metr.SetName(metric.Label) metr.SetExportable(metric.Exportable) if c, err := strconv.ParseFloat(f.value, 64); err == nil { if err = metr.SetValueFloat64(instance, c); err != nil { diff --git a/cmd/collectors/simple/simple.go b/cmd/collectors/simple/simple.go index 4ee1eaa03..9dc184043 100644 --- a/cmd/collectors/simple/simple.go +++ b/cmd/collectors/simple/simple.go @@ -54,8 +54,7 @@ func (n *NodeMon) Init(a *collector.AbstractCollector) error { func (n *NodeMon) loadMetrics(counters *node.Node) error { var ( - metric matrix.Metric - err error + err error ) n.Logger.Debug().Msg("initializing metric cache") @@ -70,10 +69,9 @@ func (n *NodeMon) loadMetrics(counters *node.Node) error { dtype := "int64" n.Logger.Trace().Msgf("handling (%s) (%s) dtype=%s", name, display, dtype) - if metric, err = mat.NewMetricType(name, dtype); err != nil { + if _, err = mat.NewMetricType(name, dtype, display); err != nil { return err } - metric.SetName(display) n.Logger.Debug().Msgf("(%s) added metric (%s)", name, display) } diff --git a/cmd/collectors/storagegrid/storagegrid.go b/cmd/collectors/storagegrid/storagegrid.go index 764207101..1e1ceb008 100644 --- a/cmd/collectors/storagegrid/storagegrid.go +++ b/cmd/collectors/storagegrid/storagegrid.go @@ -249,7 +249,7 @@ func (s *StorageGrid) handleResults(result []gjson.Result) uint64 { for _, metric := range s.Props.Metrics { metr, ok := mat.GetMetrics()[metric.Name] if !ok { - if metr, err = mat.NewMetricFloat64(metric.Name); err != nil { + if metr, err = mat.NewMetricFloat64(metric.Name, metric.Label); err != nil { s.Logger.Error().Err(err). Str("name", metric.Name). Msg("NewMetricFloat64") @@ -257,8 +257,6 @@ func (s *StorageGrid) handleResults(result []gjson.Result) uint64 { } f := instanceData.Get(metric.Name) if f.Exists() { - metr.SetName(metric.Label) - var floatValue float64 switch metric.MetricType { case "": diff --git a/cmd/collectors/unix/main.go b/cmd/collectors/unix/main.go index 0adba1f7f..f79137512 100644 --- a/cmd/collectors/unix/main.go +++ b/cmd/collectors/unix/main.go @@ -218,10 +218,9 @@ func (u *Unix) loadMetrics(counters *node.Node) error { // counter is scalar metric if _, has := _Metrics[name]; has { - if metric, err = mat.NewMetricType(name, dtype); err != nil { + if _, err = mat.NewMetricType(name, dtype, display); err != nil { return err } - metric.SetName(display) u.Logger.Debug().Msgf("(%s) added metric (%s)", name, display) // counter is histogram @@ -251,10 +250,9 @@ func (u *Unix) loadMetrics(counters *node.Node) error { continue } - if metric, err = mat.NewMetricType(name+"."+label, dtype); err != nil { + if metric, err = mat.NewMetricType(name+"."+label, dtype, name); err != nil { return err } - metric.SetName(name) metric.SetLabel("metric", ldisplay) u.histogramLabels[name] = append(u.histogramLabels[name], label) } diff --git a/cmd/collectors/zapi/collector/parsers.go b/cmd/collectors/zapi/collector/parsers.go index d92f1a670..cf146ba67 100644 --- a/cmd/collectors/zapi/collector/parsers.go +++ b/cmd/collectors/zapi/collector/parsers.go @@ -79,7 +79,6 @@ func (z *Zapi) HandleCounter(path []string, content string) string { var ( name, display, key string splitValues, fullPath []string - metric matrix.Metric err error ) @@ -94,7 +93,6 @@ func (z *Zapi) HandleCounter(path []string, content string) string { name = strings.TrimSpace(strings.TrimLeft(name, "^")) - //full_path = append(path[1:], name) fullPath = append(path, name) key = strings.Join(fullPath, ".") @@ -115,16 +113,16 @@ func (z *Zapi) HandleCounter(path []string, content string) string { } else { // use user-defined metric type if t := z.Params.GetChildContentS("metric_type"); t != "" { - metric, err = mat.NewMetricType(key, t) + _, err = mat.NewMetricType(key, t, display) // use uint64 as default, since nearly all ZAPI counters are unsigned } else { - metric, err = mat.NewMetricUint64(key) + _, err = mat.NewMetricUint64(key, display) } if err != nil { - z.Logger.Error().Stack().Err(err).Msgf("add as metric [%s]: %v", key, display) + z.Logger.Error().Err(err).Str("key", key).Str("display", display).Msg("Failed to add metric") } else { - metric.SetName(display) - z.Logger.Trace().Msgf("%sadd as metric (%s) [%s]%s => %v", color.Blue, key, display, color.End, fullPath) + z.Logger.Trace().Str("key", key).Str("display", display).Strs("fullPath", fullPath). + Msg("Add metric") } } diff --git a/cmd/collectors/zapi/plugins/qtree/qtree.go b/cmd/collectors/zapi/plugins/qtree/qtree.go index 0dc1e4f04..e8b973378 100644 --- a/cmd/collectors/zapi/plugins/qtree/qtree.go +++ b/cmd/collectors/zapi/plugins/qtree/qtree.go @@ -79,13 +79,12 @@ func (my *Qtree) Init() error { metricName, display, _, _ := util.ParseMetric(obj) - metric, err := my.data.NewMetricFloat64(metricName) + metric, err := my.data.NewMetricFloat64(metricName, display) if err != nil { my.Logger.Error().Stack().Err(err).Msg("add metric") return err } - metric.SetName(display) my.Logger.Debug().Msgf("added metric: (%s) [%s] %s", metricName, display, metric) } diff --git a/cmd/collectors/zapi/plugins/sensor/sensor.go b/cmd/collectors/zapi/plugins/sensor/sensor.go index 0e6d348d9..be053d9df 100644 --- a/cmd/collectors/zapi/plugins/sensor/sensor.go +++ b/cmd/collectors/zapi/plugins/sensor/sensor.go @@ -1,6 +1,7 @@ /* * Copyright NetApp Inc, 2021 All rights reserved */ + package sensor import ( diff --git a/cmd/collectors/zapi/plugins/shelf/shelf.go b/cmd/collectors/zapi/plugins/shelf/shelf.go index 8c12ca180..89169c880 100644 --- a/cmd/collectors/zapi/plugins/shelf/shelf.go +++ b/cmd/collectors/zapi/plugins/shelf/shelf.go @@ -126,12 +126,11 @@ func (my *Shelf) Init() error { instanceLabels.NewChildS("", display) my.Logger.Debug().Msgf("added instance label: (%s) (%s) [%s]", attribute, x.GetNameS(), display) case "float": - metric, err := my.data[attribute].NewMetricFloat64(metricName) + _, err := my.data[attribute].NewMetricFloat64(metricName, display) if err != nil { my.Logger.Error().Stack().Err(err).Msg("add metric") return err } - metric.SetName(display) my.Logger.Debug().Msgf("added metric: (%s) (%s) [%s]", attribute, x.GetNameS(), display) } } diff --git a/cmd/collectors/zapiperf/zapiperf.go b/cmd/collectors/zapiperf/zapiperf.go index 8368bf48b..40e029318 100644 --- a/cmd/collectors/zapiperf/zapiperf.go +++ b/cmd/collectors/zapiperf/zapiperf.go @@ -1031,10 +1031,9 @@ func (z *ZapiPerf) PollCounter() (map[string]*matrix.Matrix, error) { oldMetrics.Remove(name) continue } - if m, err := mat.NewMetricFloat64(name); err != nil { + if m, err := mat.NewMetricFloat64(name, "resource_latency"); err != nil { return nil, err } else { - m.SetName("resource_latency") m.SetLabel("resource", resource) m.SetProperty(service.GetProperty()) // base counter is the ops of the same resource @@ -1175,13 +1174,12 @@ func (z *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled if histogramMetric != nil { z.Logger.Trace().Str("metric", key).Msg("Updating array metric attributes") } else { - histogramMetric, err = mat.NewMetricFloat64(key) + histogramMetric, err = mat.NewMetricFloat64(key, display) if err != nil { z.Logger.Error().Err(err).Str("key", key).Msg("unable to create histogram metric") return "" } } - histogramMetric.SetName(display) histogramMetric.SetProperty(property) histogramMetric.SetComment(baseKey) histogramMetric.SetExportable(enabled) @@ -1197,14 +1195,13 @@ func (z *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled if m = mat.GetMetric(key); m != nil { z.Logger.Trace().Msgf("updating array metric [%s] attributes", key) - } else if m, err = mat.NewMetricFloat64(key); err == nil { + } else if m, err = mat.NewMetricFloat64(key, display); err == nil { z.Logger.Trace().Msgf("%s+[%s] added array metric (%s), element with label (%s)%s", color.Pink, name, display, label, color.End) } else { z.Logger.Error().Err(err).Msgf("add array metric element [%s]: ", key) return "" } - m.SetName(display) m.SetProperty(property) m.SetComment(baseKey) m.SetExportable(enabled) @@ -1231,7 +1228,7 @@ func (z *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled var m matrix.Metric if m = mat.GetMetric(name); m != nil { z.Logger.Trace().Msgf("updating scalar metric [%s] attributes", name) - } else if m, err = mat.NewMetricFloat64(name); err == nil { + } else if m, err = mat.NewMetricFloat64(name, display); err == nil { z.Logger.Trace().Msgf("%s+[%s] added scalar metric (%s)%s", color.Cyan, name, display, color.End) } else { z.Logger.Error().Err(err).Msgf("add scalar metric [%s]", name) @@ -1239,7 +1236,6 @@ func (z *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled } z.scalarCounters = append(z.scalarCounters, name) - m.SetName(display) m.SetProperty(property) m.SetComment(baseCounter) m.SetExportable(enabled) diff --git a/cmd/poller/plugin/metricagent/metric_agent.go b/cmd/poller/plugin/metricagent/metric_agent.go index 63fe74d01..146300f05 100644 --- a/cmd/poller/plugin/metricagent/metric_agent.go +++ b/cmd/poller/plugin/metricagent/metric_agent.go @@ -62,10 +62,9 @@ func (a *MetricAgent) computeMetrics(m *matrix.Matrix) error { // map values for compute_metric mapping rules for _, r := range a.computeMetricRules { - - if metric = m.GetMetric(r.metric); metric == nil { + if metric = a.getMetric(m, r.metric); metric == nil { if metric, err = m.NewMetricFloat64(r.metric); err != nil { - a.Logger.Error().Stack().Err(err).Str("new metric", r.metric).Msg("computeMetrics: failed to create metric") + a.Logger.Error().Err(err).Str("metric", r.metric).Msg("Failed to create metric") return err } else { metric.SetProperty("compute_metric mapping") @@ -76,7 +75,7 @@ func (a *MetricAgent) computeMetrics(m *matrix.Matrix) error { var result float64 // Parse first operand and store in result for further processing - if firstMetricVal = m.GetMetric(r.metricNames[0]); firstMetricVal != nil { + if firstMetricVal = a.getMetric(m, r.metricNames[0]); firstMetricVal != nil { if val, ok := firstMetricVal.GetValueFloat64(instance); ok { result = val } else { @@ -92,7 +91,7 @@ func (a *MetricAgent) computeMetrics(m *matrix.Matrix) error { if value, err := strconv.Atoi(r.metricNames[i]); err == nil { v = float64(value) } else { - metricVal = m.GetMetric(r.metricNames[i]) + metricVal = a.getMetric(m, r.metricNames[i]) if metricVal != nil { v, _ = metricVal.GetValueFloat64(instance) } else { @@ -112,16 +111,17 @@ func (a *MetricAgent) computeMetrics(m *matrix.Matrix) error { if v != 0 { result /= v } else { - a.Logger.Error(). - Str("operation", r.operation). - Msg("Division by zero operation") + a.Logger.Error().Str("operation", r.operation).Msg("Division by zero operation") + } + case "PERCENT": + if v != 0 { + result = (result / v) * 100 + } else { + a.Logger.Error().Str("operation", r.operation).Msg("Division by zero operation") } default: - a.Logger.Warn(). - Str("operation", r.operation). - Msg("Unknown operation") + a.Logger.Warn().Str("operation", r.operation).Msg("Unknown operation") } - } _ = metric.SetValueFloat64(instance, result) @@ -130,3 +130,11 @@ func (a *MetricAgent) computeMetrics(m *matrix.Matrix) error { } return nil } + +func (a *MetricAgent) getMetric(m *matrix.Matrix, name string) matrix.Metric { + metric := m.DisplayMetric(name) + if metric != nil { + return metric + } + return m.GetMetric(name) +} diff --git a/conf/rest/9.10.0/aggr.yaml b/conf/rest/9.10.0/aggr.yaml index 1a6dbd554..067367e1a 100644 --- a/conf/rest/9.10.0/aggr.yaml +++ b/conf/rest/9.10.0/aggr.yaml @@ -59,8 +59,7 @@ plugins: - total_physical_used SUBTRACT space.efficiency.logical_used space.efficiency.savings - physical_used_wo_snapshots SUBTRACT space.efficiency_without_snapshots.logical_used space.efficiency_without_snapshots.savings - physical_used_wo_snapshots_flexclones SUBTRACT space.efficiency_without_snapshots_flexclones.logical_used space.efficiency_without_snapshots_flexclones.savings - - space_used_percent DIVIDE space.block_storage.used space.block_storage.size - - space_used_percent MULTIPLY space_used_percent 100 + - space_used_percent PERCENT space.block_storage.used space.block_storage.size export_options: diff --git a/conf/rest/9.12.0/aggr.yaml b/conf/rest/9.12.0/aggr.yaml index d1e6d61fc..a9ff56fce 100644 --- a/conf/rest/9.12.0/aggr.yaml +++ b/conf/rest/9.12.0/aggr.yaml @@ -70,8 +70,7 @@ plugins: - total_physical_used SUBTRACT space.efficiency.logical_used space.efficiency.savings - physical_used_wo_snapshots SUBTRACT space.efficiency_without_snapshots.logical_used space.efficiency_without_snapshots.savings - physical_used_wo_snapshots_flexclones SUBTRACT space.efficiency_without_snapshots_flexclones.logical_used space.efficiency_without_snapshots_flexclones.savings - - space_used_percent DIVIDE space.block_storage.used space.block_storage.size - - space_used_percent MULTIPLY space_used_percent 100 + - space_used_percent PERCENT space.block_storage.used space.block_storage.size export_options: instance_keys: diff --git a/conf/rest/9.12.0/volume.yaml b/conf/rest/9.12.0/volume.yaml index 06c2c38fe..606fbba4b 100644 --- a/conf/rest/9.12.0/volume.yaml +++ b/conf/rest/9.12.0/volume.yaml @@ -66,6 +66,9 @@ plugins: - Volume: schedule: - data: 900s # should be multiple of data poll duration + - MetricAgent: + compute_metric: + - inode_used_percent PERCENT inode_files_used inode_files_total - LabelAgent: value_to_num: - new_status state online online `0` diff --git a/conf/zapi/cdot/9.8.0/volume.yaml b/conf/zapi/cdot/9.8.0/volume.yaml index e337b8d0b..8ee6b6d66 100644 --- a/conf/zapi/cdot/9.8.0/volume.yaml +++ b/conf/zapi/cdot/9.8.0/volume.yaml @@ -67,10 +67,13 @@ counters: - ^encrypt => isEncrypted plugins: - Volume: - schedule: - - data: 900s # should be multiple of data poll duration - LabelAgent: + - Volume: + schedule: + - data: 900s # should be multiple of data poll duration + - MetricAgent: + compute_metric: + - inode_used_percent PERCENT inode_files_used inode_files_total + - LabelAgent: # metric label zapi_value rest_value `default_value` value_to_num: - new_status state online online `0` @@ -92,7 +95,7 @@ plugins: # - volume `MDV_aud_.+` replace: - style style `flexgroup_constituent` `flexgroup` - Aggregator: + - Aggregator: - volumevolume node,svm,aggr,style export_options: diff --git a/docs/plugins.md b/docs/plugins.md index a133d4508..e4de680b3 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -555,7 +555,8 @@ plugins: - raid_disk_count ADD block_storage.primary.disk_count block_storage.hybrid_cache.disk_count ``` -**Note:** Metrics for creating new metric should use name defined in left side of => +**Note:** Metric names used to create new metrics can come from the left or right side of the rename operator (`=>`) +**Note:** The metric agent currently does not work for histogram or array metrics. ## compute_metric @@ -565,7 +566,7 @@ mathematical operation. You can provide a numeric value or a metric name with an operation. The plugin will use the provided number or fetch the value of a given metric, perform the requested mathematical operation, and store the result in new custom metric. -Currently, we support these operations: ADD SUBTRACT MULTIPLY DIVIDE +Currently, we support these operations: ADD SUBTRACT MULTIPLY DIVIDE PERCENT Rule syntax: @@ -621,3 +622,11 @@ compute_metric: # value of metric "transmission_rate" would be division of metric value of transfer.bytes_transferred by metric value of transfer.total_duration. # transmission_rate = transfer.bytes_transferred / transfer.total_duration ``` + +```yaml +compute_metric: + - inode_used_percent PERCENT inode_files_used inode_files_total +# a new metric named "inode_used_percent" will be created by dividing the metric "inode_files_used" by +# "inode_files_total" and multiplying the result by 100. +# inode_used_percent = inode_files_used / inode_files_total * 100 +``` diff --git a/pkg/matrix/matrix.go b/pkg/matrix/matrix.go index 61437bc96..60eb9d80e 100644 --- a/pkg/matrix/matrix.go +++ b/pkg/matrix/matrix.go @@ -20,14 +20,15 @@ import ( ) type Matrix struct { - UUID string - Object string - Identifier string - globalLabels *dict.Dict - instances map[string]*Instance - metrics map[string]Metric - exportOptions *node.Node - exportable bool + UUID string + Object string + Identifier string + globalLabels *dict.Dict + instances map[string]*Instance + metrics map[string]Metric // ONTAP metric name => metric (in templates, this is left side) + displayMetrics map[string]Metric // display name of metric to => metric (in templates, this is right side) + exportOptions *node.Node + exportable bool } func New(uuid, object string, identifier string) *Matrix { @@ -35,6 +36,7 @@ func New(uuid, object string, identifier string) *Matrix { me.globalLabels = dict.New() me.instances = make(map[string]*Instance, 0) me.metrics = make(map[string]Metric, 0) + me.displayMetrics = make(map[string]Metric, 0) me.exportable = true return &me } @@ -78,7 +80,9 @@ func (m *Matrix) Clone(withData, withMetrics, withInstances bool) *Matrix { if withMetrics { for key, metric := range m.GetMetrics() { - clone.metrics[key] = metric.Clone(withData) + c := metric.Clone(withData) + clone.metrics[key] = c + clone.displayMetrics[c.GetName()] = c } } @@ -93,6 +97,13 @@ func (m *Matrix) Reset() { } } +func (m *Matrix) DisplayMetric(name string) Metric { + if metric, has := m.displayMetrics[name]; has { + return metric + } + return nil +} + func (m *Matrix) GetMetric(key string) Metric { if metric, has := m.metrics[key]; has { return metric @@ -104,53 +115,58 @@ func (m *Matrix) GetMetrics() map[string]Metric { return m.metrics } -func (m *Matrix) NewMetricInt64(key string) (Metric, error) { - metric := &MetricInt64{AbstractMetric: &AbstractMetric{name: key, dtype: "int64", exportable: true}} +func (m *Matrix) NewMetricInt64(key string, display ...string) (Metric, error) { + metric := &MetricInt64{AbstractMetric: newAbstract(key, "int64", display...)} return metric, m.addMetric(key, metric) } -func (m *Matrix) NewMetricUint8(key string) (Metric, error) { - metric := &MetricUint8{AbstractMetric: &AbstractMetric{name: key, dtype: "uint8", exportable: true}} +func (m *Matrix) NewMetricUint8(key string, display ...string) (Metric, error) { + metric := &MetricUint8{AbstractMetric: newAbstract(key, "uint8", display...)} return metric, m.addMetric(key, metric) } -func (m *Matrix) NewMetricUint64(key string) (Metric, error) { - metric := &MetricUint64{AbstractMetric: &AbstractMetric{name: key, dtype: "uint64", exportable: true}} +func (m *Matrix) NewMetricUint64(key string, display ...string) (Metric, error) { + metric := &MetricUint64{AbstractMetric: newAbstract(key, "uint64", display...)} return metric, m.addMetric(key, metric) } -func (m *Matrix) NewMetricFloat64(key string) (Metric, error) { - metric := &MetricFloat64{AbstractMetric: &AbstractMetric{name: key, dtype: "float64", exportable: true}} +func (m *Matrix) NewMetricFloat64(key string, display ...string) (Metric, error) { + metric := &MetricFloat64{AbstractMetric: newAbstract(key, "float64", display...)} return metric, m.addMetric(key, metric) } -func (m *Matrix) NewMetricType(key, dtype string) (Metric, error) { +func (m *Matrix) NewMetricType(key string, dataType string, display ...string) (Metric, error) { - switch dtype { + switch dataType { case "int64": - return m.NewMetricInt64(key) + return m.NewMetricInt64(key, display...) case "uint8": - return m.NewMetricUint8(key) + return m.NewMetricUint8(key, display...) case "uint64": - return m.NewMetricUint64(key) + return m.NewMetricUint64(key, display...) case "float64": - return m.NewMetricFloat64(key) + return m.NewMetricFloat64(key, display...) default: - return nil, errs.New(ErrInvalidDtype, dtype) + return nil, errs.New(ErrInvalidDtype, dataType) } } -func (m *Matrix) ChangeMetricType(key, dtype string) (Metric, error) { - m.RemoveMetric(key) - return m.NewMetricType(key, dtype) +func newAbstract(key string, dataType string, display ...string) *AbstractMetric { + name := key + if len(display) > 0 { + name = display[0] + } + return &AbstractMetric{name: name, dtype: dataType, exportable: true} } func (m *Matrix) addMetric(key string, metric Metric) error { - if _, has := m.metrics[key]; has { + if _, has := m.metrics[key]; has { // Fail if a metric with the same key already exists return errs.New(ErrDuplicateMetricKey, key) } + // Histograms and arrays don't support display metrics yet, last write wins metric.Reset(len(m.instances)) m.metrics[key] = metric + m.displayMetrics[metric.GetName()] = metric return nil } @@ -164,7 +180,8 @@ func (m *Matrix) RemoveExceptMetric(key string) { return } m.metrics = make(map[string]Metric) - m.metrics[key] = prev + m.displayMetrics = make(map[string]Metric) + _ = m.addMetric(key, prev) } func (m *Matrix) GetInstance(key string) *Instance { diff --git a/pkg/matrix/metric.go b/pkg/matrix/metric.go index f39357df0..70bfe822f 100644 --- a/pkg/matrix/metric.go +++ b/pkg/matrix/metric.go @@ -21,7 +21,6 @@ type Metric interface { // methods related to metric attributes GetName() string - SetName(string) GetType() string SetLabel(string, string) SetLabels(*dict.Dict) @@ -128,10 +127,6 @@ func (m *AbstractMetric) GetName() string { return m.name } -func (m *AbstractMetric) SetName(name string) { - m.name = name -} - func (m *AbstractMetric) IsExportable() bool { return m.exportable }