Skip to main content

Problem Statement

Our company had issues where some network leafs were not connected to all our spines within an ACI fabric.Traditional methods to validate this were time consuming so we developed a basic NQE query to assist.

How it works

we leverage the LLDP information for the network leaves and match this to our spines. In our case, we rely on the device naming convention to match LLDP entries to spines. Our spines have SP within the name.

NQE Script

Function to gather the links to spines for a given device

/*
* function: countSpineNeighbours
*
* parameters: device: Device
*
* purpose
* -------
*
* count the number of links that are between the spines and the leafs
*
* spines are matched based on the SP in the device name, at position 8
*
* returns: dict
*
* {
* deviceName: string
* interfaceName: string
* }
*/
countSpineNeighbours(device) =
foreach interface in device.interfaces
where interface.interfaceType == IfaceType.IF_ETHERNET
foreach n in interface.lldp.neighbors
where length(n.deviceName) >= 10 && substring(n.deviceName, 8, 10) == "SP"
select {
deviceName: n.deviceName,
intefaceName: interface.name
};

Main script iterates over all devices, picking out any leaf nodes based on a tags, device type and naming convention. These are then processed using the function countSpineNeighbours to see if there are 3 spines connected. Anything other than 3 is a violation.

foreach device in network.devices
where device.platform.vendor == Vendor.CISCO &&
device.platform.deviceType == DeviceType.SWITCH &&
"deviceType:fabric" in device.tagNames &&
length(device.name) >= 10 &&
substring(device.name, 8, 10) == "SL"
let spines = countSpineNeighbours(device)
let violation = length(spines) != 3
let violationReason = if violation
then join(" ", toString(3 - length(spines)),
"misisng link(s) from spines to leaf",
device.name
])
else ""
select {
device: device.name,
connectedSpines: (foreach spine in spines
select spine.deviceName),
count: length(spines),
violation: length(spines) != 3,
violationReason: violationReason
}

Extension

Be great to see how this could be extended to identify the missing spines name.

One option is to find all the spines based on the naming convention - SP in position 8

findAllSpines() =
foreach device in network.devices
where device.platform.vendor == Vendor.CISCO &&
device.platform.deviceType == DeviceType.SWITCH &&
"deviceType:fabric" in device.tagNames &&
length(device.name) >= 10 &&
substring(device.name, 8, 10) == "SP"
select device.name;

The fabric name is in position 2, 6 digits, this aligns across all devices within the fabric.

getMissingSpines(allSpines, fabricName, connectedSpines) =
foreach spine in allSpines
where fabricName == substring(spine, 2, 8) && spine not in connectedSpines
select spine;

and finally adjust the original script to use these functions.

foreach x in [0]
let allSpines = findAllSpines()
foreach device in network.devices
where device.platform.vendor == Vendor.CISCO &&
device.platform.deviceType == DeviceType.SWITCH &&
"deviceType:fabric" in device.tagNames &&
length(device.name) >= 10 &&
substring(device.name, 8, 10) == "SL"
let spines = countSpineNeighbours(device)
let violation = length(spines) != 3
let violationReason = if violation
then join(" ", [toString(3 - length(spines)),
"misisng link(s) from spines to leaf",
device.name
])
else ""
let connectedSpines = (foreach spine in spines
select spine.deviceName)
let fabricName = substring(device.name, 2, 8)
let missingSpines = getMissingSpines(allSpines, fabricName, connectedSpines)
select {
device: device.name,
fabricName: fabricName,
missingSpines: missingSpines,
connectedSpines: connectedSpines,
count: length(spines),
violation: length(spines) != 3,
violationReason: violationReason
}

 


Other options include putting tags within the source devices, so this can be made simpler.

for example,

  • tag the fabric name
  • tag the device as a leaf or spine

and use this information in the NQE script


Reply