mirror of
https://github.com/nshttpd/mikrotik-exporter.git
synced 2024-11-15 10:15:18 +01:00
f2866a3a2f
* added config file implementation, refactoring * add gitignore * improved test * preperations for more metrics * added resource metrics * added first bgp metrics * added asn as label for bgp metrics * added prefix and message counts to bgp metrics * simplified * Update README.md * added yaml dependency * fixed go routine call * added timeout * clean up * added TLS support * set default api port for TLS * added routes metric * added missing log information
175 lines
3.9 KiB
Go
175 lines
3.9 KiB
Go
package collector
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/nshttpd/mikrotik-exporter/config"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
log "github.com/sirupsen/logrus"
|
|
routeros "gopkg.in/routeros.v2"
|
|
)
|
|
|
|
const (
|
|
namespace = "mikrotik"
|
|
apiPort = ":8728"
|
|
apiPortTLS = ":8729"
|
|
|
|
// DefaultTimeout defines the default timeout when connecting to a router
|
|
DefaultTimeout = 5 * time.Second
|
|
)
|
|
|
|
var (
|
|
scrapeDurationDesc = prometheus.NewDesc(
|
|
prometheus.BuildFQName(namespace, "scrape", "collector_duration_seconds"),
|
|
"mikrotik_exporter: duration of a collector scrape",
|
|
[]string{"device"},
|
|
nil,
|
|
)
|
|
scrapeSuccessDesc = prometheus.NewDesc(
|
|
prometheus.BuildFQName(namespace, "scrape", "collector_success"),
|
|
"mikrotik_exporter: whether a collector succeeded",
|
|
[]string{"device"},
|
|
nil,
|
|
)
|
|
)
|
|
|
|
type collector struct {
|
|
devices []config.Device
|
|
collectors []metricCollector
|
|
timeout time.Duration
|
|
enableTLS bool
|
|
insecureTLS bool
|
|
}
|
|
|
|
// WithBGP enables BGP routing metrics
|
|
func WithBGP() Option {
|
|
return func(c *collector) {
|
|
c.collectors = append(c.collectors, &bgpCollector{})
|
|
}
|
|
}
|
|
|
|
// WithRoutes enables routing table metrics
|
|
func WithRoutes() Option {
|
|
return func(c *collector) {
|
|
c.collectors = append(c.collectors, &routesCollector{})
|
|
}
|
|
}
|
|
|
|
// WithTimeout sets timeout for connecting to router
|
|
func WithTimeout(d time.Duration) Option {
|
|
return func(c *collector) {
|
|
c.timeout = d
|
|
}
|
|
}
|
|
|
|
// WithTLS enables TLS
|
|
func WithTLS(insecure bool) Option {
|
|
return func(c *collector) {
|
|
c.enableTLS = true
|
|
c.insecureTLS = true
|
|
}
|
|
}
|
|
|
|
// Option applies options to collector
|
|
type Option func(*collector)
|
|
|
|
// NewCollector creates a collector instance
|
|
func NewCollector(cfg *config.Config, opts ...Option) (prometheus.Collector, error) {
|
|
log.WithFields(log.Fields{
|
|
"numDevices": len(cfg.Devices),
|
|
}).Info("setting up collector for devices")
|
|
|
|
c := &collector{
|
|
devices: cfg.Devices,
|
|
timeout: DefaultTimeout,
|
|
collectors: []metricCollector{
|
|
&interfaceCollector{},
|
|
&resourceCollector{},
|
|
},
|
|
}
|
|
|
|
for _, o := range opts {
|
|
o(c)
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// Describe implements the prometheus.Collector interface.
|
|
func (c *collector) Describe(ch chan<- *prometheus.Desc) {
|
|
ch <- scrapeDurationDesc
|
|
ch <- scrapeSuccessDesc
|
|
|
|
for _, co := range c.collectors {
|
|
co.describe(ch)
|
|
}
|
|
}
|
|
|
|
// Collect implements the prometheus.Collector interface.
|
|
func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(len(c.devices))
|
|
|
|
for _, dev := range c.devices {
|
|
go func(d config.Device) {
|
|
c.collectForDevice(d, ch)
|
|
wg.Done()
|
|
}(dev)
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func (c *collector) collectForDevice(d config.Device, ch chan<- prometheus.Metric) {
|
|
begin := time.Now()
|
|
|
|
err := c.connectAndCollect(&d, ch)
|
|
|
|
duration := time.Since(begin)
|
|
var success float64
|
|
if err != nil {
|
|
log.Errorf("ERROR: %s collector failed after %fs: %s", d.Name, duration.Seconds(), err)
|
|
success = 0
|
|
} else {
|
|
log.Debugf("OK: %s collector succeeded after %fs.", d.Name, duration.Seconds())
|
|
success = 1
|
|
}
|
|
|
|
ch <- prometheus.MustNewConstMetric(scrapeDurationDesc, prometheus.GaugeValue, duration.Seconds(), d.Name)
|
|
ch <- prometheus.MustNewConstMetric(scrapeSuccessDesc, prometheus.GaugeValue, success, d.Name)
|
|
}
|
|
|
|
func (c *collector) connectAndCollect(d *config.Device, ch chan<- prometheus.Metric) 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()
|
|
|
|
for _, co := range c.collectors {
|
|
err = co.collect(ch, d, cl)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *collector) connect(d *config.Device) (*routeros.Client, error) {
|
|
if !c.enableTLS {
|
|
return routeros.DialTimeout(d.Address+apiPort, d.User, d.Password, c.timeout)
|
|
}
|
|
|
|
tls := &tls.Config{
|
|
InsecureSkipVerify: c.insecureTLS,
|
|
}
|
|
return routeros.DialTLSTimeout(d.Address+apiPortTLS, d.User, d.Password, tls, c.timeout)
|
|
}
|