#
# 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 spanning-tree in EOS
This module provides an API for working with spanning-tree configuration
in EOS. This includes both global spanning-tree configuration as well as
interface config.
Global Parameters:
mode (string): The spanning-tree operational mode. Accepted values
for this version are 'mstp' or 'none'. This configuration
parameter is not defaultable
interfaces (StpInterfaces): The collection of STP enabled interfaces.
Interface Parameters:
name (string): The name of the interface the STP configuration is in
reference to. The interface name is the full interface identifier
portfast (string): The portfast configuration value for the interface.
Accepted values are 'edge', 'network', or 'disabled'
bpduguard (boolean): True if the BPDU Guard feature is enabled on the
interface or False if it is disabled
"""
import re
from pyeapi.api import Entity, EntityCollection
[docs]class Stp(Entity):
"""The Stp class implements global configuration for spanning-tree
The spanning-tree protocol provides both global and interface
configuration options. This class is the top-level class that provides
access to all spanning-tree configuration options supported.
Example:
The below example demonstrates how to use the STP class to work
with both global configuration and interface configuration.
>>> import pyeapi.resources.stp
>>> stp = pyeapi.resources.stp.instance(node)
>>> stp.set_mode('mstp')
True
>>> stp.interfaces.set_bpduguard('Ethernet1', True)
True
Attributes:
interfaces (StpInterfaces): An instance for configuration spanning-tree
configuration interfaces
instances (StpInstances): An instance object for working with
spanning-tree global instances
"""
def __init__(self, *args, **kwargs):
super(Stp, self).__init__(*args, **kwargs)
self._interfaces = None
self._instances = None
[docs] def get(self):
"""Returns the spanning-tree configuration as a dict object
The dictionary object represents the entire spanning-tree
configuration derived from the nodes running config. This
includes both globally configuration attributes as well as
interfaces and instances. See the StpInterfaces and StpInstances
classes for the key/value pair definitions.
Note:
See the individual classes for detailed message structures
Returns:
A Python dictionary object of key/value pairs the represent
the entire supported spanning-tree configuration::
{
"mode": [mstp, none],
"interfaces": {...},
"instances": {...}
}
"""
return dict(interfaces=self.interfaces.getall(),
instances=self.instances.getall())
@property
def interfaces(self):
if self._interfaces is not None:
return self._interfaces
self._interfaces = StpInterfaces(self.node)
return self._interfaces
@property
def instances(self):
if self._instances is not None:
return self._instances
self._instances = StpInstances(self.node)
return self._instances
[docs] def set_mode(self, value=None, default=False, disable=False):
"""Configures the global spanning-tree mode
Note:
This configuration parameter is not defaultable
Args:
value (string): The value to configure the global spanning-tree
mode of operation. Valid values include 'mstp', 'none'
default (bool): Set the global spanning-tree mode to default.
disable (bool): Negate the global spanning-tree mode.
Returns:
True if the configuration operation succeeds otherwise False
Raises:
ValueError if the value is not in the accepted range
"""
if not default and not disable:
if value not in ['mstp', 'none']:
raise ValueError("Specified value must be one of "
"'mstp', 'none'")
cmds = self.command_builder('spanning-tree mode', value=value,
default=default, disable=disable)
return self.configure(cmds)
[docs]class StpInstances(EntityCollection):
"""Provides a configuration resource for spanning-tree instances
This class provides an API for working with spanning-tree instances from
the global configuration. Spanning tree instances work with MST
configuration
"""
[docs] def getall(self):
# TODO: (privateip, 20150106) stubbed out, needs implementation
return dict()
[docs]class StpInterfaces(EntityCollection):
"""Provides a configuration resource for spanning-tree interfaces
This class provides an API for working with spanning-tree interface
configurations. It provides access to managing specific interface
spanning-tree configuration options. Note that spanning-tree interfaces
cannot be created or deleted.
"""
[docs] def get(self, name):
"""Returns the specified interfaces STP configuration resource
The STP interface resource contains the following
* name (str): The interface name
* portfast (bool): The spanning-tree portfast admin state
* bpduguard (bool): The spanning-tree bpduguard admin state
* portfast_type (str): The spanning-tree portfast <type> value.
Valid values include "edge", "network", "normal"
Args:
name (string): The interface identifier to retrieve the config
for. Note: Spanning-tree interfaces are only supported on
Ethernet and Port-Channel interfaces
Returns:
dict: A resource dict object that represents the interface
configuration.
None: If the specified interace is not a STP port
"""
if not isvalidinterface(name):
return None
config = self.get_block(r'^interface\s%s$' % name)
resp = dict()
resp.update(self._parse_bpduguard(config))
resp.update(self._parse_portfast(config))
resp.update(self._parse_portfast_type(config))
return resp
def _parse_bpduguard(self, config):
value = 'spanning-tree bpduguard enable' in config
return dict(bpduguard=value)
def _parse_portfast(self, config):
value = 'no spanning-tree portfast' not in config
return dict(portfast=value)
def _parse_portfast_type(self, config):
if 'spanning-tree portfast network' in config:
value = 'network'
elif 'no spanning-tree portfast' in config:
value = 'normal'
else:
value = 'edge'
return dict(portfast_type=value)
[docs] def getall(self):
"""Returns the collection of STP interfaces
This method will return all of the configured spanning-tree
interfaces from the current nodes configuration.
Returns:
A Python dictionary object that represents all configured
spanning-tree interfaces indexed by interface name.
"""
interfaces_re = re.compile(r'(?<=^interface\s)(.+)$', re.M)
response = dict()
for name in interfaces_re.findall(self.config):
if name[0:2] in ['Et', 'Po']:
interface = self.get(name)
if interface:
response[name] = interface
return response
[docs] def set_portfast_type(self, name, value='normal'):
"""Configures the portfast value for the specified interface
Args:
name (string): The interface identifier to configure. The name
must be the full interface name (eg Ethernet1, not Et1).
value (string): The value to configure the portfast setting to.
Valid values include "edge", "network", "normal". The
default value is "normal"
Returns:
True if the command succeeds, otherwise False
Raises:
ValueError: Raised if an invalid interface name or value is
specified
"""
if value not in ['network', 'edge', 'normal', None]:
raise ValueError('invalid portfast type value specified')
cmds = ['spanning-tree portfast %s' % value]
if value == 'edge':
cmds.append('spanning-tree portfast auto')
return self.configure_interface(name, cmds)
[docs] def set_portfast(self, name, value=None, default=False, disable=False):
"""Configures the portfast value for the specified interface
Args:
name (string): The interface identifier to configure. The name
must be the full interface name (eg Ethernet1, not Et1)
value (bool): True if portfast is enabled otherwise False
default (bool): Configures the portfast parameter to its default
value using the EOS CLI default config command
disable (bool): Negates the portfast parameter using the EOS
CLI no config command
Returns:
True if the command succeeds, otherwise False
Raises:
ValueError: Rasied if an invalid interface name is specified
TypeError: Raised if the value keyword argument does not evaluate
to a valid boolean
"""
if value is False:
disable = True
string = 'spanning-tree portfast'
cmds = self.command_builder(string, value=value, default=default,
disable=disable)
return self.configure_interface(name, cmds)
[docs] def set_bpduguard(self, name, value=False, default=False, disable=False):
"""Configures the bpduguard value for the specified interface
Args:
name (string): The interface identifier to configure. The name
must be the full interface name (eg Ethernet1, not Et1)
value (bool): True if bpduguard is enabled otherwise False
default (bool): Configures the bpduguard parameter to its default
value using the EOS CLI default config command
disable (bool): Negates the bpduguard parameter using the EOS
CLI no config command
Returns:
True if the command succeeds, otherwise False
Raises:
ValueError: Rasied if an invalid interface name is specified
TypeError: Raised if the value keyword argument does not evaluate
to a valid boolean
"""
value = 'enable' if value else 'disable'
string = 'spanning-tree bpduguard'
cmds = self.command_builder(string, value=value, default=default,
disable=disable)
return self.configure_interface(name, cmds)
[docs]def isvalidinterface(value):
"""Checks value to see if it could be a spanning-tree interface
This function will check the value and return a boolean whether or not
the interface could be a spanning-tree interface
Note:
This function only checks *if* the interface could be a spanning-tree
interface but does not check if it *is* configured as a
spanning-tree interface
Args:
value (string): The interface name to validate
Returns:
True if it could be a spanning-tree interface, otherwise False
"""
return value[0:2] in ['Et', 'Po']
[docs]def instance(api):
return Stp(api)