Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/czerwonk/junos_exporter/pkg/features/mplslsp"
"github.com/czerwonk/junos_exporter/pkg/features/nat"
"github.com/czerwonk/junos_exporter/pkg/features/nat2"
"github.com/czerwonk/junos_exporter/pkg/features/ntp"
"github.com/czerwonk/junos_exporter/pkg/features/ospf"
"github.com/czerwonk/junos_exporter/pkg/features/power"
"github.com/czerwonk/junos_exporter/pkg/features/route"
Expand Down Expand Up @@ -83,6 +84,9 @@ func (c *collectors) initCollectorsForDevices(device *connector.Device, descRe *
c.addCollectorIfEnabledForDevice(device, "alarm", f.Alarm, func() collector.RPCCollector {
return alarm.NewCollector(*alarmFilter)
})
c.addCollectorIfEnabledForDevice(device, "ntp", f.NTP, func() collector.RPCCollector {
return ntp.NewCollector()
})
c.addCollectorIfEnabledForDevice(device, "bfd", f.BFD, bfd.NewCollector)
c.addCollectorIfEnabledForDevice(device, "bgp", f.BGP, func() collector.RPCCollector {
return bgp.NewCollector(c.logicalSystem, descRe)
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type DeviceConfig struct {
// FeatureConfig is the list of collectors enabled or disabled
type FeatureConfig struct {
Alarm bool `yaml:"alarm,omitempty"`
NTP bool `yaml:"ntp,omitempty"`
Environment bool `yaml:"environment,omitempty"`
BFD bool `yaml:"bfd,omitempty"`
BGP bool `yaml:"bgp,omitempty"`
Expand Down Expand Up @@ -151,6 +152,7 @@ func setDefaultValues(c *Config) {
c.LSEnabled = false
f := &c.Features
f.Alarm = true
f.NTP = false
f.BGP = true
f.Environment = true
f.Interfaces = true
Expand Down
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ var (
sshKeepAliveTimeout = flag.Duration("ssh.keep-alive-timeout", 15*time.Second, "Duration to wait for keep alive message response")
sshExpireTimeout = flag.Duration("ssh.expire-timeout", 15*time.Minute, "Duration after an connection is terminated when it is not used")
debug = flag.Bool("debug", false, "Show verbose debug output in log")
alarmEnabled = flag.Bool("alarm.enabled", true, "Scrape Alarm metrics")
alarmEnabled = flag.Bool("alarm.enabled", false, "Scrape Alarm metrics")
ntpEnabled = flag.Bool("ntp.enabled", false, "Scrape NTP metrics")
bgpEnabled = flag.Bool("bgp.enabled", true, "Scrape BGP metrics")
ospfEnabled = flag.Bool("ospf.enabled", true, "Scrape OSPFv3 metrics")
isisEnabled = flag.Bool("isis.enabled", false, "Scrape ISIS metrics")
Expand Down Expand Up @@ -229,6 +230,7 @@ func loadConfigFromFlags() *config.Config {

f := &c.Features
f.Alarm = *alarmEnabled
f.NTP = *ntpEnabled
f.BGP = *bgpEnabled
f.Environment = *environmentEnabled
f.Firewall = *firewallEnabled
Expand Down
148 changes: 148 additions & 0 deletions pkg/features/ntp/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package ntp

import (
"log"
"math"
"strconv"
"strings"

"github.com/czerwonk/junos_exporter/pkg/collector"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
)

const prefix = "junos_ntp_"

var (
ntpStratumDesc *prometheus.Desc
ntpOffsetDesc *prometheus.Desc
ntpSysJitterDesc *prometheus.Desc
ntpClkJitterDesc *prometheus.Desc
ntpRootDelayDesc *prometheus.Desc
ntpLeapDesc *prometheus.Desc
ntpPrecisionDesc *prometheus.Desc
ntpPollDesc *prometheus.Desc
)

func init() {
l := []string{"target", "server"}
ntpStratumDesc = prometheus.NewDesc(prefix+"stratum", "NTP stratum level (0: reference clock, 1-15: hops to refernce clock, 16: not syncronized)", l, nil)
ntpOffsetDesc = prometheus.NewDesc(prefix+"offset", "Time offset in msec", l, nil)
ntpSysJitterDesc = prometheus.NewDesc(prefix+"system_jitter", "System jitter in msec", l, nil)
ntpClkJitterDesc = prometheus.NewDesc(prefix+"clock_jitter", "Clock jitter in msec", l, nil)
ntpRootDelayDesc = prometheus.NewDesc(prefix+"root_delay", "Root delay in msec", l, nil)
ntpLeapDesc = prometheus.NewDesc(prefix+"leap", "Leap indicator (00=ok, 01: last minute with 61 seconds, 10: last minute with 59 seconds, 11: not syncronized)", l, nil)
ntpPrecisionDesc = prometheus.NewDesc(prefix+"precision", "Clock precision (should be -20 to -22)", l, nil)
ntpPollDesc = prometheus.NewDesc(prefix+"poll_interval", "Poll interval in seconds", l, nil)
}

type ntpCollector struct{}

func NewCollector() collector.RPCCollector {
return &ntpCollector{}
}

func (c *ntpCollector) Name() string {
return "ntp"
}

func (c *ntpCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- ntpStratumDesc
ch <- ntpOffsetDesc
ch <- ntpSysJitterDesc
ch <- ntpClkJitterDesc
ch <- ntpRootDelayDesc
ch <- ntpLeapDesc
ch <- ntpPrecisionDesc
ch <- ntpPollDesc
}

func (c *ntpCollector) Collect(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error {
var reply rpcReply

err := client.RunCommandAndParse("show ntp status | display xml", &reply)
if err != nil {
return errors.Wrap(err, "failed to execute NTP command")
}

// Hier wird das parseResult direkt aus den Metriken erzeugt
metrics := parseNTPOutput(reply.Output.Text)
if len(metrics) == 0 {
return errors.New("no NTP metrics parsed")
}

tc := mustParseFloat(metrics["tc"])
if tc == 0 {
tc = 10
}

// Konvertierung der Metriken in parseResult
result := &parseResult{
AssocID: metrics["associd"],
Stratum: mustParseFloat(metrics["stratum"]),
RefID: metrics["refid"],
Offset: mustParseFloat(metrics["offset"]),
SysJitter: mustParseFloat(metrics["sys_jitter"]),
ClkJitter: mustParseFloat(metrics["clk_jitter"]),
RootDelay: mustParseFloat(metrics["rootdelay"]),
Leap: metrics["leap"],
Precision: mustParseFloat(metrics["precision"]),
PollInterval: math.Pow(2, tc), // 2^10 = 1024
}

server := result.RefID
if server == "" {
server = "unknown"
}

labels := append(labelValues, server)

exportMetric(ch, ntpStratumDesc, result.Stratum, labels)
exportMetric(ch, ntpOffsetDesc, result.Offset, labels)
exportMetric(ch, ntpSysJitterDesc, result.SysJitter, labels)
exportMetric(ch, ntpClkJitterDesc, result.ClkJitter, labels)
exportMetric(ch, ntpRootDelayDesc, result.RootDelay, labels)
exportMetric(ch, ntpLeapDesc, parseLeap(result.Leap), labels)
exportMetric(ch, ntpPrecisionDesc, result.Precision, labels)
exportMetric(ch, ntpPollDesc, result.PollInterval, labels)

return nil
}

func exportMetric(ch chan<- prometheus.Metric, desc *prometheus.Desc, value float64, labels []string) {
ch <- prometheus.MustNewConstMetric(
desc,
prometheus.GaugeValue,
value,
labels...,
)
}

func parseLeap(leap string) float64 {
leap = strings.TrimSpace(leap)
switch leap {
case "00":
return 0
case "01":
return 1
case "10":
return 2
case "11":
return 3
default:
return -1
}
}

func mustParseFloat(s string) float64 {
s = strings.Trim(s, "+,\" ") // Kommas entfernen
if s == "" || s == "-" {
return 0
}
f, err := strconv.ParseFloat(s, 64)
if err != nil {
log.Printf("Parse error for '%s': %v", s, err)
return 0
}
return f
}
41 changes: 41 additions & 0 deletions pkg/features/ntp/rpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// In rpc.go NUR folgendes belassen:
package ntp

import (
"encoding/xml"
"regexp"
"strings"
)

type rpcReply struct {
XMLName xml.Name `xml:"rpc-reply"`
Output struct {
Text string `xml:",chardata"`
} `xml:"output"`
}

type parseResult struct {
AssocID string
Stratum float64
RefID string
Offset float64
SysJitter float64
ClkJitter float64
RootDelay float64
Leap string
Precision float64
PollInterval float64
}

func parseNTPOutput(output string) map[string]string {
re := regexp.MustCompile(`(\w+)=("[^"]+"|\S+)`)
matches := re.FindAllStringSubmatch(output, -1)

metrics := make(map[string]string)
for _, m := range matches {
key := m[1]
value := strings.Trim(m[2], "\", ")
metrics[key] = value
}
return metrics
}