Added support to auto discovery with SRV DNS registry (#60)

* Added support to auto discovery witih SRV

The yaml configuration has been modified to suport srv parameters.
Now the name is taken form the router identity to allow dynamic
discovery of devices based on SRV registry.

* Corrected format

* feat(collector): Added support to use custom DNS

* feat(collector): get default dns server from resolv.conf

* Remove getIdentity on the static config devices
This commit is contained in:
wolmi 2020-02-04 04:03:45 +01:00 committed by GitHub
parent 75b5f8be36
commit d723fdfe38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 5 deletions

View File

@ -53,6 +53,19 @@ devices:
port: 8999
user: prometheus2
password: password_to_second_router
- name: routers_srv_dns
srv:
record: _mikrotik._udp.example.com
user: prometheus
password: password_to_all_dns_routers
- name: routers_srv_custom_dns
srv:
record: _mikrotik2._udp.example.com
dns:
address: 1.1.1.1
port: 53
user: prometheus
password: password_to_all_dns_routers
features:
bgp: true
@ -64,6 +77,11 @@ features:
optics: true
```
If you add a devices with the `srv` parameter instead of `address` the exporter will perform a DNS query
to obtain the SRV record and discover the devices dynamically. Also, you can specify a DNS server to use
on the query.
###### example output
```

View File

@ -8,11 +8,15 @@ import (
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
"mikrotik-exporter/config"
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
routeros "gopkg.in/routeros.v2"
@ -22,6 +26,7 @@ const (
namespace = "mikrotik"
apiPort = "8728"
apiPortTLS = "8729"
dnsPort = 53
// DefaultTimeout defines the default timeout when connecting to a router
DefaultTimeout = 5 * time.Second
@ -194,9 +199,52 @@ func (c *collector) Describe(ch chan<- *prometheus.Desc) {
// Collect implements the prometheus.Collector interface.
func (c *collector) Collect(ch chan<- prometheus.Metric) {
wg := sync.WaitGroup{}
wg.Add(len(c.devices))
var realDevices []config.Device
for _, dev := range c.devices {
if (config.SrvRecord{}) != dev.Srv {
log.WithFields(log.Fields{
"SRV": dev.Srv.Record,
}).Info("SRV configuration detected")
conf, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
dnsServer := net.JoinHostPort(conf.Servers[0], strconv.Itoa(dnsPort))
if (config.DnsServer{}) != dev.Srv.Dns {
dnsServer = net.JoinHostPort(dev.Srv.Dns.Address, strconv.Itoa(dev.Srv.Dns.Port))
log.WithFields(log.Fields{
"DnsServer": dnsServer,
}).Info("Custom DNS config detected")
}
dnsMsg := new(dns.Msg)
dnsCli := new(dns.Client)
dnsMsg.RecursionDesired = true
dnsMsg.SetQuestion(dns.Fqdn(dev.Srv.Record), dns.TypeSRV)
r, _, err := dnsCli.Exchange(dnsMsg, dnsServer)
if err != nil {
os.Exit(1)
}
for _, k := range r.Answer {
if s, ok := k.(*dns.SRV); ok {
d := config.Device{}
d.Name = strings.TrimRight(s.Target, ".")
d.Address = strings.TrimRight(s.Target, ".")
d.User = dev.User
d.Password = dev.Password
c.getIdentity(&d)
realDevices = append(realDevices, d)
}
}
} else {
realDevices = append(realDevices, dev)
}
}
wg.Add(len(realDevices))
for _, dev := range realDevices {
go func(d config.Device) {
c.collectForDevice(d, ch)
wg.Done()
@ -206,6 +254,30 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
wg.Wait()
}
func (c *collector) getIdentity(d *config.Device) error {
cl, err := c.connect(d)
if err != nil {
log.WithFields(log.Fields{
"device": d.Name,
"error": err,
}).Error("error dialing device")
return err
}
defer cl.Close()
reply, err := cl.Run("/system/identity/print")
if err != nil {
log.WithFields(log.Fields{
"device": d.Name,
"error": err,
}).Error("error fetching ethernet interfaces")
return err
}
for _, id := range reply.Re {
d.Name = id.Map["name"]
}
return nil
}
func (c *collector) collectForDevice(d config.Device, ch chan<- prometheus.Metric) {
begin := time.Now()

View File

@ -29,13 +29,23 @@ type Config struct {
// Device represents a target device
type Device struct {
Name string `yaml:"name"`
Address string `yaml:"address"`
User string `yaml:"user"`
Password string `yaml:"password"`
Name string `yaml:"name"`
Address string `yaml:"address,omitempty"`
Srv SrvRecord `yaml:"srv,omitempty"`
User string `yaml:"user"`
Password string `yaml:"password"`
Port string `yaml:"port"`
}
type SrvRecord struct {
Record string `yaml:"record"`
Dns DnsServer `yaml:"dns,omitempty"`
}
type DnsServer struct {
Address string `yaml:"address"`
Port int `yaml:"port"`
}
// Load reads YAML from reader and unmashals in Config
func Load(r io.Reader) (*Config, error) {
b, err := ioutil.ReadAll(r)

1
go.mod
View File

@ -3,6 +3,7 @@ module mikrotik-exporter
go 1.13
require (
github.com/miekg/dns v1.1.22
github.com/prometheus/client_golang v1.2.1
github.com/prometheus/common v0.7.0
github.com/sirupsen/logrus v1.4.2

15
go.sum
View File

@ -32,6 +32,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc=
github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@ -66,17 +68,30 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=