-
Notifications
You must be signed in to change notification settings - Fork 69
/
staticroute.py
384 lines (314 loc) · 13.9 KB
/
staticroute.py
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
#
# Copyright (c) 2014, Arista Networks, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# Neither the name of Arista Networks nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
"""Module for working with EOS static routes
The staticroute resource provides configuration management of static
route resources on an EOS node. It provides the following class
implementations:
* StaticRoute - Configure static routes in EOS
StaticRoute Attributes:
ip_dest (string): The ip address of the destination in the
form of A.B.C.D/E
next_hop (string): The next hop interface or ip address
next_hop_ip (string): The next hop address on destination interface
distance (int): Administrative distance for this route
tag (int): Route tag
route_name (string): Route name
Notes:
The 'default' prefix function of the 'ip route' command,
'default ip route ...', currently equivalent to the 'no ip route ...'
command.
"""
import re
from pyeapi.api import EntityCollection
# Define the regex to match ip route lines (by lines in regex):
# 'ip route' header
# ip_dest
# next_hop
# next_hop_ip
# distance
# tag
# name
ROUTES_RE = re.compile(r'(?<=^ip route)'
r' (\d+\.\d+\.\d+\.\d+\/\d+)'
r' (\d+\.\d+\.\d+\.\d+|\S+)'
r'(?: (\d+\.\d+\.\d+\.\d+))?'
r' (\d+)'
r'(?: tag (\d+))?'
r'(?: name (\S+))?', re.M)
class StaticRoute(EntityCollection):
"""The StaticRoute class provides a configuration instance
for working with static routes
"""
def __str__(self):
return 'StaticRoute'
def get(self, name):
"""Retrieves the ip route information for the destination
ip address specified.
Args:
name (string): The ip address of the destination in the
form of A.B.C.D/E
Returns:
dict: An dict object of static route entries in the form::
{ ip_dest:
{ next_hop:
{ next_hop_ip:
{ distance:
{ 'tag': tag,
'route_name': route_name
}
}
}
}
}
If the ip address specified does not have any associated
static routes, then None is returned.
Notes:
The keys ip_dest, next_hop, next_hop_ip, and distance in
the returned dictionary are the values of those components
of the ip route specification. If a route does not contain
a next_hop_ip, then that key value will be set as 'None'.
"""
# Return the route configurations for the specified ip address,
# or None if its not found
return self.getall().get(name)
def getall(self):
"""Return all ip routes configured on the switch as a resource dict
Returns:
dict: An dict object of static route entries in the form::
{ ip_dest:
{ next_hop:
{ next_hop_ip:
{ distance:
{ 'tag': tag,
'route_name': route_name
}
}
}
}
}
If the ip address specified does not have any associated
static routes, then None is returned.
Notes:
The keys ip_dest, next_hop, next_hop_ip, and distance in
the returned dictionary are the values of those components
of the ip route specification. If a route does not contain
a next_hop_ip, then that key value will be set as 'None'.
"""
# Find all the ip routes in the config
matches = ROUTES_RE.findall(self.config)
# Parse the routes and add them to the routes dict
routes = dict()
for match in matches:
# Get the four identifying components
ip_dest = match[0]
next_hop = match[1]
next_hop_ip = None if match[2] == '' else match[2]
distance = int(match[3])
# Create the data dict with the remaining components
data = {}
data['tag'] = None if match[4] == '' else int(match[4])
data['route_name'] = None if match[5] == '' else match[5]
# Build the complete dict entry from the four components
# and the data.
# temp_dict = parent_dict[key] = parent_dict.get(key, {})
# This creates the keyed dict in the parent_dict if it doesn't
# exist, or reuses the existing keyed dict.
# The temp_dict is used to make things more readable.
ip_dict = routes[ip_dest] = routes.get(ip_dest, {})
nh_dict = ip_dict[next_hop] = ip_dict.get(next_hop, {})
nhip_dict = nh_dict[next_hop_ip] = nh_dict.get(next_hop_ip, {})
nhip_dict[distance] = data
return routes
def create(self, ip_dest, next_hop, **kwargs):
"""Create a static route
Args:
ip_dest (string): The ip address of the destination in the
form of A.B.C.D/E
next_hop (string): The next hop interface or ip address
**kwargs['next_hop_ip'] (string): The next hop address on
destination interface
**kwargs['distance'] (string): Administrative distance for this
route
**kwargs['tag'] (string): Route tag
**kwargs['route_name'] (string): Route name
Returns:
True if the operation succeeds, otherwise False.
"""
# Call _set_route with delete and default set to False
return self._set_route(ip_dest, next_hop, **kwargs)
def delete(self, ip_dest, next_hop, **kwargs):
"""Delete a static route
Args:
ip_dest (string): The ip address of the destination in the
form of A.B.C.D/E
next_hop (string): The next hop interface or ip address
**kwargs['next_hop_ip'] (string): The next hop address on
destination interface
**kwargs['distance'] (string): Administrative distance for this
route
**kwargs['tag'] (string): Route tag
**kwargs['route_name'] (string): Route name
Returns:
True if the operation succeeds, otherwise False.
"""
# Call _set_route with the delete flag set to True
kwargs.update({'delete': True})
return self._set_route(ip_dest, next_hop, **kwargs)
def default(self, ip_dest, next_hop, **kwargs):
"""Set a static route to default (i.e. delete the matching route)
Args:
ip_dest (string): The ip address of the destination in the
form of A.B.C.D/E
next_hop (string): The next hop interface or ip address
**kwargs['next_hop_ip'] (string): The next hop address on
destination interface
**kwargs['distance'] (string): Administrative distance for this
route
**kwargs['tag'] (string): Route tag
**kwargs['route_name'] (string): Route name
Returns:
True if the operation succeeds, otherwise False.
"""
# Call _set_route with the default flag set to True
kwargs.update({'default': True})
return self._set_route(ip_dest, next_hop, **kwargs)
def set_tag(self, ip_dest, next_hop, **kwargs):
"""Set the tag value for the specified route
Args:
ip_dest (string): The ip address of the destination in the
form of A.B.C.D/E
next_hop (string): The next hop interface or ip address
**kwargs['next_hop_ip'] (string): The next hop address on
destination interface
**kwargs['distance'] (string): Administrative distance for this
route
**kwargs['tag'] (string): Route tag
**kwargs['route_name'] (string): Route name
Returns:
True if the operation succeeds, otherwise False.
Notes:
Any existing route_name value must be included in call to
set_tag, otherwise the tag will be reset
by the call to EOS.
"""
# Call _set_route with the new tag information
return self._set_route(ip_dest, next_hop, **kwargs)
def set_route_name(self, ip_dest, next_hop, **kwargs):
"""Set the route_name value for the specified route
Args:
ip_dest (string): The ip address of the destination in the
form of A.B.C.D/E
next_hop (string): The next hop interface or ip address
**kwargs['next_hop_ip'] (string): The next hop address on
destination interface
**kwargs['distance'] (string): Administrative distance for this
route
**kwargs['tag'] (string): Route tag
**kwargs['route_name'] (string): Route name
Returns:
True if the operation succeeds, otherwise False.
Notes:
Any existing tag value must be included in call to
set_route_name, otherwise the tag will be reset
by the call to EOS.
"""
# Call _set_route with the new route_name information
return self._set_route(ip_dest, next_hop, **kwargs)
def _build_commands(self, ip_dest, next_hop, **kwargs):
"""Build the EOS command string for ip route interactions.
Args:
ip_dest (string): The ip address of the destination in the
form of A.B.C.D/E
next_hop (string): The next hop interface or ip address
**kwargs['next_hop_ip'] (string): The next hop address on
destination interface
**kwargs['distance'] (string): Administrative distance for this
route
**kwargs['tag'] (string): Route tag
**kwargs['route_name'] (string): Route name
Returns the ip route command string to be sent to the switch for
the given set of parameters.
"""
commands = "ip route %s %s" % (ip_dest, next_hop)
next_hop_ip = kwargs.get('next_hop_ip', None)
distance = kwargs.get('distance', None)
tag = kwargs.get('tag', None)
route_name = kwargs.get('route_name', None)
if next_hop_ip is not None:
commands += " %s" % next_hop_ip
if distance is not None:
commands += " %s" % distance
if tag is not None:
commands += " tag %s" % tag
if route_name is not None:
commands += " name %s" % route_name
return commands
def _set_route(self, ip_dest, next_hop, **kwargs):
"""Configure a static route
Args:
ip_dest (string): The ip address of the destination in the
form of A.B.C.D/E
next_hop (string): The next hop interface or ip address
**kwargs['next_hop_ip'] (string): The next hop address on
destination interface
**kwargs['distance'] (string): Administrative distance for this
route
**kwargs['tag'] (string): Route tag
**kwargs['route_name'] (string): Route name
**kwargs['delete'] (boolean): If true, deletes the specified route
instead of creating or setting values for the route
**kwargs['default'] (boolean): If true, defaults the specified
route instead of creating or setting values for the route
Returns:
True if the operation succeeds, otherwise False.
"""
commands = self._build_commands(ip_dest, next_hop, **kwargs)
delete = kwargs.get('delete', False)
default = kwargs.get('default', False)
# Prefix with 'no' if delete is set
if delete:
commands = "no " + commands
# Or with 'default' if default is setting
else:
if default:
commands = "default " + commands
return self.configure(commands)
def instance(node):
"""Returns an instance of StaticRoute
This method will create and return an instance of the StaticRoute
object passing the value of API to the object. The instance method
is required for the resource to be autoloaded by the Node object
Args:
node (Node): The node argument passes an instance of Node to the
resource
"""
return StaticRoute(node)