From fc11b9e097a6ea2bc425f25a42fab5d800856d87 Mon Sep 17 00:00:00 2001 From: takt Date: Fri, 26 Jul 2024 10:37:10 +0200 Subject: [PATCH 1/5] BGP: Implement dynamic labels from descriptions (#249) Co-authored-by: Oliver Geiselhardt-Herms --- collectors.go | 12 +- collectors_test.go | 4 +- devices.go | 10 +- internal/config/config.go | 36 +++-- junos_collector.go | 17 +-- main.go | 2 +- pkg/features/bgp/collector.go | 129 ++++++++++-------- .../interfacediagnostics/collector.go | 4 +- pkg/features/interfacequeue/collector.go | 4 +- pkg/features/interfaces/collector.go | 4 +- pkg/interfacelabels/dynamic_labels.go | 64 ++++++--- pkg/interfacelabels/dynamic_labels_test.go | 4 +- 12 files changed, 177 insertions(+), 113 deletions(-) diff --git a/collectors.go b/collectors.go index 16bd9a8e..ee4105cf 100644 --- a/collectors.go +++ b/collectors.go @@ -3,6 +3,8 @@ package main import ( + "regexp" + "github.com/czerwonk/junos_exporter/internal/config" "github.com/czerwonk/junos_exporter/pkg/collector" "github.com/czerwonk/junos_exporter/pkg/connector" @@ -44,13 +46,13 @@ import ( type collectors struct { logicalSystem string - dynamicLabels *interfacelabels.DynamicLabels + dynamicLabels *interfacelabels.DynamicLabelManager collectors map[string]collector.RPCCollector devices map[string][]collector.RPCCollector cfg *config.Config } -func collectorsForDevices(devices []*connector.Device, cfg *config.Config, logicalSystem string, dynamicLabels *interfacelabels.DynamicLabels) *collectors { +func collectorsForDevices(devices []*connector.Device, cfg *config.Config, logicalSystem string, dynamicLabels *interfacelabels.DynamicLabelManager) *collectors { c := &collectors{ logicalSystem: logicalSystem, dynamicLabels: dynamicLabels, @@ -60,13 +62,13 @@ func collectorsForDevices(devices []*connector.Device, cfg *config.Config, logic } for _, d := range devices { - c.initCollectorsForDevices(d) + c.initCollectorsForDevices(d, cfg.IfDescReg) } return c } -func (c *collectors) initCollectorsForDevices(device *connector.Device) { +func (c *collectors) initCollectorsForDevices(device *connector.Device, bgpDescRe *regexp.Regexp) { f := c.cfg.FeaturesForDevice(device.Host) c.devices[device.Host] = make([]collector.RPCCollector, 0) @@ -78,7 +80,7 @@ func (c *collectors) initCollectorsForDevices(device *connector.Device) { }) c.addCollectorIfEnabledForDevice(device, "bfd", f.BFD, bfd.NewCollector) c.addCollectorIfEnabledForDevice(device, "bgp", f.BGP, func() collector.RPCCollector { - return bgp.NewCollector(c.logicalSystem) + return bgp.NewCollector(c.logicalSystem, bgpDescRe) }) c.addCollectorIfEnabledForDevice(device, "env", f.Environment, environment.NewCollector) c.addCollectorIfEnabledForDevice(device, "firewall", f.Firewall, firewall.NewCollector) diff --git a/collectors_test.go b/collectors_test.go index 924e1e69..b5a5d9fd 100644 --- a/collectors_test.go +++ b/collectors_test.go @@ -40,7 +40,7 @@ func TestCollectorsRegistered(t *testing.T) { cols := collectorsForDevices([]*connector.Device{{ Host: "::1", - }}, c, "", interfacelabels.NewDynamicLabels()) + }}, c, "", interfacelabels.NewDynamicLabelManager()) assert.Equal(t, 20, len(cols.collectors), "collector count") } @@ -88,7 +88,7 @@ func TestCollectorsForDevices(t *testing.T) { d2 := &connector.Device{ Host: "2001:678:1e0::2", } - cols := collectorsForDevices([]*connector.Device{d1, d2}, c, "", interfacelabels.NewDynamicLabels()) + cols := collectorsForDevices([]*connector.Device{d1, d2}, c, "", interfacelabels.NewDynamicLabelManager()) assert.Equal(t, 20, len(cols.collectorsForDevice(d1)), "device 1 collector count") diff --git a/devices.go b/devices.go index ca6c55c1..9cbf8e19 100644 --- a/devices.go +++ b/devices.go @@ -3,6 +3,7 @@ package main import ( + "fmt" "os" "regexp" "strings" @@ -56,10 +57,15 @@ func deviceFromDeviceConfig(device *config.DeviceConfig, hostname string, cfg *c } // check whether there is a device specific regex otherwise fallback to global regex - if len(device.IfDescReg) == 0 { + if len(device.IfDescRegStr) == 0 { device.IfDescReg = cfg.IfDescReg } else { - regexp.MustCompile(device.IfDescReg) + re, err := regexp.Compile(device.IfDescRegStr) + if err != nil { + return nil, fmt.Errorf("unable to compile device description regex for %q: %q: %w", hostname, device.IfDescRegStr, err) + } + + device.IfDescReg = re } return &connector.Device{ diff --git a/internal/config/config.go b/internal/config/config.go index d7b30f59..945d3481 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -3,6 +3,7 @@ package config import ( + "fmt" "io" "regexp" @@ -11,12 +12,26 @@ import ( // Config represents the configuration for the exporter type Config struct { - Password string `yaml:"password"` - Targets []string `yaml:"targets,omitempty"` - Devices []*DeviceConfig `yaml:"devices,omitempty"` - Features FeatureConfig `yaml:"features,omitempty"` - LSEnabled bool `yaml:"logical_systems,omitempty"` - IfDescReg string `yaml:"interface_description_regex,omitempty"` + Password string `yaml:"password"` + Targets []string `yaml:"targets,omitempty"` + Devices []*DeviceConfig `yaml:"devices,omitempty"` + Features FeatureConfig `yaml:"features,omitempty"` + LSEnabled bool `yaml:"logical_systems,omitempty"` + IfDescReStr string `yaml:"interface_description_regex,omitempty"` + IfDescReg *regexp.Regexp `yaml:"-"` +} + +func (c *Config) load() error { + if c.IfDescReStr != "" { + re, err := regexp.Compile(c.IfDescReStr) + if err != nil { + return fmt.Errorf("unable to compile interfce description regex %q: %w", c.IfDescReStr, err) + } + + c.IfDescReg = re + } + + return nil } // DeviceConfig is the config representation of 1 device @@ -27,7 +42,8 @@ type DeviceConfig struct { KeyFile string `yaml:"key_file,omitempty"` KeyPassphrase string `yaml:"key_passphrase,omitempty"` Features *FeatureConfig `yaml:"features,omitempty"` - IfDescReg string `yaml:"interface_description_regex,omitempty"` + IfDescRegStr string `yaml:"interface_description_regex,omitempty"` + IfDescReg *regexp.Regexp `yaml:"-"` IsHostPattern bool `yaml:"host_pattern,omitempty"` HostPattern *regexp.Regexp } @@ -94,6 +110,11 @@ func Load(reader io.Reader) (*Config, error) { return nil, err } + err = c.load() + if err != nil { + return nil, err + } + for _, device := range c.Devices { if device.IsHostPattern { hostPattern, err := regexp.Compile(device.Host) @@ -110,7 +131,6 @@ func Load(reader io.Reader) (*Config, error) { func setDefaultValues(c *Config) { c.Password = "" c.LSEnabled = false - c.IfDescReg = "" f := &c.Features f.Alarm = true f.BGP = true diff --git a/junos_collector.go b/junos_collector.go index 907f936d..66d5a2c4 100644 --- a/junos_collector.go +++ b/junos_collector.go @@ -40,7 +40,7 @@ type junosCollector struct { } func newJunosCollector(ctx context.Context, devices []*connector.Device, logicalSystem string) *junosCollector { - l := interfacelabels.NewDynamicLabels() + l := interfacelabels.NewDynamicLabelManager() clients := make(map[*connector.Device]*rpc.Client) @@ -78,22 +78,13 @@ func newJunosCollector(ctx context.Context, devices []*connector.Device, logical func deviceInterfaceRegex(host string) *regexp.Regexp { dc := cfg.FindDeviceConfig(host) - if len(dc.IfDescReg) > 0 { - regex, err := regexp.Compile(dc.IfDescReg) + if len(dc.IfDescRegStr) > 0 { + regex, err := regexp.Compile(dc.IfDescRegStr) if err == nil { return regex } - log.Errorf("device specific dynamic label regex %s invalid: %v", dc.IfDescReg, err) - } - - if len(cfg.IfDescReg) > 0 { - regex, err := regexp.Compile(cfg.IfDescReg) - if err == nil { - return regex - } - - log.Errorf("global dynamic label regex (%s) invalid: %v", cfg.IfDescReg, err) + log.Errorf("device specific dynamic label regex %s invalid: %v", dc.IfDescRegStr, err) } return interfacelabels.DefaultInterfaceDescRegex() diff --git a/main.go b/main.go index 06e79ffe..8d112108 100644 --- a/main.go +++ b/main.go @@ -212,7 +212,7 @@ func loadConfigFromFlags() *config.Config { c := config.New() c.Targets = strings.Split(*sshHosts, ",") c.LSEnabled = *lsEnabled - c.IfDescReg = *interfaceDescriptionRegex + c.IfDescReStr = *interfaceDescriptionRegex f := &c.Features f.Alarm = *alarmEnabled diff --git a/pkg/features/bgp/collector.go b/pkg/features/bgp/collector.go index 8e5c1999..3d5e47ce 100644 --- a/pkg/features/bgp/collector.go +++ b/pkg/features/bgp/collector.go @@ -5,8 +5,10 @@ package bgp import ( "fmt" "math" + "regexp" "github.com/czerwonk/junos_exporter/pkg/collector" + "github.com/czerwonk/junos_exporter/pkg/interfacelabels" "github.com/prometheus/client_golang/prometheus" "strings" @@ -14,7 +16,7 @@ import ( const prefix string = "junos_bgp_session_" -var ( +type description struct { upDesc *prometheus.Desc stateDesc *prometheus.Desc receivedPrefixesDesc *prometheus.Desc @@ -31,42 +33,51 @@ var ( medDesc *prometheus.Desc preferenceDesc *prometheus.Desc holdTimeDesc *prometheus.Desc -) +} + +func newDescriptions(dynLabels interfacelabels.InterfaceLabels) *description { + d := &description{} -func init() { l := []string{"target", "asn", "ip", "description", "group"} - upDesc = prometheus.NewDesc(prefix+"up", "Session is up (1 = Established)", l, nil) - stateDesc = prometheus.NewDesc(prefix+"state", "State of the bgp Session (1 = Active, 2 = Connect, 3 = Established, 4 = Idle, 5 = OpenConfirm, 6 = OpenSent, 7 = route reflector client, 0 = Other)", l, nil) - inputMessagesDesc = prometheus.NewDesc(prefix+"messages_input_count", "Number of received messages", l, nil) - outputMessagesDesc = prometheus.NewDesc(prefix+"messages_output_count", "Number of transmitted messages", l, nil) - flapsDesc = prometheus.NewDesc(prefix+"flap_count", "Number of session flaps", l, nil) - medDesc = prometheus.NewDesc(prefix+"metric_out", "MED configured for the session", l, nil) - preferenceDesc = prometheus.NewDesc(prefix+"preference", "Preference configured for the session", l, nil) - holdTimeDesc = prometheus.NewDesc(prefix+"hold_time_seconds", "Hold time configured for the session", l, nil) + l = append(l, dynLabels.Keys()...) + d.upDesc = prometheus.NewDesc(prefix+"up", "Session is up (1 = Established)", l, nil) + d.stateDesc = prometheus.NewDesc(prefix+"state", "State of the bgp Session (1 = Active, 2 = Connect, 3 = Established, 4 = Idle, 5 = OpenConfirm, 6 = OpenSent, 7 = route reflector client, 0 = Other)", l, nil) + d.inputMessagesDesc = prometheus.NewDesc(prefix+"messages_input_count", "Number of received messages", l, nil) + d.outputMessagesDesc = prometheus.NewDesc(prefix+"messages_output_count", "Number of transmitted messages", l, nil) + d.flapsDesc = prometheus.NewDesc(prefix+"flap_count", "Number of session flaps", l, nil) + d.medDesc = prometheus.NewDesc(prefix+"metric_out", "MED configured for the session", l, nil) + d.preferenceDesc = prometheus.NewDesc(prefix+"preference", "Preference configured for the session", l, nil) + d.holdTimeDesc = prometheus.NewDesc(prefix+"hold_time_seconds", "Hold time configured for the session", l, nil) infoLabels := append(l, "local_as", "import_policy", "export_policy", "options") - infoDesc = prometheus.NewDesc(prefix+"info", "Information about the session (e.g. configuration)", infoLabels, nil) + d.infoDesc = prometheus.NewDesc(prefix+"info", "Information about the session (e.g. configuration)", infoLabels, nil) l = append(l, "table") - receivedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_received_count", "Number of received prefixes", l, nil) - acceptedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_accepted_count", "Number of accepted prefixes", l, nil) - rejectedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_rejected_count", "Number of rejected prefixes", l, nil) - activePrefixesDesc = prometheus.NewDesc(prefix+"prefixes_active_count", "Number of active prefixes (best route in RIB)", l, nil) - advertisedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_advertised_count", "Number of prefixes announced to peer", l, nil) - prefixesLimitPercentageDesc = prometheus.NewDesc(prefix+"prefixes_limit_percentage", "percentage of received prefixes against prefix-limit", l, nil) - prefixesLimitCountDesc = prometheus.NewDesc(prefix+"prefixes_limit_count", "prefix-count variable set in prefix-limit", l, nil) + d.receivedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_received_count", "Number of received prefixes", l, nil) + d.acceptedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_accepted_count", "Number of accepted prefixes", l, nil) + d.rejectedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_rejected_count", "Number of rejected prefixes", l, nil) + d.activePrefixesDesc = prometheus.NewDesc(prefix+"prefixes_active_count", "Number of active prefixes (best route in RIB)", l, nil) + d.advertisedPrefixesDesc = prometheus.NewDesc(prefix+"prefixes_advertised_count", "Number of prefixes announced to peer", l, nil) + d.prefixesLimitPercentageDesc = prometheus.NewDesc(prefix+"prefixes_limit_percentage", "percentage of received prefixes against prefix-limit", l, nil) + d.prefixesLimitCountDesc = prometheus.NewDesc(prefix+"prefixes_limit_count", "prefix-count variable set in prefix-limit", l, nil) + + return d } type bgpCollector struct { LogicalSystem string + descriptionRe *regexp.Regexp } type groupMap map[int64]group // NewCollector creates a new collector -func NewCollector(logicalSystem string) collector.RPCCollector { - return &bgpCollector{LogicalSystem: logicalSystem} +func NewCollector(logicalSystem string, descRe *regexp.Regexp) collector.RPCCollector { + return &bgpCollector{ + LogicalSystem: logicalSystem, + descriptionRe: descRe, + } } // Name returns the name of the collector @@ -76,21 +87,22 @@ func (*bgpCollector) Name() string { // Describe describes the metrics func (*bgpCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- upDesc - ch <- receivedPrefixesDesc - ch <- acceptedPrefixesDesc - ch <- rejectedPrefixesDesc - ch <- activePrefixesDesc - ch <- advertisedPrefixesDesc - ch <- inputMessagesDesc - ch <- outputMessagesDesc - ch <- flapsDesc - ch <- prefixesLimitCountDesc - ch <- prefixesLimitPercentageDesc - ch <- infoDesc - ch <- medDesc - ch <- preferenceDesc - ch <- holdTimeDesc + d := newDescriptions(nil) + ch <- d.upDesc + ch <- d.receivedPrefixesDesc + ch <- d.acceptedPrefixesDesc + ch <- d.rejectedPrefixesDesc + ch <- d.activePrefixesDesc + ch <- d.advertisedPrefixesDesc + ch <- d.inputMessagesDesc + ch <- d.outputMessagesDesc + ch <- d.flapsDesc + ch <- d.prefixesLimitCountDesc + ch <- d.prefixesLimitPercentageDesc + ch <- d.infoDesc + ch <- d.medDesc + ch <- d.preferenceDesc + ch <- d.holdTimeDesc } // Collect collects metrics from JunOS @@ -151,7 +163,7 @@ func (c *bgpCollector) collect(client collector.Client, ch chan<- prometheus.Met func (c *bgpCollector) collectForPeer(p peer, groups groupMap, ch chan<- prometheus.Metric, labelValues []string) { ip := strings.Split(p.IP, "+") - l := append(labelValues, []string{ + lv := append(labelValues, []string{ p.ASN, ip[0], p.Description, @@ -162,26 +174,31 @@ func (c *bgpCollector) collectForPeer(p peer, groups groupMap, ch chan<- prometh up = 1 } - ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, float64(up), l...) - ch <- prometheus.MustNewConstMetric(stateDesc, prometheus.GaugeValue, bgpStateToNumber(p.State), l...) - ch <- prometheus.MustNewConstMetric(inputMessagesDesc, prometheus.GaugeValue, float64(p.InputMessages), l...) - ch <- prometheus.MustNewConstMetric(outputMessagesDesc, prometheus.GaugeValue, float64(p.OutputMessages), l...) - ch <- prometheus.MustNewConstMetric(flapsDesc, prometheus.GaugeValue, float64(p.Flaps), l...) - ch <- prometheus.MustNewConstMetric(preferenceDesc, prometheus.GaugeValue, float64(p.OptionInformation.Preference), l...) - ch <- prometheus.MustNewConstMetric(medDesc, prometheus.GaugeValue, float64(p.OptionInformation.MetricOut), l...) - ch <- prometheus.MustNewConstMetric(holdTimeDesc, prometheus.GaugeValue, float64(p.OptionInformation.Holdtime), l...) + dynLabels := interfacelabels.ParseDescription(p.Description, c.descriptionRe) + lv = append(lv, dynLabels.Values()...) + + d := newDescriptions(dynLabels) + + ch <- prometheus.MustNewConstMetric(d.upDesc, prometheus.GaugeValue, float64(up), lv...) + ch <- prometheus.MustNewConstMetric(d.stateDesc, prometheus.GaugeValue, bgpStateToNumber(p.State), lv...) + ch <- prometheus.MustNewConstMetric(d.inputMessagesDesc, prometheus.GaugeValue, float64(p.InputMessages), lv...) + ch <- prometheus.MustNewConstMetric(d.outputMessagesDesc, prometheus.GaugeValue, float64(p.OutputMessages), lv...) + ch <- prometheus.MustNewConstMetric(d.flapsDesc, prometheus.GaugeValue, float64(p.Flaps), lv...) + ch <- prometheus.MustNewConstMetric(d.preferenceDesc, prometheus.GaugeValue, float64(p.OptionInformation.Preference), lv...) + ch <- prometheus.MustNewConstMetric(d.medDesc, prometheus.GaugeValue, float64(p.OptionInformation.MetricOut), lv...) + ch <- prometheus.MustNewConstMetric(d.holdTimeDesc, prometheus.GaugeValue, float64(p.OptionInformation.Holdtime), lv...) - infoValues := append(l, + infoValues := append(lv, localASNForPeer(p), formatPolicy(p.OptionInformation.ImportPolicy), formatPolicy(p.OptionInformation.ExportPolicy), p.OptionInformation.Options) - ch <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, 1, infoValues...) + ch <- prometheus.MustNewConstMetric(d.infoDesc, prometheus.GaugeValue, 1, infoValues...) - c.collectRIBForPeer(p, ch, l) + c.collectRIBForPeer(p, ch, lv, d) } -func (*bgpCollector) collectRIBForPeer(p peer, ch chan<- prometheus.Metric, labelValues []string) { +func (*bgpCollector) collectRIBForPeer(p peer, ch chan<- prometheus.Metric, labelValues []string, d *description) { var rib_name string // derive the name of the rib for which the prefix limit is configured by examining the NLRI type @@ -200,21 +217,21 @@ func (*bgpCollector) collectRIBForPeer(p peer, ch chan<- prometheus.Metric, labe } if p.OptionInformation.PrefixLimit.PrefixCount > 0 { - ch <- prometheus.MustNewConstMetric(prefixesLimitCountDesc, prometheus.GaugeValue, float64(p.OptionInformation.PrefixLimit.PrefixCount), append(labelValues, rib_name)...) + ch <- prometheus.MustNewConstMetric(d.prefixesLimitCountDesc, prometheus.GaugeValue, float64(p.OptionInformation.PrefixLimit.PrefixCount), append(labelValues, rib_name)...) } for _, rib := range p.RIBs { l := append(labelValues, rib.Name) - ch <- prometheus.MustNewConstMetric(receivedPrefixesDesc, prometheus.GaugeValue, float64(rib.ReceivedPrefixes), l...) - ch <- prometheus.MustNewConstMetric(acceptedPrefixesDesc, prometheus.GaugeValue, float64(rib.AcceptedPrefixes), l...) - ch <- prometheus.MustNewConstMetric(rejectedPrefixesDesc, prometheus.GaugeValue, float64(rib.RejectedPrefixes), l...) - ch <- prometheus.MustNewConstMetric(activePrefixesDesc, prometheus.GaugeValue, float64(rib.ActivePrefixes), l...) - ch <- prometheus.MustNewConstMetric(advertisedPrefixesDesc, prometheus.GaugeValue, float64(rib.AdvertisedPrefixes), l...) + ch <- prometheus.MustNewConstMetric(d.receivedPrefixesDesc, prometheus.GaugeValue, float64(rib.ReceivedPrefixes), l...) + ch <- prometheus.MustNewConstMetric(d.acceptedPrefixesDesc, prometheus.GaugeValue, float64(rib.AcceptedPrefixes), l...) + ch <- prometheus.MustNewConstMetric(d.rejectedPrefixesDesc, prometheus.GaugeValue, float64(rib.RejectedPrefixes), l...) + ch <- prometheus.MustNewConstMetric(d.activePrefixesDesc, prometheus.GaugeValue, float64(rib.ActivePrefixes), l...) + ch <- prometheus.MustNewConstMetric(d.advertisedPrefixesDesc, prometheus.GaugeValue, float64(rib.AdvertisedPrefixes), l...) if rib.Name == rib_name { if p.OptionInformation.PrefixLimit.PrefixCount > 0 { prefixesLimitPercent := float64(rib.ReceivedPrefixes) / float64(p.OptionInformation.PrefixLimit.PrefixCount) - ch <- prometheus.MustNewConstMetric(prefixesLimitPercentageDesc, prometheus.GaugeValue, math.Round(prefixesLimitPercent*100)/100, l...) + ch <- prometheus.MustNewConstMetric(d.prefixesLimitPercentageDesc, prometheus.GaugeValue, math.Round(prefixesLimitPercent*100)/100, l...) } } } diff --git a/pkg/features/interfacediagnostics/collector.go b/pkg/features/interfacediagnostics/collector.go index a1f32cd9..77056b40 100644 --- a/pkg/features/interfacediagnostics/collector.go +++ b/pkg/features/interfacediagnostics/collector.go @@ -17,7 +17,7 @@ import ( const prefix = "junos_interface_diagnostics_" type interfaceDiagnosticsCollector struct { - labels *interfacelabels.DynamicLabels + labels *interfacelabels.DynamicLabelManager laserBiasCurrentDesc *prometheus.Desc laserBiasCurrentHighAlarmThresholdDesc *prometheus.Desc laserBiasCurrentLowAlarmThresholdDesc *prometheus.Desc @@ -67,7 +67,7 @@ type interfaceDiagnosticsCollector struct { } // NewCollector creates a new collector -func NewCollector(labels *interfacelabels.DynamicLabels) collector.RPCCollector { +func NewCollector(labels *interfacelabels.DynamicLabelManager) collector.RPCCollector { c := &interfaceDiagnosticsCollector{ labels: labels, } diff --git a/pkg/features/interfacequeue/collector.go b/pkg/features/interfacequeue/collector.go index 4f8c6ccc..87a87786 100644 --- a/pkg/features/interfacequeue/collector.go +++ b/pkg/features/interfacequeue/collector.go @@ -12,7 +12,7 @@ import ( const prefix = "junos_interface_queues_" // NewCollector creates an queue collector instance -func NewCollector(labels *interfacelabels.DynamicLabels) collector.RPCCollector { +func NewCollector(labels *interfacelabels.DynamicLabelManager) collector.RPCCollector { c := &interfaceQueueCollector{ labels: labels, } @@ -22,7 +22,7 @@ func NewCollector(labels *interfacelabels.DynamicLabels) collector.RPCCollector } type interfaceQueueCollector struct { - labels *interfacelabels.DynamicLabels + labels *interfacelabels.DynamicLabelManager queuedPackets *prometheus.Desc queuedBytes *prometheus.Desc transferedPackets *prometheus.Desc diff --git a/pkg/features/interfaces/collector.go b/pkg/features/interfaces/collector.go index 9424a2c0..f17ebc3f 100644 --- a/pkg/features/interfaces/collector.go +++ b/pkg/features/interfaces/collector.go @@ -16,7 +16,7 @@ const prefix = "junos_interface_" // Collector collects interface metrics type interfaceCollector struct { - labels *interfacelabels.DynamicLabels + labels *interfacelabels.DynamicLabelManager receiveBytesDesc *prometheus.Desc receivePacketsDesc *prometheus.Desc receiveErrorsDesc *prometheus.Desc @@ -57,7 +57,7 @@ type interfaceCollector struct { } // NewCollector creates a new collector -func NewCollector(labels *interfacelabels.DynamicLabels) collector.RPCCollector { +func NewCollector(labels *interfacelabels.DynamicLabelManager) collector.RPCCollector { c := &interfaceCollector{ labels: labels, } diff --git a/pkg/interfacelabels/dynamic_labels.go b/pkg/interfacelabels/dynamic_labels.go index 38fd3b90..3382284f 100644 --- a/pkg/interfacelabels/dynamic_labels.go +++ b/pkg/interfacelabels/dynamic_labels.go @@ -24,18 +24,18 @@ func DefaultInterfaceDescRegex() *regexp.Regexp { return regexp.MustCompile(`\[([^=\]]+)(=[^\]]+)?\]`) } -// NewDynamicLabels create a new instance of DynamicLabels -func NewDynamicLabels() *DynamicLabels { - return &DynamicLabels{ +// NewDynamicLabelManager create a new instance of DynamicLabels +func NewDynamicLabelManager() *DynamicLabelManager { + return &DynamicLabelManager{ labelNames: make(map[string]int), - labels: make(map[interfaceKey][]*interfaceLabel), + labels: make(map[interfaceKey][]*InterfaceLabel), } } -// DynamicLabels parses and manages dynamic labels and label values -type DynamicLabels struct { +// DynamicLabelManager parses and manages dynamic labels and label values +type DynamicLabelManager struct { labelNames map[string]int - labels map[interfaceKey][]*interfaceLabel + labels map[interfaceKey][]*InterfaceLabel labelCount int mu sync.Mutex } @@ -45,13 +45,21 @@ type interfaceKey struct { ifaceName string } -type interfaceLabel struct { +type InterfaceLabel struct { name string value string } +func (il *InterfaceLabel) Name() string { + return il.name +} + +func (il *InterfaceLabel) Value() string { + return il.value +} + // CollectDescriptions collects labels from descriptions -func (l *DynamicLabels) CollectDescriptions(device *connector.Device, client collector.Client, ifDescReg *regexp.Regexp) error { +func (l *DynamicLabelManager) CollectDescriptions(device *connector.Device, client collector.Client, ifDescReg *regexp.Regexp) error { r := &result{} err := client.RunCommandAndParse("show interfaces descriptions", r) if err != nil { @@ -65,7 +73,7 @@ func (l *DynamicLabels) CollectDescriptions(device *connector.Device, client col } // LabelNames returns the names for all dynamic labels -func (l *DynamicLabels) LabelNames() []string { +func (l *DynamicLabelManager) LabelNames() []string { names := make([]string, len(l.labelNames)) for k, v := range l.labelNames { @@ -76,7 +84,7 @@ func (l *DynamicLabels) LabelNames() []string { } // ValuesForInterface returns the values for all dynamic labels -func (l *DynamicLabels) ValuesForInterface(device *connector.Device, ifaceName string) []string { +func (l *DynamicLabelManager) ValuesForInterface(device *connector.Device, ifaceName string) []string { labels := make([]string, len(l.labelNames)) k := interfaceKey{host: device.Host, ifaceName: ifaceName} @@ -92,12 +100,12 @@ func (l *DynamicLabels) ValuesForInterface(device *connector.Device, ifaceName s return labels } -func (l *DynamicLabels) parseDescriptions(device *connector.Device, ifaces []interfaceDescription, ifDescReg *regexp.Regexp) { +func (l *DynamicLabelManager) parseDescriptions(device *connector.Device, ifaces []interfaceDescription, ifDescReg *regexp.Regexp) { l.mu.Lock() defer l.mu.Unlock() for _, in := range ifaces { - labels := l.parseDescription(in, ifDescReg) + labels := ParseDescription(in.Description, ifDescReg) for _, la := range labels { if _, found := l.labelNames[la.name]; !found { @@ -111,14 +119,14 @@ func (l *DynamicLabels) parseDescriptions(device *connector.Device, ifaces []int } } -func (l *DynamicLabels) parseDescription(iface interfaceDescription, ifDescReg *regexp.Regexp) []*interfaceLabel { - labels := make([]*interfaceLabel, 0) +func ParseDescription(description string, ifDescReg *regexp.Regexp) InterfaceLabels { + labels := make(InterfaceLabels, 0) - if len(iface.Description) == 0 { + if len(description) == 0 || ifDescReg == nil { return labels } - matches := ifDescReg.FindAllStringSubmatch(iface.Description, -1) + matches := ifDescReg.FindAllStringSubmatch(description, -1) for _, m := range matches { n := strings.ToLower(m[1]) @@ -126,7 +134,7 @@ func (l *DynamicLabels) parseDescription(iface interfaceDescription, ifDescReg * continue } - label := &interfaceLabel{ + label := &InterfaceLabel{ name: n, } @@ -143,3 +151,23 @@ func (l *DynamicLabels) parseDescription(iface interfaceDescription, ifDescReg * return labels } + +type InterfaceLabels []*InterfaceLabel + +func (ils InterfaceLabels) Keys() []string { + ret := make([]string, 0, len(ils)) + for _, il := range ils { + ret = append(ret, il.name) + } + + return ret +} + +func (ils InterfaceLabels) Values() []string { + ret := make([]string, 0, len(ils)) + for _, il := range ils { + ret = append(ret, il.value) + } + + return ret +} diff --git a/pkg/interfacelabels/dynamic_labels_test.go b/pkg/interfacelabels/dynamic_labels_test.go index 94a30701..c0ea6bb1 100644 --- a/pkg/interfacelabels/dynamic_labels_test.go +++ b/pkg/interfacelabels/dynamic_labels_test.go @@ -12,7 +12,7 @@ import ( func TestParseDescriptions(t *testing.T) { t.Run("Test default", func(t *testing.T) { - l := NewDynamicLabels() + l := NewDynamicLabelManager() regex := DefaultInterfaceDescRegex() if1 := interfaceDescription{ @@ -45,7 +45,7 @@ func TestParseDescriptions(t *testing.T) { }) t.Run("Test custom regex", func(t *testing.T) { - l := NewDynamicLabels() + l := NewDynamicLabelManager() regex := regexp.MustCompile(`[[\s]([^=\[\]]+)(=[^,\]]+)?[,\]]`) if1 := interfaceDescription{ From 52dffa4a2efb2927ae30ecb29c587816568a4d77 Mon Sep 17 00:00:00 2001 From: takt Date: Fri, 26 Jul 2024 10:37:30 +0200 Subject: [PATCH 2/5] Interface Queues: Add forwarding_class label (#250) Co-authored-by: Oliver Geiselhardt-Herms --- pkg/features/interfacequeue/collector.go | 2 ++ pkg/features/interfacequeue/rpc.go | 1 + 2 files changed, 3 insertions(+) diff --git a/pkg/features/interfacequeue/collector.go b/pkg/features/interfacequeue/collector.go index 87a87786..4e2a0c0a 100644 --- a/pkg/features/interfacequeue/collector.go +++ b/pkg/features/interfacequeue/collector.go @@ -53,6 +53,7 @@ func (c *interfaceQueueCollector) init() { l := []string{"target", "name", "description"} l = append(l, c.labels.LabelNames()...) l = append(l, "queue_number") + l = append(l, "forwarding_class") c.queuedPackets = prometheus.NewDesc(prefix+"queued_packets_count", "Number of queued packets", l, nil) c.queuedBytes = prometheus.NewDesc(prefix+"queued_bytes_count", "Number of bytes of queued packets", l, nil) @@ -125,6 +126,7 @@ func (c *interfaceQueueCollector) collectForInterface(iface physicalInterface, d func (c *interfaceQueueCollector) collectForQueue(queue queue, ch chan<- prometheus.Metric, labelValues []string) { l := append(labelValues, queue.Number) + l = append(l, queue.ForwaringClassName) ch <- prometheus.MustNewConstMetric(c.queuedPackets, prometheus.CounterValue, float64(queue.QueuedPackets), l...) ch <- prometheus.MustNewConstMetric(c.queuedBytes, prometheus.CounterValue, float64(queue.QueuedBytes), l...) diff --git a/pkg/features/interfacequeue/rpc.go b/pkg/features/interfacequeue/rpc.go index 25d4d1dc..4fd1e7e0 100644 --- a/pkg/features/interfacequeue/rpc.go +++ b/pkg/features/interfacequeue/rpc.go @@ -18,6 +18,7 @@ type physicalInterface struct { type queue struct { Number string `xml:"queue-number"` + ForwaringClassName string `xml:"forwarding-class-name"` QueuedPackets uint64 `xml:"queue-counters-queued-packets"` QueuedBytes uint64 `xml:"queue-counters-queued-bytes"` TransferedPackets uint64 `xml:"queue-counters-trans-packets"` From a6e099a96879dd25ba018d48e84fc5ba55324985 Mon Sep 17 00:00:00 2001 From: takt Date: Fri, 26 Jul 2024 15:52:36 +0200 Subject: [PATCH 3/5] Interfaces: Refactor dynamic labels (#251) Co-authored-by: Oliver Geiselhardt-Herms --- README.md | 2 +- collectors.go | 17 +- collectors_test.go | 5 +- internal/config/config.go | 19 +- internal/config/config_test.go | 12 +- junos_collector.go | 38 +- main.go | 2 +- pkg/dynamiclabels/dynamic_labels.go | 86 ++++ pkg/dynamiclabels/dynamic_labels_test.go | 51 +++ pkg/features/bgp/collector.go | 6 +- .../interfacediagnostics/collector.go | 405 +++++++++--------- pkg/features/interfacediagnostics/helper.go | 7 +- pkg/features/interfacediagnostics/rpc.go | 11 +- pkg/features/interfacequeue/collector.go | 178 ++++---- pkg/features/interfaces/collector.go | 271 ++++++------ pkg/interfacelabels/dynamic_labels.go | 173 -------- pkg/interfacelabels/dynamic_labels_test.go | 77 ---- pkg/interfacelabels/rpc.go | 15 - 18 files changed, 636 insertions(+), 739 deletions(-) create mode 100644 pkg/dynamiclabels/dynamic_labels.go create mode 100644 pkg/dynamiclabels/dynamic_labels_test.go delete mode 100644 pkg/interfacelabels/dynamic_labels.go delete mode 100644 pkg/interfacelabels/dynamic_labels_test.go delete mode 100644 pkg/interfacelabels/rpc.go diff --git a/README.md b/README.md index 141eaf83..ea400bf2 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ features: ``` ## Dynamic Interface Labels -Version 0.9.5 introduced dynamic labels retrieved from the interface descriptions. Flags are supported a well. The first part (label name) has to comply to the following rules: +Version 0.9.5 introduced dynamic labels retrieved from the interface descriptions. Version 0.12.4 added support for dynamic labels on BGP metrics. Flags are supported a well. The first part (label name) has to comply to the following rules: * must not begin with a figure * must only contain this charakters: A-Z,a-z,0-9,_ * is treated lower case diff --git a/collectors.go b/collectors.go index ee4105cf..d1017fd6 100644 --- a/collectors.go +++ b/collectors.go @@ -41,34 +41,31 @@ import ( "github.com/czerwonk/junos_exporter/pkg/features/system" "github.com/czerwonk/junos_exporter/pkg/features/vpws" "github.com/czerwonk/junos_exporter/pkg/features/vrrp" - "github.com/czerwonk/junos_exporter/pkg/interfacelabels" ) type collectors struct { logicalSystem string - dynamicLabels *interfacelabels.DynamicLabelManager collectors map[string]collector.RPCCollector devices map[string][]collector.RPCCollector cfg *config.Config } -func collectorsForDevices(devices []*connector.Device, cfg *config.Config, logicalSystem string, dynamicLabels *interfacelabels.DynamicLabelManager) *collectors { +func collectorsForDevices(devices []*connector.Device, cfg *config.Config, logicalSystem string) *collectors { c := &collectors{ logicalSystem: logicalSystem, - dynamicLabels: dynamicLabels, collectors: make(map[string]collector.RPCCollector), devices: make(map[string][]collector.RPCCollector), cfg: cfg, } for _, d := range devices { - c.initCollectorsForDevices(d, cfg.IfDescReg) + c.initCollectorsForDevices(d, deviceInterfaceRegex(cfg, d.Host)) } return c } -func (c *collectors) initCollectorsForDevices(device *connector.Device, bgpDescRe *regexp.Regexp) { +func (c *collectors) initCollectorsForDevices(device *connector.Device, descRe *regexp.Regexp) { f := c.cfg.FeaturesForDevice(device.Host) c.devices[device.Host] = make([]collector.RPCCollector, 0) @@ -80,19 +77,19 @@ func (c *collectors) initCollectorsForDevices(device *connector.Device, bgpDescR }) c.addCollectorIfEnabledForDevice(device, "bfd", f.BFD, bfd.NewCollector) c.addCollectorIfEnabledForDevice(device, "bgp", f.BGP, func() collector.RPCCollector { - return bgp.NewCollector(c.logicalSystem, bgpDescRe) + return bgp.NewCollector(c.logicalSystem, descRe) }) c.addCollectorIfEnabledForDevice(device, "env", f.Environment, environment.NewCollector) c.addCollectorIfEnabledForDevice(device, "firewall", f.Firewall, firewall.NewCollector) c.addCollectorIfEnabledForDevice(device, "fpc", f.FPC, fpc.NewCollector) c.addCollectorIfEnabledForDevice(device, "ifacediag", f.InterfaceDiagnostic, func() collector.RPCCollector { - return interfacediagnostics.NewCollector(c.dynamicLabels) + return interfacediagnostics.NewCollector(descRe) }) c.addCollectorIfEnabledForDevice(device, "ifacequeue", f.InterfaceQueue, func() collector.RPCCollector { - return interfacequeue.NewCollector(c.dynamicLabels) + return interfacequeue.NewCollector(descRe) }) c.addCollectorIfEnabledForDevice(device, "iface", f.Interfaces, func() collector.RPCCollector { - return interfaces.NewCollector(c.dynamicLabels) + return interfaces.NewCollector(descRe) }) c.addCollectorIfEnabledForDevice(device, "ipsec", f.IPSec, ipsec.NewCollector) c.addCollectorIfEnabledForDevice(device, "isis", f.ISIS, isis.NewCollector) diff --git a/collectors_test.go b/collectors_test.go index b5a5d9fd..b180a67b 100644 --- a/collectors_test.go +++ b/collectors_test.go @@ -9,7 +9,6 @@ import ( "github.com/czerwonk/junos_exporter/internal/config" "github.com/czerwonk/junos_exporter/pkg/connector" - "github.com/czerwonk/junos_exporter/pkg/interfacelabels" ) func TestCollectorsRegistered(t *testing.T) { @@ -40,7 +39,7 @@ func TestCollectorsRegistered(t *testing.T) { cols := collectorsForDevices([]*connector.Device{{ Host: "::1", - }}, c, "", interfacelabels.NewDynamicLabelManager()) + }}, c, "") assert.Equal(t, 20, len(cols.collectors), "collector count") } @@ -88,7 +87,7 @@ func TestCollectorsForDevices(t *testing.T) { d2 := &connector.Device{ Host: "2001:678:1e0::2", } - cols := collectorsForDevices([]*connector.Device{d1, d2}, c, "", interfacelabels.NewDynamicLabelManager()) + cols := collectorsForDevices([]*connector.Device{d1, d2}, c, "") assert.Equal(t, 20, len(cols.collectorsForDevice(d1)), "device 1 collector count") diff --git a/internal/config/config.go b/internal/config/config.go index 945d3481..d895d5b8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -21,8 +21,8 @@ type Config struct { IfDescReg *regexp.Regexp `yaml:"-"` } -func (c *Config) load() error { - if c.IfDescReStr != "" { +func (c *Config) load(dynamicIfaceLabels bool) error { + if c.IfDescReStr != "" && dynamicIfaceLabels { re, err := regexp.Compile(c.IfDescReStr) if err != nil { return fmt.Errorf("unable to compile interfce description regex %q: %w", c.IfDescReStr, err) @@ -31,6 +31,17 @@ func (c *Config) load() error { c.IfDescReg = re } + for _, d := range c.Devices { + if d.IfDescRegStr != "" && dynamicIfaceLabels { + re, err := regexp.Compile(c.IfDescReStr) + if err != nil { + return fmt.Errorf("unable to compile interfce description regex %q: %w", c.IfDescReStr, err) + } + + d.IfDescReg = re + } + } + return nil } @@ -98,7 +109,7 @@ func New() *Config { } // Load loads a config from reader -func Load(reader io.Reader) (*Config, error) { +func Load(reader io.Reader, dynamicIfaceLabels bool) (*Config, error) { b, err := io.ReadAll(reader) if err != nil { return nil, err @@ -110,7 +121,7 @@ func Load(reader io.Reader) (*Config, error) { return nil, err } - err = c.load() + err = c.load(dynamicIfaceLabels) if err != nil { return nil, err } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index bd03da3a..63ca75cd 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -17,7 +17,7 @@ func TestShouldParse(t *testing.T) { t.Fatal(err) } - c, err := Load(bytes.NewReader(b)) + c, err := Load(bytes.NewReader(b), true) if err != nil { t.Fatal(err) } @@ -49,7 +49,7 @@ func TestShouldUseDefaults(t *testing.T) { t.Fatal(err) } - c, err := Load(bytes.NewReader(b)) + c, err := Load(bytes.NewReader(b), true) if err != nil { t.Fatal(err) } @@ -78,7 +78,7 @@ func TestShouldParseDevices(t *testing.T) { t.Fatal(err) } - c, err := Load(bytes.NewReader(b)) + c, err := Load(bytes.NewReader(b), true) if err != nil { t.Fatal(err) } @@ -124,7 +124,7 @@ func TestShouldParseDevicesWithPattern(t *testing.T) { t.Fatal(err) } - c, err := Load(bytes.NewReader(b)) + c, err := Load(bytes.NewReader(b), true) if err != nil { t.Fatal(err) } @@ -165,7 +165,7 @@ func TestShouldParseDevicesWithPatternInvalid(t *testing.T) { t.Fatal(err) } - c, err := Load(bytes.NewReader(b)) + c, err := Load(bytes.NewReader(b), true) if c != nil { t.Fatal("Parsing should fail because of invalid pattern") } @@ -185,7 +185,7 @@ func TestFindDeviceConfig(t *testing.T) { if err != nil { t.Fatal(err) } - c, err := Load(bytes.NewReader(b)) + c, err := Load(bytes.NewReader(b), true) if err != nil { t.Fatal(err) } diff --git a/junos_collector.go b/junos_collector.go index 66d5a2c4..755d6633 100644 --- a/junos_collector.go +++ b/junos_collector.go @@ -8,14 +8,16 @@ import ( "sync" "time" + "github.com/czerwonk/junos_exporter/internal/config" "github.com/czerwonk/junos_exporter/pkg/connector" - "github.com/czerwonk/junos_exporter/pkg/interfacelabels" + "github.com/czerwonk/junos_exporter/pkg/dynamiclabels" "github.com/czerwonk/junos_exporter/pkg/rpc" "github.com/prometheus/client_golang/prometheus" - log "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" + + log "github.com/sirupsen/logrus" ) const prefix = "junos_" @@ -40,8 +42,6 @@ type junosCollector struct { } func newJunosCollector(ctx context.Context, devices []*connector.Device, logicalSystem string) *junosCollector { - l := interfacelabels.NewDynamicLabelManager() - clients := make(map[*connector.Device]*rpc.Client) for _, d := range devices { @@ -52,42 +52,28 @@ func newJunosCollector(ctx context.Context, devices []*connector.Device, logical } clients[d] = cl - cta := &clientTracingAdapter{ - cl: cl, - ctx: ctx, - } - - if *dynamicIfaceLabels { - regex := deviceInterfaceRegex(d.Host) - err = l.CollectDescriptions(d, cta, regex) - if err != nil { - log.Errorf("Could not get interface descriptions %s: %s", d, err) - continue - } - } } return &junosCollector{ devices: devices, - collectors: collectorsForDevices(devices, cfg, logicalSystem, l), + collectors: collectorsForDevices(devices, cfg, logicalSystem), clients: clients, ctx: ctx, } } -func deviceInterfaceRegex(host string) *regexp.Regexp { +func deviceInterfaceRegex(cfg *config.Config, host string) *regexp.Regexp { dc := cfg.FindDeviceConfig(host) - if len(dc.IfDescRegStr) > 0 { - regex, err := regexp.Compile(dc.IfDescRegStr) - if err == nil { - return regex - } + if dc != nil { + return dc.IfDescReg + } - log.Errorf("device specific dynamic label regex %s invalid: %v", dc.IfDescRegStr, err) + if cfg.IfDescReg != nil { + return cfg.IfDescReg } - return interfacelabels.DefaultInterfaceDescRegex() + return dynamiclabels.DefaultInterfaceDescRegex() } func clientForDevice(device *connector.Device, connManager *connector.SSHConnectionManager) (*rpc.Client, error) { diff --git a/main.go b/main.go index 8d112108..77cb6558 100644 --- a/main.go +++ b/main.go @@ -205,7 +205,7 @@ func loadConfig() (*config.Config, error) { return nil, err } - return config.Load(bytes.NewReader(b)) + return config.Load(bytes.NewReader(b), *dynamicIfaceLabels) } func loadConfigFromFlags() *config.Config { diff --git a/pkg/dynamiclabels/dynamic_labels.go b/pkg/dynamiclabels/dynamic_labels.go new file mode 100644 index 00000000..aab3f778 --- /dev/null +++ b/pkg/dynamiclabels/dynamic_labels.go @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT + +package dynamiclabels + +import ( + "regexp" + "strings" +) + +var ( + nameRe *regexp.Regexp +) + +func init() { + nameRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`) +} + +func DefaultInterfaceDescRegex() *regexp.Regexp { + return regexp.MustCompile(`\[([^=\]]+)(=[^\]]+)?\]`) +} + +type Label struct { + name string + value string +} + +func (il *Label) Name() string { + return il.name +} + +func (il *Label) Value() string { + return il.value +} + +func ParseDescription(description string, ifDescReg *regexp.Regexp) Labels { + labels := make(Labels, 0) + + if len(description) == 0 || ifDescReg == nil { + return labels + } + + matches := ifDescReg.FindAllStringSubmatch(description, -1) + for _, m := range matches { + n := strings.ToLower(m[1]) + + if !nameRe.Match([]byte(n)) { + continue + } + + label := &Label{ + name: n, + } + + val := m[2] + + if strings.HasPrefix(val, "=") { + label.value = val[1:] + } else { + label.value = "1" + } + + labels = append(labels, label) + } + + return labels +} + +type Labels []*Label + +func (ils Labels) Keys() []string { + ret := make([]string, 0, len(ils)) + for _, il := range ils { + ret = append(ret, il.name) + } + + return ret +} + +func (ils Labels) Values() []string { + ret := make([]string, 0, len(ils)) + for _, il := range ils { + ret = append(ret, il.value) + } + + return ret +} diff --git a/pkg/dynamiclabels/dynamic_labels_test.go b/pkg/dynamiclabels/dynamic_labels_test.go new file mode 100644 index 00000000..b639f38f --- /dev/null +++ b/pkg/dynamiclabels/dynamic_labels_test.go @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +package dynamiclabels + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseDescriptions(t *testing.T) { + tests := []struct { + name string + description string + keys []string + values []string + }{ + { + name: "tags and kv pairs", + description: "Name1 [tag1] [foo=x]", + keys: []string{"tag1", "foo"}, + values: []string{"1", "x"}, + }, + { + name: "kv pairs", + description: "Name2 [foo=y] [bar=123]", + keys: []string{"foo", "bar"}, + values: []string{"y", "123"}, + }, + { + name: "empty", + description: "", + keys: []string{}, + values: []string{}, + }, + { + name: "more kv pairs", + description: "Internal: Test-Network [vrf=AS4711]", + keys: []string{"vrf"}, + values: []string{"AS4711"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ifLabels := ParseDescription(test.description, DefaultInterfaceDescRegex()) + assert.Equal(t, test.keys, ifLabels.Keys(), test.name) + assert.Equal(t, test.values, ifLabels.Values(), test.name) + }) + } +} diff --git a/pkg/features/bgp/collector.go b/pkg/features/bgp/collector.go index 3d5e47ce..e408196b 100644 --- a/pkg/features/bgp/collector.go +++ b/pkg/features/bgp/collector.go @@ -8,7 +8,7 @@ import ( "regexp" "github.com/czerwonk/junos_exporter/pkg/collector" - "github.com/czerwonk/junos_exporter/pkg/interfacelabels" + "github.com/czerwonk/junos_exporter/pkg/dynamiclabels" "github.com/prometheus/client_golang/prometheus" "strings" @@ -35,7 +35,7 @@ type description struct { holdTimeDesc *prometheus.Desc } -func newDescriptions(dynLabels interfacelabels.InterfaceLabels) *description { +func newDescriptions(dynLabels dynamiclabels.Labels) *description { d := &description{} l := []string{"target", "asn", "ip", "description", "group"} @@ -174,7 +174,7 @@ func (c *bgpCollector) collectForPeer(p peer, groups groupMap, ch chan<- prometh up = 1 } - dynLabels := interfacelabels.ParseDescription(p.Description, c.descriptionRe) + dynLabels := dynamiclabels.ParseDescription(p.Description, c.descriptionRe) lv = append(lv, dynLabels.Values()...) d := newDescriptions(dynLabels) diff --git a/pkg/features/interfacediagnostics/collector.go b/pkg/features/interfacediagnostics/collector.go index 77056b40..7a8666bb 100644 --- a/pkg/features/interfacediagnostics/collector.go +++ b/pkg/features/interfacediagnostics/collector.go @@ -6,72 +6,123 @@ import ( "encoding/xml" "fmt" "log" + "regexp" "strings" - "github.com/czerwonk/junos_exporter/pkg/interfacelabels" - "github.com/czerwonk/junos_exporter/pkg/collector" + "github.com/czerwonk/junos_exporter/pkg/dynamiclabels" "github.com/prometheus/client_golang/prometheus" ) const prefix = "junos_interface_diagnostics_" -type interfaceDiagnosticsCollector struct { - labels *interfacelabels.DynamicLabelManager - laserBiasCurrentDesc *prometheus.Desc - laserBiasCurrentHighAlarmThresholdDesc *prometheus.Desc - laserBiasCurrentLowAlarmThresholdDesc *prometheus.Desc - laserBiasCurrentHighWarnThresholdDesc *prometheus.Desc - laserBiasCurrentLowWarnThresholdDesc *prometheus.Desc - - laserOutputPowerDesc *prometheus.Desc - laserOutputPowerHighAlarmThresholdDesc *prometheus.Desc - laserOutputPowerLowAlarmThresholdDesc *prometheus.Desc - laserOutputPowerHighWarnThresholdDesc *prometheus.Desc - laserOutputPowerLowWarnThresholdDesc *prometheus.Desc - - laserOutputPowerDbmDesc *prometheus.Desc - laserOutputPowerHighAlarmThresholdDbmDesc *prometheus.Desc - laserOutputPowerLowAlarmThresholdDbmDesc *prometheus.Desc - laserOutputPowerHighWarnThresholdDbmDesc *prometheus.Desc - laserOutputPowerLowWarnThresholdDbmDesc *prometheus.Desc - - moduleTemperatureDesc *prometheus.Desc - moduleTemperatureHighAlarmThresholdDesc *prometheus.Desc - moduleTemperatureLowAlarmThresholdDesc *prometheus.Desc - moduleTemperatureHighWarnThresholdDesc *prometheus.Desc - moduleTemperatureLowWarnThresholdDesc *prometheus.Desc - - laserRxOpticalPowerDesc *prometheus.Desc - laserRxOpticalPowerHighAlarmThresholdDesc *prometheus.Desc - laserRxOpticalPowerLowAlarmThresholdDesc *prometheus.Desc - laserRxOpticalPowerHighWarnThresholdDesc *prometheus.Desc - laserRxOpticalPowerLowWarnThresholdDesc *prometheus.Desc - +type description struct { + laserBiasCurrentDesc *prometheus.Desc + laserBiasCurrentHighAlarmThresholdDesc *prometheus.Desc + laserBiasCurrentLowAlarmThresholdDesc *prometheus.Desc + laserBiasCurrentHighWarnThresholdDesc *prometheus.Desc + laserBiasCurrentLowWarnThresholdDesc *prometheus.Desc + laserOutputPowerDesc *prometheus.Desc + laserOutputPowerHighAlarmThresholdDesc *prometheus.Desc + laserOutputPowerLowAlarmThresholdDesc *prometheus.Desc + laserOutputPowerHighWarnThresholdDesc *prometheus.Desc + laserOutputPowerLowWarnThresholdDesc *prometheus.Desc + laserOutputPowerDbmDesc *prometheus.Desc + laserOutputPowerHighAlarmThresholdDbmDesc *prometheus.Desc + laserOutputPowerLowAlarmThresholdDbmDesc *prometheus.Desc + laserOutputPowerHighWarnThresholdDbmDesc *prometheus.Desc + laserOutputPowerLowWarnThresholdDbmDesc *prometheus.Desc + moduleTemperatureDesc *prometheus.Desc + moduleTemperatureHighAlarmThresholdDesc *prometheus.Desc + moduleTemperatureLowAlarmThresholdDesc *prometheus.Desc + moduleTemperatureHighWarnThresholdDesc *prometheus.Desc + moduleTemperatureLowWarnThresholdDesc *prometheus.Desc + laserRxOpticalPowerDesc *prometheus.Desc + laserRxOpticalPowerHighAlarmThresholdDesc *prometheus.Desc + laserRxOpticalPowerLowAlarmThresholdDesc *prometheus.Desc + laserRxOpticalPowerHighWarnThresholdDesc *prometheus.Desc + laserRxOpticalPowerLowWarnThresholdDesc *prometheus.Desc laserRxOpticalPowerDbmDesc *prometheus.Desc laserRxOpticalPowerHighAlarmThresholdDbmDesc *prometheus.Desc laserRxOpticalPowerLowAlarmThresholdDbmDesc *prometheus.Desc laserRxOpticalPowerHighWarnThresholdDbmDesc *prometheus.Desc laserRxOpticalPowerLowWarnThresholdDbmDesc *prometheus.Desc + moduleVoltageDesc *prometheus.Desc + moduleVoltageHighAlarmThresholdDesc *prometheus.Desc + moduleVoltageLowAlarmThresholdDesc *prometheus.Desc + moduleVoltageHighWarnThresholdDesc *prometheus.Desc + moduleVoltageLowWarnThresholdDesc *prometheus.Desc + rxSignalAvgOpticalPowerDesc *prometheus.Desc + rxSignalAvgOpticalPowerDbmDesc *prometheus.Desc + transceiverDesc *prometheus.Desc +} + +func newDescriptions(dynLabels dynamiclabels.Labels) *description { + d := &description{} + + l := []string{"target", "name"} + l = append(l, dynLabels.Keys()...) + + d.moduleVoltageDesc = prometheus.NewDesc(prefix+"module_voltage", "Module voltage", l, nil) + d.moduleVoltageHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"module_voltage_high_alarm_threshold", "Module voltage high alarm threshold", l, nil) + d.moduleVoltageLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"module_voltage_low_alarm_threshold", "Module voltage low alarm threshold", l, nil) + d.moduleVoltageHighWarnThresholdDesc = prometheus.NewDesc(prefix+"module_voltage_high_warn_threshold", "Module voltage high warn threshold", l, nil) + d.moduleVoltageLowWarnThresholdDesc = prometheus.NewDesc(prefix+"module_voltage_low_warn_threshold", "Module voltage low warn threshold", l, nil) + + d.moduleTemperatureDesc = prometheus.NewDesc(prefix+"temp", "Module temperature in degrees Celsius", l, nil) + d.moduleTemperatureHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"temp_high_alarm_threshold", "Module temperature high alarm threshold in degrees Celsius", l, nil) + d.moduleTemperatureLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"temp_low_alarm_threshold", "Module temperature low alarm threshold in degrees Celsius", l, nil) + d.moduleTemperatureHighWarnThresholdDesc = prometheus.NewDesc(prefix+"temp_high_warn_threshold", "Module temperature high warn threshold in degrees Celsius", l, nil) + d.moduleTemperatureLowWarnThresholdDesc = prometheus.NewDesc(prefix+"temp_low_warn_threshold", "Module temperature low warn threshold in degrees Celsius", l, nil) + + d.rxSignalAvgOpticalPowerDesc = prometheus.NewDesc(prefix+"rx_signal_avg", "Receiver signal average optical power in mW", l, nil) + d.rxSignalAvgOpticalPowerDbmDesc = prometheus.NewDesc(prefix+"rx_signal_avg_dbm", "Receiver signal average optical power in mW", l, nil) - moduleVoltageDesc *prometheus.Desc - moduleVoltageHighAlarmThresholdDesc *prometheus.Desc - moduleVoltageLowAlarmThresholdDesc *prometheus.Desc - moduleVoltageHighWarnThresholdDesc *prometheus.Desc - moduleVoltageLowWarnThresholdDesc *prometheus.Desc + l = append(l, "lane") + d.laserBiasCurrentDesc = prometheus.NewDesc(prefix+"laser_bias", "Laser bias current in mA", l, nil) + d.laserBiasCurrentHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_bias_high_alarm_threshold", "Laser bias current high alarm threshold", l, nil) + d.laserBiasCurrentLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_bias_low_alarm_threshold", "Laser bias current low alarm threshold", l, nil) + d.laserBiasCurrentHighWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_bias_high_warn_threshold", "Laser bias current high warn threshold", l, nil) + d.laserBiasCurrentLowWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_bias_low_warn_threshold", "Laser bias current low warn threshold", l, nil) + d.laserOutputPowerDesc = prometheus.NewDesc(prefix+"laser_output", "Laser output power in mW", l, nil) + d.laserOutputPowerHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_output_high_alarm_threshold", "Laser output power high alarm threshold in mW", l, nil) + d.laserOutputPowerLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_output_low_alarm_threshold", "Laser output power low alarm threshold in mW", l, nil) + d.laserOutputPowerHighWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_output_high_warn_threshold", "Laser output power high warn threshold in mW", l, nil) + d.laserOutputPowerLowWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_output_low_warn_threshold", "Laser output power low warn threshold in mW", l, nil) + + d.laserOutputPowerDbmDesc = prometheus.NewDesc(prefix+"laser_output_dbm", "Laser output power in dBm", l, nil) + d.laserOutputPowerHighAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_output_high_alarm_threshold_dbm", "Laser output power high alarm threshold in dBm", l, nil) + d.laserOutputPowerLowAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_output_low_alarm_threshold_dbm", "Laser output power low alarm threshold in dBm", l, nil) + d.laserOutputPowerHighWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_output_high_warn_threshold_dbm", "Laser output power high warn threshold in dBm", l, nil) + d.laserOutputPowerLowWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_output_low_warn_threshold_dbm", "Laser output power low warn threshold in dBm", l, nil) + + d.laserRxOpticalPowerDesc = prometheus.NewDesc(prefix+"laser_rx", "Laser rx power in mW", l, nil) + d.laserRxOpticalPowerHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_rx_high_alarm_threshold", "Laser rx power high alarm threshold in mW", l, nil) + d.laserRxOpticalPowerLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_rx_low_alarm_threshold", "Laser rx power low alarm threshold in mW", l, nil) + d.laserRxOpticalPowerHighWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_rx_high_warn_threshold", "Laser rx power high warn threshold in mW", l, nil) + d.laserRxOpticalPowerLowWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_rx_low_warn_threshold", "Laser rx power low warn threshold in mW", l, nil) + + d.laserRxOpticalPowerDbmDesc = prometheus.NewDesc(prefix+"laser_rx_dbm", "Laser rx power in dBm", l, nil) + d.laserRxOpticalPowerHighAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_high_alarm_threshold_dbm", "Laser rx power high alarm threshold_dbm in dBm", l, nil) + d.laserRxOpticalPowerLowAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_low_alarm_threshold_dbm", "Laser rx power low alarm threshold_dbm in dBm", l, nil) + d.laserRxOpticalPowerHighWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_high_warn_threshold_dbm", "Laser rx power high warn threshold_dbm in dBm", l, nil) + d.laserRxOpticalPowerLowWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_low_warn_threshold_dbm", "Laser rx power low warn threshold_dbm in dBm", l, nil) - rxSignalAvgOpticalPowerDesc *prometheus.Desc - rxSignalAvgOpticalPowerDbmDesc *prometheus.Desc + transceiver_labels := []string{"target", "name", "serial_number", "description", "speed", "fiber_type", "vendor_name", "vendor_part_number", "wavelength"} + d.transceiverDesc = prometheus.NewDesc("junos_interface_transceiver", "Transceiver Info", transceiver_labels, nil) - transceiverDesc *prometheus.Desc + return d +} + +type interfaceDiagnosticsCollector struct { + descriptionRe *regexp.Regexp } // NewCollector creates a new collector -func NewCollector(labels *interfacelabels.DynamicLabelManager) collector.RPCCollector { +func NewCollector(descriptionRe *regexp.Regexp) collector.RPCCollector { c := &interfaceDiagnosticsCollector{ - labels: labels, + descriptionRe: descriptionRe, } - c.init() return c } @@ -81,103 +132,52 @@ func (*interfaceDiagnosticsCollector) Name() string { return "Interface Diagnostics" } -func (c *interfaceDiagnosticsCollector) init() { - l := []string{"target", "name"} - l = append(l, c.labels.LabelNames()...) - - c.moduleVoltageDesc = prometheus.NewDesc(prefix+"module_voltage", "Module voltage", l, nil) - c.moduleVoltageHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"module_voltage_high_alarm_threshold", "Module voltage high alarm threshold", l, nil) - c.moduleVoltageLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"module_voltage_low_alarm_threshold", "Module voltage low alarm threshold", l, nil) - c.moduleVoltageHighWarnThresholdDesc = prometheus.NewDesc(prefix+"module_voltage_high_warn_threshold", "Module voltage high warn threshold", l, nil) - c.moduleVoltageLowWarnThresholdDesc = prometheus.NewDesc(prefix+"module_voltage_low_warn_threshold", "Module voltage low warn threshold", l, nil) - - c.moduleTemperatureDesc = prometheus.NewDesc(prefix+"temp", "Module temperature in degrees Celsius", l, nil) - c.moduleTemperatureHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"temp_high_alarm_threshold", "Module temperature high alarm threshold in degrees Celsius", l, nil) - c.moduleTemperatureLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"temp_low_alarm_threshold", "Module temperature low alarm threshold in degrees Celsius", l, nil) - c.moduleTemperatureHighWarnThresholdDesc = prometheus.NewDesc(prefix+"temp_high_warn_threshold", "Module temperature high warn threshold in degrees Celsius", l, nil) - c.moduleTemperatureLowWarnThresholdDesc = prometheus.NewDesc(prefix+"temp_low_warn_threshold", "Module temperature low warn threshold in degrees Celsius", l, nil) - - c.rxSignalAvgOpticalPowerDesc = prometheus.NewDesc(prefix+"rx_signal_avg", "Receiver signal average optical power in mW", l, nil) - c.rxSignalAvgOpticalPowerDbmDesc = prometheus.NewDesc(prefix+"rx_signal_avg_dbm", "Receiver signal average optical power in mW", l, nil) - - l = append(l, "lane") - c.laserBiasCurrentDesc = prometheus.NewDesc(prefix+"laser_bias", "Laser bias current in mA", l, nil) - c.laserBiasCurrentHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_bias_high_alarm_threshold", "Laser bias current high alarm threshold", l, nil) - c.laserBiasCurrentLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_bias_low_alarm_threshold", "Laser bias current low alarm threshold", l, nil) - c.laserBiasCurrentHighWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_bias_high_warn_threshold", "Laser bias current high warn threshold", l, nil) - c.laserBiasCurrentLowWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_bias_low_warn_threshold", "Laser bias current low warn threshold", l, nil) - c.laserOutputPowerDesc = prometheus.NewDesc(prefix+"laser_output", "Laser output power in mW", l, nil) - c.laserOutputPowerHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_output_high_alarm_threshold", "Laser output power high alarm threshold in mW", l, nil) - c.laserOutputPowerLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_output_low_alarm_threshold", "Laser output power low alarm threshold in mW", l, nil) - c.laserOutputPowerHighWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_output_high_warn_threshold", "Laser output power high warn threshold in mW", l, nil) - c.laserOutputPowerLowWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_output_low_warn_threshold", "Laser output power low warn threshold in mW", l, nil) - - c.laserOutputPowerDbmDesc = prometheus.NewDesc(prefix+"laser_output_dbm", "Laser output power in dBm", l, nil) - c.laserOutputPowerHighAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_output_high_alarm_threshold_dbm", "Laser output power high alarm threshold in dBm", l, nil) - c.laserOutputPowerLowAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_output_low_alarm_threshold_dbm", "Laser output power low alarm threshold in dBm", l, nil) - c.laserOutputPowerHighWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_output_high_warn_threshold_dbm", "Laser output power high warn threshold in dBm", l, nil) - c.laserOutputPowerLowWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_output_low_warn_threshold_dbm", "Laser output power low warn threshold in dBm", l, nil) - - c.laserRxOpticalPowerDesc = prometheus.NewDesc(prefix+"laser_rx", "Laser rx power in mW", l, nil) - c.laserRxOpticalPowerHighAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_rx_high_alarm_threshold", "Laser rx power high alarm threshold in mW", l, nil) - c.laserRxOpticalPowerLowAlarmThresholdDesc = prometheus.NewDesc(prefix+"laser_rx_low_alarm_threshold", "Laser rx power low alarm threshold in mW", l, nil) - c.laserRxOpticalPowerHighWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_rx_high_warn_threshold", "Laser rx power high warn threshold in mW", l, nil) - c.laserRxOpticalPowerLowWarnThresholdDesc = prometheus.NewDesc(prefix+"laser_rx_low_warn_threshold", "Laser rx power low warn threshold in mW", l, nil) - - c.laserRxOpticalPowerDbmDesc = prometheus.NewDesc(prefix+"laser_rx_dbm", "Laser rx power in dBm", l, nil) - c.laserRxOpticalPowerHighAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_high_alarm_threshold_dbm", "Laser rx power high alarm threshold_dbm in dBm", l, nil) - c.laserRxOpticalPowerLowAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_low_alarm_threshold_dbm", "Laser rx power low alarm threshold_dbm in dBm", l, nil) - c.laserRxOpticalPowerHighWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_high_warn_threshold_dbm", "Laser rx power high warn threshold_dbm in dBm", l, nil) - c.laserRxOpticalPowerLowWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_low_warn_threshold_dbm", "Laser rx power low warn threshold_dbm in dBm", l, nil) - - transceiver_labels := []string{"target", "name", "serial_number", "description", "speed", "fiber_type", "vendor_name", "vendor_part_number", "wavelength"} - c.transceiverDesc = prometheus.NewDesc("junos_interface_transceiver", "Transceiver Info", transceiver_labels, nil) -} - // Describe describes the metrics func (c *interfaceDiagnosticsCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- c.laserBiasCurrentDesc - ch <- c.laserBiasCurrentHighAlarmThresholdDesc - ch <- c.laserBiasCurrentLowAlarmThresholdDesc - ch <- c.laserBiasCurrentHighWarnThresholdDesc - ch <- c.laserBiasCurrentLowWarnThresholdDesc - ch <- c.laserOutputPowerDesc - ch <- c.laserOutputPowerHighAlarmThresholdDesc - ch <- c.laserOutputPowerLowAlarmThresholdDesc - ch <- c.laserOutputPowerHighWarnThresholdDesc - ch <- c.laserOutputPowerLowWarnThresholdDesc - ch <- c.laserOutputPowerDbmDesc - ch <- c.laserOutputPowerHighAlarmThresholdDbmDesc - ch <- c.laserOutputPowerLowAlarmThresholdDbmDesc - ch <- c.laserOutputPowerHighWarnThresholdDbmDesc - ch <- c.laserOutputPowerLowWarnThresholdDbmDesc - ch <- c.moduleTemperatureDesc - ch <- c.moduleTemperatureHighAlarmThresholdDesc - ch <- c.moduleTemperatureLowAlarmThresholdDesc - ch <- c.moduleTemperatureHighWarnThresholdDesc - ch <- c.moduleTemperatureLowWarnThresholdDesc - - ch <- c.laserRxOpticalPowerDesc - ch <- c.laserRxOpticalPowerHighAlarmThresholdDesc - ch <- c.laserRxOpticalPowerLowAlarmThresholdDesc - ch <- c.laserRxOpticalPowerHighWarnThresholdDesc - ch <- c.laserRxOpticalPowerLowWarnThresholdDesc - ch <- c.laserRxOpticalPowerDbmDesc - ch <- c.laserRxOpticalPowerHighAlarmThresholdDbmDesc - ch <- c.laserRxOpticalPowerLowAlarmThresholdDbmDesc - ch <- c.laserRxOpticalPowerHighWarnThresholdDbmDesc - ch <- c.laserRxOpticalPowerLowWarnThresholdDbmDesc - - ch <- c.moduleVoltageDesc - ch <- c.moduleVoltageHighAlarmThresholdDesc - ch <- c.moduleVoltageLowAlarmThresholdDesc - ch <- c.moduleVoltageHighWarnThresholdDesc - ch <- c.moduleVoltageLowWarnThresholdDesc - - ch <- c.rxSignalAvgOpticalPowerDesc - ch <- c.rxSignalAvgOpticalPowerDbmDesc - - ch <- c.transceiverDesc + d := newDescriptions(nil) + + ch <- d.laserBiasCurrentDesc + ch <- d.laserBiasCurrentHighAlarmThresholdDesc + ch <- d.laserBiasCurrentLowAlarmThresholdDesc + ch <- d.laserBiasCurrentHighWarnThresholdDesc + ch <- d.laserBiasCurrentLowWarnThresholdDesc + ch <- d.laserOutputPowerDesc + ch <- d.laserOutputPowerHighAlarmThresholdDesc + ch <- d.laserOutputPowerLowAlarmThresholdDesc + ch <- d.laserOutputPowerHighWarnThresholdDesc + ch <- d.laserOutputPowerLowWarnThresholdDesc + ch <- d.laserOutputPowerDbmDesc + ch <- d.laserOutputPowerHighAlarmThresholdDbmDesc + ch <- d.laserOutputPowerLowAlarmThresholdDbmDesc + ch <- d.laserOutputPowerHighWarnThresholdDbmDesc + ch <- d.laserOutputPowerLowWarnThresholdDbmDesc + ch <- d.moduleTemperatureDesc + ch <- d.moduleTemperatureHighAlarmThresholdDesc + ch <- d.moduleTemperatureLowAlarmThresholdDesc + ch <- d.moduleTemperatureHighWarnThresholdDesc + ch <- d.moduleTemperatureLowWarnThresholdDesc + + ch <- d.laserRxOpticalPowerDesc + ch <- d.laserRxOpticalPowerHighAlarmThresholdDesc + ch <- d.laserRxOpticalPowerLowAlarmThresholdDesc + ch <- d.laserRxOpticalPowerHighWarnThresholdDesc + ch <- d.laserRxOpticalPowerLowWarnThresholdDesc + ch <- d.laserRxOpticalPowerDbmDesc + ch <- d.laserRxOpticalPowerHighAlarmThresholdDbmDesc + ch <- d.laserRxOpticalPowerLowAlarmThresholdDbmDesc + ch <- d.laserRxOpticalPowerHighWarnThresholdDbmDesc + ch <- d.laserRxOpticalPowerLowWarnThresholdDbmDesc + + ch <- d.moduleVoltageDesc + ch <- d.moduleVoltageHighAlarmThresholdDesc + ch <- d.moduleVoltageLowAlarmThresholdDesc + ch <- d.moduleVoltageHighWarnThresholdDesc + ch <- d.moduleVoltageLowWarnThresholdDesc + + ch <- d.rxSignalAvgOpticalPowerDesc + ch <- d.rxSignalAvgOpticalPowerDbmDesc + + ch <- d.transceiverDesc } // Collect collects metrics from JunOS @@ -196,72 +196,86 @@ func (c *interfaceDiagnosticsCollector) Collect(client collector.Client, ch chan diagnostics = append(diagnostics, diagnosticsSatellite...) } - diagnostics_dict := make(map[string]*interfaceDiagnostics) + diagnosticsDict := make(map[string]*interfaceDiagnostics) - for _, d := range diagnostics { - index := strings.Split(d.Name, "-")[1] - diagnostics_dict[index] = d + ifMediaDict, err := c.interfaceMediaInfo(client) + if err != nil { + return err + } - l := append(labelValues, d.Name) - l = append(l, c.labels.ValuesForInterface(client.Device(), d.Name)...) + for _, diag := range diagnostics { + index := strings.Split(diag.Name, "-")[1] + diagnosticsDict[index] = diag - ch <- prometheus.MustNewConstMetric(c.moduleTemperatureDesc, prometheus.GaugeValue, d.ModuleTemperature, l...) - ch <- prometheus.MustNewConstMetric(c.moduleTemperatureHighAlarmThresholdDesc, prometheus.GaugeValue, d.ModuleTemperatureHighAlarmThreshold, l...) - ch <- prometheus.MustNewConstMetric(c.moduleTemperatureLowAlarmThresholdDesc, prometheus.GaugeValue, d.ModuleTemperatureLowAlarmThreshold, l...) - ch <- prometheus.MustNewConstMetric(c.moduleTemperatureHighWarnThresholdDesc, prometheus.GaugeValue, d.ModuleTemperatureHighWarnThreshold, l...) - ch <- prometheus.MustNewConstMetric(c.moduleTemperatureLowWarnThresholdDesc, prometheus.GaugeValue, d.ModuleTemperatureLowWarnThreshold, l...) + desc := "" + media := ifMediaDict[slotIndex(diag.Name)] + if media != nil { + desc = media.Description + } - if d.ModuleVoltage > 0 { - ch <- prometheus.MustNewConstMetric(c.moduleVoltageDesc, prometheus.GaugeValue, d.ModuleVoltage, l...) - ch <- prometheus.MustNewConstMetric(c.moduleVoltageHighAlarmThresholdDesc, prometheus.GaugeValue, d.ModuleVoltageHighAlarmThreshold, l...) - ch <- prometheus.MustNewConstMetric(c.moduleVoltageLowAlarmThresholdDesc, prometheus.GaugeValue, d.ModuleVoltageLowAlarmThreshold, l...) - ch <- prometheus.MustNewConstMetric(c.moduleVoltageHighWarnThresholdDesc, prometheus.GaugeValue, d.ModuleVoltageHighWarnThreshold, l...) - ch <- prometheus.MustNewConstMetric(c.moduleVoltageLowWarnThresholdDesc, prometheus.GaugeValue, d.ModuleVoltageLowWarnThreshold, l...) + dynLabels := dynamiclabels.ParseDescription(desc, c.descriptionRe) + d := newDescriptions(dynLabels) + + l := append(labelValues, diag.Name) + l = append(l, dynLabels.Values()...) + + ch <- prometheus.MustNewConstMetric(d.moduleTemperatureDesc, prometheus.GaugeValue, diag.ModuleTemperature, l...) + ch <- prometheus.MustNewConstMetric(d.moduleTemperatureHighAlarmThresholdDesc, prometheus.GaugeValue, diag.ModuleTemperatureHighAlarmThreshold, l...) + ch <- prometheus.MustNewConstMetric(d.moduleTemperatureLowAlarmThresholdDesc, prometheus.GaugeValue, diag.ModuleTemperatureLowAlarmThreshold, l...) + ch <- prometheus.MustNewConstMetric(d.moduleTemperatureHighWarnThresholdDesc, prometheus.GaugeValue, diag.ModuleTemperatureHighWarnThreshold, l...) + ch <- prometheus.MustNewConstMetric(d.moduleTemperatureLowWarnThresholdDesc, prometheus.GaugeValue, diag.ModuleTemperatureLowWarnThreshold, l...) + + if diag.ModuleVoltage > 0 { + ch <- prometheus.MustNewConstMetric(d.moduleVoltageDesc, prometheus.GaugeValue, diag.ModuleVoltage, l...) + ch <- prometheus.MustNewConstMetric(d.moduleVoltageHighAlarmThresholdDesc, prometheus.GaugeValue, diag.ModuleVoltageHighAlarmThreshold, l...) + ch <- prometheus.MustNewConstMetric(d.moduleVoltageLowAlarmThresholdDesc, prometheus.GaugeValue, diag.ModuleVoltageLowAlarmThreshold, l...) + ch <- prometheus.MustNewConstMetric(d.moduleVoltageHighWarnThresholdDesc, prometheus.GaugeValue, diag.ModuleVoltageHighWarnThreshold, l...) + ch <- prometheus.MustNewConstMetric(d.moduleVoltageLowWarnThresholdDesc, prometheus.GaugeValue, diag.ModuleVoltageLowWarnThreshold, l...) } - if d.RxSignalAvgOpticalPower > 0 { - ch <- prometheus.MustNewConstMetric(c.rxSignalAvgOpticalPowerDesc, prometheus.GaugeValue, d.RxSignalAvgOpticalPower, l...) - ch <- prometheus.MustNewConstMetric(c.rxSignalAvgOpticalPowerDbmDesc, prometheus.GaugeValue, d.RxSignalAvgOpticalPowerDbm, l...) + if diag.RxSignalAvgOpticalPower > 0 { + ch <- prometheus.MustNewConstMetric(d.rxSignalAvgOpticalPowerDesc, prometheus.GaugeValue, diag.RxSignalAvgOpticalPower, l...) + ch <- prometheus.MustNewConstMetric(d.rxSignalAvgOpticalPowerDbmDesc, prometheus.GaugeValue, diag.RxSignalAvgOpticalPowerDbm, l...) } var data []*interfaceDiagnostics - if len(d.Lanes) > 0 { - data = d.Lanes + if len(diag.Lanes) > 0 { + data = diag.Lanes } else { - data = []*interfaceDiagnostics{d} + data = []*interfaceDiagnostics{diag} } for _, e := range data { l2 := append(l, e.Index) - ch <- prometheus.MustNewConstMetric(c.laserBiasCurrentDesc, prometheus.GaugeValue, e.LaserBiasCurrent, l2...) - ch <- prometheus.MustNewConstMetric(c.laserBiasCurrentHighAlarmThresholdDesc, prometheus.GaugeValue, d.LaserBiasCurrentHighAlarmThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserBiasCurrentLowAlarmThresholdDesc, prometheus.GaugeValue, d.LaserBiasCurrentLowAlarmThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserBiasCurrentHighWarnThresholdDesc, prometheus.GaugeValue, d.LaserBiasCurrentHighWarnThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserBiasCurrentLowWarnThresholdDesc, prometheus.GaugeValue, d.LaserBiasCurrentLowWarnThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerDesc, prometheus.GaugeValue, e.LaserOutputPower, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerHighAlarmThresholdDesc, prometheus.GaugeValue, d.LaserOutputPowerHighAlarmThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerLowAlarmThresholdDesc, prometheus.GaugeValue, d.LaserOutputPowerLowAlarmThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerHighWarnThresholdDesc, prometheus.GaugeValue, d.LaserOutputPowerHighWarnThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerLowWarnThresholdDesc, prometheus.GaugeValue, d.LaserOutputPowerLowWarnThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerDbmDesc, prometheus.GaugeValue, e.LaserOutputPowerDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerHighAlarmThresholdDbmDesc, prometheus.GaugeValue, d.LaserOutputPowerHighAlarmThresholdDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerLowAlarmThresholdDbmDesc, prometheus.GaugeValue, d.LaserOutputPowerLowAlarmThresholdDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerHighWarnThresholdDbmDesc, prometheus.GaugeValue, d.LaserOutputPowerHighWarnThresholdDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserOutputPowerLowWarnThresholdDbmDesc, prometheus.GaugeValue, d.LaserOutputPowerLowWarnThresholdDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerDesc, prometheus.GaugeValue, e.LaserRxOpticalPower, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerHighAlarmThresholdDesc, prometheus.GaugeValue, d.LaserRxOpticalPowerHighAlarmThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerLowAlarmThresholdDesc, prometheus.GaugeValue, d.LaserRxOpticalPowerLowAlarmThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerHighWarnThresholdDesc, prometheus.GaugeValue, d.LaserRxOpticalPowerHighWarnThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerLowWarnThresholdDesc, prometheus.GaugeValue, d.LaserRxOpticalPowerLowWarnThreshold, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerDbmDesc, prometheus.GaugeValue, e.LaserRxOpticalPowerDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerHighAlarmThresholdDbmDesc, prometheus.GaugeValue, d.LaserRxOpticalPowerHighAlarmThresholdDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerLowAlarmThresholdDbmDesc, prometheus.GaugeValue, d.LaserRxOpticalPowerLowAlarmThresholdDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerHighWarnThresholdDbmDesc, prometheus.GaugeValue, d.LaserRxOpticalPowerHighWarnThresholdDbm, l2...) - ch <- prometheus.MustNewConstMetric(c.laserRxOpticalPowerLowWarnThresholdDbmDesc, prometheus.GaugeValue, d.LaserRxOpticalPowerLowWarnThresholdDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserBiasCurrentDesc, prometheus.GaugeValue, e.LaserBiasCurrent, l2...) + ch <- prometheus.MustNewConstMetric(d.laserBiasCurrentHighAlarmThresholdDesc, prometheus.GaugeValue, diag.LaserBiasCurrentHighAlarmThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserBiasCurrentLowAlarmThresholdDesc, prometheus.GaugeValue, diag.LaserBiasCurrentLowAlarmThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserBiasCurrentHighWarnThresholdDesc, prometheus.GaugeValue, diag.LaserBiasCurrentHighWarnThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserBiasCurrentLowWarnThresholdDesc, prometheus.GaugeValue, diag.LaserBiasCurrentLowWarnThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerDesc, prometheus.GaugeValue, e.LaserOutputPower, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerHighAlarmThresholdDesc, prometheus.GaugeValue, diag.LaserOutputPowerHighAlarmThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerLowAlarmThresholdDesc, prometheus.GaugeValue, diag.LaserOutputPowerLowAlarmThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerHighWarnThresholdDesc, prometheus.GaugeValue, diag.LaserOutputPowerHighWarnThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerLowWarnThresholdDesc, prometheus.GaugeValue, diag.LaserOutputPowerLowWarnThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerDbmDesc, prometheus.GaugeValue, e.LaserOutputPowerDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerHighAlarmThresholdDbmDesc, prometheus.GaugeValue, diag.LaserOutputPowerHighAlarmThresholdDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerLowAlarmThresholdDbmDesc, prometheus.GaugeValue, diag.LaserOutputPowerLowAlarmThresholdDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerHighWarnThresholdDbmDesc, prometheus.GaugeValue, diag.LaserOutputPowerHighWarnThresholdDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserOutputPowerLowWarnThresholdDbmDesc, prometheus.GaugeValue, diag.LaserOutputPowerLowWarnThresholdDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerDesc, prometheus.GaugeValue, e.LaserRxOpticalPower, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerHighAlarmThresholdDesc, prometheus.GaugeValue, diag.LaserRxOpticalPowerHighAlarmThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerLowAlarmThresholdDesc, prometheus.GaugeValue, diag.LaserRxOpticalPowerLowAlarmThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerHighWarnThresholdDesc, prometheus.GaugeValue, diag.LaserRxOpticalPowerHighWarnThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerLowWarnThresholdDesc, prometheus.GaugeValue, diag.LaserRxOpticalPowerLowWarnThreshold, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerDbmDesc, prometheus.GaugeValue, e.LaserRxOpticalPowerDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerHighAlarmThresholdDbmDesc, prometheus.GaugeValue, diag.LaserRxOpticalPowerHighAlarmThresholdDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerLowAlarmThresholdDbmDesc, prometheus.GaugeValue, diag.LaserRxOpticalPowerLowAlarmThresholdDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerHighWarnThresholdDbmDesc, prometheus.GaugeValue, diag.LaserRxOpticalPowerHighWarnThresholdDbm, l2...) + ch <- prometheus.MustNewConstMetric(d.laserRxOpticalPowerLowWarnThresholdDbmDesc, prometheus.GaugeValue, diag.LaserRxOpticalPowerLowWarnThresholdDbm, l2...) } } - err = c.createTransceiverMetrics(client, ch, labelValues) + err = c.createTransceiverMetrics(client, ch, labelValues, ifMediaDict) if err != nil { return err } @@ -327,7 +341,7 @@ func (c *interfaceDiagnosticsCollector) transceiverInfoFromRPCResult(client coll } func (c *interfaceDiagnosticsCollector) getPicPortsFromRPCResult(client collector.Client, fpc string, pic string) ([]picPort, error) { - var x = fPCInformationStruct{} + var x = fpcInformationStruct{} command := fmt.Sprintf("show chassis pic fpc-slot %s pic-slot %s", fpc, pic) err := client.RunCommandAndParse(command, &x) if err != nil { @@ -337,12 +351,7 @@ func (c *interfaceDiagnosticsCollector) getPicPortsFromRPCResult(client collecto return x.FPCInformation.FPC.PicDetail.PicPortInfoList, nil } -func (c *interfaceDiagnosticsCollector) createTransceiverMetrics(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { - ifMediaDict, err := c.interfaceMediaInfo(client) - if err != nil { - return err - } - +func (c *interfaceDiagnosticsCollector) createTransceiverMetrics(client collector.Client, ch chan<- prometheus.Metric, labelValues []string, ifMediaDict map[string]*physicalInterface) error { transceiverInfo, err := c.chassisHardwareInfos(client) if err != nil { return err @@ -365,7 +374,9 @@ func (c *interfaceDiagnosticsCollector) createTransceiverMetrics(client collecto transceiver_labels := append(labelValues, t.Name, chassisInfo.SerialNumber, chassisInfo.Description, port_speed, t.PicPort.FiberMode, strings.TrimSpace(t.PicPort.SFPVendorName), strings.TrimSpace(t.PicPort.SFPVendorPno), t.PicPort.Wavelength) - ch <- prometheus.MustNewConstMetric(c.transceiverDesc, prometheus.GaugeValue, oper_status, transceiver_labels...) + d := newDescriptions(nil) + + ch <- prometheus.MustNewConstMetric(d.transceiverDesc, prometheus.GaugeValue, oper_status, transceiver_labels...) } return nil diff --git a/pkg/features/interfacediagnostics/helper.go b/pkg/features/interfacediagnostics/helper.go index 01ced0b1..b71259be 100644 --- a/pkg/features/interfacediagnostics/helper.go +++ b/pkg/features/interfacediagnostics/helper.go @@ -111,10 +111,13 @@ func interfaceMediaInfoFromRPCResult(interfaceMediaList *[]physicalInterface) ma for _, i := range *interfaceMediaList { if strings.HasPrefix(i.Name, "xe") || strings.HasPrefix(i.Name, "ge") || strings.HasPrefix(i.Name, "et") { iface := i - slotIndex := iface.Name[3:] - interfaceMediaDict[slotIndex] = &iface + interfaceMediaDict[slotIndex(iface.Name)] = &iface } } return interfaceMediaDict } + +func slotIndex(ifName string) string { + return ifName[3:] +} diff --git a/pkg/features/interfacediagnostics/rpc.go b/pkg/features/interfacediagnostics/rpc.go index 8fec7510..cd7676a1 100644 --- a/pkg/features/interfacediagnostics/rpc.go +++ b/pkg/features/interfacediagnostics/rpc.go @@ -2,14 +2,14 @@ package interfacediagnostics -type fPCInformationStruct struct { - FPCInformation fPCInformation `xml:"fpc-information"` +type fpcInformationStruct struct { + FPCInformation fpcInformation `xml:"fpc-information"` } -type fPCInformation struct { - FPC fPC `xml:"fpc"` +type fpcInformation struct { + FPC fpc `xml:"fpc"` } -type fPC struct { +type fpc struct { PicDetail picDetail `xml:"pic-detail"` } @@ -46,6 +46,7 @@ type physicalInterface struct { OperStatus string `xml:"oper-status"` LocalIndex string `xml:"local-index"` SnmpIndex string `xml:"snmp-index"` + Description string `xml:"description"` IfType string `xml:"if-type"` LinkLevelType string `xml:"link-level-type"` Mtu string `xml:"mtu"` diff --git a/pkg/features/interfacequeue/collector.go b/pkg/features/interfacequeue/collector.go index 4e2a0c0a..bc02c5ea 100644 --- a/pkg/features/interfacequeue/collector.go +++ b/pkg/features/interfacequeue/collector.go @@ -3,26 +3,16 @@ package interfacequeue import ( + "regexp" + "github.com/czerwonk/junos_exporter/pkg/collector" - "github.com/czerwonk/junos_exporter/pkg/connector" - "github.com/czerwonk/junos_exporter/pkg/interfacelabels" + "github.com/czerwonk/junos_exporter/pkg/dynamiclabels" "github.com/prometheus/client_golang/prometheus" ) const prefix = "junos_interface_queues_" -// NewCollector creates an queue collector instance -func NewCollector(labels *interfacelabels.DynamicLabelManager) collector.RPCCollector { - c := &interfaceQueueCollector{ - labels: labels, - } - c.init() - - return c -} - -type interfaceQueueCollector struct { - labels *interfacelabels.DynamicLabelManager +type description struct { queuedPackets *prometheus.Desc queuedBytes *prometheus.Desc transferedPackets *prometheus.Desc @@ -44,59 +34,77 @@ type interfaceQueueCollector struct { totalDropBytes *prometheus.Desc } -// Name returns the name of the collector -func (*interfaceQueueCollector) Name() string { - return "Interface Queues" -} +func newDescriptions(dynLabels dynamiclabels.Labels) *description { + d := &description{} -func (c *interfaceQueueCollector) init() { l := []string{"target", "name", "description"} - l = append(l, c.labels.LabelNames()...) l = append(l, "queue_number") l = append(l, "forwarding_class") + l = append(l, dynLabels.Keys()...) + + d.queuedPackets = prometheus.NewDesc(prefix+"queued_packets_count", "Number of queued packets", l, nil) + d.queuedBytes = prometheus.NewDesc(prefix+"queued_bytes_count", "Number of bytes of queued packets", l, nil) + d.transferedPackets = prometheus.NewDesc(prefix+"transfered_packets_count", "Number of transfered packets", l, nil) + d.transferedBytes = prometheus.NewDesc(prefix+"transfered_bytes_count", "Number of bytes of transfered packets", l, nil) + d.rateLimitDropPackets = prometheus.NewDesc(prefix+"rate_limit_drop_packets_count", "Number of packets droped by rate limit", l, nil) + d.rateLimitDropBytes = prometheus.NewDesc(prefix+"rate_limit_drop_bytes_count", "Number of bytes droped by rate limit", l, nil) + d.redPackets = prometheus.NewDesc(prefix+"red_packets_count", "Number of queued packets", l, nil) + d.redBytes = prometheus.NewDesc(prefix+"red_bytes_count", "Number of bytes of queued packets", l, nil) + d.redPacketsLow = prometheus.NewDesc(prefix+"red_packets_low_count", "Number of queued packets", l, nil) + d.redBytesLow = prometheus.NewDesc(prefix+"red_bytes_low_count", "Number of bytes of queued packets", l, nil) + d.redPacketsMediumLow = prometheus.NewDesc(prefix+"red_packets_medium_low_count", "Number of queued packets", l, nil) + d.redBytesMediumLow = prometheus.NewDesc(prefix+"red_bytes_medium_low_count", "Number of bytes of queued packets", l, nil) + d.redPacketsMediumHigh = prometheus.NewDesc(prefix+"red_packets_medium_high_count", "Number of queued packets", l, nil) + d.redBytesMediumHigh = prometheus.NewDesc(prefix+"red_bytes_medium_high_count", "Number of bytes of queued packets", l, nil) + d.redPacketsHigh = prometheus.NewDesc(prefix+"red_packets_high_count", "Number of queued packets", l, nil) + d.redBytesHigh = prometheus.NewDesc(prefix+"red_bytes_high_count", "Number of bytes of queued packets", l, nil) + d.tailDropPackets = prometheus.NewDesc(prefix+"tail_drop_packets_count", "Number of tail droped packets", l, nil) + d.totalDropPackets = prometheus.NewDesc(prefix+"drop_packets_count", "Number of packets droped", l, nil) + d.totalDropBytes = prometheus.NewDesc(prefix+"drop_bytes_count", "Number of bytes droped", l, nil) + + return d +} - c.queuedPackets = prometheus.NewDesc(prefix+"queued_packets_count", "Number of queued packets", l, nil) - c.queuedBytes = prometheus.NewDesc(prefix+"queued_bytes_count", "Number of bytes of queued packets", l, nil) - c.transferedPackets = prometheus.NewDesc(prefix+"transfered_packets_count", "Number of transfered packets", l, nil) - c.transferedBytes = prometheus.NewDesc(prefix+"transfered_bytes_count", "Number of bytes of transfered packets", l, nil) - c.rateLimitDropPackets = prometheus.NewDesc(prefix+"rate_limit_drop_packets_count", "Number of packets droped by rate limit", l, nil) - c.rateLimitDropBytes = prometheus.NewDesc(prefix+"rate_limit_drop_bytes_count", "Number of bytes droped by rate limit", l, nil) - c.redPackets = prometheus.NewDesc(prefix+"red_packets_count", "Number of queued packets", l, nil) - c.redBytes = prometheus.NewDesc(prefix+"red_bytes_count", "Number of bytes of queued packets", l, nil) - c.redPacketsLow = prometheus.NewDesc(prefix+"red_packets_low_count", "Number of queued packets", l, nil) - c.redBytesLow = prometheus.NewDesc(prefix+"red_bytes_low_count", "Number of bytes of queued packets", l, nil) - c.redPacketsMediumLow = prometheus.NewDesc(prefix+"red_packets_medium_low_count", "Number of queued packets", l, nil) - c.redBytesMediumLow = prometheus.NewDesc(prefix+"red_bytes_medium_low_count", "Number of bytes of queued packets", l, nil) - c.redPacketsMediumHigh = prometheus.NewDesc(prefix+"red_packets_medium_high_count", "Number of queued packets", l, nil) - c.redBytesMediumHigh = prometheus.NewDesc(prefix+"red_bytes_medium_high_count", "Number of bytes of queued packets", l, nil) - c.redPacketsHigh = prometheus.NewDesc(prefix+"red_packets_high_count", "Number of queued packets", l, nil) - c.redBytesHigh = prometheus.NewDesc(prefix+"red_bytes_high_count", "Number of bytes of queued packets", l, nil) - c.tailDropPackets = prometheus.NewDesc(prefix+"tail_drop_packets_count", "Number of tail droped packets", l, nil) - c.totalDropPackets = prometheus.NewDesc(prefix+"drop_packets_count", "Number of packets droped", l, nil) - c.totalDropBytes = prometheus.NewDesc(prefix+"drop_bytes_count", "Number of bytes droped", l, nil) +// NewCollector creates an queue collector instance +func NewCollector(descRe *regexp.Regexp) collector.RPCCollector { + c := &interfaceQueueCollector{ + descriptionRe: descRe, + } + + return c +} + +type interfaceQueueCollector struct { + descriptionRe *regexp.Regexp +} + +// Name returns the name of the collector +func (*interfaceQueueCollector) Name() string { + return "Interface Queues" } // Describe describes the metrics func (c *interfaceQueueCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- c.queuedBytes - ch <- c.queuedPackets - ch <- c.transferedBytes - ch <- c.transferedPackets - ch <- c.rateLimitDropBytes - ch <- c.rateLimitDropPackets - ch <- c.redPackets - ch <- c.redBytes - ch <- c.redPacketsLow - ch <- c.redBytesLow - ch <- c.redPacketsMediumLow - ch <- c.redBytesMediumLow - ch <- c.redPacketsMediumHigh - ch <- c.redBytesMediumHigh - ch <- c.redPacketsHigh - ch <- c.redBytesHigh - ch <- c.tailDropPackets - ch <- c.totalDropBytes - ch <- c.totalDropPackets + d := newDescriptions(nil) + ch <- d.queuedBytes + ch <- d.queuedPackets + ch <- d.transferedBytes + ch <- d.transferedPackets + ch <- d.rateLimitDropBytes + ch <- d.rateLimitDropPackets + ch <- d.redPackets + ch <- d.redBytes + ch <- d.redPacketsLow + ch <- d.redBytesLow + ch <- d.redPacketsMediumLow + ch <- d.redBytesMediumLow + ch <- d.redPacketsMediumHigh + ch <- d.redBytesMediumHigh + ch <- d.redPacketsHigh + ch <- d.redBytesHigh + ch <- d.tailDropPackets + ch <- d.totalDropBytes + ch <- d.totalDropPackets } // Collect collects metrics from JunOS @@ -109,42 +117,44 @@ func (c *interfaceQueueCollector) Collect(client collector.Client, ch chan<- pro } for _, iface := range q.InterfaceInformation.Interfaces { - c.collectForInterface(iface, client.Device(), ch, labelValues) + c.collectForInterface(iface, ch, labelValues) } return nil } -func (c *interfaceQueueCollector) collectForInterface(iface physicalInterface, device *connector.Device, ch chan<- prometheus.Metric, labelValues []string) { - l := append(labelValues, iface.Name, iface.Description) - l = append(l, c.labels.ValuesForInterface(device, iface.Name)...) +func (c *interfaceQueueCollector) collectForInterface(iface physicalInterface, ch chan<- prometheus.Metric, labelValues []string) { + lv := append(labelValues, []string{iface.Name, iface.Description}...) + dynLabels := dynamiclabels.ParseDescription(iface.Description, c.descriptionRe) for _, q := range iface.QueueCounters.Queues { - c.collectForQueue(q, ch, l) + c.collectForQueue(q, ch, lv, dynLabels) } } -func (c *interfaceQueueCollector) collectForQueue(queue queue, ch chan<- prometheus.Metric, labelValues []string) { +func (c *interfaceQueueCollector) collectForQueue(queue queue, ch chan<- prometheus.Metric, labelValues []string, dynLabels dynamiclabels.Labels) { l := append(labelValues, queue.Number) l = append(l, queue.ForwaringClassName) - - ch <- prometheus.MustNewConstMetric(c.queuedPackets, prometheus.CounterValue, float64(queue.QueuedPackets), l...) - ch <- prometheus.MustNewConstMetric(c.queuedBytes, prometheus.CounterValue, float64(queue.QueuedBytes), l...) - ch <- prometheus.MustNewConstMetric(c.transferedPackets, prometheus.CounterValue, float64(queue.TransferedPackets), l...) - ch <- prometheus.MustNewConstMetric(c.transferedBytes, prometheus.CounterValue, float64(queue.TransferedBytes), l...) - ch <- prometheus.MustNewConstMetric(c.rateLimitDropPackets, prometheus.CounterValue, float64(queue.RateLimitDropPackets), l...) - ch <- prometheus.MustNewConstMetric(c.rateLimitDropBytes, prometheus.CounterValue, float64(queue.RateLimitDropBytes), l...) - ch <- prometheus.MustNewConstMetric(c.redPackets, prometheus.CounterValue, float64(queue.RedPackets), l...) - ch <- prometheus.MustNewConstMetric(c.redBytes, prometheus.CounterValue, float64(queue.RedBytes), l...) - ch <- prometheus.MustNewConstMetric(c.redPacketsLow, prometheus.CounterValue, float64(queue.RedPacketsLow), l...) - ch <- prometheus.MustNewConstMetric(c.redBytesLow, prometheus.CounterValue, float64(queue.RedBytesLow), l...) - ch <- prometheus.MustNewConstMetric(c.redPacketsMediumLow, prometheus.CounterValue, float64(queue.RedPacketsMediumLow), l...) - ch <- prometheus.MustNewConstMetric(c.redBytesMediumLow, prometheus.CounterValue, float64(queue.RedBytesMediumLow), l...) - ch <- prometheus.MustNewConstMetric(c.redPacketsMediumHigh, prometheus.CounterValue, float64(queue.RedPacketsMediumHigh), l...) - ch <- prometheus.MustNewConstMetric(c.redBytesMediumHigh, prometheus.CounterValue, float64(queue.RedBytesMediumHigh), l...) - ch <- prometheus.MustNewConstMetric(c.redPacketsHigh, prometheus.CounterValue, float64(queue.RedPacketsHigh), l...) - ch <- prometheus.MustNewConstMetric(c.redBytesHigh, prometheus.CounterValue, float64(queue.RedBytesHigh), l...) - ch <- prometheus.MustNewConstMetric(c.tailDropPackets, prometheus.CounterValue, float64(queue.TailDropPackets), l...) - ch <- prometheus.MustNewConstMetric(c.totalDropPackets, prometheus.CounterValue, float64(queue.TotalDropPackets), l...) - ch <- prometheus.MustNewConstMetric(c.totalDropBytes, prometheus.CounterValue, float64(queue.TotalDropBytes), l...) + l = append(l, dynLabels.Values()...) + + d := newDescriptions(dynLabels) + ch <- prometheus.MustNewConstMetric(d.queuedPackets, prometheus.CounterValue, float64(queue.QueuedPackets), l...) + ch <- prometheus.MustNewConstMetric(d.queuedBytes, prometheus.CounterValue, float64(queue.QueuedBytes), l...) + ch <- prometheus.MustNewConstMetric(d.transferedPackets, prometheus.CounterValue, float64(queue.TransferedPackets), l...) + ch <- prometheus.MustNewConstMetric(d.transferedBytes, prometheus.CounterValue, float64(queue.TransferedBytes), l...) + ch <- prometheus.MustNewConstMetric(d.rateLimitDropPackets, prometheus.CounterValue, float64(queue.RateLimitDropPackets), l...) + ch <- prometheus.MustNewConstMetric(d.rateLimitDropBytes, prometheus.CounterValue, float64(queue.RateLimitDropBytes), l...) + ch <- prometheus.MustNewConstMetric(d.redPackets, prometheus.CounterValue, float64(queue.RedPackets), l...) + ch <- prometheus.MustNewConstMetric(d.redBytes, prometheus.CounterValue, float64(queue.RedBytes), l...) + ch <- prometheus.MustNewConstMetric(d.redPacketsLow, prometheus.CounterValue, float64(queue.RedPacketsLow), l...) + ch <- prometheus.MustNewConstMetric(d.redBytesLow, prometheus.CounterValue, float64(queue.RedBytesLow), l...) + ch <- prometheus.MustNewConstMetric(d.redPacketsMediumLow, prometheus.CounterValue, float64(queue.RedPacketsMediumLow), l...) + ch <- prometheus.MustNewConstMetric(d.redBytesMediumLow, prometheus.CounterValue, float64(queue.RedBytesMediumLow), l...) + ch <- prometheus.MustNewConstMetric(d.redPacketsMediumHigh, prometheus.CounterValue, float64(queue.RedPacketsMediumHigh), l...) + ch <- prometheus.MustNewConstMetric(d.redBytesMediumHigh, prometheus.CounterValue, float64(queue.RedBytesMediumHigh), l...) + ch <- prometheus.MustNewConstMetric(d.redPacketsHigh, prometheus.CounterValue, float64(queue.RedPacketsHigh), l...) + ch <- prometheus.MustNewConstMetric(d.redBytesHigh, prometheus.CounterValue, float64(queue.RedBytesHigh), l...) + ch <- prometheus.MustNewConstMetric(d.tailDropPackets, prometheus.CounterValue, float64(queue.TailDropPackets), l...) + ch <- prometheus.MustNewConstMetric(d.totalDropPackets, prometheus.CounterValue, float64(queue.TotalDropPackets), l...) + ch <- prometheus.MustNewConstMetric(d.totalDropBytes, prometheus.CounterValue, float64(queue.TotalDropBytes), l...) } diff --git a/pkg/features/interfaces/collector.go b/pkg/features/interfaces/collector.go index f17ebc3f..fd382d60 100644 --- a/pkg/features/interfaces/collector.go +++ b/pkg/features/interfaces/collector.go @@ -3,20 +3,18 @@ package interfaces import ( + "regexp" "strconv" "strings" "github.com/czerwonk/junos_exporter/pkg/collector" - "github.com/czerwonk/junos_exporter/pkg/connector" - "github.com/czerwonk/junos_exporter/pkg/interfacelabels" + "github.com/czerwonk/junos_exporter/pkg/dynamiclabels" "github.com/prometheus/client_golang/prometheus" ) const prefix = "junos_interface_" -// Collector collects interface metrics -type interfaceCollector struct { - labels *interfacelabels.DynamicLabelManager +type description struct { receiveBytesDesc *prometheus.Desc receivePacketsDesc *prometheus.Desc receiveErrorsDesc *prometheus.Desc @@ -56,12 +54,62 @@ type interfaceCollector struct { transmitTotalErrorsDesc *prometheus.Desc } +func newDescriptions(dynLabels dynamiclabels.Labels) *description { + d := &description{} + l := []string{"target", "name", "description", "mac"} + l = append(l, dynLabels.Keys()...) + + d.receiveBytesDesc = prometheus.NewDesc(prefix+"receive_bytes", "Received data in bytes", l, nil) + d.receivePacketsDesc = prometheus.NewDesc(prefix+"receive_packets_total", "Received packets", l, nil) + d.receiveErrorsDesc = prometheus.NewDesc(prefix+"receive_errors", "Number of errors caused by incoming packets", l, nil) + d.receiveDropsDesc = prometheus.NewDesc(prefix+"receive_drops", "Number of dropped incoming packets", l, nil) + d.interfaceSpeedDesc = prometheus.NewDesc(prefix+"speed", "speed in in bps", l, nil) + d.interfaceBPDUErrorDesc = prometheus.NewDesc(prefix+"error_bpdublock", "Flag which tells that there's a BPDU_Block on the interface (bool)", l, nil) + d.transmitBytesDesc = prometheus.NewDesc(prefix+"transmit_bytes", "Transmitted data in bytes", l, nil) + d.transmitPacketsDesc = prometheus.NewDesc(prefix+"transmit_packets_total", "Transmitted packets", l, nil) + d.transmitErrorsDesc = prometheus.NewDesc(prefix+"transmit_errors", "Number of errors caused by outgoing packets", l, nil) + d.transmitDropsDesc = prometheus.NewDesc(prefix+"transmit_drops", "Number of dropped outgoing packets", l, nil) + d.ipv6receiveBytesDesc = prometheus.NewDesc(prefix+"IPv6_receive_bytes_total", "Received IPv6 data in bytes", l, nil) + d.ipv6receivePacketsDesc = prometheus.NewDesc(prefix+"IPv6_receive_packets_total", "Received IPv6 packets", l, nil) + d.ipv6transmitBytesDesc = prometheus.NewDesc(prefix+"IPv6_transmit_bytes_total", "Transmitted IPv6 data in bytes", l, nil) + d.ipv6transmitPacketsDesc = prometheus.NewDesc(prefix+"IPv6_transmit_packets_total", "Transmitted IPv6 packets", l, nil) + d.adminStatusDesc = prometheus.NewDesc(prefix+"admin_up", "Admin operational status", l, nil) + d.operStatusDesc = prometheus.NewDesc(prefix+"up", "Interface operational status", l, nil) + d.errorStatusDesc = prometheus.NewDesc(prefix+"error_status", "Admin and operational status differ", l, nil) + d.lastFlappedDesc = prometheus.NewDesc(prefix+"last_flapped_seconds", "Seconds since last flapped (-1 if never)", l, nil) + d.receiveUnicastsDesc = prometheus.NewDesc(prefix+"receive_unicasts_packets", "Received unicast packets", l, nil) + d.receiveBroadcastsDesc = prometheus.NewDesc(prefix+"receive_broadcasts_packets", "Received broadcast packets", l, nil) + d.receiveMulticastsDesc = prometheus.NewDesc(prefix+"receive_multicasts_packets", "Received multicast packets", l, nil) + d.receiveCRCErrorsDesc = prometheus.NewDesc(prefix+"receive_errors_crc_packets", "Number of CRC error incoming packets", l, nil) + d.transmitUnicastsDesc = prometheus.NewDesc(prefix+"transmit_unicasts_packets", "Transmitted unicast packets", l, nil) + d.transmitBroadcastsDesc = prometheus.NewDesc(prefix+"transmit_broadcasts_packets", "Transmitted broadcast packets", l, nil) + d.transmitMulticastsDesc = prometheus.NewDesc(prefix+"transmit_multicasts_packets", "Transmitted multicast packets", l, nil) + d.transmitCRCErrorsDesc = prometheus.NewDesc(prefix+"transmit_errors_crc_packets", "Number of CRC error outgoing packets", l, nil) + d.fecCcwCountDesc = prometheus.NewDesc(prefix+"fec_ccw_count", "Number FEC Corrected Errors", l, nil) + d.fecNccwCountDesc = prometheus.NewDesc(prefix+"fec_nccw_count", "Number FEC Uncorrected Errors", l, nil) + d.fecCcwErrorRateDesc = prometheus.NewDesc(prefix+"fec_ccw_error_rate", "Number FEC Corrected Errors Rate", l, nil) + d.fecNccwErrorRateDesc = prometheus.NewDesc(prefix+"fec_nccw_error_rate", "Number FEC Uncorrected Errors Rate", l, nil) + d.receiveOversizedFramesDesc = prometheus.NewDesc(prefix+"receive_oversized_frames", "Number of received Oversize Frames", l, nil) + d.receiveJabberFramesDesc = prometheus.NewDesc(prefix+"receive_jabber_frames", "Number of received Jabber Frames", l, nil) + d.receiveFragmentFramesDesc = prometheus.NewDesc(prefix+"receive_fragment_frames", "Number of received Fragment Frames", l, nil) + d.receiveVlanTaggedFramesDesc = prometheus.NewDesc(prefix+"receive_vlan_tagged_frames", "Number of received Vlan Tagged Frames", l, nil) + d.receiveCodeViolationsDesc = prometheus.NewDesc(prefix+"receive_code_violations", "Number of received Code Violations", l, nil) + d.receiveTotalErrorsDesc = prometheus.NewDesc(prefix+"receive_total_errors", "Number of received Total Errors", l, nil) + d.transmitTotalErrorsDesc = prometheus.NewDesc(prefix+"transmit_total_errors", "Number of transmitted Total Errors", l, nil) + + return d +} + +// Collector collects interface metrics +type interfaceCollector struct { + descriptionRe *regexp.Regexp +} + // NewCollector creates a new collector -func NewCollector(labels *interfacelabels.DynamicLabelManager) collector.RPCCollector { +func NewCollector(descRe *regexp.Regexp) collector.RPCCollector { c := &interfaceCollector{ - labels: labels, + descriptionRe: descRe, } - c.init() return c } @@ -71,89 +119,46 @@ func (*interfaceCollector) Name() string { return "Interfaces" } -func (c *interfaceCollector) init() { - l := []string{"target", "name", "description", "mac"} - l = append(l, c.labels.LabelNames()...) - - c.receiveBytesDesc = prometheus.NewDesc(prefix+"receive_bytes", "Received data in bytes", l, nil) - c.receivePacketsDesc = prometheus.NewDesc(prefix+"receive_packets_total", "Received packets", l, nil) - c.receiveErrorsDesc = prometheus.NewDesc(prefix+"receive_errors", "Number of errors caused by incoming packets", l, nil) - c.receiveDropsDesc = prometheus.NewDesc(prefix+"receive_drops", "Number of dropped incoming packets", l, nil) - c.interfaceSpeedDesc = prometheus.NewDesc(prefix+"speed", "speed in in bps", l, nil) - c.interfaceBPDUErrorDesc = prometheus.NewDesc(prefix+"error_bpdublock", "Flag which tells that there's a BPDU_Block on the interface (bool)", l, nil) - c.transmitBytesDesc = prometheus.NewDesc(prefix+"transmit_bytes", "Transmitted data in bytes", l, nil) - c.transmitPacketsDesc = prometheus.NewDesc(prefix+"transmit_packets_total", "Transmitted packets", l, nil) - c.transmitErrorsDesc = prometheus.NewDesc(prefix+"transmit_errors", "Number of errors caused by outgoing packets", l, nil) - c.transmitDropsDesc = prometheus.NewDesc(prefix+"transmit_drops", "Number of dropped outgoing packets", l, nil) - c.ipv6receiveBytesDesc = prometheus.NewDesc(prefix+"IPv6_receive_bytes_total", "Received IPv6 data in bytes", l, nil) - c.ipv6receivePacketsDesc = prometheus.NewDesc(prefix+"IPv6_receive_packets_total", "Received IPv6 packets", l, nil) - c.ipv6transmitBytesDesc = prometheus.NewDesc(prefix+"IPv6_transmit_bytes_total", "Transmitted IPv6 data in bytes", l, nil) - c.ipv6transmitPacketsDesc = prometheus.NewDesc(prefix+"IPv6_transmit_packets_total", "Transmitted IPv6 packets", l, nil) - c.adminStatusDesc = prometheus.NewDesc(prefix+"admin_up", "Admin operational status", l, nil) - c.operStatusDesc = prometheus.NewDesc(prefix+"up", "Interface operational status", l, nil) - c.errorStatusDesc = prometheus.NewDesc(prefix+"error_status", "Admin and operational status differ", l, nil) - c.lastFlappedDesc = prometheus.NewDesc(prefix+"last_flapped_seconds", "Seconds since last flapped (-1 if never)", l, nil) - c.receiveUnicastsDesc = prometheus.NewDesc(prefix+"receive_unicasts_packets", "Received unicast packets", l, nil) - c.receiveBroadcastsDesc = prometheus.NewDesc(prefix+"receive_broadcasts_packets", "Received broadcast packets", l, nil) - c.receiveMulticastsDesc = prometheus.NewDesc(prefix+"receive_multicasts_packets", "Received multicast packets", l, nil) - c.receiveCRCErrorsDesc = prometheus.NewDesc(prefix+"receive_errors_crc_packets", "Number of CRC error incoming packets", l, nil) - c.transmitUnicastsDesc = prometheus.NewDesc(prefix+"transmit_unicasts_packets", "Transmitted unicast packets", l, nil) - c.transmitBroadcastsDesc = prometheus.NewDesc(prefix+"transmit_broadcasts_packets", "Transmitted broadcast packets", l, nil) - c.transmitMulticastsDesc = prometheus.NewDesc(prefix+"transmit_multicasts_packets", "Transmitted multicast packets", l, nil) - c.transmitCRCErrorsDesc = prometheus.NewDesc(prefix+"transmit_errors_crc_packets", "Number of CRC error outgoing packets", l, nil) - c.fecCcwCountDesc = prometheus.NewDesc(prefix+"fec_ccw_count", "Number FEC Corrected Errors", l, nil) - c.fecNccwCountDesc = prometheus.NewDesc(prefix+"fec_nccw_count", "Number FEC Uncorrected Errors", l, nil) - c.fecCcwErrorRateDesc = prometheus.NewDesc(prefix+"fec_ccw_error_rate", "Number FEC Corrected Errors Rate", l, nil) - c.fecNccwErrorRateDesc = prometheus.NewDesc(prefix+"fec_nccw_error_rate", "Number FEC Uncorrected Errors Rate", l, nil) - c.receiveOversizedFramesDesc = prometheus.NewDesc(prefix+"receive_oversized_frames", "Number of received Oversize Frames", l, nil) - c.receiveJabberFramesDesc = prometheus.NewDesc(prefix+"receive_jabber_frames", "Number of received Jabber Frames", l, nil) - c.receiveFragmentFramesDesc = prometheus.NewDesc(prefix+"receive_fragment_frames", "Number of received Fragment Frames", l, nil) - c.receiveVlanTaggedFramesDesc = prometheus.NewDesc(prefix+"receive_vlan_tagged_frames", "Number of received Vlan Tagged Frames", l, nil) - c.receiveCodeViolationsDesc = prometheus.NewDesc(prefix+"receive_code_violations", "Number of received Code Violations", l, nil) - c.receiveTotalErrorsDesc = prometheus.NewDesc(prefix+"receive_total_errors", "Number of received Total Errors", l, nil) - c.transmitTotalErrorsDesc = prometheus.NewDesc(prefix+"transmit_total_errors", "Number of transmitted Total Errors", l, nil) - -} - // Describe describes the metrics -func (c *interfaceCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- c.receiveBytesDesc - ch <- c.receivePacketsDesc - ch <- c.receiveErrorsDesc - ch <- c.receiveDropsDesc - ch <- c.interfaceSpeedDesc - ch <- c.interfaceBPDUErrorDesc - ch <- c.transmitBytesDesc - ch <- c.transmitPacketsDesc - ch <- c.transmitDropsDesc - ch <- c.transmitErrorsDesc - ch <- c.ipv6receiveBytesDesc - ch <- c.ipv6receivePacketsDesc - ch <- c.ipv6transmitBytesDesc - ch <- c.ipv6transmitPacketsDesc - ch <- c.adminStatusDesc - ch <- c.operStatusDesc - ch <- c.errorStatusDesc - ch <- c.lastFlappedDesc - ch <- c.receiveUnicastsDesc - ch <- c.receiveBroadcastsDesc - ch <- c.receiveMulticastsDesc - ch <- c.receiveCRCErrorsDesc - ch <- c.transmitUnicastsDesc - ch <- c.transmitBroadcastsDesc - ch <- c.transmitMulticastsDesc - ch <- c.transmitCRCErrorsDesc - ch <- c.fecCcwCountDesc - ch <- c.fecNccwCountDesc - ch <- c.fecCcwErrorRateDesc - ch <- c.fecNccwErrorRateDesc - ch <- c.receiveOversizedFramesDesc - ch <- c.receiveJabberFramesDesc - ch <- c.receiveFragmentFramesDesc - ch <- c.receiveVlanTaggedFramesDesc - ch <- c.receiveCodeViolationsDesc - ch <- c.receiveTotalErrorsDesc - ch <- c.transmitTotalErrorsDesc +func (*interfaceCollector) Describe(ch chan<- *prometheus.Desc) { + d := newDescriptions(nil) + ch <- d.receiveBytesDesc + ch <- d.receivePacketsDesc + ch <- d.receiveErrorsDesc + ch <- d.receiveDropsDesc + ch <- d.interfaceSpeedDesc + ch <- d.interfaceBPDUErrorDesc + ch <- d.transmitBytesDesc + ch <- d.transmitPacketsDesc + ch <- d.transmitDropsDesc + ch <- d.transmitErrorsDesc + ch <- d.ipv6receiveBytesDesc + ch <- d.ipv6receivePacketsDesc + ch <- d.ipv6transmitBytesDesc + ch <- d.ipv6transmitPacketsDesc + ch <- d.adminStatusDesc + ch <- d.operStatusDesc + ch <- d.errorStatusDesc + ch <- d.lastFlappedDesc + ch <- d.receiveUnicastsDesc + ch <- d.receiveBroadcastsDesc + ch <- d.receiveMulticastsDesc + ch <- d.receiveCRCErrorsDesc + ch <- d.transmitUnicastsDesc + ch <- d.transmitBroadcastsDesc + ch <- d.transmitMulticastsDesc + ch <- d.transmitCRCErrorsDesc + ch <- d.fecCcwCountDesc + ch <- d.fecNccwCountDesc + ch <- d.fecCcwErrorRateDesc + ch <- d.fecNccwErrorRateDesc + ch <- d.receiveOversizedFramesDesc + ch <- d.receiveJabberFramesDesc + ch <- d.receiveFragmentFramesDesc + ch <- d.receiveVlanTaggedFramesDesc + ch <- d.receiveCodeViolationsDesc + ch <- d.receiveTotalErrorsDesc + ch <- d.transmitTotalErrorsDesc } // Collect collects metrics from JunOS @@ -164,7 +169,7 @@ func (c *interfaceCollector) Collect(client collector.Client, ch chan<- promethe } for _, s := range stats { - c.collectForInterface(s, client.Device(), ch, labelValues) + c.collectForInterface(s, ch, labelValues) } return nil @@ -258,18 +263,20 @@ func (c *interfaceCollector) interfaceStats(client collector.Client) ([]*interfa return stats, nil } -func (c *interfaceCollector) collectForInterface(s *interfaceStats, device *connector.Device, ch chan<- prometheus.Metric, labelValues []string) { - l := append(labelValues, []string{s.Name, s.Description, s.Mac}...) - l = append(l, c.labels.ValuesForInterface(device, s.Name)...) - - ch <- prometheus.MustNewConstMetric(c.receiveBytesDesc, prometheus.CounterValue, s.ReceiveBytes, l...) - ch <- prometheus.MustNewConstMetric(c.receivePacketsDesc, prometheus.CounterValue, s.ReceivePackets, l...) - ch <- prometheus.MustNewConstMetric(c.transmitBytesDesc, prometheus.CounterValue, s.TransmitBytes, l...) - ch <- prometheus.MustNewConstMetric(c.transmitPacketsDesc, prometheus.CounterValue, s.TransmitPackets, l...) - ch <- prometheus.MustNewConstMetric(c.ipv6receiveBytesDesc, prometheus.CounterValue, s.IPv6ReceiveBytes, l...) - ch <- prometheus.MustNewConstMetric(c.ipv6receivePacketsDesc, prometheus.CounterValue, s.IPv6ReceivePackets, l...) - ch <- prometheus.MustNewConstMetric(c.ipv6transmitBytesDesc, prometheus.CounterValue, s.IPv6TransmitBytes, l...) - ch <- prometheus.MustNewConstMetric(c.ipv6transmitPacketsDesc, prometheus.CounterValue, s.IPv6TransmitPackets, l...) +func (c *interfaceCollector) collectForInterface(s *interfaceStats, ch chan<- prometheus.Metric, labelValues []string) { + lv := append(labelValues, []string{s.Name, s.Description, s.Mac}...) + dynLabels := dynamiclabels.ParseDescription(s.Description, c.descriptionRe) + lv = append(lv, dynLabels.Values()...) + d := newDescriptions(dynLabels) + + ch <- prometheus.MustNewConstMetric(d.receiveBytesDesc, prometheus.CounterValue, s.ReceiveBytes, lv...) + ch <- prometheus.MustNewConstMetric(d.receivePacketsDesc, prometheus.CounterValue, s.ReceivePackets, lv...) + ch <- prometheus.MustNewConstMetric(d.transmitBytesDesc, prometheus.CounterValue, s.TransmitBytes, lv...) + ch <- prometheus.MustNewConstMetric(d.transmitPacketsDesc, prometheus.CounterValue, s.TransmitPackets, lv...) + ch <- prometheus.MustNewConstMetric(d.ipv6receiveBytesDesc, prometheus.CounterValue, s.IPv6ReceiveBytes, lv...) + ch <- prometheus.MustNewConstMetric(d.ipv6receivePacketsDesc, prometheus.CounterValue, s.IPv6ReceivePackets, lv...) + ch <- prometheus.MustNewConstMetric(d.ipv6transmitBytesDesc, prometheus.CounterValue, s.IPv6TransmitBytes, lv...) + ch <- prometheus.MustNewConstMetric(d.ipv6transmitPacketsDesc, prometheus.CounterValue, s.IPv6TransmitPackets, lv...) if s.IsPhysical { adminUp := 0 @@ -313,41 +320,41 @@ func (c *interfaceCollector) collectForInterface(s *interfaceStats, device *conn sp64, _ := strconv.ParseFloat(speed, 64) if s.BPDUError { - ch <- prometheus.MustNewConstMetric(c.interfaceBPDUErrorDesc, prometheus.GaugeValue, float64(1), l...) + ch <- prometheus.MustNewConstMetric(d.interfaceBPDUErrorDesc, prometheus.GaugeValue, float64(1), lv...) } - ch <- prometheus.MustNewConstMetric(c.adminStatusDesc, prometheus.GaugeValue, float64(adminUp), l...) - ch <- prometheus.MustNewConstMetric(c.operStatusDesc, prometheus.GaugeValue, float64(operUp), l...) - ch <- prometheus.MustNewConstMetric(c.errorStatusDesc, prometheus.GaugeValue, float64(err), l...) - ch <- prometheus.MustNewConstMetric(c.transmitErrorsDesc, prometheus.CounterValue, s.TransmitErrors, l...) - ch <- prometheus.MustNewConstMetric(c.transmitDropsDesc, prometheus.CounterValue, s.TransmitDrops, l...) - ch <- prometheus.MustNewConstMetric(c.receiveErrorsDesc, prometheus.CounterValue, s.ReceiveErrors, l...) - ch <- prometheus.MustNewConstMetric(c.receiveDropsDesc, prometheus.CounterValue, s.ReceiveDrops, l...) - ch <- prometheus.MustNewConstMetric(c.interfaceSpeedDesc, prometheus.GaugeValue, float64(sp64), l...) + ch <- prometheus.MustNewConstMetric(d.adminStatusDesc, prometheus.GaugeValue, float64(adminUp), lv...) + ch <- prometheus.MustNewConstMetric(d.operStatusDesc, prometheus.GaugeValue, float64(operUp), lv...) + ch <- prometheus.MustNewConstMetric(d.errorStatusDesc, prometheus.GaugeValue, float64(err), lv...) + ch <- prometheus.MustNewConstMetric(d.transmitErrorsDesc, prometheus.CounterValue, s.TransmitErrors, lv...) + ch <- prometheus.MustNewConstMetric(d.transmitDropsDesc, prometheus.CounterValue, s.TransmitDrops, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveErrorsDesc, prometheus.CounterValue, s.ReceiveErrors, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveDropsDesc, prometheus.CounterValue, s.ReceiveDrops, lv...) + ch <- prometheus.MustNewConstMetric(d.interfaceSpeedDesc, prometheus.GaugeValue, float64(sp64), lv...) if s.LastFlapped != 0 { - ch <- prometheus.MustNewConstMetric(c.lastFlappedDesc, prometheus.GaugeValue, s.LastFlapped, l...) + ch <- prometheus.MustNewConstMetric(d.lastFlappedDesc, prometheus.GaugeValue, s.LastFlapped, lv...) } - ch <- prometheus.MustNewConstMetric(c.receiveUnicastsDesc, prometheus.CounterValue, s.ReceiveUnicasts, l...) - ch <- prometheus.MustNewConstMetric(c.receiveBroadcastsDesc, prometheus.CounterValue, s.ReceiveBroadcasts, l...) - ch <- prometheus.MustNewConstMetric(c.receiveMulticastsDesc, prometheus.CounterValue, s.ReceiveMulticasts, l...) - ch <- prometheus.MustNewConstMetric(c.receiveCRCErrorsDesc, prometheus.CounterValue, s.ReceiveCRCErrors, l...) - ch <- prometheus.MustNewConstMetric(c.transmitUnicastsDesc, prometheus.CounterValue, s.TransmitUnicasts, l...) - ch <- prometheus.MustNewConstMetric(c.transmitBroadcastsDesc, prometheus.CounterValue, s.TransmitBroadcasts, l...) - ch <- prometheus.MustNewConstMetric(c.transmitMulticastsDesc, prometheus.CounterValue, s.TransmitMulticasts, l...) - ch <- prometheus.MustNewConstMetric(c.transmitCRCErrorsDesc, prometheus.CounterValue, s.TransmitCRCErrors, l...) - ch <- prometheus.MustNewConstMetric(c.fecCcwCountDesc, prometheus.CounterValue, s.FecCcwCount, l...) - ch <- prometheus.MustNewConstMetric(c.fecNccwCountDesc, prometheus.CounterValue, s.FecNccwCount, l...) - ch <- prometheus.MustNewConstMetric(c.fecCcwErrorRateDesc, prometheus.CounterValue, s.FecCcwErrorRate, l...) - ch <- prometheus.MustNewConstMetric(c.fecNccwErrorRateDesc, prometheus.CounterValue, s.FecNccwErrorRate, l...) - ch <- prometheus.MustNewConstMetric(c.receiveOversizedFramesDesc, prometheus.CounterValue, s.ReceiveOversizedFrames, l...) - ch <- prometheus.MustNewConstMetric(c.receiveJabberFramesDesc, prometheus.CounterValue, s.ReceiveJabberFrames, l...) - ch <- prometheus.MustNewConstMetric(c.receiveFragmentFramesDesc, prometheus.CounterValue, s.ReceiveFragmentFrames, l...) - ch <- prometheus.MustNewConstMetric(c.receiveVlanTaggedFramesDesc, prometheus.CounterValue, s.ReceiveVlanTaggedFrames, l...) - ch <- prometheus.MustNewConstMetric(c.receiveCodeViolationsDesc, prometheus.CounterValue, s.ReceiveCodeViolations, l...) - ch <- prometheus.MustNewConstMetric(c.receiveTotalErrorsDesc, prometheus.CounterValue, s.ReceiveTotalErrors, l...) - ch <- prometheus.MustNewConstMetric(c.transmitTotalErrorsDesc, prometheus.CounterValue, s.TransmitTotalErrors, l...) + ch <- prometheus.MustNewConstMetric(d.receiveUnicastsDesc, prometheus.CounterValue, s.ReceiveUnicasts, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveBroadcastsDesc, prometheus.CounterValue, s.ReceiveBroadcasts, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveMulticastsDesc, prometheus.CounterValue, s.ReceiveMulticasts, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveCRCErrorsDesc, prometheus.CounterValue, s.ReceiveCRCErrors, lv...) + ch <- prometheus.MustNewConstMetric(d.transmitUnicastsDesc, prometheus.CounterValue, s.TransmitUnicasts, lv...) + ch <- prometheus.MustNewConstMetric(d.transmitBroadcastsDesc, prometheus.CounterValue, s.TransmitBroadcasts, lv...) + ch <- prometheus.MustNewConstMetric(d.transmitMulticastsDesc, prometheus.CounterValue, s.TransmitMulticasts, lv...) + ch <- prometheus.MustNewConstMetric(d.transmitCRCErrorsDesc, prometheus.CounterValue, s.TransmitCRCErrors, lv...) + ch <- prometheus.MustNewConstMetric(d.fecCcwCountDesc, prometheus.CounterValue, s.FecCcwCount, lv...) + ch <- prometheus.MustNewConstMetric(d.fecNccwCountDesc, prometheus.CounterValue, s.FecNccwCount, lv...) + ch <- prometheus.MustNewConstMetric(d.fecCcwErrorRateDesc, prometheus.CounterValue, s.FecCcwErrorRate, lv...) + ch <- prometheus.MustNewConstMetric(d.fecNccwErrorRateDesc, prometheus.CounterValue, s.FecNccwErrorRate, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveOversizedFramesDesc, prometheus.CounterValue, s.ReceiveOversizedFrames, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveJabberFramesDesc, prometheus.CounterValue, s.ReceiveJabberFrames, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveFragmentFramesDesc, prometheus.CounterValue, s.ReceiveFragmentFrames, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveVlanTaggedFramesDesc, prometheus.CounterValue, s.ReceiveVlanTaggedFrames, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveCodeViolationsDesc, prometheus.CounterValue, s.ReceiveCodeViolations, lv...) + ch <- prometheus.MustNewConstMetric(d.receiveTotalErrorsDesc, prometheus.CounterValue, s.ReceiveTotalErrors, lv...) + ch <- prometheus.MustNewConstMetric(d.transmitTotalErrorsDesc, prometheus.CounterValue, s.TransmitTotalErrors, lv...) } } diff --git a/pkg/interfacelabels/dynamic_labels.go b/pkg/interfacelabels/dynamic_labels.go deleted file mode 100644 index 3382284f..00000000 --- a/pkg/interfacelabels/dynamic_labels.go +++ /dev/null @@ -1,173 +0,0 @@ -// SPDX-License-Identifier: MIT - -package interfacelabels - -import ( - "regexp" - "strings" - "sync" - - "github.com/czerwonk/junos_exporter/pkg/collector" - "github.com/czerwonk/junos_exporter/pkg/connector" - "github.com/pkg/errors" -) - -var ( - nameRe *regexp.Regexp -) - -func init() { - nameRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`) -} - -func DefaultInterfaceDescRegex() *regexp.Regexp { - return regexp.MustCompile(`\[([^=\]]+)(=[^\]]+)?\]`) -} - -// NewDynamicLabelManager create a new instance of DynamicLabels -func NewDynamicLabelManager() *DynamicLabelManager { - return &DynamicLabelManager{ - labelNames: make(map[string]int), - labels: make(map[interfaceKey][]*InterfaceLabel), - } -} - -// DynamicLabelManager parses and manages dynamic labels and label values -type DynamicLabelManager struct { - labelNames map[string]int - labels map[interfaceKey][]*InterfaceLabel - labelCount int - mu sync.Mutex -} - -type interfaceKey struct { - host string - ifaceName string -} - -type InterfaceLabel struct { - name string - value string -} - -func (il *InterfaceLabel) Name() string { - return il.name -} - -func (il *InterfaceLabel) Value() string { - return il.value -} - -// CollectDescriptions collects labels from descriptions -func (l *DynamicLabelManager) CollectDescriptions(device *connector.Device, client collector.Client, ifDescReg *regexp.Regexp) error { - r := &result{} - err := client.RunCommandAndParse("show interfaces descriptions", r) - if err != nil { - return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) - } - - l.parseDescriptions(device, r.Information.LogicalInterfaces, ifDescReg) - l.parseDescriptions(device, r.Information.PhysicalInterfaces, ifDescReg) - - return nil -} - -// LabelNames returns the names for all dynamic labels -func (l *DynamicLabelManager) LabelNames() []string { - names := make([]string, len(l.labelNames)) - - for k, v := range l.labelNames { - names[v] = k - } - - return names -} - -// ValuesForInterface returns the values for all dynamic labels -func (l *DynamicLabelManager) ValuesForInterface(device *connector.Device, ifaceName string) []string { - labels := make([]string, len(l.labelNames)) - - k := interfaceKey{host: device.Host, ifaceName: ifaceName} - ifaceLabels, found := l.labels[k] - if !found { - return labels - } - - for _, la := range ifaceLabels { - labels[l.labelNames[la.name]] = la.value - } - - return labels -} - -func (l *DynamicLabelManager) parseDescriptions(device *connector.Device, ifaces []interfaceDescription, ifDescReg *regexp.Regexp) { - l.mu.Lock() - defer l.mu.Unlock() - - for _, in := range ifaces { - labels := ParseDescription(in.Description, ifDescReg) - - for _, la := range labels { - if _, found := l.labelNames[la.name]; !found { - l.labelNames[la.name] = l.labelCount - l.labelCount++ - } - - k := interfaceKey{host: device.Host, ifaceName: in.Name} - l.labels[k] = append(l.labels[k], la) - } - } -} - -func ParseDescription(description string, ifDescReg *regexp.Regexp) InterfaceLabels { - labels := make(InterfaceLabels, 0) - - if len(description) == 0 || ifDescReg == nil { - return labels - } - - matches := ifDescReg.FindAllStringSubmatch(description, -1) - for _, m := range matches { - n := strings.ToLower(m[1]) - - if !nameRe.Match([]byte(n)) { - continue - } - - label := &InterfaceLabel{ - name: n, - } - - val := m[2] - - if strings.HasPrefix(val, "=") { - label.value = val[1:] - } else { - label.value = "1" - } - - labels = append(labels, label) - } - - return labels -} - -type InterfaceLabels []*InterfaceLabel - -func (ils InterfaceLabels) Keys() []string { - ret := make([]string, 0, len(ils)) - for _, il := range ils { - ret = append(ret, il.name) - } - - return ret -} - -func (ils InterfaceLabels) Values() []string { - ret := make([]string, 0, len(ils)) - for _, il := range ils { - ret = append(ret, il.value) - } - - return ret -} diff --git a/pkg/interfacelabels/dynamic_labels_test.go b/pkg/interfacelabels/dynamic_labels_test.go deleted file mode 100644 index c0ea6bb1..00000000 --- a/pkg/interfacelabels/dynamic_labels_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: MIT - -package interfacelabels - -import ( - "regexp" - "testing" - - "github.com/czerwonk/junos_exporter/pkg/connector" - "github.com/stretchr/testify/assert" -) - -func TestParseDescriptions(t *testing.T) { - t.Run("Test default", func(t *testing.T) { - l := NewDynamicLabelManager() - regex := DefaultInterfaceDescRegex() - - if1 := interfaceDescription{ - Name: "xe-0/0/0", - Description: "Name1 [tag1] [foo=x]", - } - if2 := interfaceDescription{ - Name: "xe-0/0/1", - Description: "Name2 [foo=y] [bar=123]", - } - if3 := interfaceDescription{ - Name: "xe-0/0/3", - } - if4 := interfaceDescription{ - Name: "irb.216", - Description: "Internal: Test-Network [vrf=AS4711]", - } - - d1 := &connector.Device{Host: "device1"} - d2 := &connector.Device{Host: "device2"} - - l.parseDescriptions(d1, []interfaceDescription{if1}, regex) - l.parseDescriptions(d2, []interfaceDescription{if2, if3, if4}, regex) - - assert.Equal(t, []string{"tag1", "foo", "bar", "vrf"}, l.LabelNames(), "Label names") - assert.Equal(t, []string{"1", "x", "", ""}, l.ValuesForInterface(d1, if1.Name), "Values if1") - assert.Equal(t, []string{"", "y", "123", ""}, l.ValuesForInterface(d2, if2.Name), "Values if2") - assert.Equal(t, []string{"", "", "", ""}, l.ValuesForInterface(d2, if3.Name), "Values if3") - assert.Equal(t, []string{"", "", "", "AS4711"}, l.ValuesForInterface(d2, if4.Name), "Values if4") - }) - - t.Run("Test custom regex", func(t *testing.T) { - l := NewDynamicLabelManager() - regex := regexp.MustCompile(`[[\s]([^=\[\]]+)(=[^,\]]+)?[,\]]`) - - if1 := interfaceDescription{ - Name: "xe-0/0/0", - Description: "Name1 [foo=x, bar=y, thisisatag]", - } - if2 := interfaceDescription{ - Name: "xe-0/0/1", - Description: "Name2 [onlyatag]", - } - if3 := interfaceDescription{ - Name: "xe-0/0/3", - Description: "Name2 [foo=x, bar=y, this=is]", - } - - d1 := &connector.Device{Host: "device1"} - d2 := &connector.Device{Host: "device2"} - d3 := &connector.Device{Host: "device3"} - - l.parseDescriptions(d1, []interfaceDescription{if1}, regex) - l.parseDescriptions(d2, []interfaceDescription{if2}, regex) - l.parseDescriptions(d3, []interfaceDescription{if3}, regex) - - assert.Equal(t, []string{"foo", "bar", "thisisatag", "onlyatag", "this"}, l.LabelNames(), "Label names") - assert.Equal(t, []string{"x", "y", "1", "", ""}, l.ValuesForInterface(d1, if1.Name), "Values if1") - assert.Equal(t, []string{"", "", "", "1", ""}, l.ValuesForInterface(d2, if2.Name), "Values if2") - assert.Equal(t, []string{"x", "y", "", "", "is"}, l.ValuesForInterface(d3, if3.Name), "Values if3") - }) -} diff --git a/pkg/interfacelabels/rpc.go b/pkg/interfacelabels/rpc.go deleted file mode 100644 index 6e96a09d..00000000 --- a/pkg/interfacelabels/rpc.go +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT - -package interfacelabels - -type result struct { - Information struct { - PhysicalInterfaces []interfaceDescription `xml:"physical-interface"` - LogicalInterfaces []interfaceDescription `xml:"logical-interface"` - } `xml:"interface-information"` -} - -type interfaceDescription struct { - Name string `xml:"name"` - Description string `xml:"description"` -} From fbaa9b1568170b620296df65175316670f4f0fe6 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Fri, 26 Jul 2024 18:56:27 +0200 Subject: [PATCH 4/5] Update main.go bump version --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 77cb6558..221f411b 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,7 @@ import ( log "github.com/sirupsen/logrus" ) -const version string = "0.12.3" +const version string = "0.12.4" var ( showVersion = flag.Bool("version", false, "Print version information.") From a5246386723e1b69297f6a71c1cff3e287fdc38f Mon Sep 17 00:00:00 2001 From: Gaste8 Date: Tue, 30 Jul 2024 09:35:16 +0200 Subject: [PATCH 5/5] feat: add l2vpn metrics (#248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add l2vpn metrics * fix: l2vpn tests --------- Co-authored-by: Johannes Mùˆller Aguilar --- collectors.go | 2 + collectors_test.go | 6 +- helm/junosexporter/values.yaml | 1 + internal/config/config.go | 2 + internal/config/config_test.go | 5 +- internal/config/tests/config1.yml | 1 + internal/config/tests/config3.yml | 1 + internal/config/tests/config4.yml | 2 +- pkg/features/l2vpn/collector.go | 147 ++++++++++++++++++++++++++++++ pkg/features/l2vpn/rpc.go | 33 +++++++ 10 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 pkg/features/l2vpn/collector.go create mode 100644 pkg/features/l2vpn/rpc.go diff --git a/collectors.go b/collectors.go index d1017fd6..1b94726c 100644 --- a/collectors.go +++ b/collectors.go @@ -21,6 +21,7 @@ import ( "github.com/czerwonk/junos_exporter/pkg/features/ipsec" "github.com/czerwonk/junos_exporter/pkg/features/isis" "github.com/czerwonk/junos_exporter/pkg/features/l2circuit" + "github.com/czerwonk/junos_exporter/pkg/features/l2vpn" "github.com/czerwonk/junos_exporter/pkg/features/lacp" "github.com/czerwonk/junos_exporter/pkg/features/ldp" "github.com/czerwonk/junos_exporter/pkg/features/mac" @@ -94,6 +95,7 @@ func (c *collectors) initCollectorsForDevices(device *connector.Device, descRe * c.addCollectorIfEnabledForDevice(device, "ipsec", f.IPSec, ipsec.NewCollector) c.addCollectorIfEnabledForDevice(device, "isis", f.ISIS, isis.NewCollector) c.addCollectorIfEnabledForDevice(device, "l2c", f.L2Circuit, l2circuit.NewCollector) + c.addCollectorIfEnabledForDevice(device, "l2vpn", f.L2Vpn, l2vpn.NewCollector) c.addCollectorIfEnabledForDevice(device, "lacp", f.LACP, lacp.NewCollector) c.addCollectorIfEnabledForDevice(device, "ldp", f.LDP, ldp.NewCollector) c.addCollectorIfEnabledForDevice(device, "nat", f.NAT, nat.NewCollector) diff --git a/collectors_test.go b/collectors_test.go index b180a67b..5e270fe2 100644 --- a/collectors_test.go +++ b/collectors_test.go @@ -21,6 +21,7 @@ func TestCollectorsRegistered(t *testing.T) { ISIS: true, NAT: true, L2Circuit: true, + L2Vpn: true, LDP: true, Routes: true, RoutingEngine: true, @@ -41,7 +42,7 @@ func TestCollectorsRegistered(t *testing.T) { Host: "::1", }}, c, "") - assert.Equal(t, 20, len(cols.collectors), "collector count") + assert.Equal(t, 21, len(cols.collectors), "collector count") } func TestCollectorsForDevices(t *testing.T) { @@ -54,6 +55,7 @@ func TestCollectorsForDevices(t *testing.T) { ISIS: true, NAT: true, L2Circuit: true, + L2Vpn: true, LDP: true, Routes: true, RoutingEngine: true, @@ -89,7 +91,7 @@ func TestCollectorsForDevices(t *testing.T) { } cols := collectorsForDevices([]*connector.Device{d1, d2}, c, "") - assert.Equal(t, 20, len(cols.collectorsForDevice(d1)), "device 1 collector count") + assert.Equal(t, 21, len(cols.collectorsForDevice(d1)), "device 1 collector count") cd2 := cols.collectorsForDevice(d2) assert.Equal(t, 1, len(cd2), "device 2 collector count") diff --git a/helm/junosexporter/values.yaml b/helm/junosexporter/values.yaml index 0ce99989..b224ab7b 100644 --- a/helm/junosexporter/values.yaml +++ b/helm/junosexporter/values.yaml @@ -37,6 +37,7 @@ extraArgs: [] # ospf: false # isis: false # l2circuit: false +# l2vpn: false # environment: true # routes: true # routing_engine: true diff --git a/internal/config/config.go b/internal/config/config.go index d895d5b8..4c285c90 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -70,6 +70,7 @@ type FeatureConfig struct { NAT bool `yaml:"nat,omitempty"` NAT2 bool `yaml:"nat2,omitempty"` L2Circuit bool `yaml:"l2circuit,omitempty"` + L2Vpn bool `yaml:"l2vpn,omitempty"` LACP bool `yaml:"lacp,omitempty"` LDP bool `yaml:"ldp,omitempty"` Routes bool `yaml:"routes,omitempty"` @@ -162,6 +163,7 @@ func setDefaultValues(c *Config) { f.Accounting = false f.FPC = false f.L2Circuit = false + f.L2Vpn = false f.RPKI = false f.RPM = false f.Satellite = false diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 63ca75cd..85046116 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -38,6 +38,7 @@ func TestShouldParse(t *testing.T) { assertFeature("InterfaceQueue", c.Features.InterfaceQueue, true, t) assertFeature("Interfaces", c.Features.Interfaces, false, t) assertFeature("L2Circuit", c.Features.L2Circuit, true, t) + assertFeature("L2Vpn", c.Features.L2Vpn, true, t) assertFeature("Storage", c.Features.Storage, false, t) assertFeature("FPC", c.Features.FPC, true, t) assertFeature("Power", c.Features.Power, false, t) @@ -63,7 +64,7 @@ func TestShouldUseDefaults(t *testing.T) { assertFeature("Firewall", c.Features.Firewall, true, t) assertFeature("InterfaceDiagnostic", c.Features.InterfaceDiagnostic, true, t) assertFeature("Interfaces", c.Features.Interfaces, true, t) - assertFeature("L2Circuit", c.Features.L2Circuit, false, t) + assertFeature("L2VPN", c.Features.L2Vpn, false, t) assertFeature("Storage", c.Features.Storage, false, t) assertFeature("FPC", c.Features.FPC, false, t) assertFeature("InterfaceQueue", c.Features.InterfaceQueue, true, t) @@ -104,6 +105,7 @@ func TestShouldParseDevices(t *testing.T) { assertFeature("ISIS", f.ISIS, true, t) assertFeature("NAT", f.NAT, true, t) assertFeature("L2Circuit", f.L2Circuit, true, t) + assertFeature("L2Vpn", f.L2Vpn, true, t) assertFeature("LDP", f.LDP, true, t) assertFeature("Routes", f.Routes, true, t) assertFeature("RoutingEngine", f.RoutingEngine, true, t) @@ -145,6 +147,7 @@ func TestShouldParseDevicesWithPattern(t *testing.T) { assertFeature("ISIS", f.ISIS, false, t) assertFeature("NAT", f.NAT, false, t) assertFeature("L2Circuit", f.L2Circuit, false, t) + assertFeature("L2Vpn", f.L2Vpn, false, t) assertFeature("LDP", f.LDP, false, t) assertFeature("Routes", f.Routes, false, t) assertFeature("RoutingEngine", f.RoutingEngine, false, t) diff --git a/internal/config/tests/config1.yml b/internal/config/tests/config1.yml index ec40c98a..42f5af48 100644 --- a/internal/config/tests/config1.yml +++ b/internal/config/tests/config1.yml @@ -14,6 +14,7 @@ features: interface_queue: true interfaces: false l2circuit: true + l2vpn: true storage: false fpc: true firewall: false diff --git a/internal/config/tests/config3.yml b/internal/config/tests/config3.yml index 47b3dd7c..ea05e222 100644 --- a/internal/config/tests/config3.yml +++ b/internal/config/tests/config3.yml @@ -17,6 +17,7 @@ devices: interface_queue: true interfaces: true l2circuit: true + l2vpn: true storage: true fpc: true firewall: true diff --git a/internal/config/tests/config4.yml b/internal/config/tests/config4.yml index 0b2275ee..2783a32f 100644 --- a/internal/config/tests/config4.yml +++ b/internal/config/tests/config4.yml @@ -17,6 +17,7 @@ devices: interface_queue: true interfaces: true l2circuit: false + l2vpn: false storage: false fpc: false firewall: false @@ -25,4 +26,3 @@ devices: ipsec: false rpki: false power: true - diff --git a/pkg/features/l2vpn/collector.go b/pkg/features/l2vpn/collector.go new file mode 100644 index 00000000..eec7cb79 --- /dev/null +++ b/pkg/features/l2vpn/collector.go @@ -0,0 +1,147 @@ +package l2vpn + +import ( + "strconv" + "strings" + "time" + + "github.com/czerwonk/junos_exporter/pkg/collector" + "github.com/prometheus/client_golang/prometheus" +) + +var ( + l2vpnConnectionStateDesc *prometheus.Desc + l2vpnConnectionsDesc *prometheus.Desc + l2vpnMap = map[string]int{ + "EI": 0, + "EM": 1, + "VC-Dn": 2, + "CM": 3, + "CN": 4, + "OR": 5, + "OL": 6, + "LD": 7, + "RD": 8, + "LN": 9, + "RN": 10, + "XX": 11, + "MM": 12, + "BK": 13, + "PF": 14, + "RS": 15, + "LB": 16, + "VM": 17, + "NC": 18, + "WE": 19, + "NP": 20, + "->": 21, + "<-": 22, + "Up": 23, + "Dn": 24, + "CF": 25, + "SC": 26, + "LM": 27, + "RM": 28, + "IL": 29, + "MI": 30, + "ST": 31, + "PB": 32, + "SN": 33, + "RB": 34, + "HS": 35, + } +) + +func init() { + l2vpnPrefix := "junos_l2vpn_" + + lcount := []string{"target", "routing_instance"} + lstate := []string{"target", "routing_instance", "connection_id", "remote_pe", "last_change", "up_transitions", "local_interface_name"} + l2StateDescription := "A l2vpn can have one of the following state-mappings EI: 0, EM: 1, VC-Dn: 2, CM: 3, CN: 4, OR: 5, OL: 6, LD: 7, RD: 8, LN: 9, RN: 10, XX: 11, MM: 12, BK: 13, PF: 14, RS: 15, LB: 16, VM: 17, NC: 18, WE: 19, NP: 20, ->: 21, <-: 22, Up: 23, Dn: 24, CF: 25, SC: 26, LM: 27, RM: 28, IL: 29, MI: 30, ST: 31, PB: 32, SN: 33, RB: 34, HS: 35" + l2vpnConnectionsDesc = prometheus.NewDesc(l2vpnPrefix+"connection_count", "Number of l2vpn connections", lcount, nil) + l2vpnConnectionStateDesc = prometheus.NewDesc(l2vpnPrefix+"connection_status", l2StateDescription, lstate, nil) +} + +// Collector collects l2vpn metrics +type l2vpnCollector struct { +} + +// NewCollector creates a new collector +func NewCollector() collector.RPCCollector { + return &l2vpnCollector{} +} + +// Name returns the name of the collector +func (*l2vpnCollector) Name() string { + return "L2 Circuit" +} + +// Describe describes the metrics +func (*l2vpnCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- l2vpnConnectionStateDesc + ch <- l2vpnConnectionsDesc +} + +// Collect collects metrics from JunOS +func (c *l2vpnCollector) Collect(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { + return c.collectl2vpnMetrics(client, ch, labelValues) +} + +func (c *l2vpnCollector) collectl2vpnMetrics(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error { + var x = l2vpnRpc{} + err := client.RunCommandAndParse("show l2vpn connections", &x) + if err != nil { + return err + } + + instances := x.Information.RoutingInstances + + for i := 0; i < len(instances); i++ { + connCount := 0 + for s := 0; s < len(instances[i].ReferenceSite); s++ { + connCount += +len(instances[i].ReferenceSite[s].Connections) + } + l := append(labelValues, instances[i].RoutingInstanceName) + ch <- prometheus.MustNewConstMetric(l2vpnConnectionsDesc, prometheus.GaugeValue, float64(connCount), l...) + + } + + for _, a := range instances { + for _, site := range a.ReferenceSite { + // l = append(l, site.ID) + for _, conn := range site.Connections { + l := append(labelValues, a.RoutingInstanceName) + // l = append(l, site.ID) + c.collectForConnection(ch, conn, l) + } + } + } + + return nil +} + +func (c *l2vpnCollector) collectForConnection(ch chan<- prometheus.Metric, + conn l2vpnConnection, labelValues []string) { + id := conn.ID + remotePe := conn.RemotePe + lastChange := string_to_date(conn.LastChange) + upTransitions := conn.UpTransitions + localInterface := "" + if len(conn.LocalInterface) == 1 { + localInterface = conn.LocalInterface[0].Name + } + + l := append(labelValues, id, remotePe, lastChange, upTransitions, localInterface) + state := l2vpnMap[conn.StatusString] + + ch <- prometheus.MustNewConstMetric(l2vpnConnectionStateDesc, prometheus.GaugeValue, float64(state), l...) +} + +func string_to_date(date string) string { + layout := "Jan 2 15:04:05 2006" + t, err := time.Parse(layout, strings.TrimRight(date, " \n")) + if err != nil { + return "" + } + return strconv.FormatInt(t.Unix(), 10) +} diff --git a/pkg/features/l2vpn/rpc.go b/pkg/features/l2vpn/rpc.go new file mode 100644 index 00000000..621a1457 --- /dev/null +++ b/pkg/features/l2vpn/rpc.go @@ -0,0 +1,33 @@ +package l2vpn + +type l2vpnRpc struct { + Information l2vpnInformation `xml:"l2vpn-connection-information"` +} + +type l2vpnInformation struct { + RoutingInstances []l2vpnRoutingInstance `xml:"instance"` +} + +type l2vpnRoutingInstance struct { + RoutingInstanceName string `xml:"instance-name"` + ReferenceSite []l2vpnReferenceSite `xml:"reference-site"` +} + +type l2vpnReferenceSite struct { + ID string `xml:"local-site-id"` + Connections []l2vpnConnection `xml:"connection"` +} + +type l2vpnConnection struct { + ID string `xml:"connection-id"` + Type string `xml:"connection-type"` + StatusString string `xml:"connection-status"` + RemotePe string `xml:"remote-pe"` + LastChange string `xml:"last-change"` + UpTransitions string `xml:"up-transitions"` + LocalInterface []l2vpnInterface `xml:"local-interface"` +} + +type l2vpnInterface struct { + Name string `xml:"interface-name"` +}