From beb09ce540739c70e7ea59fd705619d0dcd564af Mon Sep 17 00:00:00 2001 From: Steve Brunton Date: Tue, 20 Aug 2019 22:11:03 -0400 Subject: [PATCH] [ci skip] monitor data for ethernet ports (#45) * fix import name conflict * adding in ethernet monitor information via prometheus gauge * set CGO_ENABLED env var on build --- Makefile | 2 +- collector/collector.go | 7 ++ collector/helper.go | 6 +- collector/monitor_collector.go | 123 +++++++++++++++++++++++++++++++++ config/config.go | 1 + main.go | 6 ++ 6 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 collector/monitor_collector.go diff --git a/Makefile b/Makefile index 92a04d50..2511a93d 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ utils: go get github.com/tcnksm/ghr deploy: utils - gox -os="linux freebsd netbsd" -arch="amd64 arm arm64 386" -parallel=4 -ldflags "$(LDFLAGS)" -output "dist/mikrotik-exporter_{{.OS}}_{{.Arch}}" + CGO_ENABLED=0 gox -os="linux freebsd netbsd" -arch="amd64 arm arm64 386" -parallel=4 -ldflags "$(LDFLAGS)" -output "dist/mikrotik-exporter_{{.OS}}_{{.Arch}}" ghr -t $(GITHUB_TOKEN) -u $(CIRCLE_PROJECT_USERNAME) -r $(CIRCLE_PROJECT_REPONAME) -replace $(VERSION) dist/ dockerhub: deploy diff --git a/collector/collector.go b/collector/collector.go index d5b357b2..0f3c096f 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -105,6 +105,13 @@ func WithWlanIF() Option { } } +// WithMonitor enables ethernet monitor collector metrics +func Monitor() Option { + return func(c *collector) { + c.collectors = append(c.collectors, newMonitorCollector()) + } +} + // WithTimeout sets timeout for connecting to router func WithTimeout(d time.Duration) Option { return func(c *collector) { diff --git a/collector/helper.go b/collector/helper.go index ca486c0f..e8288e3e 100644 --- a/collector/helper.go +++ b/collector/helper.go @@ -31,13 +31,13 @@ func description(prefix, name, helpText string, labelNames []string) *prometheus } func splitStringToFloats(metric string) (float64, float64, error) { - strings := strings.Split(metric, ",") + strs := strings.Split(metric, ",") - m1, err := strconv.ParseFloat(strings[0], 64) + m1, err := strconv.ParseFloat(strs[0], 64) if err != nil { return math.NaN(), math.NaN(), err } - m2, err := strconv.ParseFloat(strings[1], 64) + m2, err := strconv.ParseFloat(strs[1], 64) if err != nil { return math.NaN(), math.NaN(), err } diff --git a/collector/monitor_collector.go b/collector/monitor_collector.go new file mode 100644 index 00000000..053680b5 --- /dev/null +++ b/collector/monitor_collector.go @@ -0,0 +1,123 @@ +package collector + +import ( + "strings" + + "gopkg.in/routeros.v2/proto" + + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" +) + +type monitorCollector struct { + props []string // props from monitor, can add other ether props later if needed + descriptions map[string]*prometheus.Desc +} + +func newMonitorCollector() routerOSCollector { + c := &monitorCollector{} + c.init() + return c +} + +func (c *monitorCollector) init() { + c.props = []string{"status", "rate", "full-duplex"} + labelNames := []string{"name", "address", "interface"} + c.descriptions = make(map[string]*prometheus.Desc) + for _, p := range c.props { + c.descriptions[p] = descriptionForPropertyName("monitor", p, labelNames) + } +} + +func (c *monitorCollector) describe(ch chan<- *prometheus.Desc) { + for _, d := range c.descriptions { + ch <- d + } +} + +func (c *monitorCollector) collect(ctx *collectorContext) error { + reply, err := ctx.client.Run("/interface/ethernet/print", "=.proplist=name") + if err != nil { + log.WithFields(log.Fields{ + "device": ctx.device.Name, + "error": err, + }).Error("error fetching ethernet interfaces") + return err + } + + eths := make([]string, len(reply.Re)) + for idx, eth := range reply.Re { + eths[idx] = eth.Map["name"] + } + + return c.collectForMonitor(eths, ctx) +} + +func (c *monitorCollector) collectForMonitor(eths []string, ctx *collectorContext) error { + reply, err := ctx.client.Run("/interface/ethernet/monitor", + "=numbers="+strings.Join(eths, ","), + "=once=", + "=.proplist=name,"+strings.Join(c.props, ",")) + + if err != nil { + log.WithFields(log.Fields{ + "device": ctx.device.Name, + "error": err, + }).Error("error fetching ethernet monitor info") + return err + } + + for _, e := range reply.Re { + c.collectMetricsForEth(e.Map["name"], e, ctx) + } + + return nil +} + +func (c *monitorCollector) collectMetricsForEth(name string, se *proto.Sentence, ctx *collectorContext) { + for _, prop := range c.props { + v, ok := se.Map[prop] + if !ok { + continue + } + + value := float64(c.valueForProp(prop, v)) + + ctx.ch <- prometheus.MustNewConstMetric(c.descriptions[prop], prometheus.GaugeValue, value, ctx.device.Name, ctx.device.Address, name) + + } + +} + +func (c *monitorCollector) valueForProp(name, value string) int { + switch { + case name == "status": + return func(v string) int { + if v == "link-ok" { + return 1 + } + return 0 + }(value) + case name == "rate": + return func(v string) int { + switch { + case v == "10Mbps": + return 10 + case v == "100Mbps": + return 100 + case v == "1Gbps": + return 1000 + } + return 0 + }(value) + case name == "full-duplex": + return func(v string) int { + if v == "true" { + return 1 + } + return 0 + }(value) + default: + return 0 + } +} diff --git a/config/config.go b/config/config.go index 387d6a31..63959477 100644 --- a/config/config.go +++ b/config/config.go @@ -19,6 +19,7 @@ type Config struct { Optics bool `yaml:"optics,omitempty"` WlanSTA bool `yaml:"wlansta,omitempty"` WlanIF bool `yaml:"wlanif,omitempty"` + Monitor bool `yaml:"monitor,omitempty"` } `yaml:"features,omitempty"` } diff --git a/main.go b/main.go index 7763a420..74d4a8e3 100644 --- a/main.go +++ b/main.go @@ -36,6 +36,7 @@ var ( withOptics = flag.Bool("with-optics", false, "retrieves optical diagnostic metrics") withWlanSTA = flag.Bool("with-wlansta", false, "retrieves connected wlan station metrics") withWlanIF = flag.Bool("with-wlanif", false, "retrieves wlan interface metrics") + withMonitor = flag.Bool("with-monitor", false, "retrieves ethernet interface monitor info") timeout = flag.Duration("timeout", collector.DefaultTimeout, "timeout when connecting to devices") tls = flag.Bool("tls", false, "use tls to connect to routers") insecure = flag.Bool("insecure", false, "skips verification of server certificate when using TLS (not recommended)") @@ -190,6 +191,11 @@ func collectorOptions() []collector.Option { opts = append(opts, collector.WithWlanIF()) } + if *withMonitor || cfg.Features.Monitor { + opts = append(opts, collector.Monitor()) + + } + if *timeout != collector.DefaultTimeout { opts = append(opts, collector.WithTimeout(*timeout)) }