Source code for pyhpecw7.features.interface

"""Manage interfaces on HPCOM7 devices.
"""

from pyhpecw7.utils.xml.lib import reverse_value_map
from pyhpecw7.features.errors import InterfaceCreateError, InterfaceTypeError,\
    InterfaceAbsentError, InterfaceParamsError, InterfaceVlanMustExist
from pyhpecw7.features.vlan import Vlan

from pyhpecw7.utils.xml.lib import *


[docs]class Interface(object): """This class is used to get and build interface configurations on ``HPCOM7`` devices. Args: device (HPCOM7): connected instance of a ``phyp.comware.HPCOM7`` object. interface_name (str): The name of the interface. Attributes: device (HPCOM7): connected instance of a ``phyp.comware.HPCOM7`` object. interface_name (str): The name of the interface. iface_index (str): The device's internal number representation of an interface. iface_type (str): The type of interface, for example: 'LoopBack', 'FortyGigE'. is_ethernet (bool): Whether the interface is ethernet. is_routed (bool): Whether the interface is in layer 3 mode. If this is ``False``, the interface is either in bridged mode or does not exist. iface_exists (bool): Whether the interface exists. Physical interfaces should always exist. Logical interfaces may or may not exist. """ def __init__(self, device, interface_name): # used to map key values from our dictionary model # to expected XML tags and vice versa self._key_map = { 'admin': 'AdminStatus', 'speed': 'ConfigSpeed', 'duplex': 'ConfigDuplex', 'description': 'Description', 'type': 'PortLayer' } # used to map value values from our dictionary model # to expected XML tags and vice versa self._value_map = { 'AdminStatus': {'1': 'up', '2': 'down'}, 'ConfigSpeed': {'1': 'auto', '2': '10', '4': '100', '32': '1000', '1024': '10000', '4096': '20000', '8192': '40000', '16384': '100000'}, 'ConfigDuplex': {'1': 'full', '2': 'half', '3': 'auto'}, 'PortLayer': {'1': 'bridged', '2': 'routed'} } self._iface_types = set(['FortyGigE', 'Tunnel', 'LoopBack', 'Vlan-interface', 'Bridge-Aggregation', 'Route-Aggregation', 'GigabitEthernet', 'TwentyGigE', 'Ten-GigabitEthernet', 'HundredGigE']) # xml tags self._iface_row_name = 'Interface' self._iface_index_name = 'IfIndex' # usd in conjunction with key map and value map above self._r_key_map = dict(reversed(item) for item in self._key_map.items()) self._r_value_map = reverse_value_map(self._r_key_map, self._value_map) # connect to the device and get more information self.interface_name, self.iface_type = self._iface_type(interface_name) self.device = device # The interface index is needed for most interface NETCONF requests self.iface_index = self._get_iface_index() self.is_ethernet, self.is_routed = self._is_ethernet_is_routed() self.iface_exists = True if self.iface_index else False def _iface_type(self, if_name): """Return the normalized interface name and type from a denormalized interface name. """ if if_name.lower().startswith('gi'): if_type = 'GigabitEthernet' elif if_name.lower().startswith('ten'): if_type = 'Ten-GigabitEthernet' elif if_name.lower().startswith('fo'): if_type = 'FortyGigE' elif if_name.lower().startswith('vl'): if_type = 'Vlan-interface' elif if_name.lower().startswith('lo'): if_type = 'LoopBack' elif if_name.lower().startswith('br'): if_type = 'Bridge-Aggregation' elif if_name.lower().startswith('ro'): if_type = 'Route-Aggregation' elif if_name.lower().startswith('tu'): if_type = 'Tunnel' elif if_name.lower().startswith('tw'): if_type = 'TwentyGigE' elif if_name.lower().startswith('hu'): if_type = 'HundredGigE' else: if_type = None number_list = if_name.split(' ') if len(number_list) == 2: number = number_list[-1].strip() else: number = self._get_number(if_name) if if_type: proper_interface = if_type + number else: proper_interface = if_name return proper_interface, if_type def _get_number(self, if_name): digits = '' for char in if_name: if char.isdigit() or char == '/' or char == ':': digits += char return digits def _get_iface_index(self): """Return the interface index given the self.interface_name attribute by asking the device. If the interface doesn't exist, return the empty string. """ E = data_element_maker() top = E.top( E.Ifmgr( E.Interfaces( E.Interface( E.Name(self.interface_name) ) ) ) ) nc_get_reply = self.device.get(('subtree', top)) reply_data = find_in_data( self._iface_index_name, nc_get_reply.data_ele) if reply_data is None: return '' return reply_data.text def _is_ethernet_is_routed(self): """Return whether the interface is ethernet and whether it is routed. If the interface doesn't exist, return False. """ E = data_element_maker() top = E.top( E.Ifmgr( E.Interfaces( E.Interface( E.IfIndex(self.iface_index) ) ) ) ) nc_get_reply = self.device.get(('subtree', top)) reply_data = find_in_data('ifType', nc_get_reply.data_ele) routed_reply_data = find_in_data('PortLayer', nc_get_reply.data_ele) is_ethernet = False is_routed = False try: if reply_data.text == '6': is_ethernet = True except AttributeError: pass try: if routed_reply_data.text == '2': is_routed = True except AttributeError: pass return is_ethernet, is_routed
[docs] def update(self): """Update ``self.iface_index`` and ``self.iface_exists``. Usually called after a logical interface is created. Raises: InterfaceCreateError: if the interface hasn't yet been successfully created. Note: It is the responsibility of the caller to call ``update()` after staging (``create_logical()``) *and* executing (``execute()`` on this class's ``device`` object) of commands to create an interface. """ if_index = self._get_iface_index() if not if_index: raise InterfaceCreateError(self.interface_name) self.iface_index = if_index self.iface_exists = True
[docs] def get_default_config(self): """Return the default configuration of an interface. Returns: A dictionary of default interface configuration parameters, depending on the type of interface. For example, for ethernet interfaces:: { 'description': 'FortyGigE1/0/1 Interface', 'admin': 'up', 'speed': 'auto', 'duplex': 'auto', 'type': 'bridged' } """ if not self.iface_type: return None defaults = {} defaults['description'] = self.interface_name + ' Interface' defaults['admin'] = 'up' if self.is_ethernet: defaults['speed'] = 'auto' defaults['duplex'] = 'auto' defaults['type'] = 'bridged' elif self.iface_type == 'Bridge-Aggregation': defaults['type'] = 'bridged' else: defaults['type'] = 'routed' return defaults
[docs] def param_check(self, **params): """Checks given parameters against the interface for various errors. Args: **params: see Keyword Args Keyword Args: admin (str): The up/down state of the interface. 'up' or 'down'. speed (str): The speed of the interface, in Mbps. duplex (str): The duplex of the interface. 'full', 'half', or 'auto'. description (str): The textual description of the interface. type (str): Whether the interface is in layer 2 or layer 3 mode. 'bridged' or 'routed'. Raises: InterfaceTypeError: if the given interface isn't a valid type. InterfaceAbsentError: if the given interface is of type is_ethernet and doesn't exist. InterfaceParamsError: if 'speed' or 'duplex' are supplied for a non ethernet interface. InterfaceVlanMustExist: if the interface is of type 'Vlan-interface' and the the associated vlan doesn't exist. """ if not self.iface_type: raise InterfaceTypeError( self.interface_name, list(self._iface_types)) if not self.iface_exists: if self.iface_type in {'FortyGigE', 'GigabitEthernet', 'TwentyGigE', 'Ten-GigabitEthernet', 'HundredGigE'}: raise InterfaceAbsentError(self.interface_name) if not self.is_ethernet: param_names = [] if params.get('speed'): param_names.append('speed') if params.get('duplex'): param_names.append('duplex') if param_names: raise InterfaceParamsError(self.interface_name, param_names) if self.iface_type == 'Vlan-interface': number = self.interface_name.split('Vlan-interface')[1] vlan = Vlan(self.device, number) if not vlan.get_config(): raise InterfaceVlanMustExist(self.interface_name, number)
[docs] def get_config(self): """Return the currently configured parameters for the interface. Returns: A dictionary of currently configured parameters for the interface, including: :admin (str): The up/down state of the interface. 'up' or 'down'. :speed (str): The speed of the interface, in Mbps. :duplex (str): The duplex of the interface. 'full', 'half', or 'auto'. :description (str): The textual description of the interface. :type (str): Whether the interface is in layer 2 or layer 3 mode. 'bridged' or 'routed'. """ E = data_element_maker() top = E.top( E.Ifmgr( E.Interfaces( E.Interface( E.IfIndex(self.iface_index) ) ) ) ) nc_get_reply = self.device.get(('subtree', top)) reply_data = find_in_data(self._iface_row_name, nc_get_reply.data_ele) if reply_data is None: return {} return data_elem_to_dict(reply_data, self._key_map, value_map=self._value_map)
[docs] def create_logical(self, stage=False): """Stage or execute the configuration to create a logical interface. Supported types include 'LoopBack', 'Vlan-interface', 'Bridge-Aggregation', and 'Route-Aggregation' Note: When stage=True, it's the caller's responsibility to call ``execute()`` on this class's ``device`` object after this method is called. Note: After execution, the caller must call ``update()`` on this class. Returns: True if successful. Raises: InterfaceCreateError: if the logical interface cannot be created. """ return self._logical_iface(stage=stage)
[docs] def remove_logical(self, stage=False): """Stage or execute the configuration to remove a logical interface. Supported types include 'LoopBack', 'Vlan-interface', 'Bridge-Aggregation', and 'Route-Aggregation' Args: stage (bool): whether to stage the commands or execute immediately Note: It's the caller's responsibility to call ``execute()`` on this class's ``device`` object after this method is called. Returns: True if stage=True and staging is successful etree.Element XML response if immediate execution Raises: InterfaceCreateError: if the logical interface cannot be removed. """ return self._logical_iface(remove=True, stage=stage)
def _logical_iface(self, remove=False, stage=False): """Stage or execute the configuration to create or remove a logical interface. Args: remove (bool): If ``True``, the logical interface is removed. If ``False``, the logical interface is created. stage (bool): whether to stage the commands or execute immediately Returns: True if stage=True and staging is successful etree.Element XML response if immediate execution """ logic_type_map = {'LoopBack': '16', 'Vlan-interface': '41', 'Bridge-Aggregation': '56', 'Route-Aggregation': '67'} if self.iface_type not in logic_type_map: raise InterfaceCreateError(self.interface_name) iface_number = self.interface_name.split(self.iface_type)[1] E = action_element_maker() top = E.top( E.Ifmgr( E.LogicInterfaces( E.Interface( E.IfTypeExt(logic_type_map[self.iface_type]), E.Number(iface_number) ) ) ) ) if remove: find_in_action('Interface', top).append(E.Remove()) if stage: return self.device.stage_config(top, 'action') else: return self.device.action(top)
[docs] def build(self, stage=False, **params): """Stage or execute the configuration to modify an interface. Args: stage (bool): whether to stage the commands or execute immediately **params: see Keyword Args. Keyword Args: admin (str): The up/down state of the interface. 'up' or 'down'. speed (str): The speed of the interface, in Mbps. duplex (str): The duplex of the interface. 'full', 'half', or 'auto'. description (str): The textual description of the interface. type (str): Whether the interface is in layer 2 or layer 3 mode. 'bridged' or 'routed'. Raises: InterfaceCreateError: if a logical interface cannot be created. Returns: True if stage=True and staging is successful etree.Element XML response if immediate execution """ return self._build_config(state='present', stage=stage, **params)
[docs] def default(self, stage=False): """Stage or execute the configuration to default an interface. stage (bool): whether to stage the commands or execute immediately Returns: True if stage=True and staging is successful etree.Element XML response if immediate execution """ return self._build_config(state='default', stage=stage)
def _build_config(self, state, stage=False, **params): """Stage or execute the configuration to configure, default, or remove an interface. Args: state (str): 'present' configures, 'absent' defaults, 'default' defaults. stage (bool): whether to stage the commands or execute immediately **params: Used when state=present, see Keyword Args. Keyword Args: admin (str): The up/down state of the interface. 'up' or 'down'. speed (str): The speed of the interface, in Mbps. duplex (str): The duplex of the interface. 'full', 'half', or 'auto'. description (str): The textual description of the interface. type (str): Whether the interface is in layer 2 or layer 3 mode. 'bridged' or 'routed'. Returns: True if stage=True and staging is successful etree.Element XML response if immediate execution False if illegal operation, e.g. removing a physical interface """ if state == 'default': if self.iface_exists: E = action_element_maker() top = E.top( E.Ifmgr( E.Interfaces( E.Interface( E.IfIndex(self.iface_index), E.Default() ) ) ) ) if stage: return self.device.stage_config(top, 'action') else: return self.device.action(top) if state == 'present': params[self._iface_index_name] = self.iface_index EN = nc_element_maker() EC = config_element_maker() config = EN.config( EC.top( EC.Ifmgr( EC.Interfaces( EC.Interface( *config_params(params, self._key_map, value_map=self._r_value_map) ) ) ) ) ) if stage: return self.device.stage_config(config, 'edit_config') else: return self.device.edit_config(config) if state == 'absent': if self.is_ethernet: return self._build_config('default', stage=stage) return False