Skip to content

Commit

Permalink
Merge
Browse files Browse the repository at this point in the history
  • Loading branch information
jgastinger committed Aug 6, 2024
2 parents 266cc56 + a524638 commit 57d687f
Show file tree
Hide file tree
Showing 21 changed files with 746 additions and 782 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 9 additions & 10 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 @@ -40,34 +42,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.DynamicLabels
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) *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)
c.initCollectorsForDevices(d, deviceInterfaceRegex(cfg, d.Host))
}

return c
}

func (c *collectors) initCollectorsForDevices(device *connector.Device) {
func (c *collectors) initCollectorsForDevices(device *connector.Device, descRe *regexp.Regexp) {
f := c.cfg.FeaturesForDevice(device.Host)

c.devices[device.Host] = make([]collector.RPCCollector, 0)
Expand All @@ -79,19 +78,19 @@ 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, 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)
Expand Down
5 changes: 2 additions & 3 deletions collectors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -41,7 +40,7 @@ func TestCollectorsRegistered(t *testing.T) {

cols := collectorsForDevices([]*connector.Device{{
Host: "::1",
}}, c, "", interfacelabels.NewDynamicLabels())
}}, c, "")

assert.Equal(t, 21, len(cols.collectors), "collector count")
}
Expand Down Expand Up @@ -90,7 +89,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, "")

assert.Equal(t, 21, 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
49 changes: 40 additions & 9 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,37 @@ 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(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)
}

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
}

// DeviceConfig is the config representation of 1 device
Expand All @@ -27,7 +53,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 @@ -83,7 +110,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
Expand All @@ -95,6 +122,11 @@ func Load(reader io.Reader) (*Config, error) {
return nil, err
}

err = c.load(dynamicIfaceLabels)
if err != nil {
return nil, err
}

for _, device := range c.Devices {
if device.IsHostPattern {
hostPattern, err := regexp.Compile(device.Host)
Expand All @@ -111,7 +143,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
12 changes: 6 additions & 6 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -50,7 +50,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)
}
Expand Down Expand Up @@ -79,7 +79,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)
}
Expand Down Expand Up @@ -126,7 +126,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)
}
Expand Down Expand Up @@ -168,7 +168,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")
}
Expand All @@ -188,7 +188,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)
}
Expand Down
45 changes: 11 additions & 34 deletions junos_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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_"
Expand All @@ -40,8 +42,6 @@ type junosCollector struct {
}

func newJunosCollector(ctx context.Context, devices []*connector.Device, logicalSystem string) *junosCollector {
l := interfacelabels.NewDynamicLabels()

clients := make(map[*connector.Device]*rpc.Client)

for _, d := range devices {
Expand All @@ -52,51 +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.IfDescReg) > 0 {
regex, err := regexp.Compile(dc.IfDescReg)
if err == nil {
return regex
}

log.Errorf("device specific dynamic label regex %s invalid: %v", dc.IfDescReg, err)
if dc != nil {
return dc.IfDescReg
}

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)
if cfg.IfDescReg != nil {
return cfg.IfDescReg
}

return interfacelabels.DefaultInterfaceDescRegex()
return dynamiclabels.DefaultInterfaceDescRegex()
}

func clientForDevice(device *connector.Device, connManager *connector.SSHConnectionManager) (*rpc.Client, error) {
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down Expand Up @@ -206,14 +206,14 @@ 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 {
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 57d687f

Please sign in to comment.