# Source code for pyeapi.api.ospf

    #
    # Copyright (c) 2016, 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 OSPF configuration in EOS

    This module provides an API for creating/modifying/deleting
    OSPF configurations

    """

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

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

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

        def get(self, vrf=None):
            """Returns the OSPF routing configuration

               Args:
                    vrf (str): VRF name to return OSPF routing config for
               Returns:
                   dict:
                        keys:
                             router_id (int): OSPF router-id

                             vrf (str): VRF of the OSPF process
                             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
            """
            match = '^router ospf .*'
            if vrf:
                match += ' vrf %s' % vrf
            config = self.get_block(match)
            if not config:
                return None

            response = dict()
            response.update(self._parse_router_id(config))
            response.update(self._parse_vrf(config))
            response.update(self._parse_networks(config))
            response.update(self._parse_ospf_process_id(config))
            response.update(self._parse_redistribution(config))
            response.update(self._parse_shutdown(config))

            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_vrf(self, config):
            """Parses config file for the OSPF vrf name

               Args:
                   config(str):  Running configuration
               Returns:
                   dict: key: ospf_vrf (str)
            """
            match = re.search(r'^router ospf \d+ vrf (\w+)', config)
            if match:
                return dict(vrf=match.group(1))
            return dict(vrf='default')

        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)
            for (network, netmask, area) in matches:
                networks.append(dict(network=network, netmask=netmask, area=area))
            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)
            for line in matches:
                ospf_redist = line.split()
                if len(ospf_redist) == 2:
                    # simple redist: eg 'redistribute bgp'
                    protocol = ospf_redist[1]
                    redistributions.append(dict(protocol=protocol))
                if len(ospf_redist) == 4:
                    # 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))
            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, vrf=None):
            """Creates a OSPF process in the specified VRF or the default VRF.

               Args:
                    ospf_process_id (str): The OSPF process Id value
                    vrf (str): The VRF to apply this OSPF process to
               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)
            if vrf:
                command += ' vrf %s' % vrf
            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):
            """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)
            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)
            return self.configure_ospf(cmd)

    def instance(api):
        """Returns an instance of Ospf
        """
        return Ospf(api)