Repository: prometheus/snmp_exporter
Pull request #1026 (2023-10-18 09:57:50)

Add a scaling factor
Allow the metric value to be pre-scaled based on a float64 multiplier. This allows for simple unit conversions like bits to bytes, centi-degrees, etc.

Fixes: https://github.com/prometheus/snmp_exporter/issues/734

SuperQ: CC @bastischubert 
bastischubert: very nice, one thing that i noticed is that we do almost the same with modbus_exporter but named it "factor" instead of "scale"
matthiasr: Conversely, in the statsd exporter we use `scale` 😂

Patch:
diff --git a/.gitignore b/.gitignore
index 0600fb9..c2f487a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,4 @@ dependencies-stamp
 
 generator/generator
 generator/mibs
+generator/snmp.yml
diff --git a/collector/collector.go b/collector/collector.go
index 9080898..7cfbf4d 100644
--- a/collector/collector.go
+++ b/collector/collector.go
@@ -607,6 +607,10 @@ func pduToSamples(indexOids []int, pdu *gosnmp.SnmpPDU, metric *config.Metric, o
 		}
 	}
 
+	if metric.Scale != 0.0 {
+		value *= metric.Scale
+	}
+
 	sample, err := prometheus.NewConstMetric(prometheus.NewDesc(metric.Name, metric.Help, labelnames, nil),
 		t, value, labelvalues...)
 	if err != nil {
diff --git a/config/config.go b/config/config.go
index 428f688..2cf1bef 100644
--- a/config/config.go
+++ b/config/config.go
@@ -191,6 +191,7 @@ type Metric struct {
 	Lookups        []*Lookup                  `yaml:"lookups,omitempty"`
 	RegexpExtracts map[string][]RegexpExtract `yaml:"regex_extracts,omitempty"`
 	EnumValues     map[int]string             `yaml:"enum_values,omitempty"`
+	Scale          float64                    `yaml:"scale,omitempty"`
 }
 
 type Index struct {
diff --git a/generator/FORMAT.md b/generator/FORMAT.md
index 80f4db0..596b7f7 100644
--- a/generator/FORMAT.md
+++ b/generator/FORMAT.md
@@ -62,6 +62,7 @@ modules:
          Temp: # A new metric will be created appending this to the metricName to become metricNameTemp.
            - regex: '(.*)' # Regex to extract a value from the returned SNMP walks's value.
              value: '$1' # Parsed as float64, defaults to $1.
+       scale: 0.125 # Scale the sample by this value, for example bits to bytes.
        enum_values: # Enum for this metric. Only used with the enum types.
           0: true
           1: false
diff --git a/generator/README.md b/generator/README.md
index edc5749..a5ff197 100644
--- a/generator/README.md
+++ b/generator/README.md
@@ -146,6 +146,7 @@ modules:
               value: '1' # The first entry whose regex matches and whose value parses wins.
             - regex: '.*'
               value: '0'
+        scale: 1.0 # Scale the value of the sample by this value.
         type: DisplayString # Override the metric type, possible types are:
                              #   gauge:   An integer with type gauge.
                              #   counter: An integer with type counter.
diff --git a/generator/config.go b/generator/config.go
index 46578c8..68802cc 100644
--- a/generator/config.go
+++ b/generator/config.go
@@ -29,6 +29,7 @@ type Config struct {
 type MetricOverrides struct {
 	Ignore         bool                              `yaml:"ignore,omitempty"`
 	RegexpExtracts map[string][]config.RegexpExtract `yaml:"regex_extracts,omitempty"`
+	Scale          float64                           `yaml:"scale,omitempty"`
 	Type           string                            `yaml:"type,omitempty"`
 }
 
diff --git a/generator/generator.yml b/generator/generator.yml
index c47796d..3e2e76e 100644
--- a/generator/generator.yml
+++ b/generator/generator.yml
@@ -154,6 +154,10 @@ modules:
         lookup: st4AdcSensorName
         drop_source_indexes: true
 
+    overrides:
+      st4TempSensorValue:
+        scale: 0.1
+
 # Palo Alto Firewalls
 #
 # Palo Alto MIBs can be found here:
diff --git a/generator/tree.go b/generator/tree.go
index a438062..d3e30da 100644
--- a/generator/tree.go
+++ b/generator/tree.go
@@ -502,6 +502,7 @@ func generateConfigModule(cfg *ModuleConfig, node *Node, nameToNode map[string]*
 		for _, metric := range out.Metrics {
 			if name == metric.Name || name == metric.Oid {
 				metric.RegexpExtracts = params.RegexpExtracts
+				metric.Scale = params.Scale
 			}
 		}
 	}
diff --git a/snmp.yml b/snmp.yml
index 3abe28c..af6e941 100644
--- a/snmp.yml
+++ b/snmp.yml
@@ -23225,6 +23225,7 @@ modules:
         labelname: st4UnitName
         oid: 1.3.6.1.4.1.1718.4.1.2.2.1.3
         type: DisplayString
+      scale: 0.1
     - name: st4TempSensorStatus
       oid: 1.3.6.1.4.1.1718.4.1.9.3.1.2
       type: gauge


Test patch:
diff --git a/collector/collector_test.go b/collector/collector_test.go
index 7a588fe..e00e0ea 100644
--- a/collector/collector_test.go
+++ b/collector/collector_test.go
@@ -231,6 +231,23 @@ func TestPduToSample(t *testing.T) {
 			oidToPdu:        make(map[string]gosnmp.SnmpPDU),
 			expectedMetrics: []string{`Desc{fqName: "test_metric", help: "Help string", constLabels: {}, variableLabels: []} gauge:{value:2}`},
 		},
+		{
+			pdu: &gosnmp.SnmpPDU{
+				Name:  "1.1.1.1.1",
+				Type:  gosnmp.Integer,
+				Value: 420,
+			},
+			indexOids: []int{},
+			metric: &config.Metric{
+				Name:  "test_metric",
+				Oid:   "1.1.1.1.1",
+				Type:  "gauge",
+				Help:  "Help string",
+				Scale: 0.1,
+			},
+			oidToPdu:        make(map[string]gosnmp.SnmpPDU),
+			expectedMetrics: []string{`Desc{fqName: "test_metric", help: "Help string", constLabels: {}, variableLabels: []} gauge:{value:42}`},
+		},
 		{
 			pdu: &gosnmp.SnmpPDU{
 				Name:  "1.1.1.1.1",