From c04b215784aa922f9f8c5ef0977abf52c2b740a4 Mon Sep 17 00:00:00 2001 From: Valer Cara Date: Tue, 23 Jun 2020 12:36:30 +0300 Subject: [PATCH] Add tools/netwatch exporter --- collector/collector.go | 7 +++ collector/netwatch_collector.go | 95 +++++++++++++++++++++++++++++++++ config/config.go | 1 + config/config.test.yml | 1 + config/config_test.go | 1 + go.mod | 1 + go.sum | 1 + main.go | 5 ++ 8 files changed, 112 insertions(+) create mode 100644 collector/netwatch_collector.go diff --git a/collector/collector.go b/collector/collector.go index 76892fa4..93cf1d3e 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -189,6 +189,13 @@ func WithLte() Option { } } +// WithNetwatch enables netwatch metrics +func WithNetwatch() Option { + return func(c *collector) { + c.collectors = append(c.collectors, newNetwatchCollector()) + } +} + // Option applies options to collector type Option func(*collector) diff --git a/collector/netwatch_collector.go b/collector/netwatch_collector.go new file mode 100644 index 00000000..2a9fcb07 --- /dev/null +++ b/collector/netwatch_collector.go @@ -0,0 +1,95 @@ +package collector + +import ( + "fmt" + "strings" + + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" + "gopkg.in/routeros.v2/proto" +) + +type netwatchCollector struct { + props []string + descriptions map[string]*prometheus.Desc +} + +func newNetwatchCollector() routerOSCollector { + c := &netwatchCollector{} + c.init() + return c +} + +func (c *netwatchCollector) init() { + c.props = []string{"host", "comment", "status"} + labelNames := []string{"name", "address", "host", "comment"} + c.descriptions = make(map[string]*prometheus.Desc) + for _, p := range c.props[1:] { + c.descriptions[p] = descriptionForPropertyName("netwatch", p, labelNames) + } +} + +func (c *netwatchCollector) describe(ch chan<- *prometheus.Desc) { + for _, d := range c.descriptions { + ch <- d + } +} + +func (c *netwatchCollector) collect(ctx *collectorContext) error { + stats, err := c.fetch(ctx) + if err != nil { + return err + } + + for _, re := range stats { + c.collectForStat(re, ctx) + } + + return nil +} + +func (c *netwatchCollector) fetch(ctx *collectorContext) ([]*proto.Sentence, error) { + reply, err := ctx.client.Run("/tool/netwatch/print", "?disabled=false", "=.proplist="+strings.Join(c.props, ",")) + if err != nil { + log.WithFields(log.Fields{ + "device": ctx.device.Name, + "error": err, + }).Error("error fetching netwatch metrics") + return nil, err + } + + return reply.Re, nil +} + +func (c *netwatchCollector) collectForStat(re *proto.Sentence, ctx *collectorContext) { + host := re.Map["host"] + comment := re.Map["comment"] + + for _, p := range c.props[2:] { + c.collectMetricForProperty(p, host, comment, re, ctx) + } +} + +func (c *netwatchCollector) collectMetricForProperty(property, host, comment string, re *proto.Sentence, ctx *collectorContext) { + desc := c.descriptions[property] + if value := re.Map[property]; value != "" { + var numericValue float64 + switch value { + case "up": + numericValue = 1 + case "unknown": + numericValue = 0 + case "down": + numericValue = -1 + default: + log.WithFields(log.Fields{ + "device": ctx.device.Name, + "host": host, + "property": property, + "value": value, + "error": fmt.Errorf("unexpected netwatch status value"), + }).Error("error parsing netwatch metric value") + } + ctx.ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, numericValue, ctx.device.Name, ctx.device.Address, host, comment) + } +} diff --git a/config/config.go b/config/config.go index 2d5b9bbc..072be396 100644 --- a/config/config.go +++ b/config/config.go @@ -28,6 +28,7 @@ type Config struct { Monitor bool `yaml:"monitor,omitempty"` Ipsec bool `yaml:"ipsec,omitempty"` Lte bool `yaml:"lte,omitempty"` + Netwatch bool `yaml:"netwatch,omitempty"` } `yaml:"features,omitempty"` } diff --git a/config/config.test.yml b/config/config.test.yml index ed8c30d0..753d44d0 100644 --- a/config/config.test.yml +++ b/config/config.test.yml @@ -21,3 +21,4 @@ features: wlanif: true ipsec: true lte: true + netwatch: true diff --git a/config/config_test.go b/config/config_test.go index aeac9ce4..7317566a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -30,6 +30,7 @@ func TestShouldParse(t *testing.T) { assertFeature("WlanIF", c.Features.WlanIF, t) assertFeature("Ipsec", c.Features.Ipsec, t) assertFeature("Lte", c.Features.Lte, t) + assertFeature("Netwatch", c.Features.Netwatch, t) } func loadTestFile(t *testing.T) []byte { diff --git a/go.mod b/go.mod index 28c09e0b..1cf7c747 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/prometheus/client_golang v1.4.1 github.com/prometheus/common v0.9.1 github.com/sirupsen/logrus v1.4.2 + github.com/stretchr/testify v1.4.0 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect gopkg.in/routeros.v2 v2.0.0-20190905230420-1bbf141cdd91 gopkg.in/yaml.v2 v2.2.5 diff --git a/go.sum b/go.sum index 0471ee13..87a975cd 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/main.go b/main.go index 2c262b45..20d33c1d 100644 --- a/main.go +++ b/main.go @@ -53,6 +53,7 @@ var ( withMonitor = flag.Bool("with-monitor", false, "retrieves ethernet interface monitor info") withIpsec = flag.Bool("with-ipsec", false, "retrieves ipsec metrics") withLte = flag.Bool("with-lte", false, "retrieves lte metrics") + withNetwatch = flag.Bool("with-netwatch", false, "retrieves netwatch metrics") cfg *config.Config @@ -258,6 +259,10 @@ func collectorOptions() []collector.Option { opts = append(opts, collector.WithLte()) } + if *withNetwatch || cfg.Features.Netwatch { + opts = append(opts, collector.WithNetwatch()) + } + if *timeout != collector.DefaultTimeout { opts = append(opts, collector.WithTimeout(*timeout)) }