text: '''
      Common base Interface classes for all OSes.
  '''
  
  import importlib
  import warnings
  import abc
  import types
  import re
  from enum import Enum
  
  from genie.utils.cisco_collections import typedset
  from genie.decorator import managedattribute, mixedmethod
  from genie.conf.base import Base, Interface as Intf, ConfigurableBase
  import genie.conf.base
  import genie.conf.base.interface
  
  from .ipv4addr import IPv4Addr
  from .ipv6addr import IPv6Addr
  from genie.libs import conf
  from genie.libs.conf.base import IPv4Address, IPv6Address, IPv4Interface, IPv6Interface, MAC
  from genie.libs.conf.vrf import Vrf
  
  
  class Option(Enum):
  
      def __str__(self):
          return str(self.value)
  
  
  class Layer(Option):
  
      # Creating static variable name for layer 2 and layer 3.
      # This way it will remove the confusion of which name to use
      L3 = 'L3'
      L2 = 'L2'
  
  
  class L2_type(Option):
      # Create the same for Trunk and Access
      # This way it will remove the confusion of which name to use
      TRUNK = 'trunk'
      ACCESS = 'access'
      DOT1Q_TUNNEL = 'dot1q-tunnel'
      FEX_FABRIC = 'fex-fabric'
      PRIVATE_VLAN = 'private-vlan'
  
  class Medium(Option):
      # Create the same for p2p or broadcast
      # This way it will remove the confusion of which name to use
      p2p = 'p2p'
      broadcast = 'broadcast'
  
  def _get_descendent_subclass(cls, subcls):
      '''Find the descendent class of cls that is a subclass of subcls.'''
      found_subclasses = set()
      subclasses_walked = set()
      subclasses_to_walk = [cls]
      while subclasses_to_walk:
          cls = subclasses_to_walk.pop(0)
          if cls in subclasses_walked:
              continue
          else:
              subclasses_walked.add(cls)
          if issubclass(cls, subcls):
              found_subclasses.add(cls)
          else:
              subclasses_to_walk.extend(cls.__subclasses__())
      if not found_subclasses:
          raise TypeError('No %r specific subclass of %r found.' % (
              subcls.__qualname__, cls.__qualname__))
      if len(found_subclasses) > 1:
          raise TypeError('Too many %r specific subclass of %r found: %r' % (
              subcls.__qualname__, cls.__qualname__, found_subclasses))
      cls = found_subclasses.pop()
      return cls
  
  
  
  
  class UnsupportedInterfaceOsWarning(UserWarning):
      pass
  
  
  class UnknownInterfaceName(UserWarning):
      pass
  
  
  class ParsedInterfaceName(types.SimpleNamespace):
  
      def __init__(self, name, device=None):
          if device is None and isinstance(name, ParsedInterfaceName):
              # copy constructor
              return super().__init__(vars(name))
          assert type(name) is str
  
          d = dict(
              # main parts
              type=None,
              number=None,
              subintf_sep=None,
              subintf=None,
              # sub-parts
              net_module=None,
              module=None,
              rack=None,
              slot=None,
              instance=None,
              port=None,
              subport=None,
              cpu=None,
              rsip=None,
          )
  
          m = re.match(r'''
              ^
              # ignore leading spaces
              \s*
              # not an empty string
              (?=\S)
              # optional <type>
              (?P<type>
                  [JTE][13]|                                      # J1, T1, E1, ...
                  (?:ODU|d|OTU|O)(?:[01234]|[12][EF]|3E[12])|     # ODU2, OTU3E2, ...
                  (?:GCC|g)[01]|                                  # GCC0, GCC1
                  [A-Za-z]+(?:-[A-Za-z]+)*                        # generic: POS, tunnel-te, odu...
              )?
              # optional spaces
              \s*
              # rest is optional too
              (?:
                  # <number>
                  (?P<number>\d+(?:[/_](?:RP|RSP|CPU)?\d+)*)
                  # optional <subintf>
                  (?:(?P<subintf_sep>[.:])(?P<subintf>\d+))?
              )?
              # ignore trailing spaces
              \s*
              $
          ''', name, re.VERBOSE | re.IGNORECASE)
          if not m:
              raise ValueError('Unrecognized interface name %r' % (name,))
          d.update(m.groupdict())
  
          if d['number']:
              m = re.match(r'^(?P<cpu>(?P<rack>\d+)[/_](?P<slot>(?:RP|RSP)?\d+)[/_](?:CPU\d+|\*))$', d['number'])
              if m:
                  # IOS-XR
                  d.update(m.groupdict())
  
              else:
                  # 0/RP0/CPU0/0
                  # 0/RSP0/CPU0/0
                  # 0/0/0/0
                  m = re.match(r'^(?P<rsip>(?P<rack>\d+)[/_](?P<slot>(?:RP|RSP)?\d+)[/_](?P<instance>\d+)[/_](?P<port>\d+))(?:[/_](?P<subport>\d+))?', d['number'])
                  if m:
                      # IOS-XR
                      d.update(m.groupdict())
                      d['cpu'] = '{rack}/{slot}/CPU0'.format_map(d)
  
                  else:
                      m = re.match(r'^(?P<module>(?:(?P<net_module>\d+)[/_])?\d+)[/_](?P<port>\d+)$', d['number'])
                      if m:
                          # IOS/NX-OS
                          d.update(m.groupdict())
  
          super().__init__(**d)
  
      def reconstruct(self):
          return '{type}{number}{subintf_sep}{subintf}'.format(
              type=self.type or '',
              number=self.number if self.number is not None else '',
              subintf_sep=(self.subintf_sep or '.') if self.subintf is not None else '',
              subintf=self.subintf if self.subintf is not None else '',
          )
  
  
  class Interface(genie.conf.base.interface.BaseInterface):
  
      ipv4addr = managedattribute(
          name='ipv4addr',
          finit=typedset(managedattribute.test_isinstance(IPv4Addr)).copy,
          type=typedset(managedattribute.test_isinstance(IPv4Addr))._from_iterable,
          doc='A `set` of IPv4Addr associated objects')
  
      def add_ipv4addr(self, ipv4addr):
          self.ipv4addr.add(ipv4addr)
  
      def remove_ipv4addr(self, ipv4addr):
          ipv4addr._device = None
          try:
              self.ipv4addr.remove(ipv4addr)
          except:
              pass
  
      ipv6addr = managedattribute(
          name='ipv6addr',
          finit=typedset(managedattribute.test_isinstance(IPv6Addr)).copy,
          type=typedset(managedattribute.test_isinstance(IPv6Addr))._from_iterable,
          doc='A `set` of IPv6Addr associated objects')
  
      def add_ipv6addr(self, ipv6addr):
          self.ipv6addr.add(ipv6addr)
  
      def remove_ipv6addr(self, ipv6addr):
          ipv6addr._device = None
          try:
              self.ipv6addr.remove(ipv6addr)
          except:
              pass
  
      ipv4 = managedattribute(
          name='ipv4',
          default=None,
          type=(None, IPv4Interface))
  
      ipv6 = managedattribute(
          name='ipv6',
          default=None,
          type=(None, IPv6Interface))
  
      vrf = managedattribute(
          name='vrf',
          default=None,
          type=(None,
                managedattribute.test_isinstance(Vrf)))
  
      # enabled
      enabled = managedattribute(
          name='enabled',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Enable the selected interfac')
  
      # link_up_down_trap_enable
      link_up_down_trap_enable = managedattribute(
          name='link_up_down_trap_enable',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Allow SNMP LINKUP and LINKDOWN traps')
  
      # link-status
      link_status = managedattribute(
          name='link_status',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='UPDOWN and CHANGE messages')
  
      # encapsulation
      class Encapsulation(Enum):
          dot1q = 'dot1q'
          isl = 'isl'
          priority_tagged = 'priority-tagged'
  
      encapsulation = managedattribute(
          name='encapsulation',
          default=None,
          type=(None, Encapsulation),
          doc='Set encapsulation type for an interface')
  
      # first_dot1q
      first_dot1q = managedattribute(
          name='first_dot1q',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='IEEE 802.1Q VLAN ID')
  
      # sedond_dot1q
      sedond_dot1q = managedattribute(
          name='second_dot1q',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Second (inner) VLAN IDs')
  
      # native_vlan_dot1q
      native_vlan_dot1q = managedattribute(
          name='native_vlan_dot1q',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Make this as native vlan')
  
      # dhcp
      dhcp = managedattribute(
          name='dhcp',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='IP Address negotiated via DHCP')
  
      # dhcp_client_id
      dhcp_client_id = managedattribute(
          name='dhcp_client_id',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Specify client-id to use')
  
      # dhcp_hostname
      dhcp_hostname = managedattribute(
          name='dhcp_hostname',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Specify value for hostname option')
  
      # unnumbered_intf_ref
      unnumbered_intf_ref = managedattribute(
          name='unnumbered_intf_ref',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Enable IP processing without an explicit address')
  
      # ipv6_unnumbered_intf_ref
      ipv6_unnumbered_intf_ref = managedattribute(
          name='ipv6_unnumbered_intf_ref',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Preferred interface for source address selection')
  
      # ipv6_enabled
      ipv6_enabled = managedattribute(
          name='ipv6_enabled',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Enable IPv6 on interface')
  
      # ipv6_autoconf
      ipv6_autoconf = managedattribute(
          name='ipv6_autoconf',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Obtain address using autoconfiguration')
  
      # ipv6_autoconf_default
      ipv6_autoconf_default = managedattribute(
          name='ipv6_autoconf_default',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Insert default route')
  
      # medium
      medium = managedattribute(
          name='medium',
          default=None,
          type=(None, Medium),
          doc='Configure Interface medium mode')
  
      # delay
      delay = managedattribute(
          name='delay',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc='Specify interface throughput delay')
  
      # load_interval
      load_interval = managedattribute(
          name='load_interval',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc='Specify interval for load calculation for an interface')
  
      # load_interval_counter
      load_interval_counter = managedattribute(
          name='load_interval_counter',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc='Specify counter for the load interval')
  
      # flowcontrol_receive
      flowcontrol_receive = managedattribute(
          name='flowcontrol_receive',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Receive pause frames')
  
      # flowcontrol_send
      flowcontrol_send = managedattribute(
          name='flowcontrol_send',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Send pause frames')
  
      # bandwidth
      bandwidth = managedattribute(
          name='bandwidth',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc='Set bandwidth informational parameter') 
  
      # cdp
      cdp = managedattribute(
          name='cdp',
          default=False,
          type=(None, managedattribute.test_istype(bool)),
          doc='Configure CDP interface parameters')
  
      # description
      description = managedattribute(
          name='description',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Enter description')
  
      # mtu
      mtu = managedattribute(
          name='mtu',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc='Configure mtu for the port')
  
      # shutdown
      shutdown = managedattribute(
          name='shutdown',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Enable/disable an interface')
      
      switchport_enable = managedattribute(
          name='switchport_enable',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Configure switchport')
  
      class SWITCHPORTMODE(Enum):
          access = 'access'
          dot1q_tunnel = 'dot1q-tunnel'
          fex_fabric = 'fex-fabric'
          private_vlan = 'private-vlan'
          trunk = 'trunk'
  
      switchport_mode = managedattribute(
          name='switchport_mode',
          default=None,
          type=(None, SWITCHPORTMODE),
          doc= 'Interface switchport mode')
  
      evpn_multisite_fabric_tracking = managedattribute(
          name='evpn_multisite_fabric_tracking',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Configure evpn multisites fabric links')
  
      evpn_multisite_dci_tracking = managedattribute(
          name='evpn_multisite_dci_tracking',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Configure evpn multisites dci links')
  
      ip_forward = managedattribute(
          name='ip_forward',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Configure ip forward')
  
  
      @abc.abstractmethod
      def build_config(self, *args, **kwargs):
          '''Derived OS-specific classes must provide build_config and
          build_unconfig methods.
  
          Example:
  
              Scenario #1: OS-specific interface module with inline configuration
              support::
  
                  # <os>/interface.py
                  class Interface(genie.libs.conf.interface.Interface):
                      def build_config(self, ...):
                          ...
                          return configurations
  
              Scenario #2: OS-specific interface module with context-specific
              configuration modules::
  
                  # <os>/interface.py
                  class Interface(genie.libs.conf.interface.Interface):
                      @abstract.lookup('context')
                      def build_config(self, ...):
                          \'\'\'Context-specific interface module provides configuration support.\'\'\'
                          raise NotImplementedError
  
                  # <os>/<context>/interface.py
                  class Interface(abstract.AbstractImplementationBase):
                      def build_config(self, ...):
                          ...
                          return configurations
  
          '''
          raise NotImplementedError
  
      @abc.abstractmethod
      def build_unconfig(self, *args, **kwargs):
          '''Derived OS-specific classes must provide build_config and
          build_unconfig methods.
  
          See build_config documentation.
          '''
  
      @classmethod
      def _build_name_to_class_map(cls):
          cls._name_to_class_map = {}
          for subcls in cls.__subclasses__():
              subcls._build_name_to_class_map()
              cls._name_to_class_map.update(subcls._name_to_class_map)
          for k in cls.__dict__.get('_interface_name_types', ()):
              cls._name_to_class_map[k] = cls
  
      def generate_sub_interface(self, range=None, **kwargs):
          name = SubInterface._generate_unused_sub_interface_name(
              parent_interface=self,
              range=range)
          return Interface(device=self.device, name=name, **kwargs)
  
      @classmethod
      def _get_os_specific_Interface_class(cls, os):
          assert type(os) is str
          if False:
              # XXXJST TODO The abstract module needs to be enhanced.
              # Use it's API to perform a fast(cached) os-specific only lookup
              # without instantiating a Lookup instance everytime and be able to
              # work on classes / class methods.
              from genie.abstract import Lookup
              lib = Lookup(os)
              osInterface = lib.conf.interface.Interface
              if osInterface is Interface:
                  raise ImportError(
                      'No Interface class found specific to OS {os!r}' \
                      .format(os=os))
  
          else:
              mod = 'genie.libs.conf.interface.{os}'.\
                  format(os=os)
              OsInterfaceModule = importlib.import_module(mod)
              osInterface = OsInterfaceModule.Interface
          return osInterface
  
      def __new__(cls, *args, **kwargs):
  
          factory_cls = cls
          if factory_cls is Interface:
              # need to load the correct interface for the right os.
              if 'device' in kwargs:
                  device = kwargs['device']
                  if not device.os and 'os' in kwargs['device'].__dict__:
                      device.os = kwargs['device'].__dict__['os']
                  if device.os is None:
                      raise AttributeError("Cannot convert interfaces for "
                                           "device {dev} as mandatory field "
                                           "'os' was not given in the "
                                           "yaml file".format(dev=device.name))
  
                  try:
                      factory_cls = cls._get_os_specific_Interface_class(device.os)
                  except (ImportError, AttributeError) as e:
                      # it does not exist, then just use the default one,
                      # but configuration is not possible
                      warnings.warn(
                          'Interfaces for {dev} OS {os!r} are not supported'
                          '; Configuration will not be available: {e}'.format(
                              dev=device.name, os=device.os, e=e),
                          UnsupportedInterfaceOsWarning)
              elif not cls.device:
                  raise TypeError('\'device\' argument missing')
  
          if factory_cls is not cls:
              self = factory_cls.__new__(factory_cls, *args, **kwargs)
          elif super().__new__ is object.__new__:
              self = super().__new__(factory_cls)
          else:
              self = super().__new__(factory_cls, *args, **kwargs)
          return self
  
      def __init__(self, *args, **kwargs):
          '''Base initialization for all Interface subclasses.
  
          This is not an abstract class since it may be used to instantiate
          generic interfaces for unsupported devices.
  
          All direct subclasses of Interface are abstract classes.
          '''
  
          super().__init__(*args, **kwargs)
  
          if 'ipv4' not in kwargs:
              if self.ipv4:
                  self.testbed.ipv4_cache.reserve(self.ipv4)
          if 'ipv6' not in kwargs:
              if self.ipv6:
                  self.testbed.ipv6_cache.reserve(self.ipv6)
  
      parent_interface = managedattribute(
          name='parent_interface',
          read_only=True,
          doc='''The parent interface. Only meaningful for a few Interface subclasses.''')
  
      @parent_interface.defaulter
      def parent_interface(self):
          # If the 'parent' attribute is set in the pyATS YAML file, look it up
          parent_interface_name = getattr(self, 'parent', None)
          if parent_interface_name:
              return self.device.interfaces.get(parent_interface_name, None)
          return None
  
      parent_controller_type = managedattribute(
          name='parent_controller_type',
          default=None,
          read_only=True,
          doc='''The associated controller type. Only meaningful for a few Interface subclasses.''')
  
      @mixedmethod
      def parse_interface_name(inst, cls, *args, **kwargs):
          if inst:
              if args or kwargs:
                  raise TypeError('Unexpected arguments: %r %r' % (args, kwargs))
              return ParsedInterfaceName(
                  name=inst.name,
                  device=inst.device)
          else:
              return ParsedInterfaceName(*args, **kwargs)
  
      @property
      def interface_type(self):
          '''The type part of the interface name (str).
  
          Examples:
              GigabitEthernet0/0/0/0 -> 'GigabitEthernet'
              GigabitEthernet0/0/0/0.2 -> 'GigabitEthernet'
              Bundle-Ether1.2 -> 'Bundle-Ether'
              tunnel-te1 -> 'tunnel-te'
              GCC0 -> 'GCC0'
          '''
  
          d_parsed = self.parse_interface_name()
          return d_parsed.type
  
      @property
      def interface_number(self):
          '''The number part of the interface name (int).
  
          Only valid for virtual interfaces constructed as "<type><number>"
  
          Examples:
              GigabitEthernet0/0/0/0 -> None
              GigabitEthernet0/0/0/0.2 -> None
              Bundle-Ether1.2 -> None
              tunnel-te1 -> 1
              GCC0 -> None
          '''
  
          d_parsed = self.parse_interface_name()
          if d_parsed.subintf:
              return None
          try:
              # Return only simple integers
              return int(d_parsed.number)
          except (TypeError, ValueError):
              # None or not a simple integer, it is a location
              return None
  
      @property
      def sub_interface_number(self):
          '''The sub-interface number part of the interface name (int).
          '''
          d_parsed = self.parse_interface_name()
          if d_parsed.subintf_sep == '.':
              return int(d_parsed.subintf)
          return None
  
      @property
      def interface_location(self):
          '''The location part of the interface name (str).
  
          Examples:
              GigabitEthernet0/0/0/0 -> '0/0/0/0'
              GigabitEthernet0/0/0/0.2 -> '0/0/0/0'
              Bundle-Ether1.2 -> None
              tunnel-te1 -> None
              GCC0 -> None
          '''
  
          # The Parent interface may know more about the location.
          parent_interface = self.parent_interface
          if parent_interface is not None:
              return parent_interface.interface_location
  
          d_parsed = self.parse_interface_name()
          try:
              # If it is a simple integer then it is not a location
              int(d_parsed.number)
              return None
          except (TypeError, ValueError):
              # None or not a simple integer, it is a location
              return d_parsed.number
  
      @mixedmethod
      def clean_interface_name(self, cls, interface_name=None):
          if interface_name is None:
              interface_name = self.name
          name_to_class_map = getattr(cls, '_name_to_class_map', {})
          d_parsed = cls.parse_interface_name(interface_name)
          # check for exact match
          if d_parsed.type in name_to_class_map:
              return d_parsed.reconstruct()
          # Apply generic short->long mappings
          try:
              d_parsed.type = {
                  'g0': 'GCC0',
                  'g1': 'GCC1',
                  'dt': 'Odu-Group-Te',  # what about Odu-Group-Mp?
                  # Special case for at that matches both ATM and Auto-Template
                  'at': 'ATM',
                  # Special case for gi that matches both GigabitEthernet and
                  'gi': 'GigabitEthernet',
                  # Special case for TenGigECtrlr... both TeEC and EC forms seen on XR
                  'ec': 'TenGigECtrlr',
                  'teec': 'TenGigECtrlr',
                  'il': 'InterflexLeft',
                  'ir': 'InterflexRight',
                  # TODO move to nxos
                  # Special case for pw (NXOS pseudowire) that matches several
                  'pw': 'pseudowire',
                  # Special case for se that matches both Serial and Service*
                  # names
                  'se': 'Serial',
                  'sa': 'ServiceApp',
                  'si': 'ServiceInfra',
                  # TODO
                  #'tu' {
                  #    if { $name eq "Tu" } {
                  #        set name_lower "tunnel"
                  #    } else {
                  #        switch -exact -- $caas_os {
                  #            "IOS" -
                  #            "IOSXE" -
                  #            "NXOS" {
                  #                set name_lower "tunnel"
                  #            }
                  #            default {
                  #                set name_lower "tunnel-uti"
                  #            }
                  #        }
                  #    }
                  #}
                  # 'vl' {
                  #     # Special case for vl/Vl/VL that matches vlan on ACSW (but
                  #     # doesn't support short names), Vlan on IOS and VASILeft
                  #     # (hardcoded short name)
                  #     if { $name eq "VL" } {
                  #         set name_lower "vasileft"
                  #     } else {
                  #         set name_lower "vlan"
                  #     }
                  # }
                  'vl': 'vlan',
                  'vr': 'VASIRight',
                  # Special case for lo that matches both Loopback and
                  # LongReachEthernet on Nexus (but doesn't support short names)
                  'lo': 'Loopback',
              }[d_parsed.type.lower()]
          except KeyError:
              for once in [1]:
                  m = re.match(r'^o([01234](?:[EF][12]?)?)$', d_parsed.type, re.IGNORECASE)
                  if m:
                      d_parsed.type = 'OTU' + m.group(1).upper()
                      break
                  m = re.match(r'^d([01234](?:[EF][12]?)?)$', d_parsed.type, re.IGNORECASE)
                  if m:
                      d_parsed.type = 'ODU' + m.group(1).upper()
                      break
              # There are still disambiguation issues for:
              #   Port-channel
              #   Service-Engine
              #   ServiceApp
              #   ServiceInfra
              #   tunnel-ipsec
              #   Virtual-Template
              #   Virtual-TokenRing
              #   MgmtIMA
              #   MgmtMultilink
          # re-check for exact match
          if d_parsed.type in name_to_class_map:
              return d_parsed.reconstruct()
          matches = []
          # check for different capitalization
          pat = d_parsed.type.lower()
          matches += [name for name in name_to_class_map
                      if name.lower() == pat]
          if not matches:
              # check for start of name
              re_pat = r'^' + re.escape(d_parsed.type) + r'[^-]+$'
              matches += [name for name in name_to_class_map
                          if re.match(re_pat, name, re.IGNORECASE)]
              # check for 2 letter abreviation of composite interface name
              if len(d_parsed.type) == 2:
                  re_pat = r'^{}.*-{}.*$'.format(re.escape(d_parsed.type[0]), re.escape(d_parsed.type[1]))
                  matches += [name for name in name_to_class_map
                              if re.match(re_pat, name, re.IGNORECASE)]
          if len(matches) == 0:
              warnings.warn(
                  'Unknown interface type/name {!r}'.format(
                      d_parsed.reconstruct()),
                  UnknownInterfaceName)
              pass
          elif len(matches) == 1:
              d_parsed.type = matches[0]
          else:
              raise ValueError('Ambiguous interface type/name {!r} matches: {}'.format(
                  d_parsed.reconstruct(),
                  ', '.join(matches)))
          return d_parsed.reconstruct()
  
      @mixedmethod
      def short_interface_name(self, cls, interface_name=None):
          if interface_name is None:
              interface_name = self.name
          interface_name = (self or cls).clean_interface_name(interface_name)
          d_parsed = cls.parse_interface_name(interface_name)
          # When a dash is present, take the first letter of each word.
          # Otherwise, take the first 2 letters
          m = re.match('^([a-z])[a-z]*-([a-z])[a-z]*$', d_parsed.type, re.IGNORECASE) \
              or re.match('^([a-z])([a-z])[a-z]*$', d_parsed.type, re.IGNORECASE)
          if m:
              d_parsed.type = m.group(1) + m.group(2)
              return d_parsed.reconstruct()
          return d_parsed.reconstruct()
  
      @property
      def sub_interfaces(self):
          return {
              interface
              for interface in self.device.interfaces
              if isinstance(interface, SubInterface) \
              and interface.parent_interface is self}
  
  
  class Controller(Interface):
      # XXXJST TODO Perhaps Interface should not be used as a base class
  
      @abc.abstractmethod
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
  
  class OpticsController(Controller):
  
      @abc.abstractmethod
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
  
  class PhysicalInterface(Interface,
                          genie.conf.base.interface.PhysicalInterface):
  
      @abc.abstractmethod
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
  
  class VirtualInterface(Interface,
                         genie.conf.base.interface.VirtualInterface):
      '''Base class for all sorts of virtual interfaces.
  
      Example:
          lo_intf = LoopbackInterface.generate_interface(device=dev)
      '''
      _interface_name_number_range = range(0, 2147483647 + 1)
  
      @abc.abstractmethod
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
      @classmethod
      def generate_interface(cls, device, range=None, **kwargs):
          name = cls._generate_unused_interface_name(
              device=device, range=range)
          return Interface(device=device, name=name, **kwargs)
  
      @classmethod
      def _generate_unused_interface_name(cls, device, range=None):
          assert isinstance(device, genie.conf.base.Device)
          # Find the os-specific version of this class
          cls = _get_descendent_subclass(
              cls._get_os_specific_Interface_class(device.os),
              cls)
  
          # Determine the interface name type
          interface_name_types = set(cls.__dict__.get('_interface_name_types', ()))
          if not interface_name_types:
              raise TypeError('No interface name types supported by %r' % (
                  cls.__qualname__))
          if len(interface_name_types) > 2:
              raise TypeError('Too many interface name types supported by %r: %r' % (
                  cls.__qualname__, set(interface_name_types)))
          interface_name_type = interface_name_types.pop()
  
          if range is None:
              range = cls._interface_name_number_range
          if type(range) is int:
              range = [range]
          for n in range:
              interface_name = '{}{}'.format(
                  interface_name_type, n)
              if interface_name not in device.interfaces:
                  break
          else:
              raise TypeError('No more %r interface numbers available on %r' % (
                  interface_name_type, device))
          return interface_name
  
  
  class PseudoInterface(VirtualInterface,
                        genie.conf.base.interface.PseudoInterface):
      pass
  
  
  class EmulatedInterface(Interface):
  
      @classmethod
      def _get_os_specific_EmulatedInterface_class(cls, os):
          assert type(os) is str
          mod = 'genie.libs.conf.interface.{os}'.\
              format(os=os)
          OsInterfaceModule = importlib.import_module(mod)
          return OsInterfaceModule.EmulatedInterface
  
      @property
      def tgen_interface(self):
          return self.device.tgen_interface
  
      @property
      def tgen_device(self):
          return self.device.tgen_device
  
      def build_config(self, *args, **kwargs):
          return ''
  
      def build_unconfig(self, *args, **kwargs):
          return ''
  
  class LagInterface(VirtualInterface):
  
      enabled_lacp = managedattribute(
          name='enabled_lacp',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc= 'enabled_lacp')
  
      lag_lacp_system_priority = managedattribute(
          name='lag_lacp_system_priority',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc= 'lag_lacp_system_priority')
  
      lag_lacp_max_bundle = managedattribute(
          name='lag_lacp_max_bundle',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc= 'lag_lacp_max_bundle')
  
      lag_lacp_min_bundle = managedattribute(
          name='lag_lacp_min_bundle',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc= 'lag_lacp_min_bundle')
  
      lag_bfd_v4_destination = managedattribute(
          name='lag_bfd_v4_destination',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc= 'lag_bfd_v4_destination')
  
      lag_bfd_v4_fast_detect = managedattribute(
          name='lag_bfd_v4_fast_detect',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc= 'lag_bfd_v4_fast_detect')
  
      lag_bfd_v4_min_interval = managedattribute(
          name='lag_bfd_v4_min_interval',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc= 'lag_bfd_v4_min_interval')
  
      lag_bfd_v6_destination = managedattribute(
          name='lag_bfd_v6_destination',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc= 'lag_bfd_v6_destination')
  
      lag_bfd_v6_fast_detect = managedattribute(
          name='lag_bfd_v6_fast_detect',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc= 'lag_bfd_v6_fast_detect')
  
      lag_bfd_v6_min_interval = managedattribute(
          name='lag_bfd_v6_min_interval',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc= 'lag_bfd_v6_min_interval')
  
  class LoopbackInterface(VirtualInterface,
                          genie.conf.base.interface.LoopbackInterface):
      pass
  
  
  class ManagementInterface(PhysicalInterface):
      pass
  
  
  class EthernetInterface(PhysicalInterface):
  
      # mac_address
      mac_address = managedattribute(
          name='mac_address',
          default=None,
          type=(None, MAC))
  
      burnin_mac_address = managedattribute(
          name='burnin_mac_address',
          default=None,
          type=(None, MAC))
  
      @property
      def effective_mac_address(self):
          return self.mac_address or self.burnin_mac_address
  
      auto_negotiation = managedattribute(
          name='auto_negotiation',
          default=None,
          type=(None, managedattribute.test_istype(bool)))
  
      speed = managedattribute(
          name='speed',
          default=None,
          type=(None, int, managedattribute.test_istype(str)))
  
      duplex = managedattribute(
          name='duplex',
          default=None,
          type=(None, managedattribute.test_istype(str)))
  
      eth_encap_type1 = managedattribute(
          name='eth_encap_type1',
          type=(None, managedattribute.test_istype(str)))
  
      @eth_encap_type1.defaulter
      def eth_encap_type1(self):
          if self.eth_encap_val1 is not None:
              return "dot1q"
          return None
  
      eth_encap_val1 = managedattribute(
          name='eth_encap_val1',
          default=None,
          type=(None,
                managedattribute.test_istype((int, range))))
  
      eth_encap_type2 = managedattribute(
          name='eth_encap_type2',
          type=(None, managedattribute.test_istype(str)))
  
      @eth_encap_type2.defaulter
      def eth_encap_type2(self):
          if self.eth_encap_val2 is not None:
              if self.eth_encap_type1 == 'dot1q':
                  return 'second-dot1q'
              if self.eth_encap_type1 == 'dot1ad':
                  return 'dot1q'
          return None
  
      eth_encap_val2 = managedattribute(
          name='eth_encap_val2',
          default=None,
          type=(None, managedattribute.test_istype((int, range))))
  
      eth_dot1q_type = managedattribute(
          name='eth_dot1q_type',
          type=(None, managedattribute.test_istype(str)))
  
      @eth_dot1q_type.defaulter
      def eth_dot1q_type(self):
          if self.eth_dot1q_value is not None:
              return "native"
          return None
  
      eth_dot1q_value = managedattribute(
          name='eth_dot1q_value',
          default=None,
          type=(None, managedattribute.test_istype(str)))
  
      # port_speed
      class PORTSPEED(Enum):
          sp1 = '10'
          sp2 = '100'
          sp3 = '1000'
          sp4 = '10000'
          sp5 = '100000'
          sp6 = '40000'
          auto = 'auto'
  
      port_speed = managedattribute(
          name='port_speed',
          default=None,
          type=(None, PORTSPEED),
          doc= 'port_speed')
  
      # access_vlan
      access_vlan = managedattribute(
          name='access_vlan',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Set access mode characteristics of the interface')
  
      # trunk_vlan
      trunk_vlan = managedattribute(
          name='trunk_vlans',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Configure trunking parameters on an interface')
  
      # trunk_add_vlans
      trunk_add_vlans = managedattribute(
          name='trunk_add_vlans',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Add VLANs to the current list')
  
      # trunk_remove_vlans
      trunk_remove_vlans = managedattribute(
          name='trunk_remove_vlans',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Remove VLANs from the current list')
  
      # native_vlan
      native_vlan = managedattribute(
          name='native_vlan',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Set trunking native characteristics when interface is in trunking mode')
  
      # auto_negotiate
      auto_negotiate = managedattribute(
          name='auto_negotiate',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Auto negotiate speed for speed and duplex')
  
      # duplex_mode
      class Duplex_Mode(Enum):
          full = 'full'
          half = 'half'
  
      duplex_mode = managedattribute(
          name='duplex_mode',
          default=None,
          type=(None, Duplex_Mode),
          doc='the port duplex mode')
  
      # flow_control_receive
      flow_control_receive = managedattribute(
          name='flow_control_receive',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Receive pause frames')
  
      # flow_control_send
      flow_control_send = managedattribute(
          name='flow_control_send',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Send pause frames')
  
      lag_bundle_id = managedattribute(
          name='lag_bundle_id',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc= 'lag_bundle_id')
  
      lag_activity = managedattribute(
          name='lag_activity',
          default=None,
          type=(None, managedattribute.test_in(['active','passive','on','auto','desirable'])),
          doc= 'lag_activity')
  
      lag_non_silent = managedattribute(
          name='lag_non_silent',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc= 'lag_non_silent')
  
      lag_force = managedattribute(
          name='lag_force',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc= 'lag_force')
  
      lag_lacp_port_priority = managedattribute(
          name='lag_lacp_port_priority',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc= 'lag_lacp_port_priority')
  
      lag_pagp_port_priority = managedattribute(
          name='lag_pagp_port_priority',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc= 'lag_pagp_port_priority')
  
      @abc.abstractmethod
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
          if 'mac_address' not in kwargs:
              try:
                  self.mac_address
              except AttributeError:
                  pass
              else:
                  if self.mac_address:
                      self.testbed.mac_cache.reserve(self.mac_address)
          if 'auto_negotiation' not in kwargs:
              try:
                  self.auto_negotiation
              except AttributeError:
                  pass
          if 'speed' not in kwargs:
              try:
                  self.speed
              except AttributeError:
                  pass
          if 'duplex' not in kwargs:
              try:
                  self.duplex
              except AttributeError:
                  pass
  
  
  class PosInterface(PhysicalInterface):
      pass
  
  
  class AtmInterface(PhysicalInterface):
      pass
  
  
  class SubInterface(VirtualInterface):
      '''Base class for sub-interfaces.
  
      Example:
          sub_intf = intf.generate_sub_interface(eth_encap_val1=1)
          assert sub_intf.parent_interface is intf
          assert sub_intf in intf.sub_interfaces
          number_after_dot = sub_intf.sub_interface_number
      '''
  
      _interface_name_subintf_range = range(0, 2147483647 + 1)
  
      @property
      def mac_address(self):
          '''Not accessible.'''
          raise AttributeError('mac_address not accessible on SubInterface objects')
  
      @property
      def burnin_mac_address(self):
          '''Not accessible.'''
          raise AttributeError('burnin_mac_address not accessible on SubInterface objects')
  
      @property
      def effective_mac_address(self):
          return self.parent_interface.effective_mac_address
  
      eth_encap_type1 = EthernetInterface.eth_encap_type1.copy()
      eth_encap_val1 = EthernetInterface.eth_encap_val1.copy()
      eth_encap_type2 = EthernetInterface.eth_encap_type2.copy()
      eth_encap_val2 = EthernetInterface.eth_encap_val2.copy()
  
      parent_interface = managedattribute(
          name='parent_interface',
          read_only=True,
          doc='''The parent interface.''')
  
      @parent_interface.getter
      def parent_interface(self):
          d_parsed = self.parse_interface_name()
          if d_parsed.subintf is not None:
              d_parsed.subintf = None
              parent_interface_name = d_parsed.reconstruct()
          else:
              return None
          return self.device.interfaces.get(parent_interface_name, None)
  
      @classmethod
      def _generate_unused_sub_interface_name(cls, parent_interface, range=None):
          assert isinstance(parent_interface, genie.conf.base.Interface)
          device = parent_interface.device
          # Find the os-specific version of this class
          cls = _get_descendent_subclass(
              cls._get_os_specific_Interface_class(device.os),
              cls)
  
          parent_interface_name = parent_interface.name
          if range is None:
              range = cls._interface_name_subintf_range
          if type(range) is int:
              range = [range]
          for n in range:
              interface_name = '{}.{}'.format(
                  parent_interface_name, n)
              if interface_name not in device.interfaces:
                  break
          else:
              raise TypeError('No more %r subinterface numbers available on %r' % (
                  parent_interface_name, device))
          return interface_name
  
  
  class VlanInterface(VirtualInterface):
      pass
  
  
  class AggregatedInterface(VirtualInterface):
      '''Base class for all aggregated/bundle interfaces.
  
      Example:
          bundle_intf = BundleEtherInterface.generate_interface(device=dev)
          bundle_intf.members = [intf1, intf2]
          bundle_id = bundle_intf.interface_number
          assert intf1.bundle is bundle_intf
          assert intf2.bundle is bundle_intf
          intf3.bundle = bundle_intf
          assert set(bundle_intf.members) == set([intf1, intf2, intf3])
          intf2.bundle = None
          assert set(bundle_intf.members) == set([intf1, intf3])
      '''
  
      @property
      @abc.abstractmethod
      def members(self):
          '''Derived classes must provide the members property'''
          pass
  
  
  class TunnelInterface(VirtualInterface):
  
      named_tunnel = managedattribute(
          name='named_tunnel',
          default=False,
          read_only=True)
  
  
  class NamedTunnelInterface(TunnelInterface, PseudoInterface):
  
      named_tunnel = managedattribute(
          name='named_tunnel',
          default=True,
          read_only=True)
  
      signalled_name = managedattribute(
          name='signalled_name',
          read_only=True)
  
      @signalled_name.getter
      def signalled_name(self):
          return self.name
  
  
  class TunnelTeInterface(TunnelInterface):
  
      _interface_name_number_range = range(0, 65535 + 1)
  
      def __new__(cls, *args, **kwargs):
  
          factory_cls = cls
          if factory_cls is TunnelTeInterface:
              # need to load the correct interface for the right os.
              if 'device' not in kwargs:
                  raise TypeError('\'device\' argument missing')
              device = kwargs['device']
              if device.os is None:
                  raise AttributeError("Cannot convert interfaces for "
                                       "device {dev} as mandatory field "
                                       "'os' was not given in the "
                                       "yaml file".format(dev=device.name))
              try:
                  factory_cls = _get_descendent_subclass(
                      factory_cls._get_os_specific_Interface_class(device.os),
                      factory_cls)
              except (ImportError, AttributeError, TypeError) as e:
                  # it does not exist, then just use the default one,
                  # but configuration is not possible
                  warnings.warn(
                      'TunnelTeInterfaces for {dev} OS {os!r} are not'
                      ' supported; Configuration will not be available:'
                      ' {e}'.format(
                          dev=device.name, os=device.os, e=e),
                      UnsupportedInterfaceOsWarning)
  
          if factory_cls is not cls:
              self = factory_cls.__new__(factory_cls, *args, **kwargs)
          elif super().__new__ is object.__new__:
              self = super().__new__(factory_cls)
          else:
              self = super().__new__(factory_cls, *args, **kwargs)
          return self
  
  
  class NamedTunnelTeInterface(NamedTunnelInterface, TunnelTeInterface):
  
      def __new__(cls, *args, **kwargs):
  
          factory_cls = cls
          if factory_cls is NamedTunnelTeInterface:
              # need to load the correct interface for the right os.
              if 'device' not in kwargs:
                  raise TypeError('\'device\' argument missing')
              device = kwargs['device']
              if device.os is None:
                  raise AttributeError("Cannot convert interfaces for "
                                       "device {dev} as mandatory field "
                                       "'os' was not given in the "
                                       "yaml file".format(dev=device.name))
              try:
                  factory_cls = _get_descendent_subclass(
                      factory_cls._get_os_specific_Interface_class(device.os),
                      factory_cls)
              except (ImportError, AttributeError, TypeError) as e:
                  # it does not exist, then just use the default one,
                  # but configuration is not possible
                  warnings.warn(
                      'NamedTunnelTeInterfaces for {dev} OS {os!r} are not'
                      ' supported; Configuration will not be available:'
                      ' {e}'.format(
                          dev=device.name, os=device.os, e=e),
                      UnsupportedInterfaceOsWarning)
  
          if factory_cls is not cls:
              self = factory_cls.__new__(factory_cls, *args, **kwargs)
          elif super().__new__ is object.__new__:
              self = super().__new__(factory_cls)
          else:
              self = super().__new__(factory_cls, *args, **kwargs)
          return self
  
  
  class BviInterface(VirtualInterface):
  
      mac_address = EthernetInterface.mac_address.copy()
      burnin_mac_address = EthernetInterface.burnin_mac_address.copy()
      effective_mac_address = EthernetInterface.effective_mac_address
      eth_encap_type1 = EthernetInterface.eth_encap_type1.copy()
      eth_encap_val1 = EthernetInterface.eth_encap_val1.copy()
      eth_encap_type2 = EthernetInterface.eth_encap_type2.copy()
      eth_encap_val2 = EthernetInterface.eth_encap_val2.copy()
  
  
  class NveInterface(VirtualInterface):
  
      name = managedattribute(
          name='name',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Nve name')
  
      class HOST_REACHABILTY_PROTOCOL(Enum):
          bgp = 'bgp'
  
      nve_host_reachability_protocol = managedattribute(
          name='nve_host_reachability_protocol',
          default=None,
          type=(None, HOST_REACHABILTY_PROTOCOL),
          doc='Nve host reachability protocol')
  
      nve_adv_virtual_rmac = managedattribute(
          name='nve_adv_virtual_rmac',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Nve virtual rmac')
  
      nve_src_intf_loopback = managedattribute(
          name='nve_src_intf_loopback',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Nve source interface')
  
      @abc.abstractmethod
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
  
  class VniInterface(NveInterface):
  
      nve_vni = managedattribute(
          name='nve_vni',
          default=None,
          type=(None, managedattribute.test_istype(int)),
          doc='Nve vni')
  
      nve_vni_associate_vrf = managedattribute(
          name='nve_vni_associate_vrf',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Nve vni associate vrf')
  
      nve_vni_suppress_arp = managedattribute(
          name='nve_vni_suppress_arp',
          default=None,
          type=(None, managedattribute.test_istype(bool)),
          doc='Nve vni suppress arp')
  
      nve_vni_mcast_group = managedattribute(
          name='nve_vni_mcast_group',
          default=None,
          type=(None, managedattribute.test_istype(str)),
          doc='Nve vni mcast group')
  
      @abc.abstractmethod
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
  
  