Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added arp metric #268

Merged
merged 16 commits into from
Dec 12, 2024
2 changes: 2 additions & 0 deletions collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/czerwonk/junos_exporter/pkg/connector"
"github.com/czerwonk/junos_exporter/pkg/features/accounting"
"github.com/czerwonk/junos_exporter/pkg/features/alarm"
"github.com/czerwonk/junos_exporter/pkg/features/arp"
"github.com/czerwonk/junos_exporter/pkg/features/bfd"
"github.com/czerwonk/junos_exporter/pkg/features/bgp"
"github.com/czerwonk/junos_exporter/pkg/features/environment"
Expand Down Expand Up @@ -120,6 +121,7 @@ func (c *collectors) initCollectorsForDevices(device *connector.Device, descRe *
c.addCollectorIfEnabledForDevice(device, "mpls_lsp", f.MPLSLSP, mplslsp.NewCollector)
c.addCollectorIfEnabledForDevice(device, "subscriber", f.Subscriber, subscriber.NewCollector)
c.addCollectorIfEnabledForDevice(device, "macsec", f.MACSec, macsec.NewCollector)
c.addCollectorIfEnabledForDevice(device, "arp", f.ARP, arp.NewCollector)
c.addCollectorIfEnabledForDevice(device, "poe", f.Poe, poe.NewCollector)
}

Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ type FeatureConfig struct {
License bool `yaml:"license,omitempty"`
Subscriber bool `yaml:"subscriber,omitempty"`
MACSec bool `yaml:"macsec,omitempty"`
ARP bool `yaml:"arp,omitempty"`
Poe bool `yaml:"poe,omitempty"`
}

Expand Down
8 changes: 6 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import (
"syscall"
"time"

"github.com/czerwonk/junos_exporter/pkg/connector"
"go.opentelemetry.io/otel/codes"

"github.com/czerwonk/junos_exporter/internal/config"
"github.com/czerwonk/junos_exporter/pkg/connector"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"

"github.com/czerwonk/junos_exporter/internal/config"
)

const version string = "0.12.6"
Expand Down Expand Up @@ -85,6 +87,7 @@ var (
tracingCollectorEndpoint = flag.String("tracing.collector.grpc-endpoint", "", "Sets the tracing provider (stdout or collector)")
subscriberEnabled = flag.Bool("subscriber.enabled", false, "Scrape subscribers detail")
macsecEnabled = flag.Bool("macsec.enabled", true, "Scrape MACSec metrics")
arpEnabled = flag.Bool("arps.enabled", true, "Scrape ARP metrics")
poeEnabled = flag.Bool("poe.enabled", true, "Scrape PoE metrics")
cfg *config.Config
devices []*connector.Device
Expand Down Expand Up @@ -252,6 +255,7 @@ func loadConfigFromFlags() *config.Config {
f.License = *licenseEnabled
f.Subscriber = *subscriberEnabled
f.MACSec = *macsecEnabled
f.ARP = *arpEnabled
f.Poe = *poeEnabled
return c
}
Expand Down
53 changes: 53 additions & 0 deletions pkg/features/arp/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package arp

import (
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"

"github.com/czerwonk/junos_exporter/pkg/collector"
)

const prefix string = "junos_arp_"

var (
arpEntriesCountDesc *prometheus.Desc
)

func init() {
l := []string{"target", "interface"}
arpEntriesCountDesc = prometheus.NewDesc(prefix+"entries", "Amount of ARP entries on an interface", l, nil)
}

type arpCollector struct{}

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

func (c *arpCollector) Name() string {
return "arp"
}

func (c *arpCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- arpEntriesCountDesc
}

func (c *arpCollector) Collect(client collector.Client, ch chan<- prometheus.Metric, labelValues []string) error {
var res results
err := client.RunCommandAndParse("show arp no-resolve", &res)
if err != nil {
return errors.Wrap(err, "failed to run command 'show arp no-resolve'")
}

interfaces := make(map[string]float64)
for _, a := range res.ArpTableInformation.ArpTableEntry {
interfaces[a.InterfaceName] += 1
}

for key, value := range interfaces {
labels := append(labelValues, key)
ch <- prometheus.MustNewConstMetric(arpEntriesCountDesc, prometheus.GaugeValue, value, labels...)
}

return nil
}
15 changes: 15 additions & 0 deletions pkg/features/arp/rpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package arp

type results struct {
Text string `xml:",chardata"`
ArpTableInformation struct {
Text string `xml:",chardata"`
ArpTableEntry []struct {
InterfaceName string `xml:"interface-name"`
ArpTableEntryFlags struct {
Text string `xml:",chardata"`
} `xml:"arp-table-entry-flags"`
} `xml:"arp-table-entry"`
ArpEntryCount string `xml:"arp-entry-count"`
} `xml:"arp-table-information"`
}
74 changes: 74 additions & 0 deletions pkg/features/arp/rpc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package arp

import (
"encoding/xml"
"testing"

"github.com/stretchr/testify/assert"
)

func TestARP(t *testing.T) {
resultsData := `
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/23.2R2-S1.3/junos">
<arp-table-information xmlns="http://xml.juniper.net/junos/23.2R0/junos-arp" junos:style="no-resolve">
<arp-table-entry>
<ip-address>55.55.55.55</ip-address>
<interface-name>xe-0/0/5:0.0</interface-name>
</arp-table-entry>
<arp-table-entry>
<ip-address>55.55.55.55</ip-address>
<interface-name>bme1.0</interface-name>
</arp-table-entry>
<arp-table-entry>
<ip-address>55.55.55.55</ip-address>
<interface-name>bme0.0</interface-name>
</arp-table-entry>
<arp-table-entry>
<ip-address>55.55.55.55</ip-address>
<interface-name>bme2.0</interface-name>
</arp-table-entry>
<arp-table-entry>
<ip-address>55.55.55.55</ip-address>
<interface-name>xe-0/0/3:0.0</interface-name>
</arp-table-entry>
<arp-table-entry>
<ip-address>55.55.55.55</ip-address>
<interface-name>xe-0/0/3:0.0</interface-name>
</arp-table-entry>
<arp-table-entry>
<ip-address>55.55.55.55</ip-address>
<interface-name>fxp0.0</interface-name>
</arp-table-entry>
<arp-table-entry>
<ip-address>55.55.55.55</ip-address>
<interface-name>em1.32768</interface-name>
</arp-table-entry>
<arp-entry-count>7</arp-entry-count>
</arp-table-information>
</rpc-reply>
`
var results results
// Parse the XML data for ARP
err := xml.Unmarshal([]byte(resultsData), &results)
assert.NoError(t, err)

assert.Len(t, results.ArpTableInformation.ArpTableEntry, 8)

expected := map[string]int64{
"xe-0/0/5:0.0": 1,
"bme1.0": 1,
"bme0.0": 1,
"bme2.0": 1,
"xe-0/0/3:0.0": 2,
"fxp0.0": 1,
"em1.32768": 1,
}
inTest := make(map[string]float64)
for _, a := range results.ArpTableInformation.ArpTableEntry {
inTest[a.InterfaceName] += 1
}
assert.Equal(t, len(expected), len(inTest))
for key, _ := range inTest {
assert.Equal(t, int64(expected[key]), int64(inTest[key]))
}
}
Loading