Skip to content

Commit

Permalink
Merge bb9e132 into e583b85
Browse files Browse the repository at this point in the history
  • Loading branch information
dathelen committed Sep 8, 2016
2 parents e583b85 + bb9e132 commit 638bba3
Show file tree
Hide file tree
Showing 5 changed files with 414 additions and 18 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# make unittest -- runs the unit tests
# make systest -- runs the system tests
# make clean -- clean distutils
# make coverage_report -- code coverage report
#
########################################################
# variable section
Expand Down
202 changes: 184 additions & 18 deletions pyeapi/api/ospf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2014, Arista Networks, Inc.
# Copyright (c) 2016, Arista Networks, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -29,33 +29,47 @@
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
'''
API module for Ospf
'''
"""Module for working with OSPF configuration in EOS
import re

from collections import namedtuple
This module provides an API for creating/modifying/deleting
OSPF configurations
import netaddr
"""

from pyeapi.api import Entity, EntityCollection
import re
from pyeapi.api import Entity
from pyeapi.utils import make_iterable


class Ospf(Entity):
# The Ospf class implements global Ospf router configuration
""" The Ospf class implements global Ospf router configuration
"""

def __init__(self, *args, **kwargs):
super(Ospf, self).__init__(*args, **kwargs)
pass

def get(self):
# Returns the OSPF routing configuration as a dict object
"""Returns the OSPF routing configuration
Args:
None
Returns:
dict:
keys: router_id (int): OSPF router-id
networks (dict): All networks that
are advertised in OSPF
ospf_process_id (int): OSPF proc id
redistribution (dict): All protocols that
are configured to be
redistributed in OSPF
shutdown (bool): Gives the current shutdown
off the process
"""

config = self.get_block('^router ospf .*')
if not config:
return None

response = dict()
response.update(self._parse_router_id(config))
response.update(self._parse_networks(config))
Expand All @@ -66,15 +80,41 @@ def get(self):
return response

def _parse_ospf_process_id(self, config):
"""Parses config file for the OSPF proc ID
Args:
config(str): Running configuration
Returns:
dict: key: ospf_process_id (int)
"""
match = re.search(r'^router ospf (\d+)', config)
return dict(ospf_process_id=int(match.group(1)))

def _parse_router_id(self, config):
"""Parses config file for the OSPF router ID
Args:
config(str): Running configuration
Returns:
dict: key: router_id (str)
"""
match = re.search(r'router-id ([^\s]+)', config)
value = match.group(1) if match else None
return dict(router_id=value)

def _parse_networks(self, config):
"""Parses config file for the networks advertised
by the OSPF process
Args:
config(str): Running configuration
Returns:
list: dict:
keys: network (str)
netmask (str)
area (str)
"""

networks = list()
regexp = r'network (.+)/(\d+) area (\d+\.\d+\.\d+\.\d+)'
matches = re.findall(regexp, config)
Expand All @@ -83,6 +123,15 @@ def _parse_networks(self, config):
return dict(networks=networks)

def _parse_redistribution(self, config):
"""Parses config file for the OSPF router ID
Args:
config (str): Running configuration
Returns:
list: dict:
keys: protocol (str)
route-map (optional) (str)
"""
redistributions = list()
regexp = r'redistribute .*'
matches = re.findall(regexp, config)
Expand All @@ -96,77 +145,194 @@ def _parse_redistribution(self, config):
# complex redist eg 'redistribute bgp route-map NYSE-RP-MAP'
protocol = ospf_redist[1]
route_map_name = ospf_redist[3]
redistributions.append(dict(protocol=protocol, route_map=route_map_name))
redistributions.append(dict(protocol=protocol,
route_map=route_map_name))
return dict(redistributions=redistributions)

def _parse_shutdown(self, config):
"""Parses config file for the OSPF router ID
Args:
config(str): Running configuration
Returns:
dict: key: shutdown (bool)
"""

value = 'no shutdown' in config
return dict(shutdown=not value)

def set_shutdown(self):
"""Shutdowns the OSPF process
Args:
None
Returns:
bool: True if the commands are completed successfully
"""

cmd = 'shutdown'
return self.configure_ospf(cmd)

def set_no_shutdown(self):
"""Removes the shutdown property from the OSPF process
Args:
None
Returns:
bool: True if the commands are completed successfully
"""


cmd = 'no shutdown'
return self.configure_ospf(cmd)

def delete(self):
"""Removes the entire ospf process from the running configuration
Args:
None
Returns:
bool: True if the command completed succssfully
"""
config = self.get()
if not config:
return True
command = 'no router ospf {}'.format(config['ospf_process_id'])
return self.configure(command)

def create(self, ospf_process_id):
"""Creates a OSPF process in the default VRF
Args:
ospf_process_id (str): The OSPF proccess Id value
Returns:
bool: True if the command completed successfully
Exception:
ValueError: If the ospf_process_id passed in less
than 0 or greater than 65536
"""
value = int(ospf_process_id)
if not 0 < value < 65536:
raise ValueError('ospf as must be between 1 and 65535')
command = 'router ospf {}'.format(ospf_process_id)
return self.configure(command)

def configure_ospf(self, cmd):
"""Allows for a list of OSPF subcommands to be configured"
Args:
cmd: (list or str): Subcommand to be entered
Returns:
bool: True if all the commands completed successfully
"""
config = self.get()
cmds = ['router ospf {}'.format(config['ospf_process_id'])]
cmds.extend(make_iterable(cmd))
return super(Ospf, self).configure(cmds)

def set_router_id(self, value=None, default=False, disable=False):
cmd = self.command_builder('router-id', value=value, default=default, disable=disable)
"""Controls the router id property for the OSPF Proccess
Args:
value (str): The router-id value
default (bool): Controls the use of the default keyword
disable (bool): Controls the use of the no keyword
Returns:
bool: True if the commands are completed successfully
"""
cmd = self.command_builder('router-id', value=value,
default=default, disable=disable)
return self.configure_ospf(cmd)

def add_network(self, network, netmask, area=0):
"""Adds a network to be advertised by OSPF
Args:
network (str): The network to be advertised in dotted decimal
notation
netmask (str): The netmask to configure
area (str): The area the network belongs to.
By default this value is 0
Returns:
bool: True if the command completes successfully
Exception:
ValueError: This will get raised if network or netmask
are not passed to the method
"""
if network == '' or netmask == '':
raise ValueError('network and mask values '
'may not be empty')
cmd = 'network {}/{} area {}'.format(network, netmask, area)
return self.configure_ospf(cmd)

def remove_network(self, network, netmask, area=0):
"""Removes a network advertisment by OSPF
Args:
network (str): The network to be removed in dotted decimal
notation
netmask (str): The netmask to configure
area (str): The area the network belongs to.
By default this value is 0
Returns:
bool: True if the command completes successfully
Exception:
ValueError: This will get raised if network or netmask
are not passed to the method
"""

if network == '' or netmask == '':
raise ValueError('network and mask values '
'may not be empty')
cmd = 'no network {}/{} area {}'.format(network, netmask, area)
return self.configure_ospf(cmd)

def add_redistribution(self, protocol, route_map_name=None):
"""Adds a protocol redistribution to OSPF
Args:
protocol (str): protocol to redistribute
route_map_name (str): route-map to be used to
filter the protocols
Returns:
bool: True if the command completes successfully
Exception:
ValueError: This will be raised if the protocol pass is not one
of the following: [rip, bgp, static, connected]
"""
protocols = ['bgp', 'rip', 'static', 'connected']
if protocol not in protocols:
raise ValueError('redistributed protocol must be'
'bgp, connected, rip or static')
if route_map_name is None:
cmd = 'redistribute {}'.format(protocol)
else:
cmd = 'redistribute {} route-map {}'.format(protocol, route_map_name)
cmd = 'redistribute {} route-map {}'.format(protocol,
route_map_name)
return self.configure_ospf(cmd)

def remove_redistribution(self, protocol):
"""Removes a protocol redistribution to OSPF
Args:
protocol (str): protocol to redistribute
route_map_name (str): route-map to be used to
filter the protocols
Returns:
bool: True if the command completes successfully
Exception:
ValueError: This will be raised if the protocol pass is not one
of the following: [rip, bgp, static, connected]
"""

protocols = ['bgp', 'rip', 'static', 'connected']
if protocol not in protocols:
raise ValueError('redistributed protocol must be'
'bgp, connected, rip or static')
cmd = 'no redistribute {}'.format(protocol)
cmd = 'no redistribute {}'.format(protocol)
return self.configure_ospf(cmd)

def instance(api):
"""Returns an instance of Ospf
"""
return Ospf(api)
1 change: 1 addition & 0 deletions test/fixtures/running_config.ospf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ router ospf 65000
distance ospf inter-area 110
redistribute bgp route-map RM-IN
redistribute bgp route-map RM-OUT
redistribute static
area 0.0.0.0 default-cost 10
network 172.16.10.0/24 area 0.0.0.0
network 172.17.0.0/16 area 0.0.0.0
Expand Down
Loading

0 comments on commit 638bba3

Please sign in to comment.