Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add type to parse date and time #1234

Merged
merged 2 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/gosnmp/gosnmp"
"github.com/itchyny/timefmt-go"
"github.com/prometheus/client_golang/prometheus"

"github.com/prometheus/snmp_exporter/config"
Expand Down Expand Up @@ -544,6 +545,15 @@ func parseDateAndTime(pdu *gosnmp.SnmpPDU) (float64, error) {
return float64(t.Unix()), nil
}

func parseDateAndTimeWithPattern(metric *config.Metric, pdu *gosnmp.SnmpPDU, metrics Metrics) (float64, error) {
pduValue := pduValueAsString(pdu, "DisplayString", metrics)
t, err := timefmt.Parse(pduValue, metric.DateTimePattern)
if err != nil {
return 0, fmt.Errorf("error parsing date and time %q", err)
}
return float64(t.Unix()), nil
}

func pduToSamples(indexOids []int, pdu *gosnmp.SnmpPDU, metric *config.Metric, oidToPdu map[string]gosnmp.SnmpPDU, logger log.Logger, metrics Metrics) []prometheus.Metric {
var err error
// The part of the OID that is the indexes.
Expand Down Expand Up @@ -573,6 +583,13 @@ func pduToSamples(indexOids []int, pdu *gosnmp.SnmpPDU, metric *config.Metric, o
level.Debug(logger).Log("msg", "Error parsing DateAndTime", "err", err)
return []prometheus.Metric{}
}
case "ParseDateAndTime":
t = prometheus.GaugeValue
value, err = parseDateAndTimeWithPattern(metric, pdu, metrics)
if err != nil {
level.Debug(logger).Log("msg", "Error parsing ParseDateAndTime", "err", err)
return []prometheus.Metric{}
}
case "EnumAsInfo":
return enumAsInfo(metric, int(value), labelnames, labelvalues)
case "EnumAsStateSet":
Expand Down
34 changes: 34 additions & 0 deletions collector/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,40 @@ func TestParseDateAndTime(t *testing.T) {
}
}

func TestParseDateAndTimeWithPattern(t *testing.T) {
cases := []struct {
pdu *gosnmp.SnmpPDU
metric config.Metric
result float64
shouldErr bool
}{
{
pdu: &gosnmp.SnmpPDU{Value: "Apr 01 2025"},
metric: config.Metric{DateTimePattern: "%b %d %Y"},
result: 1.7434656e+09,
shouldErr: false,
},
{
pdu: &gosnmp.SnmpPDU{Value: "ABC"},
metric: config.Metric{DateTimePattern: "%b %d %Y"},
result: 0,
shouldErr: true,
},
}
for _, c := range cases {
got, err := parseDateAndTimeWithPattern(&c.metric, c.pdu, Metrics{})
if c.shouldErr && err == nil {
t.Fatalf("Was expecting error, but none returned.")
}
if !c.shouldErr && err != nil {
t.Fatalf("Was expecting no error, but one returned.")
}
if !reflect.DeepEqual(got, c.result) {
t.Errorf("parseDateAndTime(%v) result: got %v, want %v", c.pdu, got, c.result)
}
}
}

func TestIndexesToLabels(t *testing.T) {
cases := []struct {
oid []int
Expand Down
21 changes: 11 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,17 @@ type DynamicFilter struct {
}

type Metric struct {
Name string `yaml:"name"`
Oid string `yaml:"oid"`
Type string `yaml:"type"`
Help string `yaml:"help"`
Indexes []*Index `yaml:"indexes,omitempty"`
Lookups []*Lookup `yaml:"lookups,omitempty"`
RegexpExtracts map[string][]RegexpExtract `yaml:"regex_extracts,omitempty"`
EnumValues map[int]string `yaml:"enum_values,omitempty"`
Offset float64 `yaml:"offset,omitempty"`
Scale float64 `yaml:"scale,omitempty"`
Name string `yaml:"name"`
Oid string `yaml:"oid"`
Type string `yaml:"type"`
Help string `yaml:"help"`
Indexes []*Index `yaml:"indexes,omitempty"`
Lookups []*Lookup `yaml:"lookups,omitempty"`
RegexpExtracts map[string][]RegexpExtract `yaml:"regex_extracts,omitempty"`
DateTimePattern string `yaml:"datetime_pattern,omitempty"`
EnumValues map[int]string `yaml:"enum_values,omitempty"`
Offset float64 `yaml:"offset,omitempty"`
Scale float64 `yaml:"scale,omitempty"`
}

type Index struct {
Expand Down
2 changes: 2 additions & 0 deletions generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,15 @@ modules:
value: '1' # The first entry whose regex matches and whose value parses wins.
- regex: '.*'
value: '0'
datetime_pattern: # Used if type = ParseDateAndTime. Uses the strptime format (See: man 3 strptime)
offset: 1.0 # Add the value to the same. Applied after scale.
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.
# OctetString: A bit string, rendered as 0xff34.
# DateAndTime: An RFC 2579 DateAndTime byte sequence. If the device has no time zone data, UTC is used.
# ParseDateAndTime: Parse a DisplayString and return the timestamp. See datetime_pattern config option
# DisplayString: An ASCII or UTF-8 string.
# PhysAddress48: A 48 bit MAC address, rendered as 00:01:02:03:04:ff.
# Float: A 32 bit floating-point value with type gauge.
Expand Down
16 changes: 9 additions & 7 deletions generator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ package main

import (
"fmt"
"github.com/prometheus/snmp_exporter/config"
"strconv"

"github.com/prometheus/snmp_exporter/config"
)

// The generator config.
Expand All @@ -27,12 +28,13 @@ type Config struct {
}

type MetricOverrides struct {
Ignore bool `yaml:"ignore,omitempty"`
RegexpExtracts map[string][]config.RegexpExtract `yaml:"regex_extracts,omitempty"`
Offset float64 `yaml:"offset,omitempty"`
Scale float64 `yaml:"scale,omitempty"`
Type string `yaml:"type,omitempty"`
Help string `yaml:"help,omitempty"`
Ignore bool `yaml:"ignore,omitempty"`
RegexpExtracts map[string][]config.RegexpExtract `yaml:"regex_extracts,omitempty"`
DateTimePattern string `yaml:"datetime_pattern,omitempty"`
Offset float64 `yaml:"offset,omitempty"`
Scale float64 `yaml:"scale,omitempty"`
Type string `yaml:"type,omitempty"`
Help string `yaml:"help,omitempty"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
Expand Down
6 changes: 6 additions & 0 deletions generator/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ func prepareTree(nodes *Node, logger log.Logger) map[string]*Node {
if n.TextualConvention == "DateAndTime" {
n.Type = "DateAndTime"
}
if n.TextualConvention == "ParseDateAndTime" {
n.Type = "ParseDateAndTime"
}
// Convert RFC 4001 InetAddress types textual convention to type.
if n.TextualConvention == "InetAddressIPv4" || n.TextualConvention == "InetAddressIPv6" || n.TextualConvention == "InetAddress" {
n.Type = n.TextualConvention
Expand Down Expand Up @@ -167,6 +170,8 @@ func metricType(t string) (string, bool) {
return t, true
case "DateAndTime":
return t, true
case "ParseDateAndTime":
return t, true
case "EnumAsInfo", "EnumAsStateSet":
return t, true
default:
Expand Down Expand Up @@ -528,6 +533,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.DateTimePattern = params.DateTimePattern
metric.Offset = params.Offset
metric.Scale = params.Scale
if params.Help != "" {
Expand Down
12 changes: 12 additions & 0 deletions generator/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ func TestTreePrepare(t *testing.T) {
in: &Node{Oid: "1", Type: "DisplayString", TextualConvention: "DateAndTime"},
out: &Node{Oid: "1", Type: "DateAndTime", TextualConvention: "DateAndTime"},
},
// ParseDateAndTime
{
in: &Node{Oid: "1", Type: "DisplayString", TextualConvention: "ParseDateAndTime"},
out: &Node{Oid: "1", Type: "ParseDateAndTime", TextualConvention: "ParseDateAndTime"},
},
// RFC 4100 InetAddress conventions.
{
in: &Node{Oid: "1", Type: "OctectString", TextualConvention: "InetAddressIPv4"},
Expand Down Expand Up @@ -340,6 +345,7 @@ func TestGenerateConfigModule(t *testing.T) {
{Oid: "1.202", Access: "ACCESS_READONLY", Label: "DateAndTime", Type: "DisplayString", TextualConvention: "DateAndTime"},
{Oid: "1.203", Access: "ACCESS_READONLY", Label: "InetAddressIPv4", Type: "OCTETSTR", TextualConvention: "InetAddressIPv4"},
{Oid: "1.204", Access: "ACCESS_READONLY", Label: "InetAddressIPv6", Type: "OCTETSTR", TextualConvention: "InetAddressIPv6"},
{Oid: "1.205", Access: "ACCESS_READONLY", Label: "ParseDateAndTime", Type: "DisplayString", TextualConvention: "ParseDateAndTime"},
}},
cfg: &ModuleConfig{
Walk: []string{"root", "1.3"},
Expand Down Expand Up @@ -461,6 +467,12 @@ func TestGenerateConfigModule(t *testing.T) {
Type: "InetAddressIPv6",
Help: " - 1.204",
},
{
Name: "ParseDateAndTime",
Oid: "1.205",
Type: "ParseDateAndTime",
Help: " - 1.205",
},
},
},
},
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/go-kit/log v0.2.1
github.com/gosnmp/gosnmp v1.37.0
github.com/itchyny/timefmt-go v0.1.6
github.com/prometheus/client_golang v1.19.1
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.55.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gosnmp/gosnmp v1.37.0 h1:/Tf8D3b9wrnNuf/SfbvO+44mPrjVphBhRtcGg22V07Y=
github.com/gosnmp/gosnmp v1.37.0/go.mod h1:GDH9vNqpsD7f2HvZhKs5dlqSEcAS6s6Qp099oZRCR+M=
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q=
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
Expand Down