1# 2# Copyright (c) 2014, Arista Networks, Inc. 3# All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are 7# met: 8# 9# Redistributions of source code must retain the above copyright notice, 10# this list of conditions and the following disclaimer. 11# 12# Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# Neither the name of Arista Networks nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS 24# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 30# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31# 32"""Module for working with EOS static routes 33 34The staticroute resource provides configuration management of static 35route resources on an EOS node. It provides the following class 36implementations: 37 38 * StaticRoute - Configure static routes in EOS 39 40StaticRoute Attributes: 41 ip_dest (string): The ip address of the destination in the 42 form of A.B.C.D/E 43 44 next_hop (string): The next hop interface or ip address 45 next_hop_ip (string): The next hop address on destination interface 46 distance (int): Administrative distance for this route 47 tag (int): Route tag 48 route_name (string): Route name 49 50Notes: 51 The 'default' prefix function of the 'ip route' command, 52 'default ip route ...', currently equivalent to the 'no ip route ...' 53 command. 54""" 55 56import re 57 58from pyeapi.api import EntityCollection 59 60# Define the regex to match ip route lines (by lines in regex): 61# 'ip route' header 62# ip_dest 63# next_hop 64# next_hop_ip 65# distance 66# tag 67# name 68ROUTES_RE = re.compile(r'(?<=^ip route)' 69 r' (\d+\.\d+\.\d+\.\d+\/\d+)' 70 r' (\d+\.\d+\.\d+\.\d+|\S+)' 71 r'(?: (\d+\.\d+\.\d+\.\d+))?' 72 r' (\d+)' 73 r'(?: tag (\d+))?' 74 r'(?: name (\S+))?', re.M) 75 76 77class StaticRoute(EntityCollection): 78 """The StaticRoute class provides a configuration instance 79 for working with static routes 80 81 """ 82 83 def __str__(self): 84 return 'StaticRoute' 85 86 def get(self, name): 87 """Retrieves the ip route information for the destination 88 ip address specified. 89 90 Args: 91 name (string): The ip address of the destination in the 92 form of A.B.C.D/E 93 94 Returns: 95 dict: An dict object of static route entries in the form:: 96 97 { ip_dest: 98 { next_hop: 99 { next_hop_ip: 100 { distance: 101 { 'tag': tag, 102 'route_name': route_name 103 } 104 } 105 } 106 } 107 } 108 109 If the ip address specified does not have any associated 110 static routes, then None is returned. 111 112 Notes: 113 The keys ip_dest, next_hop, next_hop_ip, and distance in 114 the returned dictionary are the values of those components 115 of the ip route specification. If a route does not contain 116 a next_hop_ip, then that key value will be set as 'None'. 117 """ 118 119 # Return the route configurations for the specified ip address, 120 # or None if its not found 121 return self.getall().get(name) 122 123 def getall(self): 124 """Return all ip routes configured on the switch as a resource dict 125 126 Returns: 127 dict: An dict object of static route entries in the form:: 128 129 { ip_dest: 130 { next_hop: 131 { next_hop_ip: 132 { distance: 133 { 'tag': tag, 134 'route_name': route_name 135 } 136 } 137 } 138 } 139 } 140 141 If the ip address specified does not have any associated 142 static routes, then None is returned. 143 144 Notes: 145 The keys ip_dest, next_hop, next_hop_ip, and distance in 146 the returned dictionary are the values of those components 147 of the ip route specification. If a route does not contain 148 a next_hop_ip, then that key value will be set as 'None'. 149 """ 150 151 # Find all the ip routes in the config 152 matches = ROUTES_RE.findall(self.config) 153 154 # Parse the routes and add them to the routes dict 155 routes = dict() 156 for match in matches: 157 158 # Get the four identifying components 159 ip_dest = match[0] 160 next_hop = match[1] 161 next_hop_ip = None if match[2] == '' else match[2] 162 distance = int(match[3]) 163 164 # Create the data dict with the remaining components 165 data = {} 166 data['tag'] = None if match[4] == '' else int(match[4]) 167 data['route_name'] = None if match[5] == '' else match[5] 168 169 # Build the complete dict entry from the four components 170 # and the data. 171 # temp_dict = parent_dict[key] = parent_dict.get(key, {}) 172 # This creates the keyed dict in the parent_dict if it doesn't 173 # exist, or reuses the existing keyed dict. 174 # The temp_dict is used to make things more readable. 175 ip_dict = routes[ip_dest] = routes.get(ip_dest, {}) 176 nh_dict = ip_dict[next_hop] = ip_dict.get(next_hop, {}) 177 nhip_dict = nh_dict[next_hop_ip] = nh_dict.get(next_hop_ip, {}) 178 nhip_dict[distance] = data 179 180 return routes 181 182 def create(self, ip_dest, next_hop, **kwargs): 183 """Create a static route 184 185 Args: 186 ip_dest (string): The ip address of the destination in the 187 form of A.B.C.D/E 188 next_hop (string): The next hop interface or ip address 189 **kwargs['next_hop_ip'] (string): The next hop address on 190 destination interface 191 **kwargs['distance'] (string): Administrative distance for this 192 route 193 **kwargs['tag'] (string): Route tag 194 **kwargs['route_name'] (string): Route name 195 196 Returns: 197 True if the operation succeeds, otherwise False. 198 """ 199 200 # Call _set_route with delete and default set to False 201 return self._set_route(ip_dest, next_hop, **kwargs) 202 203 def delete(self, ip_dest, next_hop, **kwargs): 204 """Delete a static route 205 206 Args: 207 ip_dest (string): The ip address of the destination in the 208 form of A.B.C.D/E 209 next_hop (string): The next hop interface or ip address 210 **kwargs['next_hop_ip'] (string): The next hop address on 211 destination interface 212 **kwargs['distance'] (string): Administrative distance for this 213 route 214 **kwargs['tag'] (string): Route tag 215 **kwargs['route_name'] (string): Route name 216 217 Returns: 218 True if the operation succeeds, otherwise False. 219 """ 220 221 # Call _set_route with the delete flag set to True 222 kwargs.update({'delete': True}) 223 return self._set_route(ip_dest, next_hop, **kwargs) 224 225 def default(self, ip_dest, next_hop, **kwargs): 226 """Set a static route to default (i.e. delete the matching route) 227 228 Args: 229 ip_dest (string): The ip address of the destination in the 230 form of A.B.C.D/E 231 next_hop (string): The next hop interface or ip address 232 **kwargs['next_hop_ip'] (string): The next hop address on 233 destination interface 234 **kwargs['distance'] (string): Administrative distance for this 235 route 236 **kwargs['tag'] (string): Route tag 237 **kwargs['route_name'] (string): Route name 238 239 Returns: 240 True if the operation succeeds, otherwise False. 241 """ 242 243 # Call _set_route with the default flag set to True 244 kwargs.update({'default': True}) 245 return self._set_route(ip_dest, next_hop, **kwargs) 246 247 def set_tag(self, ip_dest, next_hop, **kwargs): 248 """Set the tag value for the specified route 249 250 Args: 251 ip_dest (string): The ip address of the destination in the 252 form of A.B.C.D/E 253 next_hop (string): The next hop interface or ip address 254 **kwargs['next_hop_ip'] (string): The next hop address on 255 destination interface 256 **kwargs['distance'] (string): Administrative distance for this 257 route 258 **kwargs['tag'] (string): Route tag 259 **kwargs['route_name'] (string): Route name 260 261 Returns: 262 True if the operation succeeds, otherwise False. 263 264 Notes: 265 Any existing route_name value must be included in call to 266 set_tag, otherwise the tag will be reset 267 by the call to EOS. 268 """ 269 270 # Call _set_route with the new tag information 271 return self._set_route(ip_dest, next_hop, **kwargs) 272 273 def set_route_name(self, ip_dest, next_hop, **kwargs): 274 """Set the route_name value for the specified route 275 276 Args: 277 ip_dest (string): The ip address of the destination in the 278 form of A.B.C.D/E 279 next_hop (string): The next hop interface or ip address 280 **kwargs['next_hop_ip'] (string): The next hop address on 281 destination interface 282 **kwargs['distance'] (string): Administrative distance for this 283 route 284 **kwargs['tag'] (string): Route tag 285 **kwargs['route_name'] (string): Route name 286 287 Returns: 288 True if the operation succeeds, otherwise False. 289 290 Notes: 291 Any existing tag value must be included in call to 292 set_route_name, otherwise the tag will be reset 293 by the call to EOS. 294 """ 295 296 # Call _set_route with the new route_name information 297 return self._set_route(ip_dest, next_hop, **kwargs) 298 299 def _build_commands(self, ip_dest, next_hop, **kwargs): 300 """Build the EOS command string for ip route interactions. 301 302 Args: 303 ip_dest (string): The ip address of the destination in the 304 form of A.B.C.D/E 305 next_hop (string): The next hop interface or ip address 306 **kwargs['next_hop_ip'] (string): The next hop address on 307 destination interface 308 **kwargs['distance'] (string): Administrative distance for this 309 route 310 **kwargs['tag'] (string): Route tag 311 **kwargs['route_name'] (string): Route name 312 313 Returns the ip route command string to be sent to the switch for 314 the given set of parameters. 315 """ 316 317 commands = "ip route %s %s" % (ip_dest, next_hop) 318 319 next_hop_ip = kwargs.get('next_hop_ip', None) 320 distance = kwargs.get('distance', None) 321 tag = kwargs.get('tag', None) 322 route_name = kwargs.get('route_name', None) 323 324 if next_hop_ip is not None: 325 commands += " %s" % next_hop_ip 326 if distance is not None: 327 commands += " %s" % distance 328 if tag is not None: 329 commands += " tag %s" % tag 330 if route_name is not None: 331 commands += " name %s" % route_name 332 333 return commands 334 335 def _set_route(self, ip_dest, next_hop, **kwargs): 336 """Configure a static route 337 338 Args: 339 ip_dest (string): The ip address of the destination in the 340 form of A.B.C.D/E 341 next_hop (string): The next hop interface or ip address 342 **kwargs['next_hop_ip'] (string): The next hop address on 343 destination interface 344 **kwargs['distance'] (string): Administrative distance for this 345 route 346 **kwargs['tag'] (string): Route tag 347 **kwargs['route_name'] (string): Route name 348 **kwargs['delete'] (boolean): If true, deletes the specified route 349 instead of creating or setting values for the route 350 **kwargs['default'] (boolean): If true, defaults the specified 351 route instead of creating or setting values for the route 352 353 Returns: 354 True if the operation succeeds, otherwise False. 355 """ 356 357 commands = self._build_commands(ip_dest, next_hop, **kwargs) 358 359 delete = kwargs.get('delete', False) 360 default = kwargs.get('default', False) 361 362 # Prefix with 'no' if delete is set 363 if delete: 364 commands = "no " + commands 365 # Or with 'default' if default is setting 366 else: 367 if default: 368 commands = "default " + commands 369 370 return self.configure(commands) 371 372 373def instance(node): 374 """Returns an instance of StaticRoute 375 376 This method will create and return an instance of the StaticRoute 377 object passing the value of API to the object. The instance method 378 is required for the resource to be autoloaded by the Node object 379 380 Args: 381 node (Node): The node argument passes an instance of Node to the 382 resource 383 """ 384 return StaticRoute(node) 385