Skip to main content

Here is a useful combination of a custom command and an NQE query to show the state of all OSPF neighbors.

OSPF data is not collected by default in Forward Enterprise, so we add the custom command "sh ip ospf neighbor"

Output data for this command from one device looks like this:

Command: sh ip ospf neighbor

Neighbor ID     Pri   State           Dead Time   Address         Interface
10.200.30.42      0   FULL/  -        00:00:35    10.45.6.1    GigE1/0/0
10.200.30.42      0   FULL/  -        00:00:32    10.45.6.2    GigE1/0/1
10.200.30.42      0   FULL/  -        00:00:35    10.45.6.3    GigE1/0/2
10.200.30.42      0   FULL/  -        00:00:31    10.45.6.4    GigE1/0/3
10.10.10.200      1   FULL/BDR        00:00:03    10.45.6.5    Vlan200

This NQE query will list the OSPF neighbors from all devices by parsing the data from the custom command.

/**
 * @intent Show OSPF neighbors on all devices collected with custom command 'sh ip ospf neighbor'
 * @description Show OSPF neighbors on all devices collected with custom command 'sh ip ospf neighbor'
 */

pattern = ```
{neighbor_id:ipv4Address} {Pri:number} {State:string} {string} {Address:ipv4Address} {Interface:string}
```;

foreach device in network.devices
foreach command in device.outputs.commands
where command.commandText == "sh ip ospf neighbor" 
let filtered_response = replace(command.response, "-", "")
let blocks = parseConfigBlocks(OS.IOS_XE, filtered_response)
foreach match in blockMatches(blocks, pattern)


// where !matches(match.data.State, "*FULL*")
select {
  name:d.name, 
  neighbor_id: match.data.neighbor_id, 
  Pri: match.data.Pri,
  State:match.data.State,
  Address:match.data.Address,
  Interface:match.data.Interface
  
}

Note the following:

The "//" can be removed from the line "// where !matches(match.data.State, "*FULL*")" to see only OSPF neighbors that are NOT in the FULL state.

The command text in the query "sh ip ospf neighbor" must match the text in the custom command exactly. Since the custom command was entered with "sh" rather than "show", the query must also use "sh".

The pattern of the data in the command output must match the pattern at the top of the NQE. Since white space is used to determine where each field begins and ends, the lines in the command output with a "-" aren't the same as the line with no "-". We apply a filter to delete each "-". Then the data is parsed according to the pattern.

Since output from this command could differ based on device type, the NQE query may need to be expanded the parse each type of device data differently.
 

Thanks Chris,

Very useful.

Here is an adjustment to pull the same info for Arista Devices, and also add Hardware Platform in the Query

/**
* @intent Show OSPF neighbors on Arista devices
* @description Show OSPF neighbors on all devices collected with custom command 'sh ip ospf neighbor'
*/

pattern = ```
{neighbor_id:ipv4Address} {Instance:number} {VRF:string} {Pri:number} {State:string} {Dead_time:string} {Address:ipv4Address} {Interface:string}
```;

foreach device in network.devices
let Platform = device.platform
foreach command in device.outputs.commands
where command.commandText == "sh ip ospf neighbor"
let filtered_response = replace(command.response, "-", "")
let blocks = parseConfigBlocks(OS.ARISTA_EOS, filtered_response)
foreach match in blockMatches(blocks, pattern)


select {
name:device.name,
platform: Platform.model,
neighbor_id: match.data.neighbor_id,
Pri: match.data.Pri,
State:match.data.State,
"OSPF Network":match.data.Address,
Interface:match.data.Interface

}

 

The Goal would be to combine each of these into a single Query by defining a function for each device type,  define 2 different patterns, and combine the results.


And here is the combo of Arista and IOS




patternEOS = ```
{neighbor_id:ipv4Address} {Instance:number} {VRF:string} {Pri:number} {State:string} {Dead_time:string} {Address:ipv4Address} {Interface:string}
```;

patternIOS = ```
{neighbor_id:ipv4Address} {Pri:number} {State:string} {string} {Address:ipv4Address} {Interface:string}
```;

EOS_Stats =
foreach device in network.devices
let Platform = device.platform
foreach command in device.outputs.commands
where command.commandText == "sh ip ospf neighbor"
let filtered_response = replace(command.response, "-", "")
let blocks = parseConfigBlocks(OS.ARISTA_EOS, filtered_response)
foreach match in blockMatches(blocks, patternEOS)


select {
name:device.name,
platform: Platform.model,
neighbor_id: match.data.neighbor_id,
Pri: match.data.Pri,
State:match.data.State,
Network:match.data.Address,
Interface:match.data.Interface
};

Cisco_Stats =
foreach device in network.devices
let Platform = device.platform
foreach command in device.outputs.commands
where command.commandText == "sh ip ospf neighbor"
let filtered_response = replace(command.response, "-", "")
let blocks = parseConfigBlocks(OS.IOS_XE, filtered_response)
foreach match in blockMatches(blocks, patternIOS)

select {
name:device.name,
platform: Platform.model,
neighbor_id: match.data.neighbor_id,
Pri: match.data.Pri,
State:match.data.State,
Network:match.data.Address,
Interface:match.data.Interface
};

// Combine Results
OSPF_Peers = EOS_Stats + Cisco_Stats;

// Unify Table Results
foreach result in OSPF_Peers
select {
Hostname: result.name,
Platform: result.platform,
Priority: result.Pri,
State: result.State,
"OSPF Network": result.Network,
Interface: result.Interface
}

 


Great additions, thanks @cariddir


here’s a query that will run juniper and cisco, also modified it to run a bit faster by just passing the custom command output to the function for each vendor

 

patternJUNOS = ```
{Address:ipv4Address} {Interface:string} {State:string} {neighbor_id:ipv4Address} {Pri:number} {Dead_time:string}
```;

patternIOS = ```
{neighbor_id:ipv4Address} {Pri:number} {State:string} {string} {Address:ipv4Address} {Interface:string}
```;

JUNOS_Stats(response) =
foreach match in blockMatches(parseConfigBlocks(OS.JUNOS, response), patternJUNOS)
select {
neighbor_id: match.data.neighbor_id,
Pri: match.data.Pri,
State:match.data.State,
Network:match.data.Address,
Interface:match.data.Interface
};

Cisco_Stats(response) =
foreach match in blockMatches(parseConfigBlocks(OS.IOS_XE, response), patternIOS)
select {
neighbor_id: match.data.neighbor_id,
Pri: match.data.Pri,
State:match.data.State,
Network:match.data.Address,
Interface:match.data.Interface
};

foreach device in network.devices
where isPresent(device.platform)
let Platform = device.platform
foreach command in device.outputs.commands
where command.commandText == "show ip ospf neighbor" || command.commandText == "show ospf neighbor"
let filtered_response = replace(command.response, "-", "")
let results = if Platform.os == OS.IOS || Platform.os == OS.IOS_XE then Cisco_Stats(filtered_response) else if Platform.os == OS.JUNOS then JUNOS_Stats(filtered_response) else snull : {neighbor_id: IpAddress, Pri: Number, State: String, Network: IpAddress, Interface: String}]
foreach result in results
select {
DeviceName: device.name,
Model: Platform.model,
Neighbor: result.neighbor_id,
Priority: result.Pri,
State: result.State,
"OSPF Network": result.Network,
Interface: result.Interface
}

 


Reply