Layer 2 and Layer 3 examples

Number of ARP entries across all devices

let data = `*:/Smash/arp/status/_counts`
sum(data | map(merge(_value)) | where(dictHasKey(_value, "arpEntry")) | map(_value["arpEntry"]))
Number of ARP entries across all devices

Download the Dashboard JSON here

BGP States

BGP Session Status

let neighbors = `analytics:/Devices/*/versioned-data/routing/bgp/status/vrf/default/bgpPeerInfoStatusEntry/*`

# Dict to store the states and counts
let res = newDict()
# Loop over each device
for device, deviceSessions in neighbors{
    # Loop over each session on each device
    for ip, sessionData in deviceSessions{
        let data = merge(sessionData)
        # Have we used this status yet?
        let status = data["bgpState"]["Name"]
        if !dictHasKey(res, status) {
            # If not lets set it use count to zero
            res[status] = 0
        }
        # Add one to the total times this status is used
        res[status] = res[status] + 1
    }
}
res

BGP Session Details in the Default VRF for all Devices

let devices = merge(`analytics:/tags/labels/devices/pod_name/value/<_POD_NAME>/elements`)

if str(_POD_NAME) == "" {
    let bgpNeighbors =`analytics:/Devices/*/versioned-data/routing/bgp/status/vrf/default/bgpPeerInfoStatusEntry/*`
} else {
    let bgpNeighbors =`analytics:/Devices/*/versioned-data/routing/bgp/status/vrf/default/bgpPeerInfoStatusEntry/*` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")))
}

# This is the table
let res = newDict()
let id = 0
# Lets loop over every device
for device, deviceSessions in bgpNeighbors{
    # And each session on the devices
    for ip, sessionData in deviceSessions{
        let data = merge(sessionData)
        # Add one to the ID
        let id = id + 1
        res[id] = newDict()
        # This is where we add the various columns
        res[id]["0. Device"] = device
        res[id]["1. Status"] = data["bgpState"]["Name"]
        res[id]["2. Peering Address"] = data["bgpPeerLocalAddr"]
        res[id]["3. Neighbor Address"] = data["key"]
        res[id]["4. Neighbor AS"] = data["bgpPeerAs"]["value"]
    }
}
res

BGP Sessions that are Not Established

let devices = merge(`analytics:/tags/labels/devices/pod_name/value/<_POD_NAME>/elements`)

if str(_POD_NAME) == "" {
    let bgpNeighbors =`analytics:/Devices/*/versioned-data/routing/bgp/status/vrf/default/bgpPeerInfoStatusEntry/*`
} else {
    let bgpNeighbors =`analytics:/Devices/*/versioned-data/routing/bgp/status/vrf/default/bgpPeerInfoStatusEntry/*` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")))
}

# This is the table
let res = newDict()
let id = 0
# Lets loop over every device
for device, deviceSessions in bgpNeighbors{
    # And each session on the devices
    for ip, sessionData in deviceSessions{
        let data = merge(sessionData)
        # Add one to the ID
        let id = id + 1
        res[id] = newDict()
        # This is where we add the various columns
        res[id]["0. Device"] = device
        res[id]["1. Status"] = data["bgpState"]["Name"]
        res[id]["2. Peering Address"] = data["bgpPeerLocalAddr"]
        res[id]["3. Neighbor Address"] = data["key"]
        res[id]["4. Neighbor AS"] = data["bgpPeerAs"]["value"]
    }
}

res | where(_value["1. Status"] != "Established")
BGP Sessions that are Not Established

Download the Dashboard JSON here

TAC Webinar03 2023 - BGP States

This dashboards helps to provide current and historical BGP status and provides link flap counters.

Note

Some of the BGP states are not streamed by default and the /Smash/routing/bgp/bgpPeerInfoStatus/default/bgpPeerStatisticsEntry needs to be added to the TerminAttr include list using the tastreaming.TerminattrStreaming service API. Examples can be found here

Pre-requisites

  • Devices will need to be tagged with tag label site_name and the label value of the site’s name, for example a customer has 3 sites: ABC, DEF, XYZ

    • Tag site_name:ALL to all devices

    • Tag site_name:ABC to ABC devices

    • repeat for DEF and XYZ

  • Devices with multiple supervisors upon supervisor failover might not show the correct states

BGP Summary

# set vars
let afis = newDict()
let afi_name = newDict()
let bgpPeerAfiSafiActive = newDict() | setFields("l2vpnEvpn", 3, "ipv4Unicast", 1)
let bgpPeerAfiSafiActiveName = newDict() | setFields("l2vpnEvpn","L2VPN EVPN", "ipv4Unicast", "IPv4 Unicast")

# Get all devices from a specific site
let devices = merge(`analytics:/tags/labels/devices/site_name/value/<_SITE_NAME>/elements`)

let bgpNeighbors =`analytics:/Devices/*/versioned-data/routing/bgp/status/vrf/*/bgpPeerInfoStatusEntry/*`
if _device != "" {
    let bgpNeighbors = bgpNeighbors | fields(_device)
} else {
    if str(_SITE_NAME) != "" {
        let bgpNeighbors = bgpNeighbors | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")))
    }
}

let bgpPeerInfoStatusEntry = `*:/Sysdb/cell/1/routing/bgp/export/vrfBgpPeerInfoStatusEntryTable/*/bgpPeerInfoStatusEntry/*`
let bgpPeerStatisticsEntry = `*:/Smash/routing/bgp/bgpPeerInfoStatus/*/bgpPeerStatisticsEntry`

# This is the table
let result = newDict()
let id = 0
# Lets loop over every device
for deviceKey, deviceSessions in bgpNeighbors{
    # And each session on the devices
    if _device == "" || deviceKey == _device {
        for vrfKey, vrfValue in deviceSessions{
            # Check if _vrf is empty or if the current vrfKey is in _vrf
            if length(_vrf) == 0 || _vrf == vrfKey {
                for ip, sessionData in vrfValue {
                    let data = merge(sessionData)
                    # Add one to the ID
                    let id = id + 1
                    result[id] = newDict()
                    # This is where we add the various columns
                    result[id]["0. Device"] = deviceKey
                    result[id]["1. Status"] = data["bgpState"]["Name"]
                    result[id]["2. Peering Address"] = data["bgpPeerLocalAddr"]
                    result[id]["3. Neighbor Address"] = data["key"]
                    result[id]["4. Neighbor AS"] = data["bgpPeerAs"]["value"]
                    result[id]["6. VRF"] = vrfKey
                    if dictHasKey(bgpPeerInfoStatusEntry, deviceKey) && dictHasKey(bgpPeerStatisticsEntry, deviceKey) {
                        if dictHasKey(bgpPeerInfoStatusEntry[deviceKey], vrfKey) {
                            let test = merge(bgpPeerInfoStatusEntry[deviceKey][vrfKey][ip])
                            for kafi, kval in test["bgpPeerAfiSafiActive"]{
                                if kval == true {
                                    afis[deviceKey] = bgpPeerAfiSafiActive[kafi]
                                    afi_name[deviceKey] = bgpPeerAfiSafiActiveName[kafi]
                                    result[id]["7. AFI/SAFI"] = afi_name[deviceKey]
                                }
                            }
                            result[id]["5. Peer Description"] = test["bgpPeerDescription"]
                        }
                        if dictHasKey(bgpPeerStatisticsEntry[deviceKey], vrfKey) {
                            let bgpPeerAfiSafiStats = merge(bgpPeerStatisticsEntry[deviceKey][vrfKey])
                            if dictHasKey(afis, deviceKey) && dictHasKey(bgpPeerAfiSafiStats, ip){
                                result[id]["8. PfxRcd"] = bgpPeerAfiSafiStats[ip]["bgpPeerAfiSafiStats"][afis[deviceKey]]["prefixIn"]
                                result[id]["9. PfxAcc"] = bgpPeerAfiSafiStats[ip]["bgpPeerAfiSafiStats"][afis[deviceKey]]["prefixAcceptedIn"]
                            }
                        }


                    } else {
                        # need to figure out why sometimes there's no prefix info
                        result[id]["8. PfxRcd"] = "N/A"
                        result[id]["9. PfxAcc"] = "N/A"
                    }
                    if dictHasKey(data, "bgpPeerIntoOrOutOfEstablishedTime") {
                        result[id]["10. Up/Down"] = str(duration(1000000000*round(num(now() - time(data["bgpPeerIntoOrOutOfEstablishedTime"]*1000000000))/1000000000)))
                    }
                }
            }
        }
    }
}
result
BGP Summary BGP Summary2 BGP Summary23

BGP Sessions Flaps

let data =`analytics:/Devices/*/versioned-data/routing/bgp/status/vrf/default/bgpPeerInfoStatusEntry/*`[24h]
let devices = merge(`analytics:/tags/labels/devices/site_name/value/<_SITE_NAME>/elements`)
let data = data | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")))
let result = newDict()

for device, deviceValues in data {
        let establishedNeighbors = newDict()
        let flapCount = 0
        for ip, tseries in deviceValues{
            for timestamp, values in tseries {
                if dictHasKey(values, "bgpState"){
                    if values["bgpState"]["Name"] == "Established"{
                        let flapCount = flapCount +1
                        establishedNeighbors[ip] = true
                    }
                }
            }
        }
        let count = flapCount - length(establishedNeighbors)
        if count > 0 {
            result[device] = newDict() | setFields("Flaps", count )
        }

}

result

BGP Historical state tracker

# BGP Session historical state tracker
let data = `analytics:/Devices/<_bgpDevice>/versioned-data/routing/bgp/status/vrf/default/bgpPeerInfoStatusEntry/*`[4h]
let res = newDict()
for ip, tseries in data {
    for timestamp, values in tseries {
        # only show selected neighbors or all if none selected
        if length(_NeighborIP) == 0 || dictHasKey(_NeighborIP, ip){

            if !dictHasKey(res, str(timestamp)) {
                res[str(timestamp)] = newDict() | setFields(ip, dictHasKey(values, "bgpState") ? values["bgpState"]["Name"] : 0)
            } else {
                res[str(timestamp)][ip]  = dictHasKey(values, "bgpState") ? values["bgpState"]["Name"] : 0
            }
        }
    }
}
res
BGP Flaps and Historical state tracker

BGP Syslog Messages

let data = `<_bgpDevice>:/Logs/var/log/messages`[48h] | field("line") | where(reMatch(_value, "BGP"))
let logs = newDict()
for timest, logentry in data {
    logs[str(timest+(_tz_offset))] = newDict()
    logs[str(timest+(_tz_offset))]["Log"] = logentry
}
logs
BGP Syslog Messages

Download the Dashboard JSON here

Capacity Planning Routing and Switching

High Density Leaf Switches Numbers (7050X3) Tagged with HDL Label

let devices = merge(`analytics:/tags/labels/devices/pod_name/value/<_POD_NAME>/elements`)
let devicesSwitchLabel = merge(`analytics:/tags/labels/devices/switch_role/value/HDL/elements`)

if str(_POD_NAME) == "" {
    let macConfigCount =`*:/Sysdb/bridging/config/_counts` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let macStatusCount =`*:/Smash/bridging/status/_counts` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let vrfCount =`*:/Sysdb/routing/vrf/config/vrfConfig` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}"))) | map(length(_value) == 0 ? 0 : _value)
    let arpCount = `*:/Smash/arp/status/_counts`  | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
} else {
    let macConfigCount =`*:/Sysdb/bridging/config/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let macStatusCount =`*:/Smash/bridging/status/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let vrfCount =`*:/Sysdb/routing/vrf/config/vrfConfig` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}"))) | map(length(_value) == 0 ? 0 : _value)
    let arpCount = `*:/Smash/arp/status/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
}

let smash = macStatusCount | map(merge(_value) | fields("smashFdbStatus"))
let nbvlan = macConfigCount | map(merge(_value) | fields("vlanConfig"))
let nbVRF = vrfCount | map(length(merge(_value)))
let nbARP = arpCount | map(merge(_value))

for deviceKey, deviceValue in nbVRF {
    let nbr = newDict()
    nbr["VRF number"]=deviceValue
    nbVRF[deviceKey]= nbr
}

for devicekey, devicevalue in smash {
    smash[devicekey]["VLAN count"]=nbvlan[devicekey]["vlanConfig"]
    smash[devicekey]["VRF count"]=nbVRF[devicekey]["VRF number"]
    smash[devicekey]["ARP count"]=nbARP[devicekey]["arpEntry"]
    smash[devicekey]["ND count"]=nbARP[devicekey]["neighborEntry"]
}

smash | map(_value | renameFields("smashFdbStatus","MAC count"))

Low Density Leaf Switches Numbers (7020R) Tagged with the LDL Label

let devices = merge(`analytics:/tags/labels/devices/pod_name/value/<_POD_NAME>/elements`)
let devicesSwitchLabel = merge(`analytics:/tags/labels/devices/switch_role/value/LDL/elements`)

if str(_POD_NAME) == "" {
    let macConfigCount =`*:/Sysdb/bridging/config/_counts` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let macStatusCount =`*:/Smash/bridging/status/_counts` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let vrfCount =`*:/Sysdb/routing/vrf/config/vrfConfig` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}"))) | map(length(_value) == 0 ? 0 : _value)
    let arpCount = `*:/Smash/arp/status/_counts`  | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
} else {
    let macConfigCount =`*:/Sysdb/bridging/config/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let macStatusCount =`*:/Smash/bridging/status/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let vrfCount =`*:/Sysdb/routing/vrf/config/vrfConfig` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}"))) | map(length(_value) == 0 ? 0 : _value)
    let arpCount = `*:/Smash/arp/status/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
}

let smash = macStatusCount | map(merge(_value) | fields("smashFdbStatus"))
let nbvlan = macConfigCount | map(merge(_value) | fields("vlanConfig"))
let nbVRF = vrfCount | map(length(merge(_value)))
let nbARP = arpCount | map(merge(_value))

for deviceKey, deviceValue in nbVRF {
    let nbr = newDict()
    nbr["VRF number"] = deviceValue
    nbVRF[deviceKey] = nbr
}

for devicekey, devicevalue in smash {
    smash[devicekey]["VLAN count"] = nbvlan[devicekey]["vlanConfig"]
    smash[devicekey]["VRF count"] = dictHasKey(nbVRF, devicekey)? nbVRF[devicekey]["VRF number"] : 0
    smash[devicekey]["ARP count"] = nbARP[devicekey]["arpEntry"]
    smash[devicekey]["ND count"] = nbARP[devicekey]["neighborEntry"]
}

smash | map(_value | renameFields("smashFdbStatus","MAC count"))

EVPN Gateways Numbers (7280R2)

let devices = merge(`analytics:/tags/labels/devices/pod_name/value/<_POD_NAME>/elements`)
let devicesSwitchLabel = merge(`analytics:/tags/labels/devices/switch_role/value/EGW/elements`)

if str(_POD_NAME) == "" {
    let macConfigCount =`*:/Sysdb/bridging/config/_counts` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let macStatusCount =`*:/Smash/bridging/status/_counts` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let vrfCount =`*:/Sysdb/routing/vrf/config/vrfConfig` | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}"))) | map(length(_value) == 0 ? 0 : _value)
    let arpCount = `*:/Smash/arp/status/_counts`  | where(dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
} else {
    let macConfigCount =`*:/Sysdb/bridging/config/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let macStatusCount =`*:/Smash/bridging/status/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
    let vrfCount =`*:/Sysdb/routing/vrf/config/vrfConfig` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}"))) | map(length(_value) == 0 ? 0 : _value)
    let arpCount = `*:/Smash/arp/status/_counts` | where(dictHasKey(devices, complexKey("{\"deviceID\": \""+_key+"\"}")) && dictHasKey(devicesSwitchLabel, complexKey("{\"deviceID\": \""+_key+"\"}")))
}

let smash = macStatusCount | map(merge(_value) | fields("smashFdbStatus"))
let nbvlan = macConfigCount | map(merge(_value) | fields("vlanConfig"))
let nbVRF = vrfCount | map(length(merge(_value)))
let nbARP = arpCount | map(merge(_value))

for deviceKey, deviceValue in nbVRF {
    let nbr = newDict()
    nbr["VRF number"]=deviceValue
    nbVRF[deviceKey]= nbr
}

for devicekey, devicevalue in smash {
    smash[devicekey]["VLAN count"]=nbvlan[devicekey]["vlanConfig"]
    smash[devicekey]["VRF count"]=nbVRF[devicekey]["VRF number"]
    smash[devicekey]["ARP count"]=nbARP[devicekey]["arpEntry"]
    smash[devicekey]["ND count"]=nbARP[devicekey]["neighborEntry"]
}

smash | map(_value | renameFields("smashFdbStatus","MAC count"))

Download the Dashboard JSON here

IGMP Snooping Table

Note

IGMP Snooping states are not streamed by default and the “/Sysdb/bridging/igmpsnooping” needs to be added to the TerminAttr include list using the tastreaming.TerminattrStreaming service API. Examples can be found on Examples

let igmpSnooping = `<_device>:/Sysdb/bridging/igmpsnooping/forwarding/status/vlanStatus/*/ethGroup/*/intf`
let filteredIgmpSnooping = igmpSnooping | recmap(2, merge(_value))
let result = newDict()
for vlanKey, macAddrIntf in filteredIgmpSnooping {
    for macAddr, intfState in macAddrIntf {
        # Create a list of interfaces a vlan is mapped to
        let interfaceList = ""
        if length(intfState) > 1{
            for interface, intfBool in intfState {
                let interfaceList = interfaceList + str(interface) + ", "
            }
        } else {
            # if there is only 1 interface no need to add a comma
            let interfaceList = str(dictKeys(intfState)[0])
        }
        let vlan = reFindCaptures(str(vlanKey), "{\"value\":(\d+)}")[0][1]
        result[macAddr] = newDict() | setFields("VLAN", vlan, "Members", interfaceList)
    }
}
result
IGMP Snooping Table

Download the Dashboard JSON here

Number of MAC addresses across all devices

let data = `*:/Smash/bridging/status/_counts`
sum(data | map(merge(_value)) | where(dictHasKey(_value, "smashFdbStatus")) | map(_value["smashFdbStatus"]))
Number of MAC addresses across all devices

Download the Dashboard JSON here

Number of MACs per device per interface

let data = merge(`<_device>:/Smash/bridging/status/smashFdbStatus`)
let numberMAC = newDict()

for deviceKey, deviceValue in data {
   if dictHasKey(numberMAC, data[deviceKey]["intf"]) {
       numberMAC[data[deviceKey]["intf"]]["MACs"] = numberMAC[data[deviceKey]["intf"]]["MACs"] + 1
   } else {
       numberMAC[data[deviceKey]["intf"]] = newDict()
       numberMAC[data[deviceKey]["intf"]]["MACs"] = 1
   }
}

numberMAC
Number of MACs per device per interface

Download the Dashboard JSON here

List of Configured VLANs per VRFs

# Get the VRF configuration for all routed interfaces for all devices
let data=`*:/Sysdb/l3/intf/config/intfConfig/*`

# Build a new dictionary and select only the SVIs that are configured in a specific VRF
let res = newDict()
let id = 0
for deviceKey, deviceValue in data {
    for interfaceKey, interfaceValue in deviceValue {
        if strContains(interfaceKey, "Vlan"){
            let data1 = merge(interfaceValue)
            if data1["vrf"]["value"] == _VRF {
                let id = id + 1
                res[id] = newDict()
                res[id]["Device"] = deviceKey
                res[id]["Interfaces"] = interfaceKey
                res[id]["VRFs"] = _VRF
            }
        }
    }
}
res
# Get the VRF configuration for all routed interfaces for all devices
let data=`*:/Sysdb/l3/intf/config/intfConfig/*`

# Build a new dictionary and select only the SVIs and the VRFs they are configured in
let res = newDict()
let id = 0
for deviceKey, deviceValue in data {
    for interfaceKey, interfaceValue in deviceValue {
        if strContains(interfaceKey, "Vlan"){
            let id = id + 1
            res[id] = newDict()
            res[id]["Device"] = deviceKey
            res[id]["Interfaces"] = interfaceKey
            res[id]["VRFs"] = merge(interfaceValue)["vrf"]["value"]
        }
    }
}
res
List of Configured VLANs per VRF

Download the Dashboard JSON here