Skip to content

Commit

Permalink
Merge pull request #76 from tynany/fix-bgp-vrf-desc
Browse files Browse the repository at this point in the history
fix: bgp neighbor description vrf support
  • Loading branch information
tynany authored Aug 19, 2022
2 parents e73ac61 + dacd6ed commit 999c914
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 23 deletions.
105 changes: 82 additions & 23 deletions collector/bgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ func processBGPSummary(ch chan<- prometheus.Metric, jsonBGPSum []byte, AFI strin
return err
}

var peerDescJSON map[string]map[string]string
var peerDescText map[string]string
var peerDescJSON map[string]map[string]map[string]string
var peerDescText map[string]map[string]string
var err error
if *bgpPeerTypes || *bgpPeerDescs {
peerDescJSON, peerDescText, err = getBGPPeerDesc(logger)
Expand Down Expand Up @@ -206,9 +206,9 @@ func processBGPSummary(ch chan<- prometheus.Metric, jsonBGPSum []byte, AFI strin
if *bgpPeerDescs {
d := ""
if *bgpPeerDescsText {
d = peerDescText[peerIP]
d = peerDescText[vrfName][peerIP]
} else {
d = peerDescJSON[peerIP]["desc"]
d = peerDescJSON[vrfName][peerIP]["desc"]
}
// The labels are "vrf", "afi", "safi", "local_as", "peer", "remote_as", "peer_desc"
peerLabels = append(peerLabels, d)
Expand Down Expand Up @@ -237,9 +237,9 @@ func processBGPSummary(ch chan<- prometheus.Metric, jsonBGPSum []byte, AFI strin

if *bgpPeerTypes {
for _, descKey := range *frrBGPDescKey {
if peerDescJSON[peerIP][descKey] != "" {
if _, exist := peerTypes[strings.TrimSpace(peerDescJSON[peerIP][descKey])]; !exist {
peerTypes[strings.TrimSpace(peerDescJSON[peerIP][descKey])] = 0
if peerDescJSON[vrfName][peerIP][descKey] != "" {
if _, exist := peerTypes[strings.TrimSpace(peerDescJSON[vrfName][peerIP][descKey])]; !exist {
peerTypes[strings.TrimSpace(peerDescJSON[vrfName][peerIP][descKey])] = 0
}
}
}
Expand All @@ -250,8 +250,8 @@ func processBGPSummary(ch chan<- prometheus.Metric, jsonBGPSum []byte, AFI strin
peerState = 1
if *bgpPeerTypes {
for _, descKey := range *frrBGPDescKey {
if peerDescJSON[peerIP][descKey] != "" {
peerTypes[strings.TrimSpace(peerDescJSON[peerIP][descKey])]++
if peerDescJSON[vrfName][peerIP][descKey] != "" {
peerTypes[strings.TrimSpace(peerDescJSON[vrfName][peerIP][descKey])]++
}
}
}
Expand Down Expand Up @@ -328,29 +328,88 @@ type bgpAdvertisedRoutes struct {
// - Map from JSON formatted BGP peer descriptions
// - Plain text description of peers
// - Error
func getBGPPeerDesc(logger log.Logger) (map[string]map[string]string, map[string]string, error) {
descJSON := make(map[string]map[string]string)
descText := make(map[string]string)
func getBGPPeerDesc(logger log.Logger) (map[string]map[string]map[string]string, map[string]map[string]string, error) {

output, err := executeBGPCommand("show bgp neighbors json")
output, err := executeBGPCommand("show bgp vrf all neighbors json")
if err != nil {
return nil, nil, err
}
return processBGPPeerDesc(logger, output)
}

var neighbors map[string]bgpBGPNeighbor
if err := json.Unmarshal(output, &neighbors); err != nil {
func processBGPPeerDesc(logger log.Logger, output []byte) (map[string]map[string]map[string]string, map[string]map[string]string, error) {

// Expected map format: map["vrf"]["peer IP"]["json desc field"]["json desc value"]
descJSON := make(map[string]map[string]map[string]string)

// Expected map format: map["vrf"]["peer IP"]["text desc"]
descText := make(map[string]map[string]string)

// Unfortunately, the 'show bgp vrf all neighbors json' output is poorly structured -- neighbors are
// fields on the same level of the vrfName and vrfId field. As such, loop through each key and apply
// logic to determine whether the key is a neighbor.
//
// Example:
// {
// "default":{
// "vrfId":-1,
// "vrfName":"default",
// "swp2":{
// "nbrDesc":"desc"
// },
// "10.189.0.178":{
// "nbrDesc":"desc"
// }
// },
// "vrf1":{
// "vrfId":-1,
// "vrfName":"vrf1",
// "swp1":{
// "nbrDesc":"desc"
// }
// }
// }
var jsonMap map[string]json.RawMessage
if err := json.Unmarshal(output, &jsonMap); err != nil {
return nil, nil, err
}
for neighbor, data := range neighbors {
if !*bgpPeerDescsText {
var peerDesc map[string]string
if err := json.Unmarshal([]byte(data.NbrDesc), &peerDesc); err != nil {
// Don't return an error as unmarshalling is best effort.
level.Error(logger).Log("msg", "cannot unmarshall bgp description", "description", data.NbrDesc, "neighbor", neighbor, "err", err)

for vrfName, vrfData := range jsonMap {
var vrfFields map[string]json.RawMessage

if err := json.Unmarshal(vrfData, &vrfFields); err != nil {
return nil, nil, err
}

for neighbor, neighborValues := range vrfFields {
switch neighbor {
case "vrfName", "vrfId":
// Do nothing as we do not need the value of these fields.
default:
// All other fields are neighbors.
var neighborData bgpBGPNeighbor
if err := json.Unmarshal(neighborValues, &neighborData); err != nil {
return nil, nil, err
}

if !*bgpPeerDescsText {

var peerDesc map[string]string
if err := json.Unmarshal([]byte(neighborData.NbrDesc), &peerDesc); err != nil {
// Don't return an error as unmarshalling is best effort.
level.Error(logger).Log("msg", "cannot unmarshall bgp description", "description", neighborData.NbrDesc, "neighbor", neighbor, "err", err)
}
if _, exists := descJSON[vrfName]; !exists {
descJSON[vrfName] = make(map[string]map[string]string)
}
descJSON[vrfName][neighbor] = peerDesc
}
if _, exists := descText[vrfName]; !exists {
descText[vrfName] = make(map[string]string)
}
descText[vrfName][neighbor] = neighborData.NbrDesc
}
descJSON[neighbor] = peerDesc
}
descText[neighbor] = data.NbrDesc
}
return descJSON, descText, nil
}
Expand Down
54 changes: 54 additions & 0 deletions collector/bgp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package collector

import (
"fmt"
"reflect"
"regexp"
"strings"
"testing"
Expand All @@ -11,6 +12,25 @@ import (
)

var (
bgpNeighborDesc = []byte(`{
"default":{
"vrfId":0,
"vrfName":"default",
"swp2":{
"nbrDesc":"{\"desc\":\"fw1\"}"
},
"10.1.1.10":{
"nbrDesc":"{\"desc\":\"rt1\"}"
}
},
"vrf1":{
"vrfId":-1,
"vrfName":"vrf1",
"10.2.0.1":{
"nbrDesc":"{\"desc\":\"remote\"}"
}
}
}`)
bgpSumV4Unicast = []byte(`{
"default":{
"routerId":"192.168.0.1",
Expand Down Expand Up @@ -406,3 +426,37 @@ func TestProcessBgpL2vpnEvpnSummary(t *testing.T) {
gotMetrics := prepareMetrics(ch, t)
compareMetrics(t, gotMetrics, expectedBgpL2vpnMetrics)
}

func TestProcessBGPPeerDesc(t *testing.T) {
expectedJSONOutput := make(map[string]map[string]map[string]string)
expectedJSONOutput["default"] = make(map[string]map[string]string)
expectedJSONOutput["default"]["10.1.1.10"] = make(map[string]string)
expectedJSONOutput["default"]["10.1.1.10"]["desc"] = "rt1"
expectedJSONOutput["default"]["swp2"] = make(map[string]string)
expectedJSONOutput["default"]["swp2"]["desc"] = "fw1"
expectedJSONOutput["vrf1"] = make(map[string]map[string]string)
expectedJSONOutput["vrf1"]["10.2.0.1"] = make(map[string]string)
expectedJSONOutput["vrf1"]["10.2.0.1"]["desc"] = "remote"

expectedTextOutput := make(map[string]map[string]string)
expectedTextOutput["default"] = make(map[string]string)
expectedTextOutput["default"]["10.1.1.10"] = "{\"desc\":\"rt1\"}"
expectedTextOutput["default"]["swp2"] = "{\"desc\":\"fw1\"}"
expectedTextOutput["vrf1"] = make(map[string]string)
expectedTextOutput["vrf1"]["10.2.0.1"] = "{\"desc\":\"remote\"}"

descJSON, descText, err := processBGPPeerDesc(nil, bgpNeighborDesc)
if err != nil {
t.Errorf("error calling processBGPPeerDesc: %s", err)
}

textEq := reflect.DeepEqual(descText, expectedTextOutput)
if !textEq {
t.Errorf("error comparing bgp neighbor description text output: %s does not match expected %s", descText, expectedTextOutput)
}

jsonEq := reflect.DeepEqual(descJSON, expectedJSONOutput)
if !jsonEq {
t.Errorf("error comparing bgp neighbor description JSON output: %s does not match expected %s", descJSON, expectedJSONOutput)
}
}

0 comments on commit 999c914

Please sign in to comment.