Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--------------------------------------------------------------------------------
New
--------------------------------------------------------------------------------
* NXOS
* Added ShowIpOspfNeighbors:
* Added parser and schema for show ip ospf neighbors.
* Extracts process, VRF, neighbor count, interface, router ID, priority,
adjacency state, neighbor role, uptime, and neighbor address.
* Preserves identical router IDs on different interfaces and reports
explicit zero-neighbor summaries as structured data.
* Rejects incomplete tables whose parsed row count differs from the
reported count.
* Adds lab-derived and synthetic fixtures and malformed-input tests.
16 changes: 16 additions & 0 deletions sdk_generator/outputs/github_parser.json
Original file line number Diff line number Diff line change
Expand Up @@ -43362,6 +43362,22 @@
}
}
},
"show ip ospf neighbors": {
"folders": {
"nxos": {
"class": "ShowIpOspfNeighbors",
"doc": "Parser for 'show ip ospf neighbors'.",
"module_name": "nxos.show_ospf",
"package": "genie.libs.parser",
"schema": "{\n 'vrf': {\n Any('*'): {\n 'address_family': {\n 'ipv4': {\n 'instance': {\n Any('*'): {\n 'total_neighbors': int,\n Optional('interfaces'): {\n Any('*'): {\n 'neighbors': {\n Any('*'): {\n 'neighbor_router_id': str,\n 'priority': int,\n 'state': str,\n 'neighbor_role': str,\n 'up_time': str,\n 'address': str,\n },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n}",
"tokens": {
"os": "nxos"
},
"uid": "show_ip_ospf_neighbors",
"url": "https://github.com/CiscoTestAutomation/genieparser/tree/master/src/genie/libs/parser/nxos/show_ospf.py#L2393"
}
}
},
"show ip ospf neighbors detail": {
"folders": {
"nxos": {
Expand Down
118 changes: 118 additions & 0 deletions src/genie/libs/parser/nxos/show_ospf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* show ip ospf interface
* show ip ospf interface vrf <WORD>
* show ip ospf neighbors detail
* show ip ospf neighbors
* show ip ospf neighbors detail vrf <WORD>
* show ip ospf database external detail
* show ip ospf database external detail vrf <WORD>
Expand Down Expand Up @@ -2352,6 +2353,123 @@ def cli(self, vrf='', interface='', output=None):
return ret_dict


# ======================================================
# Schema for 'show ip ospf neighbors'
# ======================================================
class ShowIpOspfNeighborsSchema(MetaParser):
"""Schema for 'show ip ospf neighbors'."""

schema = {
'vrf': {
Any(): {
'address_family': {
'ipv4': {
'instance': {
Any(): {
'total_neighbors': int,
Optional('interfaces'): {
Any(): {
'neighbors': {
Any(): {
'neighbor_router_id': str,
'priority': int,
'state': str,
'neighbor_role': str,
'up_time': str,
'address': str,
},
},
},
},
},
},
},
},
},
},
}


class ShowIpOspfNeighbors(ShowIpOspfNeighborsSchema):
"""Parser for 'show ip ospf neighbors'."""

cli_command = 'show ip ospf neighbors'

def cli(self, output=None):
if output is None:
output = self.device.execute(self.cli_command)

result = {}
instance = None
# OSPF Process ID UNDERLAY VRF default
p_header = re.compile(
r'^OSPF Process ID (?P<process>\S+) VRF (?P<vrf>\S+)$')
# Total number of neighbors: 2
p_total = re.compile(r'^Total number of neighbors:\s*(?P<total>\d+)$')
# 10.2.0.3 1 FULL/ - 02:00:11 10.4.0.2 Eth1/1
p_neighbor = re.compile(
r'^(?P<neighbor_router_id>\d+\.\d+\.\d+\.\d+)\s+'
r'(?P<priority>\d+)\s+(?P<state>\S+?)\s*/\s*'
r'(?P<neighbor_role>\S+)\s+(?P<up_time>\S+)\s+'
r'(?P<address>\d+\.\d+\.\d+\.\d+)\s+'
r'(?P<interface>\S+)$')

for line in output.splitlines():
line = line.strip()
if not line:
continue
match = p_header.match(line)
if match:
group = match.groupdict()
instance = (
result.setdefault('vrf', {})
.setdefault(group['vrf'], {})
.setdefault('address_family', {})
.setdefault('ipv4', {})
.setdefault('instance', {})
.setdefault(group['process'], {})
)
continue
match = p_total.match(line)
if match:
if instance is None:
raise ValueError(
'OSPF neighbor count without process header')
instance['total_neighbors'] = int(match.group('total'))
continue
match = p_neighbor.match(line)
if match:
if instance is None:
raise ValueError('OSPF neighbor row without process header')
group = match.groupdict()
interface = Common.convert_intf_name(group.pop('interface'))
group['priority'] = int(group['priority'])
group['state'] = group['state'].lower()
group['neighbor_role'] = group['neighbor_role'].lower()
neighbors = instance.setdefault('interfaces', {}).setdefault(
interface, {}).setdefault('neighbors', {})
neighbor_id = group['neighbor_router_id']
if neighbor_id in neighbors:
raise ValueError(
'Duplicate OSPF neighbor on the same interface')
neighbors[neighbor_id] = group

# A positive count must not silently become an empty/partial table.
for vrf_data in result.get('vrf', {}).values():
instances = vrf_data['address_family']['ipv4']['instance']
for instance_data in instances.values():
count = sum(len(data['neighbors']) for data in
instance_data.get('interfaces', {}).values())
if 'total_neighbors' not in instance_data:
raise ValueError(
'OSPF summary is missing the neighbor count')
if count != instance_data['total_neighbors']:
raise ValueError(
'OSPF neighbor count does not match parsed rows')

return result


# =======================================================
# Schema for 'show ip ospf neighbors detail [vrf <WORD>]'
# =======================================================
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
expected_output = {
"vrf": {
"default": {
"address_family": {
"ipv4": {
"instance": {
"UNDERLAY": {
"total_neighbors": 1,
"interfaces": {
"Ethernet1/2": {
"neighbors": {
"10.2.0.3": {
"neighbor_router_id": "10.2.0.3",
"priority": 1,
"state": "full",
"neighbor_role": "-",
"up_time": "01:51:39",
"address": "10.4.0.5"
}
}
}
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
OSPF Process ID UNDERLAY VRF default
Total number of neighbors: 1
Neighbor ID Pri State Up Time Address Interface
10.2.0.3 1 FULL/ - 01:51:39 10.4.0.5 Eth1/2
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
expected_output = {
"vrf": {
"default": {
"address_family": {
"ipv4": {
"instance": {
"UNDERLAY": {
"total_neighbors": 2,
"interfaces": {
"Ethernet1/1": {
"neighbors": {
"10.2.0.2": {
"neighbor_router_id": "10.2.0.2",
"priority": 1,
"state": "full",
"neighbor_role": "-",
"up_time": "02:00:19",
"address": "10.4.0.1"
}
}
},
"Ethernet1/2": {
"neighbors": {
"10.2.0.1": {
"neighbor_router_id": "10.2.0.1",
"priority": 1,
"state": "full",
"neighbor_role": "-",
"up_time": "01:51:38",
"address": "10.4.0.6"
}
}
}
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
OSPF Process ID UNDERLAY VRF default
Total number of neighbors: 2
Neighbor ID Pri State Up Time Address Interface
10.2.0.2 1 FULL/ - 02:00:19 10.4.0.1 Eth1/1
10.2.0.1 1 FULL/ - 01:51:38 10.4.0.6 Eth1/2
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
expected_output = {
"vrf": {
"default": {
"address_family": {
"ipv4": {
"instance": {
"UNDERLAY": {
"total_neighbors": 3,
"interfaces": {
"Ethernet1/1": {
"neighbors": {
"20.2.0.1": {
"neighbor_router_id": "20.2.0.1",
"priority": 1,
"state": "full",
"neighbor_role": "-",
"up_time": "01:51:30",
"address": "20.4.0.2"
}
}
},
"Ethernet1/2": {
"neighbors": {
"20.2.0.2": {
"neighbor_router_id": "20.2.0.2",
"priority": 1,
"state": "full",
"neighbor_role": "-",
"up_time": "01:59:59",
"address": "20.4.0.6"
}
}
},
"Ethernet1/3": {
"neighbors": {
"20.2.0.3": {
"neighbor_router_id": "20.2.0.3",
"priority": 1,
"state": "full",
"neighbor_role": "-",
"up_time": "01:59:57",
"address": "20.4.0.10"
}
}
}
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
OSPF Process ID UNDERLAY VRF default
Total number of neighbors: 3
Neighbor ID Pri State Up Time Address Interface
20.2.0.1 1 FULL/ - 01:51:30 20.4.0.2 Eth1/1
20.2.0.2 1 FULL/ - 01:59:59 20.4.0.6 Eth1/2
20.2.0.3 1 FULL/ - 01:59:57 20.4.0.10 Eth1/3
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
expected_output = {
"vrf": {
"default": {
"address_family": {
"ipv4": {
"instance": {
"10": {
"total_neighbors": 2,
"interfaces": {
"Ethernet1/1": {
"neighbors": {
"192.0.2.1": {
"neighbor_router_id": "192.0.2.1",
"priority": 1,
"state": "full",
"neighbor_role": "dr",
"up_time": "1d02h",
"address": "192.0.2.1"
}
}
},
"Ethernet1/2": {
"neighbors": {
"192.0.2.1": {
"neighbor_router_id": "192.0.2.1",
"priority": 0,
"state": "2way",
"neighbor_role": "drother",
"up_time": "00:00:03",
"address": "192.0.2.5"
}
}
}
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
OSPF Process ID 10 VRF default
Total number of neighbors: 2
Neighbor ID Pri State Up Time Address Interface
192.0.2.1 1 FULL/ DR 1d02h 192.0.2.1 Eth1/1
192.0.2.1 0 2WAY/ DROTHER 00:00:03 192.0.2.5 Eth1/2
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
expected_output = {
"vrf": {
"default": {
"address_family": {
"ipv4": {
"instance": {
"UNDERLAY": {
"total_neighbors": 0
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
OSPF Process ID UNDERLAY VRF default
Total number of neighbors: 0
Neighbor ID Pri State Up Time Address Interface
Loading