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
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)
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
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)
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
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)
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
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()
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()
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