def before_update(self, introspection_data, node_info, **kwargs):
        """Detect root disk from root device hints and IPA inventory."""
        hints = node_info.node().properties.get('root_device')
        if not hints:
            LOG.debug('Root device hints are not provided',
                      node_info=node_info, data=introspection_data)
            return

        inventory = utils.get_inventory(introspection_data,
                                        node_info=node_info)
        try:
            device = il_utils.match_root_device_hints(inventory['disks'],
                                                      hints)
        except (TypeError, ValueError) as e:
            raise utils.Error(
                _('No disks could be found using the root device hints '
                  '%(hints)s because they failed to validate. '
                  'Error: %(error)s') % {'hints': hints, 'error': e},
                node_info=node_info, data=introspection_data)

        if not device:
            raise utils.Error(_('No disks satisfied root device hints'),
                              node_info=node_info, data=introspection_data)

        LOG.debug('Disk %(disk)s of size %(size)s satisfies '
                  'root device hints',
                  {'disk': device.get('name'), 'size': device['size']},
                  node_info=node_info, data=introspection_data)
        introspection_data['root_disk'] = device
    def before_update(self, introspection_data, node_info, **kwargs):
        """Process LLDP data and patch Ironic port local link connection"""
        inventory = utils.get_inventory(introspection_data)

        ironic_ports = node_info.ports()

        for iface in inventory['interfaces']:
            if iface['name'] not in introspection_data['all_interfaces']:
                continue

            mac_address = iface['mac_address']
            port = ironic_ports.get(mac_address)
            if not port:
                LOG.debug("Skipping LLC processing for interface %s, matching "
                          "port not found in Ironic.", mac_address,
                          node_info=node_info, data=introspection_data)
                continue

            lldp_data = iface.get('lldp')
            if lldp_data is None:
                LOG.warning("No LLDP Data found for interface %s",
                            mac_address, node_info=node_info,
                            data=introspection_data)
                continue

            patches = []
            # First check if lldp data was already processed by lldp_basic
            # plugin which stores data in 'all_interfaces'
            proc_data = introspection_data['all_interfaces'][iface['name']]

            for name, item in LLDP_PROC_DATA_MAPPING.items():
                patch = self._get_lldp_processed_patch(name, item,
                                                       proc_data, port,
                                                       node_info)
                if patch is not None:
                    patches.append(patch)

            # If no processed lldp data was available then parse raw lldp data
            if not patches:
                for tlv_type, tlv_value in lldp_data:
                    patch = self._get_local_link_patch(tlv_type, tlv_value,
                                                       port, node_info)
                    if patch is not None:
                        patches.append(patch)

            try:
                node_info.patch_port(port, patches)
            except exceptions.BadRequest as e:
                LOG.warning("Failed to update port %(uuid)s: %(error)s",
                            {'uuid': port.uuid, 'error': e},
                            node_info=node_info)
    def before_update(self, introspection_data, node_info, **kwargs):
        """Process LLDP data and patch Ironic port local link connection"""
        inventory = utils.get_inventory(introspection_data)

        ironic_ports = node_info.ports()

        for iface in inventory['interfaces']:
            if iface['name'] not in introspection_data['all_interfaces']:
                continue

            mac_address = iface['mac_address']
            port = ironic_ports.get(mac_address)
            if not port:
                LOG.debug(
                    "Skipping LLC processing for interface %s, matching "
                    "port not found in Ironic.",
                    mac_address,
                    node_info=node_info,
                    data=introspection_data)
                continue

            lldp_data = iface.get('lldp')
            if lldp_data is None:
                LOG.warning("No LLDP Data found for interface %s",
                            mac_address,
                            node_info=node_info,
                            data=introspection_data)
                continue

            patches = []
            # First check if lldp data was already processed by lldp_basic
            # plugin which stores data in 'all_interfaces'
            proc_data = introspection_data['all_interfaces'][iface['name']]

            for name, item in LLDP_PROC_DATA_MAPPING.items():
                patch = self._get_lldp_processed_patch(name, item, proc_data,
                                                       port)
                if patch is not None:
                    patches.append(patch)

            # If no processed lldp data was available then parse raw lldp data
            if not patches:
                for tlv_type, tlv_value in lldp_data:
                    patch = self._get_local_link_patch(tlv_type, tlv_value,
                                                       port, node_info)
                    if patch is not None:
                        patches.append(patch)

            node_info.patch_port(port, patches)
Exemple #4
0
    def _get_interfaces(self, data=None):
        """Convert inventory to a dict with interfaces.

        :return: dict interface name -> dict with keys 'mac' and 'ip'
        """
        result = {}
        inventory = utils.get_inventory(data)

        for iface in inventory['interfaces']:
            name = iface.get('name')
            mac = iface.get('mac_address')
            ip = iface.get('ipv4_address')
            client_id = iface.get('client_id')

            if not name:
                LOG.error('Malformed interface record: %s', iface, data=data)
                continue

            if not mac:
                LOG.debug('Skipping interface %s without link information',
                          name,
                          data=data)
                continue

            if not netutils.is_valid_mac(mac):
                LOG.warning(
                    'MAC %(mac)s for interface %(name)s is '
                    'not valid, skipping', {
                        'mac': mac,
                        'name': name
                    },
                    data=data)
                continue

            mac = mac.lower()

            LOG.debug(
                'Found interface %(name)s with MAC "%(mac)s", '
                'IP address "%(ip)s" and client_id "%(client_id)s"', {
                    'name': name,
                    'mac': mac,
                    'ip': ip,
                    'client_id': client_id
                },
                data=data)
            result[name] = {'ip': ip, 'mac': mac, 'client_id': client_id}

        return result
    def before_update(self, introspection_data, node_info, **kwargs):
        """Process LLDP data and patch Ironic port local link connection"""
        inventory = utils.get_inventory(introspection_data)

        ironic_ports = node_info.ports()

        for iface in inventory['interfaces']:
            if iface['name'] not in introspection_data['all_interfaces']:
                continue

            mac_address = iface['mac_address']
            port = ironic_ports.get(mac_address)
            if not port:
                LOG.debug("Skipping LLC processing for interface %s, matching "
                          "port not found in Ironic.", mac_address,
                          node_info=node_info, data=introspection_data)
                continue

            lldp_data = iface.get('lldp')
            if lldp_data is None:
                LOG.warning(_LW("No LLDP Data found for interface %s"),
                            mac_address, node_info=node_info,
                            data=introspection_data)
                continue

            patches = []
            for tlv_type, tlv_value in lldp_data:
                patch = self._get_local_link_patch(tlv_type, tlv_value, port)
                if patch is not None:
                    patches.append(patch)

            try:
                # NOTE(sambetts) We need a newer version of Ironic API for this
                # transaction, so create a new ironic client and explicitly
                # pass it into the function.
                cli = ironic.get_client(api_version=REQUIRED_IRONIC_VERSION)
                node_info.patch_port(port, patches, ironic=cli)
            except client_exc.NotAcceptable:
                LOG.error(_LE("Unable to set Ironic port local link "
                              "connection information because Ironic does not "
                              "support the required version"),
                          node_info=node_info, data=introspection_data)
                # NOTE(sambetts) May as well break out out of the loop here
                # because Ironic version is not going to change for the other
                # interfaces.
                break
    def before_update(self, introspection_data, node_info, **kwargs):
        inventory = utils.get_inventory(introspection_data)
        caps = {}
        if CONF.capabilities.boot_mode:
            caps.update(self._detect_boot_mode(inventory, node_info,
                                               introspection_data))

        caps.update(self._detect_cpu_flags(inventory, node_info,
                                           introspection_data))

        if caps:
            LOG.debug('New capabilities: %s', caps, node_info=node_info,
                      data=introspection_data)
            node_info.update_capabilities(**caps)
        else:
            LOG.debug('No new capabilities detected', node_info=node_info,
                      data=introspection_data)
    def before_update(self, introspection_data, node_info, **kwargs):
        """Process LLDP data and patch Ironic port local link connection"""
        inventory = utils.get_inventory(introspection_data)

        ironic_ports = node_info.ports()

        for iface in inventory['interfaces']:
            if iface['name'] not in introspection_data['all_interfaces']:
                continue

            mac_address = iface['mac_address']
            port = ironic_ports.get(mac_address)
            if not port:
                LOG.debug("Skipping LLC processing for interface %s, matching "
                          "port not found in Ironic.", mac_address,
                          node_info=node_info, data=introspection_data)
                continue

            lldp_data = iface.get('lldp')
            if lldp_data is None:
                LOG.warning(_LW("No LLDP Data found for interface %s"),
                            mac_address, node_info=node_info,
                            data=introspection_data)
                continue

            patches = []
            for tlv_type, tlv_value in lldp_data:
                patch = self._get_local_link_patch(tlv_type, tlv_value, port)
                if patch is not None:
                    patches.append(patch)

            try:
                # NOTE(sambetts) We need a newer version of Ironic API for this
                # transaction, so create a new ironic client and explicitly
                # pass it into the function.
                cli = ironic.get_client(api_version=REQUIRED_IRONIC_VERSION)
                node_info.patch_port(port, patches, ironic=cli)
            except client_exc.NotAcceptable:
                LOG.error(_LE("Unable to set Ironic port local link "
                              "connection information because Ironic does not "
                              "support the required version"),
                          node_info=node_info, data=introspection_data)
                # NOTE(sambetts) May as well break out out of the loop here
                # because Ironic version is not going to change for the other
                # interfaces.
                break
    def before_update(self, introspection_data, node_info, **kwargs):
        inventory = utils.get_inventory(introspection_data)
        caps = {}
        if CONF.capabilities.boot_mode:
            caps.update(self._detect_boot_mode(inventory, node_info,
                                               introspection_data))

        caps.update(self._detect_cpu_flags(inventory, node_info,
                                           introspection_data))

        if caps:
            LOG.debug('New capabilities: %s', caps, node_info=node_info,
                      data=introspection_data)
            node_info.update_capabilities(**caps)
        else:
            LOG.debug('No new capabilities detected', node_info=node_info,
                      data=introspection_data)
    def before_update(self, introspection_data, node_info, **kwargs):
        """Update node with scheduler properties."""
        inventory = utils.get_inventory(introspection_data,
                                        node_info=node_info)
        errors = []

        root_disk = introspection_data.get('root_disk')
        if root_disk:
            introspection_data['local_gb'] = root_disk['size'] // units.Gi
            if CONF.processing.disk_partitioning_spacing:
                introspection_data['local_gb'] -= 1
        else:
            introspection_data['local_gb'] = 0

        try:
            introspection_data['cpus'] = int(inventory['cpu']['count'])
            introspection_data['cpu_arch'] = six.text_type(
                inventory['cpu']['architecture'])
        except (KeyError, ValueError, TypeError):
            errors.append(_('malformed or missing CPU information: %s') %
                          inventory.get('cpu'))

        try:
            introspection_data['memory_mb'] = int(
                inventory['memory']['physical_mb'])
        except (KeyError, ValueError, TypeError):
            errors.append(_('malformed or missing memory information: %s; '
                            'introspection requires physical memory size '
                            'from dmidecode') % inventory.get('memory'))

        if errors:
            raise utils.Error(_('The following problems encountered: %s') %
                              '; '.join(errors),
                              node_info=node_info, data=introspection_data)

        LOG.info(_LI('Discovered data: CPUs: %(cpus)s %(cpu_arch)s, '
                     'memory %(memory_mb)s MiB, disk %(local_gb)s GiB'),
                 {key: introspection_data.get(key) for key in self.KEYS},
                 node_info=node_info, data=introspection_data)

        overwrite = CONF.processing.overwrite_existing
        properties = {key: str(introspection_data[key])
                      for key in self.KEYS if overwrite or
                      not node_info.node().properties.get(key)}
        node_info.update_properties(**properties)
Exemple #10
0
    def before_update(self, introspection_data, node_info, **kwargs):
        """Update node with scheduler properties."""
        inventory = utils.get_inventory(introspection_data,
                                        node_info=node_info)
        errors = []

        try:
            introspection_data['cpus'] = int(inventory['cpu']['count'])
            introspection_data['cpu_arch'] = six.text_type(
                inventory['cpu']['architecture'])
        except (KeyError, ValueError, TypeError):
            errors.append(
                _('malformed or missing CPU information: %s') %
                inventory.get('cpu'))

        try:
            introspection_data['memory_mb'] = int(
                inventory['memory']['physical_mb'])
        except (KeyError, ValueError, TypeError):
            errors.append(
                _('malformed or missing memory information: %s; '
                  'introspection requires physical memory size '
                  'from dmidecode') % inventory.get('memory'))

        if errors:
            raise utils.Error(_('The following problems encountered: %s') %
                              '; '.join(errors),
                              node_info=node_info,
                              data=introspection_data)

        LOG.info(
            'Discovered data: CPUs: %(cpus)s %(cpu_arch)s, '
            'memory %(memory_mb)s MiB',
            {key: introspection_data.get(key)
             for key in self.KEYS},
            node_info=node_info,
            data=introspection_data)

        overwrite = CONF.processing.overwrite_existing
        properties = {
            key: str(introspection_data[key])
            for key in self.KEYS
            if overwrite or not node_info.node().properties.get(key)
        }
        node_info.update_properties(**properties)
Exemple #11
0
    def before_update(self, introspection_data, node_info, **kwargs):
        """Detect root disk from root device hints and IPA inventory."""
        hints = node_info.node().properties.get('root_device')
        if not hints:
            LOG.debug('Root device hints are not provided',
                      node_info=node_info,
                      data=introspection_data)
            return

        inventory = utils.get_inventory(introspection_data,
                                        node_info=node_info)
        try:
            device = il_utils.match_root_device_hints(inventory['disks'],
                                                      hints)
        except (TypeError, ValueError) as e:
            raise utils.Error(
                _('No disks could be found using the root device hints '
                  '%(hints)s because they failed to validate. '
                  'Error: %(error)s') % {
                      'hints': hints,
                      'error': e
                  },
                node_info=node_info,
                data=introspection_data)

        if not device:
            raise utils.Error(_('No disks satisfied root device hints'),
                              node_info=node_info,
                              data=introspection_data)

        LOG.debug(
            'Disk %(disk)s of size %(size)s satisfies '
            'root device hints', {
                'disk': device.get('name'),
                'size': device['size']
            },
            node_info=node_info,
            data=introspection_data)
        introspection_data['root_disk'] = device
    def _get_interfaces(self, data=None):
        """Convert inventory to a dict with interfaces.

        :return: dict interface name -> dict with keys 'mac' and 'ip'
        """
        result = {}
        inventory = utils.get_inventory(data)

        for iface in inventory['interfaces']:
            name = iface.get('name')
            mac = iface.get('mac_address')
            ip = iface.get('ipv4_address')

            if not name:
                LOG.error(_LE('Malformed interface record: %s'),
                          iface, data=data)
                continue

            if not mac:
                LOG.debug('Skipping interface %s without link information',
                          name, data=data)
                continue

            if not netutils.is_valid_mac(mac):
                LOG.warning(_LW('MAC %(mac)s for interface %(name)s is '
                                'not valid, skipping'),
                            {'mac': mac, 'name': name},
                            data=data)
                continue

            mac = mac.lower()

            LOG.debug('Found interface %(name)s with MAC "%(mac)s" and '
                      'IP address "%(ip)s"',
                      {'name': name, 'mac': mac, 'ip': ip}, data=data)
            result[name] = {'ip': ip, 'mac': mac}

        return result
    def before_update(self, introspection_data, node_info, **kwargs):
        """Process LLDP data and update all_interfaces with processed data"""

        inventory = utils.get_inventory(introspection_data)

        for iface in inventory['interfaces']:
            if_name = iface['name']

            tlvs = iface.get('lldp')
            if tlvs is None:
                LOG.warning("No LLDP Data found for interface %s",
                            if_name, node_info=node_info)
                continue

            LOG.debug("Processing LLDP Data for interface %s",
                      if_name, node_info=node_info)

            nv = self._parse_lldp_tlvs(tlvs, node_info)

            if nv:
                # Store lldp data per interface in "all_interfaces"
                iface_to_update = introspection_data['all_interfaces'][if_name]
                iface_to_update['lldp_processed'] = nv
    def before_update(self, introspection_data, node_info, **kwargs):
        """Process LLDP data and update all_interfaces with processed data"""

        inventory = utils.get_inventory(introspection_data)

        for iface in inventory['interfaces']:
            if_name = iface['name']

            tlvs = iface.get('lldp')
            if tlvs is None:
                LOG.warning(_LW("No LLDP Data found for interface %s"),
                            if_name, node_info=node_info)
                continue

            LOG.debug("Processing LLDP Data for interface %s",
                      if_name, node_info=node_info)

            nv = self._parse_lldp_tlvs(tlvs, node_info)

            if nv:
                # Store lldp data per interface in "all_interfaces"
                iface_to_update = introspection_data['all_interfaces'][if_name]
                iface_to_update['lldp_processed'] = nv
Exemple #15
0
    def before_update(self, introspection_data, node_info, **kwargs):
        """Update node with scheduler properties."""
        inventory = utils.get_inventory(introspection_data,
                                        node_info=node_info)
        try:
            introspection_data['cpus'] = int(inventory['cpu']['count'])
            introspection_data['cpu_arch'] = six.text_type(
                inventory['cpu']['architecture'])
        except (KeyError, ValueError, TypeError):
            LOG.warning('malformed or missing CPU information: %s',
                        inventory.get('cpu'))

        try:
            introspection_data['memory_mb'] = int(
                inventory['memory']['physical_mb'])
        except (KeyError, ValueError, TypeError):
            LOG.warning(
                'malformed or missing memory information: %s; '
                'introspection requires physical memory size '
                'from dmidecode', inventory.get('memory'))

        LOG.info(
            'Discovered data: CPUs: count %(cpus)s, architecture '
            '%(cpu_arch)s, memory %(memory_mb)s MiB',
            {key: introspection_data.get(key)
             for key in self.KEYS},
            node_info=node_info,
            data=introspection_data)

        overwrite = CONF.processing.overwrite_existing
        properties = {
            key: str(introspection_data[key])
            for key in self.KEYS if introspection_data.get(key) and (
                overwrite or not node_info.node().properties.get(key))
        }
        if properties:
            node_info.update_properties(**properties)
Exemple #16
0
    def before_update(self, introspection_data, node_info, **kwargs):
        """Process root disk information."""
        inventory = utils.get_inventory(introspection_data,
                                        node_info=node_info)
        self._process_root_device_hints(introspection_data, node_info,
                                        inventory)

        root_disk = introspection_data.get('root_disk')
        if root_disk:
            local_gb = root_disk['size'] // units.Gi
            if CONF.processing.disk_partitioning_spacing:
                local_gb -= 1
            LOG.info('Root disk %(disk)s, local_gb %(local_gb)s GiB',
                     {'disk': root_disk, 'local_gb': local_gb},
                     node_info=node_info, data=introspection_data)
        else:
            local_gb = 0
            LOG.info('No root device found, assuming a diskless node',
                     node_info=node_info, data=introspection_data)

        introspection_data['local_gb'] = local_gb
        if (CONF.processing.overwrite_existing or not
                node_info.node().properties.get('local_gb')):
            node_info.update_properties(local_gb=str(local_gb))
    def before_update(self, introspection_data, node_info, **kwargs):
        """Process introspection data and patch port physical network."""
        inventory = utils.get_inventory(introspection_data)

        LOG.info("Plugin: %s", type(self))
        # Use a client with a version set explicitly.
        client = ironic.get_client(api_version=REQUIRED_IRONIC_VERSION)

        ironic_ports = node_info.ports()
        if (ironic_ports and
                not all(hasattr(port, 'physical_network')
                        for port in ironic_ports.values())):
            # If the ports do not have a physical network field, use our newer
            # versioned client to fetch some that do.
            port_list = client.node.list_ports(node_info.uuid, limit=0,
                                               detail=True)
            ironic_ports = {p.address: p for p in port_list}

        for iface in inventory['interfaces']:
            if iface['name'] not in introspection_data['all_interfaces']:
                continue

            mac_address = iface['mac_address']
            port = ironic_ports.get(mac_address)
            if not port:
                LOG.debug("Skipping physical network processing for interface "
                          "%s, matching port not found in Ironic.",
                          mac_address,
                          node_info=node_info, data=introspection_data)
                continue

            # Determine the physical network for this port.
            # Port not touched in here.
            physnet = self.get_physnet(port, iface['name'], introspection_data)
            if physnet is None:
                LOG.debug("Skipping physical network processing for interface "
                          "%s, no physical network mapping",
                          mac_address,
                          node_info=node_info, data=introspection_data)
                continue

            patch = self._get_physnet_patch(physnet, port)
            if patch is None:
                LOG.debug("Skipping physical network processing for interface "
                          "%s, no update required",
                          mac_address,
                          node_info=node_info, data=introspection_data)
                continue

            try:
                node_info.patch_port(port, [patch], ironic=client)
            except client_exc.NotAcceptable:
                LOG.error("Unable to set Ironic port physical network "
                          "because Ironic does not support the required "
                          "version (%(version)s)",
                          node_info=node_info, data=introspection_data,
                          version=REQUIRED_IRONIC_VERSION)
                # NOTE(sambetts) May as well break out out of the loop here
                # because Ironic version is not going to change for the other
                # interfaces.
                break
Exemple #18
0
    def _get_interfaces(self, data=None):
        """Convert inventory to a dict with interfaces.

        :return: dict interface name -> dict with keys 'mac' and 'ip'
        """
        result = {}
        inventory = utils.get_inventory(data)

        pxe_mac = utils.get_pxe_mac(data)

        for iface in inventory['interfaces']:
            name = iface.get('name')
            mac = iface.get('mac_address')
            ipv4_address = iface.get('ipv4_address')
            ipv6_address = iface.get('ipv6_address')
            # NOTE(kaifeng) ipv6 address may in the form of fd00::1%enp2s0,
            # which is not supported by netaddr, remove the suffix if exists.
            if ipv6_address and '%' in ipv6_address:
                ipv6_address = ipv6_address.split('%')[0]
            ip = ipv4_address or ipv6_address
            client_id = iface.get('client_id')

            if not name:
                LOG.error('Malformed interface record: %s', iface, data=data)
                continue

            if not mac:
                LOG.debug('Skipping interface %s without link information',
                          name,
                          data=data)
                continue

            if not netutils.is_valid_mac(mac):
                LOG.warning(
                    'MAC %(mac)s for interface %(name)s is '
                    'not valid, skipping', {
                        'mac': mac,
                        'name': name
                    },
                    data=data)
                continue

            mac = mac.lower()

            LOG.debug(
                'Found interface %(name)s with MAC "%(mac)s", '
                'IP address "%(ip)s" and client_id "%(client_id)s"', {
                    'name': name,
                    'mac': mac,
                    'ip': ip,
                    'client_id': client_id
                },
                data=data)
            result[name] = {
                'ip': ip,
                'mac': mac,
                'client_id': client_id,
                'pxe': (mac == pxe_mac)
            }

        return result