Skip to content

Commit

Permalink
feat: add l2vpn metrics (czerwonk#248)
Browse files Browse the repository at this point in the history
* feat: add l2vpn metrics

* fix: l2vpn tests

---------

Co-authored-by: Johannes Müller Aguilar <JMuellerAguilar@anexia-it.com>
  • Loading branch information
Gaste8 and Johannes Müller Aguilar authored Jul 30, 2024
1 parent fbaa9b1 commit a524638
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 4 deletions.
2 changes: 2 additions & 0 deletions collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions collectors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func TestCollectorsRegistered(t *testing.T) {
ISIS: true,
NAT: true,
L2Circuit: true,
L2Vpn: true,
LDP: true,
Routes: true,
RoutingEngine: true,
Expand All @@ -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) {
Expand All @@ -54,6 +55,7 @@ func TestCollectorsForDevices(t *testing.T) {
ISIS: true,
NAT: true,
L2Circuit: true,
L2Vpn: true,
LDP: true,
Routes: true,
RoutingEngine: true,
Expand Down Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions helm/junosexporter/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ extraArgs: []
# ospf: false
# isis: false
# l2circuit: false
# l2vpn: false
# environment: true
# routes: true
# routing_engine: true
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions internal/config/tests/config1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ features:
interface_queue: true
interfaces: false
l2circuit: true
l2vpn: true
storage: false
fpc: true
firewall: false
1 change: 1 addition & 0 deletions internal/config/tests/config3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ devices:
interface_queue: true
interfaces: true
l2circuit: true
l2vpn: true
storage: true
fpc: true
firewall: true
Expand Down
2 changes: 1 addition & 1 deletion internal/config/tests/config4.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ devices:
interface_queue: true
interfaces: true
l2circuit: false
l2vpn: false
storage: false
fpc: false
firewall: false
Expand All @@ -25,4 +26,3 @@ devices:
ipsec: false
rpki: false
power: true

147 changes: 147 additions & 0 deletions pkg/features/l2vpn/collector.go
Original file line number Diff line number Diff line change
@@ -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)
}
33 changes: 33 additions & 0 deletions pkg/features/l2vpn/rpc.go
Original file line number Diff line number Diff line change
@@ -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"`
}

0 comments on commit a524638

Please sign in to comment.