Пример #1
0
    def __init__(self, snmp_hander=None, logger=None):
        self._snmp_handler = None
        self.snmp_handler = snmp_hander
        self._root = RootElement()
        self._chassis = None
        self._ports = {}
        self._snmp_cache = {}
        self._logger = logger

        overridden_config = override_attributes_from_config(AireOSAutoload)
        self.supported_os = overridden_config.SUPPORTED_OS
    def __init__(self, snmp_handler=None, logger=None, config=None, cli_service=None, snmp_community=None):
        self._content_indexes = None
        self._if_indexes = None
        self._logger = logger
        self._snmp_handler = None
        self.snmp_handler = snmp_handler
        self._config = config
        self._cli_service = cli_service
        self._snmp_community = snmp_community

        self._logical_generic_ports = {}
        self._physical_generic_ports = {}
        self._generic_physical_ports_by_description = None
        self._generic_logical_ports_by_description = None
        self._ports = {}
        self.sub_modules = {}
        self._modules = {}
        self._chassis = {}
        self._root = RootElement()

        self._ipv4_table = None
        self._ipv6_table = None
        self._if_duplex_table = None
        self._autoneg = None
        self._lldp_keys = None

        """Override attributes from global config"""
        overridden_config = override_attributes_from_config(JuniperSnmpAutoload, config=self.config)
        self._supported_os = overridden_config.SUPPORTED_OS
Пример #3
0
class AireOSAutoload(AutoloadOperationsInterface):
    PORT_DESCRIPTION_FILTER = [r'[Vv]irtual', r'[Cc]hannel']
    SUPPORTED_OS = [r'Controller']

    def __init__(self, snmp_hander=None, logger=None):
        self._snmp_handler = None
        self.snmp_handler = snmp_hander
        self._root = RootElement()
        self._chassis = None
        self._ports = {}
        self._snmp_cache = {}
        self._logger = logger

        overridden_config = override_attributes_from_config(AireOSAutoload)
        self.supported_os = overridden_config.SUPPORTED_OS

    @property
    def snmp_handler(self):
        if self._snmp_handler is None:
            self.snmp_handler = inject.instance(SNMP_HANDLER)
        return self._snmp_handler

    @snmp_handler.setter
    def snmp_handler(self, snmp_handler):
        if snmp_handler:
            self._snmp_handler = snmp_handler
            path = os.path.abspath(
                os.path.join(os.path.dirname(__file__), '..', 'mibs'))
            self._snmp_handler.update_mib_sources(path)

    @property
    def logger(self):
        if self._logger is not None:
            return self._logger
        return inject.instance(LOGGER)

    def _snmp_request(self, request_data):
        if isinstance(request_data, tuple):
            if request_data in self._snmp_cache:
                result = self._snmp_cache[request_data]
            else:
                if len(request_data) == 2:
                    result = self.snmp_handler.walk(request_data)
                elif len(request_data) == 3:
                    result = self.snmp_handler.get_property(*request_data)
                else:
                    raise Exception('_snmp_request',
                                    'Request tuple len has to be 2 or 3')
                self._snmp_cache[request_data] = result
        else:
            raise Exception('_snmp_request', 'Has to be tuple')
        return result

    def _get_from_table(self, key, table):
        if key in table:
            return table[key]
        else:
            return None

    def _match_pattern_list(self, pattern_list, data):
        for pattern in pattern_list:
            if re.search(pattern, data):
                return True
        return False

    def _build_root_elements(self):
        root_attributes = dict()
        root_attributes[RootAttributes.CONTACT_NAME] = self._snmp_request(
            ('SNMPv2-MIB', 'sysContact', '0'))
        root_attributes[RootAttributes.SYSTEM_NAME] = self._snmp_request(
            ('SNMPv2-MIB', 'sysName', '0'))
        root_attributes[RootAttributes.LOCATION] = self._snmp_request(
            ('SNMPv2-MIB', 'sysLocation', '0'))
        root_attributes[RootAttributes.OS_VERSION] = self._snmp_request(
            ('ENTITY-MIB', 'entPhysicalSoftwareRev', '1'))
        root_attributes[RootAttributes.VENDOR] = self._snmp_request(
            ('SNMPv2-MIB', 'sysObjectID', '0'))
        root_attributes[RootAttributes.MODEL] = self._snmp_request(
            ('ENTITY-MIB', 'entPhysicalDescr', '1'))
        self._root.build_attributes(root_attributes)

    def _build_chassis_elements(self):

        entity_table = self._snmp_request(('ENTITY-MIB', 'entPhysicalTable'))

        chassis_id = None
        entity_data = None

        if entity_table is not None and len(entity_table) > 0:
            for id, table in entity_table.iteritems():
                if re.search(r'[Cc]hassis',
                             table.get('entPhysicalName')) and table.get(
                                 'entPhysicalParentRelPos') == '-1':
                    chassis_id = id
                    entity_data = table
                    break
        else:
            raise Exception('_build_chassis_elements', 'Entity table is empty')

        if chassis_id and entity_data:
            self._chassis = Chassis(chassis_id)
        else:
            raise Exception('_build_chassis_elements',
                            'Cannot find chassis data in entity table')

        chassis_attributes = dict()
        chassis_attributes[ChassisAttributes.MODEL] = self._get_from_table(
            'entPhysicalModelName', entity_data)
        chassis_attributes[
            ChassisAttributes.SERIAL_NUMBER] = self._get_from_table(
                'entPhysicalSerialNum', entity_data)
        self._chassis.build_attributes(chassis_attributes)
        self._root.chassis.append(self._chassis)

    def _build_ports(self):
        if_mib_table = self._snmp_request(('IF-MIB', 'ifTable'))

        for index, table in if_mib_table.iteritems():
            port_description = self._get_from_table('ifDescr', table)
            if self._match_pattern_list(self.PORT_DESCRIPTION_FILTER,
                                        port_description):
                break
            port_index = self._get_from_table('ifIndex', table)
            port = Port(port_index,
                        self._convert_port_description(port_description))
            port_attributes = dict()
            port_attributes[
                PortAttributes.PORT_DESCRIPTION] = self._snmp_request(
                    ('IF-MIB', 'ifAlias', index))
            port_attributes[PortAttributes.L2_PROTOCOL_TYPE] = str(
                self._get_from_table('ifType', table)).replace("""'""", '')
            port_attributes[PortAttributes.MAC_ADDRESS] = self._get_from_table(
                'ifPhysAddress', table)
            port_attributes[PortAttributes.MTU] = self._get_from_table(
                'ifMtu', table)
            port_attributes[PortAttributes.BANDWIDTH] = self._get_from_table(
                'ifSpeed', table)
            port_attributes[PortAttributes.
                            IPV4_ADDRESS] = self._find_associated_ipv4(index)
            port_attributes[PortAttributes.
                            IPV6_ADDRESS] = self._find_associated_ipv6(index)
            port_attributes[PortAttributes.DUPLEX] = self._get_duplex(index)
            port_attributes[
                PortAttributes.AUTO_NEGOTIATION] = self._get_autonegotiation(
                    index)
            # port_attributes[PortAttributes.ADJACENT] = self._get_adjacent(index)
            port.build_attributes(port_attributes)
            self._ports[port_index] = port
            self._chassis.ports.append(port)

    def _build_port_channels(self):
        if_mib_table = self._snmp_request(('IF-MIB', 'ifTable'))
        for index, table in if_mib_table.iteritems():
            description = table['ifDescr']
            if re.search(r'[Cc]hannel',
                         description) and '.' not in description:
                suitable_description = self._convert_port_description(
                    description)
                port = PortChannel(self._get_from_table('ifIndex', table),
                                   suitable_description)
                pc_attributes = dict()
                pc_attributes[PortChannelAttributes.
                              PORT_DESCRIPTION] = self._snmp_request(
                                  ('IF-MIB', 'ifAlias', index))
                pc_attributes[PortChannelAttributes.
                              PROTOCOL_TYPE] = self._get_from_table(
                                  'ifType', table)
                pc_attributes[PortChannelAttributes.
                              IPV4_ADDRESS] = self._find_associated_ipv4(index)
                pc_attributes[PortChannelAttributes.
                              IPV6_ADDRESS] = self._find_associated_ipv6(index)
                pc_attributes[PortChannelAttributes.
                              ASSOCIATED_PORTS] = self._get_associated_ports(
                                  index)
                port.build_attributes(pc_attributes)
                self._root.port_channels.append(port)

    def _get_duplex(self, index):
        duplex_table = self._snmp_request(
            ('EtherLike-MIB', 'dot3StatsDuplexStatus'))
        duplex = None
        if len(duplex_table) > 0:
            if index in duplex_table:
                duplex = duplex_table[index]
        return duplex

    def _get_autonegotiation(self, index):
        """Get Autonegotiation for interface

        :param index: port id
        :return: Autonegotiation for interface
        :rtype string
        """
        autoneg = 'False'
        try:
            auto_negotiation = self.snmp_handler.get(
                ('MAU-MIB', 'ifMauAutoNegAdminStatus', index, 1)).values()[0]
            if 'enabled' in auto_negotiation.lower():
                autoneg = 'True'
        except Exception as e:
            self.logger.error(
                'Failed to load auto negotiation property for interface {0}'.
                format(e.message))
        return autoneg

    def _get_adjacent(self, interface_id):
        """Get connected device interface and device name to the specified port id, using cdp or lldp protocols

        :param interface_id: port id
        :return: device's name and port connected to port id
        :rtype string
        """

        lldp_local_table = self._snmp_request(('LLDP-MIB', 'lldpLocPortDesc'))
        lldp_remote_table = self._snmp_request(('LLDP-MIB', 'lldpRemTable'))
        # cdp_index_table = self._snmp_request(('CISCO-CDP-MIB', 'cdpInterface'))
        cdp_table = self._snmp_request(('CISCO-CDP-MIB', 'cdpCacheTable'))

        result = ''
        for key, value in cdp_table.iteritems():
            if 'cdpCacheDeviceId' in value and 'cdpCacheDevicePort' in value:
                if re.search('^\d+', str(key)).group(0) == interface_id:
                    result = '{0} through {1}'.format(
                        value['cdpCacheDeviceId'], value['cdpCacheDevicePort'])
        if result == '' and lldp_remote_table:
            for key, value in lldp_local_table.iteritems():
                interface_name = self._snmp_request(
                    ('IF-MIB', 'ifTable'))[interface_id]['ifDescr']
                if interface_name == '':
                    break
                if 'lldpLocPortDesc' in value and interface_name in value[
                        'lldpLocPortDesc']:
                    if 'lldpRemSysName' in lldp_remote_table and 'lldpRemPortDesc' in lldp_remote_table:
                        result = '{0} through {1}'.format(
                            lldp_remote_table[key]['lldpRemSysName'],
                            lldp_remote_table[key]['lldpRemPortDesc'])
        return result

    def _find_associated_ipv4(self, port_index):
        ip_addr_table = self._snmp_request(('IP-MIB', 'ipAddrTable'))
        for ip, data in ip_addr_table.iteritems():
            if 'ipAdEntIfIndex' in data and port_index == data[
                    'ipAdEntIfIndex']:
                return data['ipAdEntAddr']
        return None

    def _find_associated_ipv6(self, port_index):
        ipv6_table = self._snmp_request(('IPV6-MIB', 'ipv6AddrEntry'))
        for ip, data in ipv6_table.iteritems():
            if 'ipAdEntIfIndex' in data and port_index == data[
                    'ipAdEntIfIndex']:
                return data['ipAdEntAddr']
        return None

    def _get_associated_ports(self, index):
        agg_table = self._snmp_request(
            ('IEEE8023-LAG-MIB', 'dot3adAggPortAttachedAggID'))
        result = ''
        for key, value in agg_table.iteritems():
            if str(index) in value['dot3adAggPortAttachedAggID']:
                if key in self._ports:
                    phisical_port = self._ports[key]
                    if result:
                        result += ',' + phisical_port.name
                    else:
                        result = phisical_port.name
        return result.strip(' \t\n\r')

    def _convert_port_description(self, description):
        return description.replace('/', '-').replace(' ', '').replace(':', '-')

    def _is_valid_device_os(self):
        """Validate device OS using snmp
            :return: True or False
        """

        system_description = self._snmp_request(('SNMPv2-MIB', 'sysDescr', 0))
        self.logger.debug(
            'Detected system description: \'{0}\''.format(system_description))
        result = re.search(r"({0})".format("|".join(self.supported_os)),
                           system_description,
                           flags=re.DOTALL | re.IGNORECASE)

        if result:
            return True
        else:
            error_message = 'Incompatible driver! Please use this driver for \'{0}\' operation system(s)'. \
                format(str(tuple(self.supported_os)))
            self.logger.error(error_message)
            return False

    def discover(self):
        if not self._is_valid_device_os():
            raise Exception(
                self.__class__.__name__,
                'Unsupported device OS, see logs for more details')
        self._build_root_elements()
        self._build_chassis_elements()
        self._build_ports()
        self._build_port_channels()
        self._root.build_relative_path()
        autoload_details = AutoLoadDetails(self._root.get_resources(),
                                           self._root.get_attributes())
        return autoload_details
class JuniperSnmpAutoload(AutoloadOperationsInterface):
    """
    Load inventory by snmp and build device elements and attributes
    """
    FILTER_PORTS_BY_DESCRIPTION = ['bme', 'vme', 'me', 'vlan', 'gr', 'vt', 'mt', 'mams', 'irb', 'lsi', 'tap', 'fxp']
    FILTER_PORTS_BY_TYPE = ['tunnel', 'other', 'pppMultilinkBundle', 'mplsTunnel', 'softwareLoopback']
    SUPPORTED_OS = [r'[Jj]uniper']

    def __init__(self, snmp_handler=None, logger=None, config=None, cli_service=None, snmp_community=None):
        self._content_indexes = None
        self._if_indexes = None
        self._logger = logger
        self._snmp_handler = None
        self.snmp_handler = snmp_handler
        self._config = config
        self._cli_service = cli_service
        self._snmp_community = snmp_community

        self._logical_generic_ports = {}
        self._physical_generic_ports = {}
        self._generic_physical_ports_by_description = None
        self._generic_logical_ports_by_description = None
        self._ports = {}
        self.sub_modules = {}
        self._modules = {}
        self._chassis = {}
        self._root = RootElement()

        self._ipv4_table = None
        self._ipv6_table = None
        self._if_duplex_table = None
        self._autoneg = None
        self._lldp_keys = None

        """Override attributes from global config"""
        overridden_config = override_attributes_from_config(JuniperSnmpAutoload, config=self.config)
        self._supported_os = overridden_config.SUPPORTED_OS

    @property
    def snmp_community(self):
        if self._snmp_community is None:
            self._snmp_community = get_attribute_by_name("SNMP Read Community") or "quali"
        return self._snmp_community

    def enable_snmp(self):
        """Enable SNMP Agent on the device and configure community string if needed

        NOTE: this will work only for devices with Junos OS
        """
        snmp_community_info = self.cli_service.send_config_command('show snmp community "{}"'
                                                                   .format(self.snmp_community))

        if "authorization read" not in snmp_community_info:
            self.logger.debug("SNMP community string '{}' is not present on the device. "
                              "Trying to add it".format(self.snmp_community))

            self.cli_service.send_command_list([
                enable_disable_snmp.ENTER_EDIT_SNMP.get_command(),
                enable_disable_snmp.ENABLE_SNMP.get_command(self.snmp_community),
                enable_disable_snmp.EXIT_EDIT_SNMP.get_command(),
            ])

            self.cli_service.commit()
            self.logger.debug("SNMP community string '{}' was added to the the device.".format(self.snmp_community))

    def disable_snmp(self):
        """Disable SNMP Agent on the device

        NOTE: this will work only for devices with Junos OS
        """
        self.logger.debug("Trying to delete SNMP configuration from the device")
        self.cli_service.send_config_command(enable_disable_snmp.DISABLE_SNMP.get_command())
        self.cli_service.commit()
        self.logger.debug("SNMP configuration was successfully deleted from the device")

    @property
    def logger(self):
        return self._logger or inject.instance(LOGGER)

    @property
    def config(self):
        return self._config or inject.instance(CONFIG)

    @property
    def snmp_handler(self):
        if self._snmp_handler is None:
            self.snmp_handler = inject.instance(SNMP_HANDLER)
        return self._snmp_handler

    @snmp_handler.setter
    def snmp_handler(self, snmp_handler):
        if snmp_handler:
            self._snmp_handler = snmp_handler
            self._initialize_snmp_handler()

    @property
    def cli_service(self):
        return self._cli_service or inject.instance(CLI_SERVICE)

    @property
    def ipv4_table(self):
        if not self._ipv4_table:
            self._ipv4_table = sort_elements_by_attributes(
                self._snmp_handler.walk(('IP-MIB', 'ipAddrTable')), 'ipAdEntIfIndex')
        return self._ipv4_table

    @property
    def ipv6_table(self):
        if not self._ipv6_table:
            self._ipv6_table = sort_elements_by_attributes(
                self._snmp_handler.walk(('IPV6-MIB', 'ipv6AddrEntry')), 'ipAdEntIfIndex')
        return self._ipv6_table

    @property
    def generic_physical_ports_by_description(self):
        if not self._generic_physical_ports_by_description:
            self._generic_physical_ports_by_description = {}
            for index, generic_port in self._physical_generic_ports.iteritems():
                self._generic_physical_ports_by_description[generic_port.port_description] = generic_port
        return self._generic_physical_ports_by_description

    @property
    def generic_logical_ports_by_description(self):
        if not self._generic_logical_ports_by_description:
            self._generic_logical_ports_by_description = {}
            for index, generic_port in self._logical_generic_ports.iteritems():
                self._generic_logical_ports_by_description[generic_port.port_description] = generic_port
        return self._generic_logical_ports_by_description

    def _build_lldp_keys(self):
        result_dict = {}
        keys = self.snmp_handler.walk(('LLDP-MIB', 'lldpRemPortId')).keys()
        for key in keys:
            key_splited = str(key).split('.')
            if len(key_splited) == 3:
                result_dict[key_splited[1]] = key
            elif len(key_splited) == 1:
                result_dict[key_splited[0]] = key
        return result_dict

    @property
    def lldp_keys(self):
        if not self._lldp_keys:
            self._lldp_keys = self._build_lldp_keys()
        return self._lldp_keys

    def _initialize_snmp_handler(self):
        """
        Snmp settings and load specific mibs
        :return:
        """
        path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'mibs'))
        self.snmp_handler.update_mib_sources(path)
        self.logger.info("Loading mibs")
        self.snmp_handler.load_mib('JUNIPER-MIB')
        self.snmp_handler.load_mib('JUNIPER-IF-MIB')
        self.snmp_handler.load_mib('IF-MIB')
        self.snmp_handler.load_mib('JUNIPER-CHASSIS-DEFINES-MIB')
        self.snmp_handler.load_mib('IEEE8023-LAG-MIB')
        self.snmp_handler.load_mib('EtherLike-MIB')
        self.snmp_handler.load_mib('IP-MIB')
        self.snmp_handler.load_mib('IPV6-MIB')
        self.snmp_handler.load_mib('LLDP-MIB')

    def _build_root(self):
        """
        Collect device root attributes
        :return:
        """
        self.logger.info("Building Root")
        vendor = ''
        model = ''
        os_version = ''
        sys_obj_id = self.snmp_handler.get_property('SNMPv2-MIB', 'sysObjectID', 0)
        model_search = re.search('^(?P<vendor>\w+)-\S+jnxProductName(?P<model>\S+)', sys_obj_id
                                 )
        if model_search:
            vendor = model_search.groupdict()['vendor'].capitalize()
            model = model_search.groupdict()['model']
        sys_descr = self.snmp_handler.get_property('SNMPv2-MIB', 'sysDescr', '0')
        os_version_search = re.search('JUNOS \S+(,)?\s', sys_descr, re.IGNORECASE)
        if os_version_search:
            os_version = os_version_search.group(0).replace('JUNOS ', '').replace(',', '').strip(' \t\n\r')
        root_attributes = dict()
        root_attributes[RootAttributes.CONTACT_NAME] = self.snmp_handler.get_property('SNMPv2-MIB', 'sysContact', '0')
        root_attributes[RootAttributes.SYSTEM_NAME] = self.snmp_handler.get_property('SNMPv2-MIB', 'sysName', '0')
        root_attributes[RootAttributes.LOCATION] = self.snmp_handler.get_property('SNMPv2-MIB', 'sysLocation', '0')
        root_attributes[RootAttributes.OS_VERSION] = os_version
        root_attributes[RootAttributes.VENDOR] = vendor
        root_attributes[RootAttributes.MODEL] = model
        self._root.build_attributes(root_attributes)

    def _get_content_indexes(self):
        container_indexes = self.snmp_handler.walk(('JUNIPER-MIB', 'jnxContentsContainerIndex'))
        content_indexes = {}
        for index, value in container_indexes.iteritems():
            ct_index = value['jnxContentsContainerIndex']
            if ct_index in content_indexes:
                content_indexes[ct_index].append(index)
            else:
                content_indexes[ct_index] = [index]
        return content_indexes

    @property
    def content_indexes(self):
        if not self._content_indexes:
            self._content_indexes = self._get_content_indexes()
        return self._content_indexes

    @property
    def if_indexes(self):
        if not self._if_indexes:
            self._if_indexes = self.snmp_handler.walk(('JUNIPER-IF-MIB', 'ifChassisPort')).keys()
        return self._if_indexes

    def _build_chassis(self):
        """
        Build Chassis resources and attributes
        :return:
        """
        self.logger.debug('Building Chassis')
        element_index = '1'
        chassis_snmp_attributes = {'jnxContentsModel': 'str', 'jnxContentsType': 'str', 'jnxContentsSerialNo': 'str',
                                   'jnxContentsChassisId': 'str'}
        if element_index in self.content_indexes:
            for index in self.content_indexes[element_index]:
                content_data = self.snmp_handler.get_properties('JUNIPER-MIB', index, chassis_snmp_attributes).get(
                    index)
                index1, index2, index3, index4 = index.split('.')[:4]
                chassis_id = index2
                chassis = Chassis(chassis_id)

                chassis_attributes = dict()
                chassis_attributes[ChassisAttributes.MODEL] = self._get_element_model(content_data)
                chassis_attributes[ChassisAttributes.SERIAL_NUMBER] = content_data.get('jnxContentsSerialNo')
                chassis.build_attributes(chassis_attributes)
                self._root.chassis.append(chassis)
                chassis_id_str = content_data.get('jnxContentsChassisId')
                if chassis_id_str:
                    self._chassis[chassis_id_str.strip('\'')] = chassis

    def _build_power_modules(self):
        """
        Build Power modules resources and attributes
        :return:
        """
        self.logger.debug('Building PowerPorts')
        power_modules_snmp_attributes = {'jnxContentsModel': 'str', 'jnxContentsType': 'str', 'jnxContentsDescr': 'str',
                                         'jnxContentsSerialNo': 'str', 'jnxContentsRevision': 'str',
                                         'jnxContentsChassisId': 'str'}
        element_index = '2'
        if element_index in self.content_indexes:
            for index in self.content_indexes[element_index]:
                content_data = self.snmp_handler.get_properties('JUNIPER-MIB', index,
                                                                power_modules_snmp_attributes).get(index)
                index1, index2, index3, index4 = index.split('.')[:4]
                element_id = index2
                element = PowerPort(element_id)

                element_attributes = dict()
                element_attributes[PowerPortAttributes.MODEL] = self._get_element_model(content_data)
                element_attributes[PowerPortAttributes.PORT_DESCRIPTION] = content_data.get('jnxContentsDescr')
                element_attributes[PowerPortAttributes.SERIAL_NUMBER] = content_data.get('jnxContentsSerialNo')
                element_attributes[PowerPortAttributes.VERSION] = content_data.get('jnxContentsRevision')
                element.build_attributes(element_attributes)
                chassis_id_str = content_data.get('jnxContentsChassisId')
                if chassis_id_str:
                    chassis = self._chassis.get(chassis_id_str.strip('\''))
                    if chassis:
                        chassis.power_ports.append(element)

    def _build_modules(self):
        """
        Build Modules resources and attributes
        :return:
        """
        self.logger.debug('Building Modules')
        modules_snmp_attributes = {'jnxContentsModel': 'str',
                                   'jnxContentsType': 'str',
                                   'jnxContentsSerialNo': 'str',
                                   'jnxContentsRevision': 'str',
                                   'jnxContentsChassisId': 'str'}
        element_index = '7'
        if element_index in self.content_indexes:
            for index in self.content_indexes[element_index]:
                content_data = self.snmp_handler.get_properties('JUNIPER-MIB', index,
                                                                modules_snmp_attributes).get(index)
                index1, index2, index3, index4 = index.split('.')[:4]
                element_id = index2
                element = Module(element_id)

                element_attributes = dict()
                element_attributes[ModuleAttributes.MODEL] = self._get_element_model(content_data)
                element_attributes[ModuleAttributes.SERIAL_NUMBER] = content_data.get('jnxContentsSerialNo')
                element_attributes[ModuleAttributes.VERSION] = content_data.get('jnxContentsRevision')
                element.build_attributes(element_attributes)
                chassis_id_str = content_data.get('jnxContentsChassisId')
                if chassis_id_str:
                    chassis = self._chassis.get(chassis_id_str.strip('\''))
                    if chassis:
                        chassis.modules.append(element)
                        self._modules[element_id] = element

    def _build_sub_modules(self):
        """
        Build SubModules resources and attributes
        :return:
        """
        self.logger.debug('Building Sub Modules')
        sub_modules_snmp_attributes = {'jnxContentsModel': 'str',
                                       'jnxContentsType': 'str',
                                       'jnxContentsSerialNo': 'str',
                                       'jnxContentsRevision': 'str'}

        element_index = '8'
        if element_index in self.content_indexes:
            for index in self.content_indexes[element_index]:
                content_data = self.snmp_handler.get_properties('JUNIPER-MIB', index,
                                                                sub_modules_snmp_attributes).get(index)
                index1, index2, index3, index4 = index.split('.')[:4]
                parent_id = index2
                element_id = index3
                element = SubModule(element_id)
                element_attributes = dict()
                element_attributes[SubModuleAttributes.MODEL] = self._get_element_model(content_data)
                element_attributes[SubModuleAttributes.SERIAL_NUMBER] = content_data.get('jnxContentsSerialNo')
                element_attributes[SubModuleAttributes.VERSION] = content_data.get('jnxContentsRevision')
                element.build_attributes(element_attributes)
                if parent_id in self._modules:
                    self._modules[parent_id].sub_modules.append(element)
                    self.sub_modules[element_id] = element

    @staticmethod
    def _get_element_model(content_data):
        model_string = content_data.get('jnxContentsModel')
        if not model_string:
            model_string = content_data.get('jnxContentsType').split('::')[-1]
        return model_string

    def _build_generic_ports(self):
        """
        Build GenericPort instances
        :return:
        """
        self.logger.debug("Building generic ports")

        for index in self.if_indexes:
            index = int(index)
            generic_port = GenericPort(index, self.snmp_handler)
            if not self._port_filtered_by_description(generic_port) and not self._port_filtered_by_type(generic_port):
                if generic_port.logical_unit == '0':
                    self._physical_generic_ports[index] = generic_port
                else:
                    self._logical_generic_ports[index] = generic_port

    def _associate_ipv4_addresses(self):
        """
        Associates ipv4 with generic port
        :return:
        """
        self.logger.debug("Associate ipv4")
        for index in self.ipv4_table:
            if int(index) in self._logical_generic_ports:
                logical_port = self._logical_generic_ports[int(index)]
                physical_port = self._get_associated_phisical_port_by_description(logical_port.port_description)
                ipv4_address = self.ipv4_table[index].get('ipAdEntAddr')
                if physical_port and ipv4_address:
                    physical_port.ipv4_addresses.append(ipv4_address)

    def _associate_ipv6_addresses(self):
        """
        Associate ipv6 with generic port
        :return:
        """
        self.logger.debug("Associate ipv6")
        for index in self.ipv6_table:
            if int(index) in self._logical_generic_ports:
                logical_port = self._logical_generic_ports[int(index)]
                physical_port = self._get_associated_phisical_port_by_description(logical_port.port_description)
                ipv6_address = self.ipv6_table[index].get('ipAdEntAddr')
                if ipv6_address:
                    physical_port.ipv6_addresses.append(ipv6_address)

    def _associate_portchannels(self):
        """
        Associate physical ports with the portchannel
        :return:
        """
        self.logger.debug("Associate portchannels")
        snmp_data = self._snmp_handler.walk(('IEEE8023-LAG-MIB', 'dot3adAggPortAttachedAggID'))
        for port_index in snmp_data:
            port_index = int(port_index)
            if port_index in self._logical_generic_ports:
                associated_phisical_port = self._get_associated_phisical_port_by_description(
                    self._logical_generic_ports[port_index].port_description)
                logical_portchannel_index = snmp_data[port_index].get('dot3adAggPortAttachedAggID')
                if logical_portchannel_index and int(logical_portchannel_index) in self._logical_generic_ports:
                    associated_phisical_portchannel = self._get_associated_phisical_port_by_description(
                        self._logical_generic_ports[int(logical_portchannel_index)].port_description)
                    if associated_phisical_portchannel:
                        associated_phisical_portchannel.is_portchannel = True
                        if associated_phisical_port:
                            associated_phisical_portchannel.associated_port_names.append(associated_phisical_port.name)

    def _associate_adjacent(self):
        for index in self.lldp_keys:
            if int(index) in self._logical_generic_ports:
                rem_port_descr = self._snmp_handler.get_property('LLDP-MIB', 'lldpRemPortDesc', self.lldp_keys[index])
                rem_sys_descr = self._snmp_handler.get_property('LLDP-MIB', 'lldpRemSysDesc', self.lldp_keys[index])
                physical_port = self._get_associated_phisical_port_by_description(
                    self._logical_generic_ports[int(index)].port_description)
                physical_port.port_adjacent = '{0}, {1}'.format(rem_port_descr, rem_sys_descr)

    def _get_associated_phisical_port_by_description(self, description):
        """
        Associate physical port by description
        :param description:
        :return:
        """
        for port_description in self.generic_physical_ports_by_description:
            if port_description in description:
                return self.generic_physical_ports_by_description[port_description]
        return None

    def _port_filtered_by_description(self, port):
        """
        Filter ports by description
        :param port:
        :return:
        """
        for pattern in self.FILTER_PORTS_BY_DESCRIPTION:
            if re.search(pattern, port.port_description):
                return True
        return False

    def _port_filtered_by_type(self, port):
        """
        Filter ports by type
        :param port:
        :return:
        """
        if port.type in self.FILTER_PORTS_BY_TYPE:
            return True
        return False

    def _build_ports(self):
        """
        Associate ports with the structure resources and build Ports and PortChannels
        :return:
        """
        self.logger.debug("Building ports")
        self._build_generic_ports()
        self._associate_ipv4_addresses()
        self._associate_ipv6_addresses()
        self._associate_portchannels()
        self._associate_adjacent()
        for generic_port in self._physical_generic_ports.values():
            if generic_port.is_portchannel:
                self._root.port_channels.append(generic_port.get_portchannel())
            else:
                port = generic_port.get_port()
                if generic_port.fpc_id > 0 and generic_port.fpc_id in self._modules:
                    fpc = self._modules.get(generic_port.fpc_id)
                    if fpc and generic_port.pic_id > 0:
                        pic = self._get_pic_by_index(fpc, int(generic_port.pic_id))
                        if pic:
                            pic.ports.append(port)
                        else:
                            self.logger.info('Port {} is not allowed'.format(port.name))
                    else:
                        fpc.ports.append(port)
                else:
                    chassis = self._chassis.values()[0]
                    chassis.ports.append(generic_port.get_port())

    def _get_pic_by_index(self, fpc, index):
        for pic in fpc.sub_modules:
            if pic.element_id == index:
                return pic
        return None

    def _is_valid_device_os(self):
        """Validate device OS using snmp
            :return: True or False
        """

        system_description = self.snmp_handler.get_property('SNMPv2-MIB', 'sysDescr', '0')
        self.logger.debug('Detected system description: \'{0}\''.format(system_description))
        result = re.search(r"({0})".format("|".join(self._supported_os)),
                           system_description,
                           flags=re.DOTALL | re.IGNORECASE)

        if result:
            return True
        else:
            error_message = 'Incompatible driver! Please use this driver for \'{0}\' operation system(s)'. \
                format(str(tuple(self._supported_os)))
            self.logger.error(error_message)
            return False

    def _log_autoload_details(self, autoload_details):
        """
        Logging autoload details
        :param autoload_details:
        :return:
        """
        self.logger.debug('-------------------- <RESOURCES> ----------------------')
        for resource in autoload_details.resources:
            self.logger.debug('{0}, {1}'.format(resource.relative_address, resource.name))
        self.logger.debug('-------------------- </RESOURCES> ----------------------')

        self.logger.debug('-------------------- <ATTRIBUTES> ---------------------')
        for attribute in autoload_details.attributes:
            self.logger.debug('-- {0}, {1}, {2}'.format(attribute.relative_address, attribute.attribute_name,
                                                        attribute.attribute_value))
        self.logger.debug('-------------------- </ATTRIBUTES> ---------------------')

    def discover(self):
        """
        Call methods in specific order to build resources and attributes
        :return:
        """
        try:
            enable_snmp = get_attribute_by_name('Enable SNMP').lower() == 'true'
            disable_snmp = get_attribute_by_name('Disable SNMP').lower() == 'true'
        except Exception:
            enable_snmp = True
            disable_snmp = False

        if enable_snmp:
            self.enable_snmp()

        if not self._is_valid_device_os():
            raise Exception(self.__class__.__name__, 'Unsupported device OS')

        try:
            self._build_root()
            self._build_chassis()
            self._build_power_modules()
            self._build_modules()
            self._build_sub_modules()
            self._build_ports()
            autoload_details = self._root.get_autoload_details()
            self._log_autoload_details(autoload_details)
        finally:
            if disable_snmp:
                self.disable_snmp()

        return autoload_details