Skip to content

Commit

Permalink
Merge pull request #6 from kaminek/master
Browse files Browse the repository at this point in the history
Add BGP L2VPN AFI collector
  • Loading branch information
tynany authored Nov 27, 2019
2 parents 75bbd77 + e1ef442 commit 6e41680
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 70 deletions.
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

# Created by https://www.gitignore.io/api/go
# Edit at https://www.gitignore.io/?templates=go

### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

### Go Patch ###
/vendor/
/Godeps/

frr_exporter

# End of https://www.gitignore.io/api/go
35 changes: 20 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,26 @@ To view available flags:
usage: frr_exporter [<flags>]
Flags:
-h, --help Show context-sensitive help (also try --help-long and --help-man).
-h, --help Show context-sensitive help (also try --help-long and --help-man).
--collector.bgp.peer-types
Enable scraping of BGP peer types from peer descriptions (default: disabled).
Enable scraping of BGP peer types from peer descriptions (default:
disabled).
--web.listen-address=":9342"
Address on which to expose metrics and web interface.
Address on which to expose metrics and web interface.
--web.telemetry-path="/metrics"
Path under which to expose metrics.
Path under which to expose metrics.
--frr.vtysh.path="/usr/bin/vtysh"
Path of vtysh.
--collector.bgp Collect BGP Metrics (default: enabled).
--collector.ospf Collect OSPF Metrics (default: enabled).
--collector.bgp6 Collect BGP IPv6 Metrics (default: enabled).
--log.level="info" Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]
Path of vtysh.
--collector.bgp Collect BGP Metrics (default: enabled).
--collector.ospf Collect OSPF Metrics (default: enabled).
--collector.bgp6 Collect BGP IPv6 Metrics (default: enabled).
--collector.bgpl2vpn Collect BGP L2VPN Metrics (default: disabled).
--log.level="info" Only log messages with the given severity or above. Valid levels: [debug,
info, warn, error, fatal]
--log.format="logger:stderr"
Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or
"logger:stdout?json=true"
--version Show application version.
Set the log target and format. Example: "logger:syslog?appname=bob&local=7"
or "logger:stdout?json=true"
--version Show application version.
```

Promethues configuraiton:
Expand All @@ -52,13 +55,15 @@ scrape_configs:
```

## Collectors
To disable a default collector, use the `--no-collector.$name` flag.
To disable a default collector, use the `--no-collector.$name` flag, or
`--collector.$name` to enable it.

### Enabled by Default
Name | Description
--- | ---
BGP | Per VRF and address family (currently support unicast only) BGP metrics:<br> - RIB entries<br> - RIB memory usage<br> - Configured peer count<br> - Peer memory usage<br> - Configure peer group count<br> - Peer group memory usage<br> - Peer messages in<br> - Peer messages out<br> - Peer active prfixes<br> - Peer state (established/down)<br> - Peer uptime
BGP IPv6 | Per VRF and address family (currently support unicast only) BGP IPv6 metrics:<br> - RIB entries<br> - RIB memory usage<br> - Configured peer count<br> - Peer memory usage<br> - Configure peer group count<br> - Peer group memory usage<br> - Peer messages in<br> - Peer messages out<br> - Peer active prfixes<br> - Peer state (established/down)<br> - Peer uptime
BGP L2VPN | Per VRF and address family (currently support EVPN only) BGP L2VPN EVPN metrics:<br> - RIB entries<br> - RIB memory usage<br> - Configured peer count<br> - Peer memory usage<br> - Configure peer group count<br> - Peer group memory usage<br> - Peer messages in<br> - Peer messages out<br> - Peer active prfixes<br> - Peer state (established/down)<br> - Peer uptime
OSPFv4 | Per VRF OSPF metrics:<br> - Neighbors<br> - Neighbor adjacencies

### BGP: frr_bgp_peer_types_up
Expand All @@ -79,6 +84,6 @@ go build
## TODO
- Collector and main tests
- OSPF6
- isis
- Additional BGP address families
- ISIS
- Additional BGP SAFI
- Feel free to submit a new feature request
168 changes: 113 additions & 55 deletions collector/bgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,35 @@ import (
)

var (
bgpSubsystem = "bgp"
bgpSubsystem = "bgp"
bgpPeerMetricPrefix = "bgp_peer"

bgpLabels = []string{"vrf", "address_family", "local_as"}
bgpLabels = []string{"vrf", "afi", "safi", "local_as"}
bgpPeerLabels = append(bgpLabels, "peer", "peer_as")
bgpPeerTypeLabels = []string{"type", "address_family"}
bgpPeerTypeLabels = []string{"type", "afi", "safi"}

bgpDesc = map[string]*prometheus.Desc{
"ribEntries": colPromDesc(bgpSubsystem, "rib_entries", "Number of routes in the RIB.", bgpLabels),
"ribMemUsgage": colPromDesc(bgpSubsystem, "rib_memory_usage_bytes", "Memory consumbed by the RIB.", bgpLabels),
"peerTotal": colPromDesc(bgpSubsystem, "peers", "Number peers configured.", bgpLabels),
"peerMemUsage": colPromDesc(bgpSubsystem, "peers_memory_usage_bytes", "Memory consumed by peers.", bgpLabels),
"peerGrps": colPromDesc(bgpSubsystem, "peer_groups", "Number of peer groups configured.", bgpLabels),
"peerGrpsMemUsage": colPromDesc(bgpSubsystem, "peer_groups_memory_bytes", "Memory consumed by peer groups.", bgpLabels),

"peerMsgIn": colPromDesc(bgpSubsystem, "message_input_total", "Number of received messages.", bgpPeerLabels),
"peerMsgOut": colPromDesc(bgpSubsystem, "message_output_total", "Number of sent messages.", bgpPeerLabels),
"peerPrfAct": colPromDesc(bgpSubsystem, "prefixes_active", "Number of active prefixes.", bgpPeerLabels),
"peerUp": colPromDesc(bgpSubsystem, "peer_up", "State of the peer (1 = Established, 0 = Down).", bgpPeerLabels),
"peerUptimeSec": colPromDesc(bgpSubsystem, "peer_uptime_seconds", "How long has the peer been up.", bgpPeerLabels),
"peerTypesUp": colPromDesc(bgpSubsystem, "peer_types_up", "Total Number of Peer Types that are Up.", bgpPeerTypeLabels),
"ribCount": colPromDesc(bgpSubsystem, "rib_count_total", "Number of routes in the RIB.", bgpLabels),
"ribMemory": colPromDesc(bgpSubsystem, "rib_memory_bytes", "Memory consumbed by the RIB.", bgpLabels),
"peerCount": colPromDesc(bgpSubsystem, "peers_count_total", "Number peers configured.", bgpLabels),
"peerMemory": colPromDesc(bgpSubsystem, "peers_memory_bytes", "Memory consumed by peers.", bgpLabels),
"peerGroupCount": colPromDesc(bgpSubsystem, "peer_groups_count_total", "Number of peer groups configured.", bgpLabels),
"peerGroupMemory": colPromDesc(bgpSubsystem, "peer_groups_memory_bytes", "Memory consumed by peer groups.", bgpLabels),

"msgRcvd": colPromDesc(bgpPeerMetricPrefix, "message_received_total", "Number of received messages.", bgpPeerLabels),
"msgSent": colPromDesc(bgpPeerMetricPrefix, "message_sent_total", "Number of sent messages.", bgpPeerLabels),
"prefixReceivedCount": colPromDesc(bgpPeerMetricPrefix, "prefixes_received_count_total", "Number active prefixes received.", bgpPeerLabels),
"state": colPromDesc(bgpPeerMetricPrefix, "state", "State of the peer (1 = Established, 0 = Down).", bgpPeerLabels),
"UptimeSec": colPromDesc(bgpPeerMetricPrefix, "uptime_seconds", "How long has the peer been up.", bgpPeerLabels),
"peerTypesUp": colPromDesc(bgpPeerMetricPrefix, "types_up", "Total Number of Peer Types that are Up.", bgpPeerTypeLabels),
}

bgpErrors = []error{}
totalBGPErrors = 0.0
bgp6Errors = []error{}
totalBGP6Errors = 0.0
bgpErrors = []error{}
totalBGPErrors = 0.0
bgp6Errors = []error{}
totalBGP6Errors = 0.0
bgpL2VPNErrors = []error{}
totalBGPL2VPNErrors = 0.0

bgpPeerTypes = kingpin.Flag("collector.bgp.peer-types", "Enable scraping of BGP peer types from peer descriptions (default: disabled).").Default("False").Bool()
)
Expand Down Expand Up @@ -108,7 +111,7 @@ func (*BGP6Collector) Help() string {

// EnabledByDefault describes whether this collector is enabled by default. Used to populate flag default.
func (*BGP6Collector) EnabledByDefault() bool {
return true
return false
}

// Describe implemented as per the prometheus.Collector interface.
Expand All @@ -133,48 +136,103 @@ func (*BGP6Collector) CollectTotalErrors() float64 {
return totalBGP6Errors
}

func collectBGP(ch chan<- prometheus.Metric, addressFamily string) {
// BGPL2VPNCollector collects BGP metrics, implemented as per prometheus.Collector interface.
type BGPL2VPNCollector struct{}

// NewBGPL2VPNCollector returns a BGPL2VPNCollector struct.
func NewBGPL2VPNCollector() *BGPL2VPNCollector {
return &BGPL2VPNCollector{}
}

// Name of the collector. Used to populate flag name.
func (*BGPL2VPNCollector) Name() string {
return bgpSubsystem + "l2vpn"
}

// Help describes the metrics this collector scrapes. Used to populate flag help.
func (*BGPL2VPNCollector) Help() string {
return "Collect BGP L2VPN Metrics"
}

// EnabledByDefault describes whether this collector is enabled by default. Used to populate flag default.
func (*BGPL2VPNCollector) EnabledByDefault() bool {
return false
}

// Describe implemented as per the prometheus.Collector interface.
func (*BGPL2VPNCollector) Describe(ch chan<- *prometheus.Desc) {
for _, desc := range bgpDesc {
ch <- desc
}
}

// Collect implemented as per the prometheus.Collector interface.
func (c *BGPL2VPNCollector) Collect(ch chan<- prometheus.Metric) {
collectBGP(ch, "l2vpn")
}

// CollectErrors returns what errors have been gathered.
func (*BGPL2VPNCollector) CollectErrors() []error {
return bgpL2VPNErrors
}

// CollectTotalErrors returns total errors.
func (*BGPL2VPNCollector) CollectTotalErrors() float64 {
return totalBGPL2VPNErrors
}

func collectBGP(ch chan<- prometheus.Metric, AFI string) {
SAFI := ""
errors := []error{}
totalErrors := 0.0

afMod := "unicast"
if (AFI == "ipv4") || (AFI == "ipv6") {
SAFI = "unicast"

jsonBGPSum, err := getBGPSummary(addressFamily, afMod)
} else if AFI == "l2vpn" {
SAFI = "evpn"
}

jsonBGPSum, err := getBGPSummary(AFI, SAFI)
if err != nil {
totalErrors++
errors = append(errors, fmt.Errorf("cannot get bgp %s %s summary: %s", addressFamily, afMod, err))
errors = append(errors, fmt.Errorf("cannot get bgp %s %s summary: %s", AFI, SAFI, err))
} else {
if err := processBGPSummary(ch, jsonBGPSum, addressFamily+afMod); err != nil {
if err := processBGPSummary(ch, jsonBGPSum, AFI, SAFI); err != nil {
totalErrors++
errors = append(errors, fmt.Errorf("%s", err))
}
}

if totalErrors > 0 {
if addressFamily == "ipv4" {
totalBGPErrors = totalBGPErrors + totalErrors
} else if addressFamily == "ipv6" {
totalBGP6Errors = totalBGP6Errors + totalErrors
}
}

if addressFamily == "ipv4" {
if AFI == "ipv4" {
bgpErrors = errors
} else if addressFamily == "ipv6" {
if totalErrors > 0 {
totalBGPErrors += totalErrors
}
} else if AFI == "ipv6" {
bgp6Errors = errors
if totalErrors > 0 {
totalBGP6Errors += totalErrors
}
} else if AFI == "l2vpn" {
bgpL2VPNErrors = errors
if totalErrors > 0 {
totalBGPL2VPNErrors += totalErrors
}
}

}

func getBGPSummary(addressFamily string, addressFamilyModifier string) ([]byte, error) {
args := []string{"-c", fmt.Sprintf("show ip bgp vrf all %s %s summary json", addressFamily, addressFamilyModifier)}
func getBGPSummary(AFI string, SAFI string) ([]byte, error) {
args := []string{"-c", fmt.Sprintf("show bgp vrf all %s %s summary json", AFI, SAFI)}
output, err := exec.Command(vtyshPath, args...).Output()
if err != nil {
return nil, err
}
return output, nil
}

func processBGPSummary(ch chan<- prometheus.Metric, jsonBGPSum []byte, addressFamily string) error {
func processBGPSummary(ch chan<- prometheus.Metric, jsonBGPSum []byte, AFI string, SAFI string) error {
var jsonMap map[string]bgpProcess

if err := json.Unmarshal(jsonBGPSum, &jsonMap); err != nil {
Expand All @@ -193,27 +251,27 @@ func processBGPSummary(ch chan<- prometheus.Metric, jsonBGPSum []byte, addressFa
peerTypes := make(map[string]float64)

for vrfName, vrfData := range jsonMap {
// The labels are "vrf", "address_family", "local_as"
// The labels are "vrf", "afi", "safi", "local_as"
localAs := strconv.FormatInt(vrfData.AS, 10)
bgpProcLabels := []string{strings.ToLower(vrfName), strings.ToLower(addressFamily), localAs}
bgpProcLabels := []string{strings.ToLower(vrfName), strings.ToLower(AFI), strings.ToLower(SAFI), localAs}
// No point collecting metrics if no peers configured.
if vrfData.PeerCount != 0 {

newGauge(ch, bgpDesc["ribEntries"], vrfData.RIBCount, bgpProcLabels...)
newGauge(ch, bgpDesc["ribMemUsgage"], vrfData.RIBMemory, bgpProcLabels...)
newGauge(ch, bgpDesc["peerTotal"], vrfData.PeerCount, bgpProcLabels...)
newGauge(ch, bgpDesc["peerMemUsage"], vrfData.PeerMemory, bgpProcLabels...)
newGauge(ch, bgpDesc["peerGrps"], vrfData.PeerGroupCount, bgpProcLabels...)
newGauge(ch, bgpDesc["peerGrpsMemUsage"], vrfData.PeerGroupMemory, bgpProcLabels...)
newGauge(ch, bgpDesc["ribCount"], vrfData.RIBCount, bgpProcLabels...)
newGauge(ch, bgpDesc["ribMemory"], vrfData.RIBMemory, bgpProcLabels...)
newGauge(ch, bgpDesc["peerCount"], vrfData.PeerCount, bgpProcLabels...)
newGauge(ch, bgpDesc["peerMemory"], vrfData.PeerMemory, bgpProcLabels...)
newGauge(ch, bgpDesc["peerGroupCount"], vrfData.PeerGroupCount, bgpProcLabels...)
newGauge(ch, bgpDesc["peerGroupMemory"], vrfData.PeerGroupMemory, bgpProcLabels...)

for peerIP, peerData := range vrfData.Peers {
// The labels are "vrf", "address_family", "local_as", "peer", "remote_as"
bgpPeerLabels := []string{strings.ToLower(vrfName), strings.ToLower(addressFamily), localAs, peerIP, strconv.FormatInt(peerData.RemoteAs, 10)}
// The labels are "vrf", "afi", "safi", "local_as", "peer", "remote_as"
bgpPeerLabels := []string{strings.ToLower(vrfName), strings.ToLower(AFI), strings.ToLower(SAFI), localAs, peerIP, strconv.FormatInt(peerData.RemoteAs, 10)}

newCounter(ch, bgpDesc["peerMsgIn"], peerData.MsgRcvd, bgpPeerLabels...)
newCounter(ch, bgpDesc["peerMsgOut"], peerData.MsgSent, bgpPeerLabels...)
newGauge(ch, bgpDesc["peerPrfAct"], peerData.PrefixReceivedCount, bgpPeerLabels...)
newGauge(ch, bgpDesc["peerUptimeSec"], peerData.PeerUptimeMsec*0.001, bgpPeerLabels...)
newCounter(ch, bgpDesc["msgRcvd"], peerData.MsgRcvd, bgpPeerLabels...)
newCounter(ch, bgpDesc["msgSent"], peerData.MsgSent, bgpPeerLabels...)
newGauge(ch, bgpDesc["prefixReceivedCount"], peerData.PrefixReceivedCount, bgpPeerLabels...)
newGauge(ch, bgpDesc["UptimeSec"], peerData.PeerUptimeMsec*0.001, bgpPeerLabels...)

peerState := 0.0
if strings.ToLower(peerData.State) == "established" {
Expand All @@ -236,13 +294,13 @@ func processBGPSummary(ch chan<- prometheus.Metric, jsonBGPSum []byte, addressFa
}
}
NoPeerType:
newGauge(ch, bgpDesc["peerUp"], peerState, bgpPeerLabels...)
newGauge(ch, bgpDesc["state"], peerState, bgpPeerLabels...)
}
}
}

for peerType, count := range peerTypes {
peerTypeLabels := []string{peerType, strings.ToLower(addressFamily)}
peerTypeLabels := []string{peerType, strings.ToLower(AFI), strings.ToLower(SAFI)}
newGauge(ch, bgpDesc["peerTypesUp"], count, peerTypeLabels...)
}
return nil
Expand Down
7 changes: 7 additions & 0 deletions frr_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ func initCollectors() {
Errors: bgp6,
CLIHelper: bgp6,
})
bgpl2vpn := collector.NewBGPL2VPNCollector()
collectors = append(collectors, &collector.Collector{
Name: bgpl2vpn.Name(),
PromCollector: bgpl2vpn,
Errors: bgpl2vpn,
CLIHelper: bgpl2vpn,
})
}

func handler(w http.ResponseWriter, r *http.Request) {
Expand Down

0 comments on commit 6e41680

Please sign in to comment.