Skip to content

Commit

Permalink
BGP: Implement dynamic labels from descriptions (#249)
Browse files Browse the repository at this point in the history
Co-authored-by: Oliver Geiselhardt-Herms <ogh@deepl.com>
  • Loading branch information
taktv6 and Oliver Geiselhardt-Herms authored Jul 26, 2024
1 parent c65bae5 commit fc11b9e
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 113 deletions.
12 changes: 7 additions & 5 deletions collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions collectors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down Expand Up @@ -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")

Expand Down
10 changes: 8 additions & 2 deletions devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package main

import (
"fmt"
"os"
"regexp"
"strings"
Expand Down Expand Up @@ -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{
Expand Down
36 changes: 28 additions & 8 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package config

import (
"fmt"
"io"
"regexp"

Expand All @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
17 changes: 4 additions & 13 deletions junos_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit fc11b9e

Please sign in to comment.