2018-03-21 02:28:10 +01:00
|
|
|
package collector
|
|
|
|
|
|
|
|
import (
|
2019-02-22 04:39:25 +01:00
|
|
|
"fmt"
|
2019-01-02 22:16:09 +01:00
|
|
|
"regexp"
|
2018-03-21 02:28:10 +01:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2019-01-02 22:16:09 +01:00
|
|
|
"time"
|
2018-03-21 02:28:10 +01:00
|
|
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"gopkg.in/routeros.v2/proto"
|
|
|
|
)
|
|
|
|
|
2019-01-02 22:16:09 +01:00
|
|
|
var uptimeRegex *regexp.Regexp
|
|
|
|
var uptimeParts [5]time.Duration
|
|
|
|
|
|
|
|
func init() {
|
2019-02-22 04:39:25 +01:00
|
|
|
uptimeRegex = regexp.MustCompile(`(?:(\d*)w)?(?:(\d*)d)?(?:(\d*)h)?(?:(\d*)m)?(?:(\d*)s)?`)
|
2019-01-02 22:16:09 +01:00
|
|
|
uptimeParts = [5]time.Duration{time.Hour * 168, time.Hour * 24, time.Hour, time.Minute, time.Second}
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
type resourceCollector struct {
|
|
|
|
props []string
|
|
|
|
descriptions map[string]*prometheus.Desc
|
|
|
|
}
|
2018-03-21 02:28:10 +01:00
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
func newResourceCollector() routerOSCollector {
|
|
|
|
c := &resourceCollector{}
|
|
|
|
c.init()
|
|
|
|
return c
|
2018-03-21 02:28:10 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
func (c *resourceCollector) init() {
|
2019-09-02 20:50:20 +02:00
|
|
|
c.props = []string{"free-memory", "total-memory", "cpu-load", "free-hdd-space", "total-hdd-space", "uptime", "board-name", "version"}
|
2018-04-11 15:21:38 +02:00
|
|
|
|
2019-09-02 20:50:20 +02:00
|
|
|
labelNames := []string{"name", "address", "boardname", "version"}
|
2018-04-11 15:21:38 +02:00
|
|
|
c.descriptions = make(map[string]*prometheus.Desc)
|
|
|
|
for _, p := range c.props {
|
|
|
|
c.descriptions[p] = descriptionForPropertyName("system", p, labelNames)
|
|
|
|
}
|
2018-03-21 02:28:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *resourceCollector) describe(ch chan<- *prometheus.Desc) {
|
2018-04-11 15:21:38 +02:00
|
|
|
for _, d := range c.descriptions {
|
2018-03-21 02:28:10 +01:00
|
|
|
ch <- d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
func (c *resourceCollector) collect(ctx *collectorContext) error {
|
|
|
|
stats, err := c.fetch(ctx)
|
2018-03-21 02:28:10 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, re := range stats {
|
2019-12-03 04:01:36 +01:00
|
|
|
c.collectForStat(re, ctx)
|
2018-03-21 02:28:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
func (c *resourceCollector) fetch(ctx *collectorContext) ([]*proto.Sentence, error) {
|
|
|
|
reply, err := ctx.client.Run("/system/resource/print", "=.proplist="+strings.Join(c.props, ","))
|
2018-03-21 02:28:10 +01:00
|
|
|
if err != nil {
|
|
|
|
log.WithFields(log.Fields{
|
2018-04-11 15:21:38 +02:00
|
|
|
"device": ctx.device.Name,
|
2018-03-21 02:28:10 +01:00
|
|
|
"error": err,
|
|
|
|
}).Error("error fetching system resource metrics")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return reply.Re, nil
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
func (c *resourceCollector) collectForStat(re *proto.Sentence, ctx *collectorContext) {
|
2019-09-02 20:50:20 +02:00
|
|
|
for _, p := range c.props[:6] {
|
2018-04-11 15:21:38 +02:00
|
|
|
c.collectMetricForProperty(p, re, ctx)
|
2018-03-21 02:28:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
func (c *resourceCollector) collectMetricForProperty(property string, re *proto.Sentence, ctx *collectorContext) {
|
2019-01-02 22:16:09 +01:00
|
|
|
var v float64
|
2021-10-25 14:10:09 +02:00
|
|
|
var vtype prometheus.ValueType
|
2019-01-02 22:16:09 +01:00
|
|
|
var err error
|
2019-12-03 04:01:36 +01:00
|
|
|
// const boardname = "BOARD"
|
|
|
|
// const version = "3.33.3"
|
2019-09-02 20:50:20 +02:00
|
|
|
|
|
|
|
boardname := re.Map["board-name"]
|
|
|
|
version := re.Map["version"]
|
2019-01-02 22:16:09 +01:00
|
|
|
|
|
|
|
if property == "uptime" {
|
|
|
|
v, err = parseUptime(re.Map[property])
|
2021-10-25 14:10:09 +02:00
|
|
|
vtype = prometheus.CounterValue
|
2019-01-02 22:16:09 +01:00
|
|
|
} else {
|
2019-12-03 04:01:36 +01:00
|
|
|
if re.Map[property] == "" {
|
|
|
|
return
|
|
|
|
}
|
2019-01-02 22:16:09 +01:00
|
|
|
v, err = strconv.ParseFloat(re.Map[property], 64)
|
2021-10-25 14:10:09 +02:00
|
|
|
vtype = prometheus.GaugeValue
|
2019-01-02 22:16:09 +01:00
|
|
|
}
|
|
|
|
|
2018-03-21 02:28:10 +01:00
|
|
|
if err != nil {
|
|
|
|
log.WithFields(log.Fields{
|
2018-04-11 15:21:38 +02:00
|
|
|
"device": ctx.device.Name,
|
2018-03-21 02:28:10 +01:00
|
|
|
"property": property,
|
|
|
|
"value": re.Map[property],
|
|
|
|
"error": err,
|
|
|
|
}).Error("error parsing system resource metric value")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-04-11 15:21:38 +02:00
|
|
|
desc := c.descriptions[property]
|
2021-10-25 14:10:09 +02:00
|
|
|
ctx.ch <- prometheus.MustNewConstMetric(desc, vtype, v, ctx.device.Name, ctx.device.Address, boardname, version)
|
2018-03-21 02:28:10 +01:00
|
|
|
}
|
2019-01-02 22:16:09 +01:00
|
|
|
|
|
|
|
func parseUptime(uptime string) (float64, error) {
|
|
|
|
var u time.Duration
|
|
|
|
|
2019-02-22 04:39:25 +01:00
|
|
|
reMatch := uptimeRegex.FindAllStringSubmatch(uptime, -1)
|
|
|
|
|
|
|
|
// should get one and only one match back on the regex
|
|
|
|
if len(reMatch) != 1 {
|
|
|
|
return 0, fmt.Errorf("invalid uptime value sent to regex")
|
2020-02-04 03:33:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, match := range reMatch[0] {
|
|
|
|
if match != "" && i != 0 {
|
|
|
|
v, err := strconv.Atoi(match)
|
|
|
|
if err != nil {
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"uptime": uptime,
|
|
|
|
"value": match,
|
|
|
|
"error": err,
|
|
|
|
}).Error("error parsing uptime field value")
|
|
|
|
return float64(0), err
|
2019-01-02 22:16:09 +01:00
|
|
|
}
|
2020-02-04 03:33:02 +01:00
|
|
|
u += time.Duration(v) * uptimeParts[i-1]
|
2019-01-02 22:16:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return u.Seconds(), nil
|
|
|
|
}
|