-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathietf_ospf.py
More file actions
259 lines (211 loc) · 10.1 KB
/
ietf_ospf.py
File metadata and controls
259 lines (211 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
from .common import insert
from .host import HOST
def frr_to_ietf_neighbor_state(state):
"""Fetch OSPF neighbor state from Frr"""
state = state.split("/")[0]
if state == "TwoWay":
return "2-way"
return state.lower()
def frr_to_ietf_neighbor_role(role):
"""Translate FRR neighbor role to YANG enumeration values"""
if role == "Backup":
return "BDR"
# DR and DROther are already correct
return role
def add_routes(ospf):
"""Fetch OSPF routes from Frr"""
cmd = ['vtysh', '-c', "show ip ospf route json"]
data = HOST.run_json(cmd, default=[])
if data == []:
return # No OSPF routes available
routes = []
for prefix, info in data.items():
if prefix.find("/") == -1: # Ignore router IDs
continue
route = {}
route["prefix"] = prefix
nexthops = []
routetype = info["routeType"].split(" ")
if len(routetype) > 1:
if routetype[1] == "E1":
route["route-type"] = "external-1"
elif routetype[1] == "E2":
route["route-type"] = "external-2"
elif routetype[1] == "IA":
route["route-type"] = "inter-area"
elif routetype[0] == "N":
route["route-type"] = "intra-area"
# Add area information if available
# Note: augmented by infix-routing.yang since standard ietf-ospf doesn't include it
# Must use the augmenting module's prefix
if info.get("area") is not None:
route["infix-routing:area-id"] = info["area"]
# Add metric (cost) if available
if info.get("cost") is not None:
route["metric"] = info["cost"]
elif info.get("metric") is not None:
route["metric"] = info["metric"]
# Add route-tag for external routes
if info.get("tag") is not None:
route["route-tag"] = info["tag"]
for hop in info["nexthops"]:
nexthop = {}
if hop["ip"] != " ":
nexthop["next-hop"] = hop["ip"]
else:
nexthop["outgoing-interface"] = hop["directlyAttachedTo"]
nexthops.append(nexthop)
route["next-hops"] = {}
route["next-hops"]["next-hop"] = nexthops
routes.append(route)
insert(ospf, "ietf-ospf:local-rib", "ietf-ospf:route", routes)
def add_areas(control_protocols):
"""Populate OSPF status"""
cmd = ['/usr/libexec/statd/ospf-status']
data = HOST.run_json(cmd, default={})
if data == {}:
return # No OSPF data available
control_protocol = {}
control_protocol["type"] = "infix-routing:ospfv2"
control_protocol["name"] = "default"
control_protocol["ietf-ospf:ospf"] = {}
control_protocol["ietf-ospf:ospf"]["ietf-ospf:areas"] = {}
control_protocol["ietf-ospf:ospf"]["ietf-ospf:router-id"] = data.get("routerId")
control_protocol["ietf-ospf:ospf"]["ietf-ospf:address-family"] = "ipv4"
areas = []
for area_id, values in data.get("areas", {}).items():
area = {}
area["ietf-ospf:area-id"] = area_id
area["ietf-ospf:interfaces"] = {}
if values.get("area-type"):
area["ietf-ospf:area-type"] = values["area-type"]
interfaces = []
for iface in values.get("interfaces", {}):
interface = {}
interface["ietf-ospf:neighbors"] = {}
interface["name"] = iface["name"]
if iface.get("drId"):
interface["dr-router-id"] = iface["drId"]
if iface.get("drAddress"):
interface["dr-ip-addr"] = iface["drAddress"]
if iface.get("bdrId"):
interface["bdr-router-id"] = iface["bdrId"]
if iface.get("bdrAddress"):
interface["bdr-ip-addr"] = iface["bdrAddress"]
if iface.get("timerPassiveIface"):
interface["passive"] = True
else:
interface["passive"] = False
interface["enabled"] = iface["ospfEnabled"]
if iface["networkType"] == "POINTOPOINT":
interface["interface-type"] = "point-to-point"
elif iface["networkType"] == "BROADCAST":
interface["interface-type"] = "broadcast"
elif iface["networkType"] == "POINTOMULTIPOINT":
if iface.get("p2mpNonBroadcast", False):
interface["interface-type"] = "point-to-multipoint"
else:
interface["interface-type"] = "hybrid"
elif iface["networkType"] == "NBMA":
interface["interface-type"] = "non-broadcast"
if iface.get("state"):
# Wev've never seen "DependUpon", and has no entry in
# the YANG model, but is listed before down in Frr
xlate = {
"DependUpon": "down",
"Down": "down",
"Waiting": "waiting",
"Loopback": "loopback",
"Point-To-Point": "point-to-point",
"DROther": "dr-other",
"Backup": "bdr",
"DR": "dr"
}
val = xlate.get(iface["state"], "unknown")
interface["state"] = val
# Interface priority (for DR/BDR election)
if iface.get("priority") is not None:
interface["priority"] = iface["priority"]
# Interface cost
if iface.get("cost") is not None:
interface["cost"] = iface["cost"]
# Configuration timers (in seconds)
if iface.get("timerDeadSecs") is not None:
interface["dead-interval"] = iface["timerDeadSecs"]
if iface.get("timerRetransmitSecs") is not None:
interface["retransmit-interval"] = iface["timerRetransmitSecs"]
if iface.get("transmitDelaySecs") is not None:
interface["transmit-delay"] = iface["transmitDelaySecs"]
# Hello interval - convert from milliseconds to seconds
if iface.get("timerMsecs") is not None:
hello_sec = iface["timerMsecs"] // 1000
# timer-value-seconds16 requires range 1..65535, use max(1, value)
if hello_sec >= 1:
interface["hello-interval"] = hello_sec
# Operational state timers (config false)
# Hello timer - time remaining until next Hello (convert ms to seconds)
if iface.get("timerHelloInMsecs") is not None:
hello_timer_sec = iface["timerHelloInMsecs"] // 1000
# timer-value-seconds16 requires range 1..65535, use max(1, value)
if hello_timer_sec >= 1:
interface["hello-timer"] = hello_timer_sec
# Wait timer - time until interface exits Waiting state
if iface.get("timerWaitSecs") is not None:
wait_sec = iface["timerWaitSecs"]
# timer-value-seconds16 requires range 1..65535
if wait_sec >= 1:
interface["wait-timer"] = wait_sec
neighbors = []
for neigh in iface["neighbors"]:
neighbor = {}
neighbor["neighbor-router-id"] = neigh["neighborIp"]
neighbor["address"] = neigh["ifaceAddress"]
# Priority - use existing YANG leaf for operational data
if neigh.get("nbrPriority") is not None:
neighbor["priority"] = neigh["nbrPriority"]
# Uptime - convert from milliseconds to seconds
# Note: augmented by infix-routing.yang
# Use lastPrgrsvChangeMsec from detail output (time since last progressive state change)
if neigh.get("lastPrgrsvChangeMsec") is not None:
uptime_sec = neigh["lastPrgrsvChangeMsec"] // 1000
neighbor["infix-routing:uptime"] = uptime_sec
# Dead timer - convert from milliseconds to seconds
# timer-value-seconds16 requires range 1..65535
if neigh.get("routerDeadIntervalTimerDueMsec") is not None:
dead_timer_sec = neigh["routerDeadIntervalTimerDueMsec"] // 1000
if dead_timer_sec >= 1:
neighbor["dead-timer"] = dead_timer_sec
neighbor["state"] = frr_to_ietf_neighbor_state(neigh["nbrState"])
# Store role (DR/BDR/DROther) for display
# Note: augmented by infix-routing.yang
if neigh.get("role"):
neighbor["infix-routing:role"] = frr_to_ietf_neighbor_role(neigh["role"])
# Store interface name with local address (e.g., "e5:10.0.23.1")
# Note: augmented by infix-routing.yang
# Compose from ifaceName and localIfaceAddress
if neigh.get("ifaceName") and neigh.get("localIfaceAddress"):
neighbor["infix-routing:interface-name"] = f"{neigh['ifaceName']}:{neigh['localIfaceAddress']}"
elif neigh.get("ifaceName"):
neighbor["infix-routing:interface-name"] = neigh["ifaceName"]
if neigh.get("routerDesignatedId"):
neighbor["dr-router-id"] = neigh["routerDesignatedId"]
if neigh.get("routerDesignatedBackupId"):
neighbor["bdr-router-id"] = neigh["routerDesignatedBackupId"]
neighbors.append(neighbor)
interface["ietf-ospf:neighbors"] = {}
interface["ietf-ospf:neighbors"]["ietf-ospf:neighbor"] = neighbors
interfaces.append(interface)
area["ietf-ospf:interfaces"]["ietf-ospf:interface"] = interfaces
areas.append(area)
add_routes(control_protocol["ietf-ospf:ospf"])
control_protocol["ietf-ospf:ospf"]["ietf-ospf:areas"]["ietf-ospf:area"] = areas
insert(control_protocols, "control-plane-protocol", [control_protocol])
def operational():
out = {
"ietf-routing:routing": {
"control-plane-protocols": {
}
}
}
add_areas(out['ietf-routing:routing']['control-plane-protocols'])
return out