2017-09-05 04:52:14 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2018-03-21 02:28:10 +01:00
|
|
|
"bytes"
|
2017-09-05 04:52:14 +02:00
|
|
|
"flag"
|
2018-03-21 02:28:10 +01:00
|
|
|
"io/ioutil"
|
2017-09-05 04:52:14 +02:00
|
|
|
"os"
|
|
|
|
|
2019-09-02 20:51:14 +02:00
|
|
|
"github.com/prometheus/common/version"
|
|
|
|
|
2017-11-29 05:58:39 +01:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
2019-11-16 06:05:31 +01:00
|
|
|
"mikrotik-exporter/collector"
|
|
|
|
"mikrotik-exporter/config"
|
|
|
|
|
2017-11-29 05:58:39 +01:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
2017-11-30 04:42:59 +01:00
|
|
|
log "github.com/sirupsen/logrus"
|
2017-09-05 04:52:14 +02:00
|
|
|
)
|
|
|
|
|
2019-09-02 20:51:14 +02:00
|
|
|
// single device can be defined via CLI flags, multiple via config file.
|
2017-09-05 04:52:14 +02:00
|
|
|
var (
|
2017-11-30 04:42:59 +01:00
|
|
|
address = flag.String("address", "", "address of the device to monitor")
|
2019-09-02 20:51:14 +02:00
|
|
|
configFile = flag.String("config-file", "", "config file to load")
|
|
|
|
device = flag.String("device", "", "single device to monitor")
|
|
|
|
insecure = flag.Bool("insecure", false, "skips verification of server certificate when using TLS (not recommended)")
|
2017-11-30 04:42:59 +01:00
|
|
|
logFormat = flag.String("log-format", "json", "logformat text or json (default json)")
|
2019-09-02 20:51:14 +02:00
|
|
|
logLevel = flag.String("log-level", "info", "log level")
|
2017-11-30 04:42:59 +01:00
|
|
|
metricsPath = flag.String("path", "/metrics", "path to answer requests on")
|
2019-09-02 20:51:14 +02:00
|
|
|
password = flag.String("password", "", "password for authentication for single device")
|
2019-12-26 19:17:27 +01:00
|
|
|
deviceport = flag.String("deviceport", "8728", "port for single device")
|
2019-09-02 20:51:14 +02:00
|
|
|
port = flag.String("port", ":9436", "port number to listen on")
|
|
|
|
timeout = flag.Duration("timeout", collector.DefaultTimeout, "timeout when connecting to devices")
|
|
|
|
tls = flag.Bool("tls", false, "use tls to connect to routers")
|
|
|
|
user = flag.String("user", "", "user for authentication with single device")
|
|
|
|
ver = flag.Bool("version", false, "find the version of binary")
|
|
|
|
|
2020-04-08 05:06:38 +02:00
|
|
|
withBgp = flag.Bool("with-bgp", false, "retrieves BGP routing infrormation")
|
|
|
|
withRoutes = flag.Bool("with-routes", false, "retrieves routing table information")
|
|
|
|
withDHCP = flag.Bool("with-dhcp", false, "retrieves DHCP server metrics")
|
|
|
|
withDHCPL = flag.Bool("with-dhcpl", false, "retrieves DHCP server lease metrics")
|
|
|
|
withDHCPv6 = flag.Bool("with-dhcpv6", false, "retrieves DHCPv6 server metrics")
|
|
|
|
withFirmware = flag.Bool("with-firmware", false, "retrieves firmware versions")
|
|
|
|
withHealth = flag.Bool("with-health", false, "retrieves board Health metrics")
|
|
|
|
withPOE = flag.Bool("with-poe", false, "retrieves PoE metrics")
|
|
|
|
withPools = flag.Bool("with-pools", false, "retrieves IP(v6) pool metrics")
|
|
|
|
withOptics = flag.Bool("with-optics", false, "retrieves optical diagnostic metrics")
|
|
|
|
withW60G = flag.Bool("with-w60g", false, "retrieves w60g interface 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")
|
|
|
|
withIpsec = flag.Bool("with-ipsec", false, "retrieves ipsec metrics")
|
2019-09-02 20:51:14 +02:00
|
|
|
|
|
|
|
cfg *config.Config
|
|
|
|
|
|
|
|
appVersion = "DEVELOPMENT"
|
|
|
|
shortSha = "0xDEADBEEF"
|
2017-09-05 04:52:14 +02:00
|
|
|
)
|
|
|
|
|
2017-11-29 05:58:39 +01:00
|
|
|
func init() {
|
|
|
|
prometheus.MustRegister(version.NewCollector("mikrotik_exporter"))
|
|
|
|
}
|
|
|
|
|
2018-03-21 02:28:10 +01:00
|
|
|
func main() {
|
|
|
|
flag.Parse()
|
|
|
|
|
2019-09-02 20:51:14 +02:00
|
|
|
if *ver {
|
|
|
|
fmt.Printf("\nVersion: %s\nShort SHA: %s\n\n", appVersion, shortSha)
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2018-03-21 02:28:10 +01:00
|
|
|
configureLog()
|
|
|
|
|
|
|
|
c, err := loadConfig()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Could not load config: %v", err)
|
|
|
|
os.Exit(3)
|
|
|
|
}
|
|
|
|
cfg = c
|
|
|
|
|
|
|
|
startServer()
|
|
|
|
}
|
|
|
|
|
|
|
|
func configureLog() {
|
|
|
|
ll, err := log.ParseLevel(*logLevel)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.SetLevel(ll)
|
2018-12-17 03:53:29 +01:00
|
|
|
|
|
|
|
if *logFormat == "text" {
|
|
|
|
log.SetFormatter(&log.TextFormatter{})
|
|
|
|
} else {
|
|
|
|
log.SetFormatter(&log.JSONFormatter{})
|
|
|
|
}
|
2018-03-21 02:28:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func loadConfig() (*config.Config, error) {
|
|
|
|
if *configFile != "" {
|
|
|
|
return loadConfigFromFile()
|
|
|
|
}
|
|
|
|
|
|
|
|
return loadConfigFromFlags()
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadConfigFromFile() (*config.Config, error) {
|
|
|
|
b, err := ioutil.ReadFile(*configFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return config.Load(bytes.NewReader(b))
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadConfigFromFlags() (*config.Config, error) {
|
2020-05-27 12:59:15 +02:00
|
|
|
// Attempt to read credentials from env if not already defined
|
|
|
|
if *user == "" {
|
|
|
|
*user = os.Getenv("MIKROTIK_USER")
|
|
|
|
}
|
|
|
|
if *password == "" {
|
|
|
|
*password = os.Getenv("MIKROTIK_PASSWORD")
|
|
|
|
}
|
2018-03-21 02:28:10 +01:00
|
|
|
if *device == "" || *address == "" || *user == "" || *password == "" {
|
|
|
|
return nil, fmt.Errorf("missing required param for single device configuration")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &config.Config{
|
|
|
|
Devices: []config.Device{
|
|
|
|
config.Device{
|
|
|
|
Name: *device,
|
|
|
|
Address: *address,
|
|
|
|
User: *user,
|
|
|
|
Password: *password,
|
2020-02-10 19:55:10 +01:00
|
|
|
Port: *deviceport,
|
2018-03-21 02:28:10 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func startServer() {
|
2018-04-11 15:21:38 +02:00
|
|
|
h, err := createMetricsHandler()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
http.Handle(*metricsPath, h)
|
2018-03-21 02:28:10 +01:00
|
|
|
|
|
|
|
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
2019-07-03 02:46:19 +02:00
|
|
|
_, _ = w.Write([]byte("ok"))
|
2018-03-21 02:28:10 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
2019-07-03 02:46:19 +02:00
|
|
|
_, _ = w.Write([]byte(`<html>
|
2018-03-21 02:28:10 +01:00
|
|
|
<head><title>Mikrotik Exporter</title></head>
|
|
|
|
<body>
|
|
|
|
<h1>Mikrotik Exporter</h1>
|
|
|
|
<p><a href="` + *metricsPath + `">Metrics</a></p>
|
|
|
|
</body>
|
|
|
|
</html>`))
|
|
|
|
})
|
|
|
|
|
2019-01-02 22:16:09 +01:00
|
|
|
log.Info("Listening on ", *port)
|
2018-03-21 02:28:10 +01:00
|
|
|
log.Fatal(http.ListenAndServe(*port, nil))
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
func createMetricsHandler() (http.Handler, error) {
|
2018-03-21 02:28:10 +01:00
|
|
|
opts := collectorOptions()
|
|
|
|
nc, err := collector.NewCollector(cfg, opts...)
|
2017-11-29 05:58:39 +01:00
|
|
|
if err != nil {
|
2018-04-11 15:21:38 +02:00
|
|
|
return nil, err
|
2017-11-29 05:58:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
registry := prometheus.NewRegistry()
|
|
|
|
err = registry.Register(nc)
|
|
|
|
if err != nil {
|
2018-04-11 15:21:38 +02:00
|
|
|
return nil, err
|
2017-11-29 05:58:39 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
return promhttp.HandlerFor(registry,
|
2017-11-29 05:58:39 +01:00
|
|
|
promhttp.HandlerOpts{
|
2017-11-30 04:42:59 +01:00
|
|
|
ErrorLog: log.New(),
|
2017-11-29 05:58:39 +01:00
|
|
|
ErrorHandling: promhttp.ContinueOnError,
|
2018-04-11 15:21:38 +02:00
|
|
|
}), nil
|
2017-11-29 05:58:39 +01:00
|
|
|
}
|
2017-09-05 04:52:14 +02:00
|
|
|
|
2018-03-21 02:28:10 +01:00
|
|
|
func collectorOptions() []collector.Option {
|
|
|
|
opts := []collector.Option{}
|
2017-09-05 04:52:14 +02:00
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
if *withBgp || cfg.Features.BGP {
|
2018-03-21 02:28:10 +01:00
|
|
|
opts = append(opts, collector.WithBGP())
|
2017-09-05 04:52:14 +02:00
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
if *withRoutes || cfg.Features.Routes {
|
2018-03-21 02:28:10 +01:00
|
|
|
opts = append(opts, collector.WithRoutes())
|
2017-09-05 04:52:14 +02:00
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
if *withDHCP || cfg.Features.DHCP {
|
|
|
|
opts = append(opts, collector.WithDHCP())
|
|
|
|
}
|
|
|
|
|
2020-02-04 04:00:00 +01:00
|
|
|
if *withDHCPL || cfg.Features.DHCPL {
|
|
|
|
opts = append(opts, collector.WithDHCPL())
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
if *withDHCPv6 || cfg.Features.DHCPv6 {
|
|
|
|
opts = append(opts, collector.WithDHCPv6())
|
|
|
|
}
|
|
|
|
|
2020-04-08 05:06:38 +02:00
|
|
|
if *withFirmware || cfg.Features.Firmware {
|
|
|
|
opts = append(opts, collector.WithFirmware())
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:55:10 +01:00
|
|
|
if *withHealth || cfg.Features.Health {
|
|
|
|
opts = append(opts, collector.WithHealth())
|
|
|
|
}
|
|
|
|
|
2019-10-25 03:12:19 +02:00
|
|
|
if *withPOE || cfg.Features.POE {
|
|
|
|
opts = append(opts, collector.WithPOE())
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
if *withPools || cfg.Features.Pools {
|
|
|
|
opts = append(opts, collector.WithPools())
|
|
|
|
}
|
|
|
|
|
2018-05-07 20:31:19 +02:00
|
|
|
if *withOptics || cfg.Features.Optics {
|
|
|
|
opts = append(opts, collector.WithOptics())
|
|
|
|
}
|
|
|
|
|
2019-12-26 19:20:24 +01:00
|
|
|
if *withW60G || cfg.Features.W60G {
|
|
|
|
opts = append(opts, collector.WithW60G())
|
|
|
|
}
|
|
|
|
|
2018-09-11 05:30:13 +02:00
|
|
|
if *withWlanSTA || cfg.Features.WlanSTA {
|
|
|
|
opts = append(opts, collector.WithWlanSTA())
|
|
|
|
}
|
|
|
|
|
|
|
|
if *withWlanIF || cfg.Features.WlanIF {
|
|
|
|
opts = append(opts, collector.WithWlanIF())
|
|
|
|
}
|
|
|
|
|
2019-08-21 04:11:03 +02:00
|
|
|
if *withMonitor || cfg.Features.Monitor {
|
|
|
|
opts = append(opts, collector.Monitor())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-10-25 03:11:05 +02:00
|
|
|
if *withIpsec || cfg.Features.Ipsec {
|
|
|
|
opts = append(opts, collector.WithIpsec())
|
|
|
|
}
|
|
|
|
|
2018-03-21 02:28:10 +01:00
|
|
|
if *timeout != collector.DefaultTimeout {
|
|
|
|
opts = append(opts, collector.WithTimeout(*timeout))
|
|
|
|
}
|
2017-11-29 05:58:39 +01:00
|
|
|
|
2018-03-21 02:28:10 +01:00
|
|
|
if *tls {
|
|
|
|
opts = append(opts, collector.WithTLS(*insecure))
|
2017-09-05 04:52:14 +02:00
|
|
|
}
|
|
|
|
|
2018-03-21 02:28:10 +01:00
|
|
|
return opts
|
2017-09-05 04:52:14 +02:00
|
|
|
}
|