Ejemplo n.º 1
0
    def inspect_hardware(self, task):
        """Inspect hardware to obtain the hardware properties.

        This particular implementation only starts inspection using
        ironic-inspector. Results will be checked in a periodic task.

        :param task: a task from TaskManager.
        :returns: states.INSPECTWAIT
        :raises: HardwareInspectionFailure on failure
        """
        ironic_manages_boot = _ironic_manages_boot(
            task, raise_exc=CONF.inspector.require_managed_boot)

        utils.set_node_nested_field(task.node, 'driver_internal_info',
                                    _IRONIC_MANAGES_BOOT,
                                    ironic_manages_boot)
        task.node.save()

        LOG.debug('Starting inspection for node %(uuid)s using '
                  'ironic-inspector, booting is managed by %(project)s',
                  {'uuid': task.node.uuid,
                   'project': 'ironic' if ironic_manages_boot
                   else 'ironic-inspector'})

        if ironic_manages_boot:
            _start_managed_inspection(task)
        else:
            # NOTE(dtantsur): spawning a short-living green thread so that
            # we can release a lock as soon as possible and allow
            # ironic-inspector to operate on the node.
            eventlet.spawn_n(_start_inspection, task.node.uuid, task.context)
        return states.INSPECTWAIT
Ejemplo n.º 2
0
 def _test_status_clean_up_failed(self, mock_client):
     utils.set_node_nested_field(self.node, 'driver_internal_info',
                                 'inspector_manage_boot', True)
     self.node.save()
     mock_get = mock_client.return_value.get_introspection
     mock_get.return_value = mock.Mock(is_finished=True,
                                       error=None,
                                       spec=['is_finished', 'error'])
     inspector._check_status(self.task)
     mock_get.assert_called_once_with(self.node.uuid)
     self.task.process_event.assert_called_once_with('fail')
     self.assertIn('boom', self.node.last_error)
Ejemplo n.º 3
0
    def inspect_hardware(self, task):
        """Inspect hardware to obtain the hardware properties.

        This particular implementation only starts inspection using
        ironic-inspector. Results will be checked in a periodic task.

        :param task: a task from TaskManager.
        :returns: states.INSPECTWAIT
        :raises: HardwareInspectionFailure on failure
        """
        try:
            enabled_macs = task.driver.management.get_mac_addresses(task)
            if enabled_macs:
                inspect_utils.create_ports_if_not_exist(
                    task, enabled_macs, get_mac_address=lambda x: x[0])
            else:
                LOG.warning(
                    "Not attempting to create any port as no NICs "
                    "were discovered in 'enabled' state for node "
                    "%(node)s: %(mac_data)s", {
                        'mac_data': enabled_macs,
                        'node': task.node.uuid
                    })

        except exception.UnsupportedDriverExtension:
            LOG.debug(
                'Pre-creating ports prior to inspection not supported'
                ' on node %s.', task.node.uuid)

        ironic_manages_boot = _ironic_manages_boot(
            task, raise_exc=CONF.inspector.require_managed_boot)

        utils.set_node_nested_field(task.node, 'driver_internal_info',
                                    _IRONIC_MANAGES_BOOT, ironic_manages_boot)
        task.node.save()

        LOG.debug(
            'Starting inspection for node %(uuid)s using '
            'ironic-inspector, booting is managed by %(project)s', {
                'uuid': task.node.uuid,
                'project':
                'ironic' if ironic_manages_boot else 'ironic-inspector'
            })

        if ironic_manages_boot:
            _start_managed_inspection(task)
        else:
            # NOTE(dtantsur): spawning a short-living green thread so that
            # we can release a lock as soon as possible and allow
            # ironic-inspector to operate on the node.
            eventlet.spawn_n(_start_inspection, task.node.uuid, task.context)
        return states.INSPECTWAIT
Ejemplo n.º 4
0
 def test_status_ok_managed_no_power_off(self, mock_client):
     CONF.set_override('power_off', False, group='inspector')
     utils.set_node_nested_field(self.node, 'driver_internal_info',
                                 'inspector_manage_boot', True)
     self.node.save()
     mock_get = mock_client.return_value.get_introspection
     mock_get.return_value = mock.Mock(is_finished=True,
                                       error=None,
                                       spec=['is_finished', 'error'])
     inspector._check_status(self.task)
     mock_get.assert_called_once_with(self.node.uuid)
     self.task.process_event.assert_called_once_with('done')
     self.driver.network.remove_inspection_network.assert_called_once_with(
         self.task)
     self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
     self.assertFalse(self.driver.power.set_power_state.called)
Ejemplo n.º 5
0
def _set_boot_device(task, system, device, persistent=False):
    """An internal routine to set the boot device.

    :param task: a task from TaskManager.
    :param system: a Redfish System object.
    :param device: the Redfish boot device.
    :param persistent: Boolean value. True if the boot device will
                       persist to all future boots, False if not.
                       Default: False.
    :raises: SushyError on an error from the Sushy library
    """
    desired_enabled = BOOT_DEVICE_PERSISTENT_MAP_REV[persistent]
    current_enabled = system.boot.get('enabled')

    # NOTE(etingof): this can be racy, esp if BMC is not RESTful
    enabled = (desired_enabled if desired_enabled != current_enabled else None)

    try:
        system.set_system_boot_options(device, enabled=enabled)
    except sushy.exceptions.SushyError as e:
        if enabled == sushy.BOOT_SOURCE_ENABLED_CONTINUOUS:
            # NOTE(dtantsur): continuous boot device settings have been
            # removed from Redfish, and some vendors stopped supporting
            # it before an alternative was provided. As a work around,
            # use one-time boot and restore the boot device on every
            # reboot via RedfishPower.
            LOG.debug(
                'Error %(error)s when trying to set a '
                'persistent boot device on node %(node)s, '
                'falling back to one-time boot settings', {
                    'error': e,
                    'node': task.node.uuid
                })
            system.set_system_boot_options(
                device, enabled=sushy.BOOT_SOURCE_ENABLED_ONCE)
            LOG.warning(
                'Could not set persistent boot device to '
                '%(dev)s for node %(node)s, using one-time '
                'boot device instead', {
                    'dev': device,
                    'node': task.node.uuid
                })
            utils.set_node_nested_field(task.node, 'driver_internal_info',
                                        'redfish_boot_device', device)
            task.node.save()
        else:
            raise
Ejemplo n.º 6
0
 def test_status_error_managed(self, mock_client):
     utils.set_node_nested_field(self.node, 'driver_internal_info',
                                 'inspector_manage_boot', True)
     self.node.save()
     mock_get = mock_client.return_value.get_introspection
     mock_get.return_value = mock.Mock(is_finished=True,
                                       error='boom',
                                       spec=['is_finished', 'error'])
     inspector._check_status(self.task)
     mock_get.assert_called_once_with(self.node.uuid)
     self.task.process_event.assert_called_once_with('fail')
     self.assertIn('boom', self.node.last_error)
     self.driver.network.remove_inspection_network.assert_called_once_with(
         self.task)
     self.driver.boot.clean_up_ramdisk.assert_called_once_with(self.task)
     self.driver.power.set_power_state.assert_called_once_with(
         self.task, 'power off', timeout=None)
Ejemplo n.º 7
0
def _set_boot_device(task, system, device, enabled=None):
    """An internal routine to set the boot device.

    :param task: a task from TaskManager.
    :param system: a Redfish System object.
    :param device: the Redfish boot device.
    :param enabled: Redfish boot device persistence value or None.
    :raises: SushyError on an error from the Sushy library
    """
    try:
        system.set_system_boot_options(device, enabled=enabled)
    except sushy.exceptions.SushyError as e:
        if enabled == sushy.BOOT_SOURCE_ENABLED_CONTINUOUS:
            # NOTE(dtantsur): continuous boot device settings have been
            # removed from Redfish, and some vendors stopped supporting
            # it before an alternative was provided. As a work around,
            # use one-time boot and restore the boot device on every
            # reboot via RedfishPower.
            LOG.debug(
                'Error %(error)s when trying to set a '
                'persistent boot device on node %(node)s, '
                'falling back to one-time boot settings', {
                    'error': e,
                    'node': task.node.uuid
                })
            system.set_system_boot_options(
                device, enabled=sushy.BOOT_SOURCE_ENABLED_ONCE)
            LOG.warning(
                'Could not set persistent boot device to '
                '%(dev)s for node %(node)s, using one-time '
                'boot device instead', {
                    'dev': device,
                    'node': task.node.uuid
                })
            utils.set_node_nested_field(task.node, 'driver_internal_info',
                                        'redfish_boot_device', device)
            task.node.save()
        else:
            raise
Ejemplo n.º 8
0
    def write_image(self, task):
        """Method invoked when deployed using iSCSI.

        This method is invoked during a heartbeat from an agent when
        the node is in wait-call-back state. This deploys the image on
        the node and then configures the node to boot according to the
        desired boot option (netboot or localboot).

        :param task: a TaskManager object containing the node.
        :param kwargs: the kwargs passed from the heartbeat method.
        :raises: InstanceDeployFailure, if it encounters some error during
            the deploy.
        """
        if not task.driver.storage.should_write_image(task):
            LOG.debug('Skipping write_image for node %s', task.node.uuid)
            return

        node = task.node
        LOG.debug('Continuing the deployment on node %s', node.uuid)
        if utils.is_memory_insufficent():
            # Insufficent memory, but we can just let the agent heartbeat
            # again in order to initiate deployment when the situation has
            # changed.
            LOG.warning(
                'Insufficent memory to write image for node '
                '%(node)s. Skipping until next heartbeat.',
                {'node': node.uuid})
            info = node.driver_internal_info
            info['skip_current_deploy_step'] = False
            node.driver_internal_info = info
            node.last_error = "Deploy delayed due to insufficent memory"
            node.save()
            return states.DEPLOYWAIT
        uuid_dict_returned = do_agent_iscsi_deploy(task, self._client)
        utils.set_node_nested_field(node, 'driver_internal_info',
                                    'deployment_uuids', uuid_dict_returned)
        node.save()
Ejemplo n.º 9
0
    def write_image(self, task):
        """Method invoked when deployed using iSCSI.

        This method is invoked during a heartbeat from an agent when
        the node is in wait-call-back state. This deploys the image on
        the node and then configures the node to boot according to the
        desired boot option (netboot or localboot).

        :param task: a TaskManager object containing the node.
        :param kwargs: the kwargs passed from the heartbeat method.
        :raises: InstanceDeployFailure, if it encounters some error during
            the deploy.
        """
        if not task.driver.storage.should_write_image(task):
            LOG.debug('Skipping write_image for node %s', task.node.uuid)
            return

        node = task.node
        LOG.debug('Continuing the deployment on node %s', node.uuid)

        uuid_dict_returned = do_agent_iscsi_deploy(task, self._client)
        utils.set_node_nested_field(node, 'driver_internal_info',
                                    'deployment_uuids', uuid_dict_returned)
        node.save()
Ejemplo n.º 10
0
def _set_boot_device(task, system, device, persistent=False):
    """An internal routine to set the boot device.

    :param task: a task from TaskManager.
    :param system: a Redfish System object.
    :param device: the Redfish boot device.
    :param persistent: Boolean value. True if the boot device will
                       persist to all future boots, False if not.
                       Default: False.
    :raises: SushyError on an error from the Sushy library
    """

    # The BMC handling of the persistent setting is vendor specific.
    # Some vendors require that it not be set if currently equal to
    # desired state (see https://storyboard.openstack.org/#!/story/2007355).
    # Supermicro BMCs handle it in the opposite manner - the
    # persistent setting must be set when setting the boot device
    # (see https://storyboard.openstack.org/#!/story/2008547).
    vendor = task.node.properties.get('vendor', None)
    if vendor and vendor.lower() == 'supermicro':
        enabled = BOOT_DEVICE_PERSISTENT_MAP_REV[persistent]
        LOG.debug(
            'Setting BootSourceOverrideEnable to %(enable)s '
            'on Supermicro BMC, node %(node)s', {
                'enable': enabled,
                'node': task.node.uuid
            })
    else:
        desired_enabled = BOOT_DEVICE_PERSISTENT_MAP_REV[persistent]
        current_enabled = system.boot.get('enabled')

        # NOTE(etingof): this can be racy, esp if BMC is not RESTful
        enabled = (desired_enabled
                   if desired_enabled != current_enabled else None)

    try:
        system.set_system_boot_options(device, enabled=enabled)
    except sushy.exceptions.SushyError as e:
        if enabled == sushy.BOOT_SOURCE_ENABLED_CONTINUOUS:
            # NOTE(dtantsur): continuous boot device settings have been
            # removed from Redfish, and some vendors stopped supporting
            # it before an alternative was provided. As a work around,
            # use one-time boot and restore the boot device on every
            # reboot via RedfishPower.
            LOG.debug(
                'Error %(error)s when trying to set a '
                'persistent boot device on node %(node)s, '
                'falling back to one-time boot settings', {
                    'error': e,
                    'node': task.node.uuid
                })
            system.set_system_boot_options(
                device, enabled=sushy.BOOT_SOURCE_ENABLED_ONCE)
            LOG.warning(
                'Could not set persistent boot device to '
                '%(dev)s for node %(node)s, using one-time '
                'boot device instead', {
                    'dev': device,
                    'node': task.node.uuid
                })
            utils.set_node_nested_field(task.node, 'driver_internal_info',
                                        'redfish_boot_device', device)
            task.node.save()
        else:
            raise