def _get_power_state(node): """Returns the current power state of the node. :param node: The node. :returns: power state, one of :mod: `ironic.common.states`. :raises: InvalidParameterValue if required iLO credentials are missing. :raises: IloOperationError on an error from IloClient library. """ ilo_object = ilo_common.get_ilo_object(node) # Check the current power state. try: power_status = ilo_object.get_host_power_status() except ilo_client.IloError as ilo_exception: LOG.error(_LE("iLO get_power_state failed for node %(node_id)s with " "error: %(error)s."), {'node_id': node.uuid, 'error': ilo_exception}) operation = _('iLO get_power_status') raise exception.IloOperationError(operation=operation, error=ilo_exception) if power_status == "ON": return states.POWER_ON elif power_status == "OFF": return states.POWER_OFF else: return states.ERROR
def _execute_ilo_clean_step(node, step, *args, **kwargs): """Executes a particular clean step. :param node: an Ironic node object. :param step: a clean step to be executed. :param args: The args to be passed to the clean step. :param kwargs: The kwargs to be passed to the clean step. :raises: NodeCleaningFailure, on failure to execute step. """ ilo_object = ilo_common.get_ilo_object(node) try: clean_step = getattr(ilo_object, step) except AttributeError: # The specified clean step is not present in the proliantutils # package. Raise exception to update the proliantutils package # to newer version. raise exception.NodeCleaningFailure( _("Clean step '%s' not found. 'proliantutils' package needs to be " "updated.") % step) try: clean_step(*args, **kwargs) except ilo_error.IloCommandNotSupportedError: # This clean step is not supported on Gen8 and below servers. # Log the failure and continue with cleaning. LOG.warning("'%(step)s' clean step is not supported on node " "%(uuid)s. Skipping the clean step.", {'step': step, 'uuid': node.uuid}) except ilo_error.IloError as ilo_exception: raise exception.NodeCleaningFailure(_( "Clean step %(step)s failed " "on node %(node)s with error: %(err)s") % {'node': node.uuid, 'step': step, 'err': ilo_exception})
def delete_configuration(self, task): """Delete the RAID configuration. :param task: a TaskManager instance containing the node to act on. :raises: NodeCleaningFailure, on failure to execute step. """ node = task.node LOG.debug("OOB RAID delete_configuration invoked for node %s.", node.uuid) driver_internal_info = node.driver_internal_info ilo_object = ilo_common.get_ilo_object(node) try: # Raid configuration in progress, checking status if not driver_internal_info.get('ilo_raid_delete_in_progress'): ilo_object.delete_raid_configuration() self._prepare_for_read_raid(task, 'delete_raid') return states.CLEANWAIT else: # Raid configuration is done, updating raid_config raid_conf = ilo_object.read_raid_configuration() if not len(raid_conf['logical_disks']): node.raid_config = {} LOG.debug("Node %(uuid)s raid delete clean step is done.", {'uuid': node.uuid}) self._pop_driver_internal_values( task, 'ilo_raid_delete_in_progress', 'cleaning_reboot', 'skip_current_clean_step') node.driver_internal_info = driver_internal_info node.save() else: # Raid configuration failed msg = ("Unable to delete this logical disks: %s" % raid_conf['logical_disks']) self._pop_driver_internal_values( task, 'ilo_raid_delete_in_progress', 'cleaning_reboot', 'skip_current_clean_step') node.driver_internal_info = driver_internal_info node.save() raise exception.NodeCleaningFailure( "Clean step delete_configuration failed " "on node %(node)s with error: %(err)s" % {'node': node.uuid, 'err': msg}) except ilo_error.IloLogicalDriveNotFoundError: LOG.info("No logical drive found to delete on node %(node)s", {'node': node.uuid}) except ilo_error.IloError as ilo_exception: operation = (_("Failed to delete raid configuration on node %s") % node.uuid) self._pop_driver_internal_values(task, 'ilo_raid_delete_in_progress', 'cleaning_reboot', 'skip_current_clean_step') node.driver_internal_info = driver_internal_info node.save() self._set_clean_failed(task, operation, ilo_exception)
def test_get_ilo_object(self, ilo_client_mock): self.info['client_timeout'] = 60 self.info['client_port'] = 443 ilo_client_mock.return_value = 'ilo_object' returned_ilo_object = ilo_common.get_ilo_object(self.node) ilo_client_mock.assert_called_with( self.info['ilo_address'], self.info['ilo_username'], self.info['ilo_password'], self.info['client_timeout'], self.info['client_port']) self.assertEqual('ilo_object', returned_ilo_object)
def test_get_ilo_object(self, ilo_client_mock): info = INFO_DICT info['client_timeout'] = 60 info['client_port'] = 443 ilo_client_mock.IloClient.return_value = 'ilo_object' returned_ilo_object = ilo_common.get_ilo_object(self.node) ilo_client_mock.IloClient.assert_called_with( INFO_DICT['ilo_address'], INFO_DICT['ilo_username'], INFO_DICT['ilo_password'], info['client_timeout'], info['client_port']) self.assertEqual('ilo_object', returned_ilo_object)
def test_get_ilo_object(self, ilo_client_mock): self.info["client_timeout"] = 60 self.info["client_port"] = 443 ilo_client_mock.return_value = "ilo_object" returned_ilo_object = ilo_common.get_ilo_object(self.node) ilo_client_mock.assert_called_with( self.info["ilo_address"], self.info["ilo_username"], self.info["ilo_password"], self.info["client_timeout"], self.info["client_port"], ) self.assertEqual("ilo_object", returned_ilo_object)
def _test_get_ilo_object(self, ilo_client_mock, isFile_mock, ca_file=None): self.info["client_timeout"] = 600 self.info["client_port"] = 4433 self.info["ca_file"] = ca_file self.node.driver_info = self.info ilo_client_mock.return_value = "ilo_object" returned_ilo_object = ilo_common.get_ilo_object(self.node) ilo_client_mock.assert_called_with( self.info["ilo_address"], self.info["ilo_username"], self.info["ilo_password"], self.info["client_timeout"], self.info["client_port"], cacert=self.info["ca_file"], ) self.assertEqual("ilo_object", returned_ilo_object)
def test_get_ilo_object(self, ilo_client_mock): info = INFO_DICT info['client_timeout'] = 60 info['client_port'] = 443 node = obj_utils.create_test_node(self.context, driver='ilo', driver_info=INFO_DICT) ilo_client_mock.IloClient.return_value = 'ilo_object' returned_ilo_object = ilo_common.get_ilo_object(node) ilo_client_mock.IloClient.assert_called_with( INFO_DICT['ilo_address'], INFO_DICT['ilo_username'], INFO_DICT['ilo_password'], info['client_timeout'], info['client_port']) self.assertEqual('ilo_object', returned_ilo_object)
def _set_power_state(task, target_state): """Turns the server power on/off or do a reboot. :param task: a TaskManager instance containing the node to act on. :param target_state: target state of the node. :raises: InvalidParameterValue if an invalid power state was specified. :raises: IloOperationError on an error from IloClient library. :raises: PowerStateFailure if the power couldn't be set to target_state. """ node = task.node ilo_object = ilo_common.get_ilo_object(node) # Trigger the operation based on the target state. try: if target_state == states.POWER_OFF: ilo_object.hold_pwr_btn() elif target_state == states.POWER_ON: _attach_boot_iso(task) ilo_object.set_host_power('ON') elif target_state == states.REBOOT: _attach_boot_iso(task) ilo_object.reset_server() target_state = states.POWER_ON else: msg = _("_set_power_state called with invalid power state " "'%s'") % target_state raise exception.InvalidParameterValue(msg) except ilo_client.IloError as ilo_exception: LOG.error(_LE("iLO set_power_state failed to set state to %(tstate)s " " for node %(node_id)s with error: %(error)s"), {'tstate': target_state, 'node_id': node.uuid, 'error': ilo_exception}) operation = _('iLO set_power_state') raise exception.IloOperationError(operation=operation, error=ilo_exception) # Wait till the state change gets reflected. state = _wait_for_state_change(node, target_state) if state != target_state: timeout = (CONF.ilo.power_wait) * (CONF.ilo.power_retry) LOG.error(_LE("iLO failed to change state to %(tstate)s " "within %(timeout)s sec"), {'tstate': target_state, 'timeout': timeout}) raise exception.PowerStateFailure(pstate=target_state)
def get_boot_device(self, task): """Get the current boot device for a node. Returns the current boot device of the node. :param task: a task from TaskManager. :raises: MissingParameterValue if a required iLO parameter is missing. :raises: IloOperationError on an error from IloClient library. :returns: a dictionary containing: :boot_device: the boot device, one of the supported devices listed in :mod:`ironic.common.boot_devices` or None if it is unknown. :persistent: Whether the boot device will persist to all future boots or not, None if it is unknown. """ ilo_object = ilo_common.get_ilo_object(task.node) persistent = False try: # Return one time boot device if set, else return # the persistent boot device next_boot = ilo_object.get_one_time_boot() if next_boot == 'Normal': # One time boot is not set. Check for persistent boot. persistent = True next_boot = ilo_object.get_persistent_boot_device() except ilo_error.IloError as ilo_exception: operation = _("Get boot device") raise exception.IloOperationError(operation=operation, error=ilo_exception) boot_device = BOOT_DEVICE_ILO_TO_GENERIC.get(next_boot, None) if boot_device is None: persistent = None return {'boot_device': boot_device, 'persistent': persistent}
def set_boot_device(self, task, device, persistent=False): """Set the boot device for a node. Set the boot device to use on next reboot of the node. :param task: a task from TaskManager. :param device: the boot device, one of the supported devices listed in :mod:`ironic.common.boot_devices`. :param persistent: Boolean value. True if the boot device will persist to all future boots, False if not. Default: False. :raises: InvalidParameterValue if an invalid boot device is specified. :raises: MissingParameterValue if a required parameter is missing. :raises: IloOperationError on an error from IloClient library. """ try: boot_device = BOOT_DEVICE_MAPPING_TO_ILO[device] except KeyError: raise exception.InvalidParameterValue(_( "Invalid boot device %s specified.") % device) try: ilo_object = ilo_common.get_ilo_object(task.node) if not persistent: ilo_object.set_one_time_boot(boot_device) else: ilo_object.update_persistent_boot([boot_device]) except ilo_error.IloError as ilo_exception: operation = _("Setting %s as boot device") % device raise exception.IloOperationError(operation=operation, error=ilo_exception) LOG.debug("Node %(uuid)s set to boot from %(device)s.", {'uuid': task.node.uuid, 'device': device})
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential and additional hardware properties. It fails if any of the essential properties are not received from the node. It doesn't fail if node fails to return any capabilities as the capabilities differ from hardware to hardware mostly. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :raises: IloOperationError if system fails to get power state. :returns: The resulting state of inspection. """ power_turned_on = False ilo_object = ilo_common.get_ilo_object(task.node) try: state = task.driver.power.get_power_state(task) except exception.IloOperationError as ilo_exception: operation = (_("Inspecting hardware (get_power_state) on %s") % task.node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) if state != states.POWER_ON: LOG.info( _LI("The node %s is not powered on. Powering on the " "node for inspection."), task.node.uuid) conductor_utils.node_power_action(task, states.POWER_ON) power_turned_on = True # get the essential properties and update the node properties # with it. inspected_properties = {} result = _get_essential_properties(task.node, ilo_object) properties = result['properties'] for known_property in ESSENTIAL_PROPERTIES_KEYS: inspected_properties[known_property] = properties[known_property] node_properties = task.node.properties node_properties.update(inspected_properties) task.node.properties = node_properties # Inspect the hardware for additional hardware capabilities. # Since additional hardware capabilities may not apply to all the # hardwares, the method inspect_hardware() doesn't raise an error # for these capabilities. capabilities = _get_capabilities(task.node, ilo_object) if capabilities: valid_cap = _create_supported_capabilities_dict(capabilities) capabilities = utils.get_updated_capabilities( task.node.properties.get('capabilities'), valid_cap) if capabilities: node_properties['capabilities'] = capabilities task.node.properties = node_properties task.node.save() # Create ports for the nics detected. _create_ports_if_not_exist(task.node, result['macs']) LOG.debug(("Node properties for %(node)s are updated as " "%(properties)s"), { 'properties': inspected_properties, 'node': task.node.uuid }) LOG.info(_LI("Node %s inspected."), task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info( _LI("The node %s was powered on for inspection. " "Powered off the node as inspection completed."), task.node.uuid) return states.MANAGEABLE
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential and additional hardware properties. It fails if any of the essential properties are not received from the node. It doesn't fail if node fails to return any capabilities as the capabilities differ from hardware to hardware mostly. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :raises: IloOperationError if system fails to get power state. :returns: The resulting state of inspection. """ power_turned_on = False ilo_object = ilo_common.get_ilo_object(task.node) try: state = task.driver.power.get_power_state(task) except exception.IloOperationError as ilo_exception: operation = (_("Inspecting hardware (get_power_state) on %s") % task.node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) if state != states.POWER_ON: LOG.info( "The node %s is not powered on. Powering on the " "node for inspection.", task.node.uuid) conductor_utils.node_power_action(task, states.POWER_ON) power_turned_on = True # get the essential properties and update the node properties # with it. inspected_properties = {} result = _get_essential_properties(task.node, ilo_object) # A temporary hook for OOB inspection to not to update 'local_gb' # for hardware if the storage is a "Direct Attached Storage" or # "Dynamic Smart Array Controllers" and the operator has manually # updated the local_gb in node properties prior to node inspection. # This will be removed once we have inband inspection support for # ilo drivers. current_local_gb = task.node.properties.get('local_gb') properties = result['properties'] if current_local_gb: if properties['local_gb'] == 0 and current_local_gb > 0: properties['local_gb'] = current_local_gb LOG.warning( 'Could not discover size of disk on the node ' '%s. Value of `properties/local_gb` of the ' 'node is not overwritten.', task.node.uuid) for known_property in self.ESSENTIAL_PROPERTIES: inspected_properties[known_property] = properties[known_property] node_properties = task.node.properties node_properties.update(inspected_properties) task.node.properties = node_properties # Inspect the hardware for additional hardware capabilities. # Since additional hardware capabilities may not apply to all the # hardwares, the method inspect_hardware() doesn't raise an error # for these capabilities. capabilities = _get_capabilities(task.node, ilo_object) model = None if capabilities: model = capabilities.get('server_model') valid_cap = _create_supported_capabilities_dict(capabilities) capabilities = utils.get_updated_capabilities( task.node.properties.get('capabilities'), valid_cap) if capabilities: node_properties['capabilities'] = capabilities task.node.properties = node_properties # RIBCL(Gen8) protocol cannot determine if a NIC # is physically connected with cable or not when the server # is not provisioned. RIS(Gen9) can detect the same for few NIC # adapters but not for all. However it is possible to determine # the same using Redfish(Gen10) protocol. Hence proliantutils # returns ALL MACs for Gen8 and Gen9 while it returns # only active MACs for Gen10. A warning is being added # for the user so that he knows that he needs to remove the # ironic ports created for inactive ports for Gen8 and Gen9. servers = ['Gen8', 'Gen9'] if model is not None and any(serv in model for serv in servers): LOG.warning( 'iLO cannot determine if the NICs are physically ' 'connected or not for ProLiant Gen8 and Gen9 servers. ' 'Hence returns all the MACs present on the server. ' 'Please remove the ironic ports created for inactive ' 'NICs manually for the node %(node)', {"node": task.node.uuid}) task.node.save() # Create ports for the nics detected. inspect_utils.create_ports_if_not_exist(task, result['macs']) LOG.debug( "Node properties for %(node)s are updated as " "%(properties)s", { 'properties': inspected_properties, 'node': task.node.uuid }) LOG.info("Node %s inspected.", task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info( "The node %s was powered on for inspection. " "Powered off the node as inspection completed.", task.node.uuid) return states.MANAGEABLE
def _set_power_state(task, target_state, timeout=None): """Turns the server power on/off or do a reboot. :param task: a TaskManager instance containing the node to act on. :param target_state: target state of the node. :param timeout: timeout (in seconds) positive integer (> 0) for any power state. ``None`` indicates default timeout. :raises: InvalidParameterValue if an invalid power state was specified. :raises: IloOperationError on an error from IloClient library. :raises: PowerStateFailure if the power couldn't be set to target_state. """ node = task.node ilo_object = ilo_common.get_ilo_object(node) # Check if its soft power operation soft_power_op = target_state in [states.SOFT_POWER_OFF, states.SOFT_REBOOT] if target_state == states.SOFT_REBOOT: if _get_power_state(node) == states.POWER_OFF: target_state = states.POWER_ON # Trigger the operation based on the target state. try: if target_state == states.POWER_OFF: ilo_object.hold_pwr_btn() elif target_state == states.POWER_ON: _attach_boot_iso_if_needed(task) ilo_object.set_host_power('ON') elif target_state == states.REBOOT: _attach_boot_iso_if_needed(task) ilo_object.reset_server() target_state = states.POWER_ON elif target_state in (states.SOFT_POWER_OFF, states.SOFT_REBOOT): ilo_object.press_pwr_btn() else: msg = _("_set_power_state called with invalid power state " "'%s'") % target_state raise exception.InvalidParameterValue(msg) except ilo_error.IloError as ilo_exception: LOG.error("iLO set_power_state failed to set state to %(tstate)s " " for node %(node_id)s with error: %(error)s", {'tstate': target_state, 'node_id': node.uuid, 'error': ilo_exception}) operation = _('iLO set_power_state') raise exception.IloOperationError(operation=operation, error=ilo_exception) # Wait till the soft power state change gets reflected. time_consumed = 0 if soft_power_op: # For soft power-off, bare metal reaches final state with one # power operation. In case of soft reboot it takes two; soft # power-off followed by power-on. Also, for soft reboot we # need to ensure timeout does not expire during power-off # and power-on operation. is_final_state = target_state in (states.SOFT_POWER_OFF, states.POWER_ON) time_consumed = _wait_for_state_change( node, target_state, is_final_state=is_final_state, timeout=timeout) if target_state == states.SOFT_REBOOT: _attach_boot_iso_if_needed(task) try: ilo_object.set_host_power('ON') except ilo_error.IloError as ilo_exception: operation = (_('Powering on failed after soft power off for ' 'node %s') % node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) # Re-calculate timeout available for power-on operation rem_timeout = timeout - time_consumed time_consumed += _wait_for_state_change( node, states.SOFT_REBOOT, is_final_state=True, timeout=rem_timeout) else: time_consumed = _wait_for_state_change( node, target_state, is_final_state=True, timeout=timeout) LOG.info("The node %(node_id)s operation of '%(state)s' " "is completed in %(time_consumed)s seconds.", {'node_id': node.uuid, 'state': target_state, 'time_consumed': time_consumed})
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential and additional hardware properties. It fails if any of the essential properties are not received from the node. It doesn't fail if node fails to return any capabilities as the capabilities differ from hardware to hardware mostly. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :raises: IloOperationError if system fails to get power state. :returns: The resulting state of inspection. """ power_turned_on = False ilo_object = ilo_common.get_ilo_object(task.node) try: state = task.driver.power.get_power_state(task) except exception.IloOperationError as ilo_exception: operation = (_("Inspecting hardware (get_power_state) on %s") % task.node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) if state != states.POWER_ON: LOG.info(_LI("The node %s is not powered on. Powering on the " "node for inspection."), task.node.uuid) conductor_utils.node_power_action(task, states.POWER_ON) power_turned_on = True # get the essential properties and update the node properties # with it. inspected_properties = {} result = _get_essential_properties(task.node, ilo_object) # A temporary hook for OOB inspection to not to update 'local_gb' # for hardware if the storage is a "Direct Attached Storage" or # "Dynamic Smart Array Controllers" and the operator has manually # updated the local_gb in node properties prior to node inspection. # This will be removed once we have inband inspection support for # ilo drivers. current_local_gb = task.node.properties.get('local_gb') properties = result['properties'] if current_local_gb: if properties['local_gb'] == 0 and current_local_gb > 0: properties['local_gb'] = current_local_gb LOG.warning(_LW('Could not discover size of disk on the node ' '%s. Value of `properties/local_gb` of the ' 'node is not overwritten.'), task.node.uuid) for known_property in self.ESSENTIAL_PROPERTIES: inspected_properties[known_property] = properties[known_property] node_properties = task.node.properties node_properties.update(inspected_properties) task.node.properties = node_properties # Inspect the hardware for additional hardware capabilities. # Since additional hardware capabilities may not apply to all the # hardwares, the method inspect_hardware() doesn't raise an error # for these capabilities. capabilities = _get_capabilities(task.node, ilo_object) if capabilities: valid_cap = _create_supported_capabilities_dict(capabilities) capabilities = utils.get_updated_capabilities( task.node.properties.get('capabilities'), valid_cap) if capabilities: node_properties['capabilities'] = capabilities task.node.properties = node_properties task.node.save() # Create ports for the nics detected. _create_ports_if_not_exist(task, result['macs']) LOG.debug(("Node properties for %(node)s are updated as " "%(properties)s"), {'properties': inspected_properties, 'node': task.node.uuid}) LOG.info(_LI("Node %s inspected."), task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info(_LI("The node %s was powered on for inspection. " "Powered off the node as inspection completed."), task.node.uuid) return states.MANAGEABLE
def _extract_fw_from_file(node, target_file): """Extracts firmware image file. Extracts the firmware image file thru proliantutils and uploads it to the conductor webserver, if needed. :param node: an Ironic node object. :param target_file: firmware file to be extracted from :returns: tuple of: a) wrapper object of raw firmware image location b) a boolean, depending upon whether the raw firmware file was already in raw format(same file remains, no need to extract) or compact format (thereby extracted and hence different file). If uploaded then, then also its a different file. :raises: ImageUploadFailed, if upload to web server fails. :raises: SwiftOperationError, if upload to Swift fails. :raises: IloOperationError, on failure to process firmware file. """ ilo_object = ilo_common.get_ilo_object(node) try: # Note(deray): Based upon different iLO firmwares, the firmware file # which needs to be updated has to be either an http/https or a simple # file location. If it has to be a http/https location, then conductor # will take care of uploading the firmware file to web server or # swift (providing a temp url). fw_image_location, to_upload, is_extracted = ( proliantutils_utils.process_firmware_image(target_file, ilo_object)) except (proliantutils_error.InvalidInputError, proliantutils_error.ImageExtractionFailed) as proliantutils_exc: operation = _("Firmware file extracting as part of manual cleaning") raise exception.IloOperationError(operation=operation, error=proliantutils_exc) is_different_file = is_extracted fw_image_filename = os.path.basename(fw_image_location) fw_image_location_obj = FirmwareImageLocation(fw_image_location, fw_image_filename) if to_upload: is_different_file = True try: if CONF.ilo.use_web_server_for_images: # upload firmware image file to conductor webserver LOG.debug("For firmware update on node %(node)s, hosting " "firmware file %(firmware_image)s on web server ...", {'firmware_image': fw_image_location, 'node': node.uuid}) fw_image_uploaded_url = ilo_common.copy_image_to_web_server( fw_image_location, fw_image_filename) fw_image_location_obj.fw_image_location = fw_image_uploaded_url fw_image_location_obj.remove = types.MethodType( _remove_webserver_based_me, fw_image_location_obj) else: # upload firmware image file to swift LOG.debug("For firmware update on node %(node)s, hosting " "firmware file %(firmware_image)s on swift ...", {'firmware_image': fw_image_location, 'node': node.uuid}) fw_image_uploaded_url = ilo_common.copy_image_to_swift( fw_image_location, fw_image_filename) fw_image_location_obj.fw_image_location = fw_image_uploaded_url fw_image_location_obj.remove = types.MethodType( _remove_swift_based_me, fw_image_location_obj) finally: if is_extracted: # Note(deray): remove the file `fw_image_location` irrespective # of status of uploading (success or failure) and only if # extracted (and not passed as in plain binary format). If the # file is passed in binary format, then the invoking method # takes care of handling the deletion of the file. ilo_common.remove_single_or_list_of_files(fw_image_location) LOG.debug("For firmware update on node %(node)s, hosting firmware " "file: %(fw_image_location)s ... done. Hosted firmware " "file: %(fw_image_uploaded_url)s", {'fw_image_location': fw_image_location, 'node': node.uuid, 'fw_image_uploaded_url': fw_image_uploaded_url}) else: fw_image_location_obj.remove = types.MethodType( _remove_file_based_me, fw_image_location_obj) return fw_image_location_obj, is_different_file
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential and additional hardware properties. It fails if any of the essential properties are not received from the node. It doesn't fail if node fails to return any capabilities as the capabilities differ from hardware to hardware mostly. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :raises: IloOperationError if system fails to get power state. :returns: The resulting state of inspection. """ power_turned_on = False ilo_object = ilo_common.get_ilo_object(task.node) try: state = task.driver.power.get_power_state(task) except exception.IloOperationError as ilo_exception: operation = (_("Inspecting hardware (get_power_state) on %s") % task.node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) if state != states.POWER_ON: LOG.info(_LI("The node %s is not powered on. Powering on the " "node for inspection."), task.node.uuid) conductor_utils.node_power_action(task, states.POWER_ON) power_turned_on = True # get the essential properties and update the node properties # with it. inspected_properties = {} result = _get_essential_properties(task.node, ilo_object) properties = result['properties'] for known_property in ESSENTIAL_PROPERTIES_KEYS: inspected_properties[known_property] = properties[known_property] node_properties = task.node.properties node_properties.update(inspected_properties) task.node.properties = node_properties # Inspect the hardware for additional hardware capabilities. # Since additional hardware capabilities may not apply to all the # hardwares, the method inspect_hardware() doesn't raise an error # for these capabilities. capabilities = _get_capabilities(task.node, ilo_object) if capabilities: valid_cap = _create_supported_capabilities_dict(capabilities) capabilities = _update_capabilities(task.node, valid_cap) if capabilities: node_properties['capabilities'] = capabilities task.node.properties = node_properties task.node.save() # Create ports for the nics detected. _create_ports_if_not_exist(task.node, result['macs']) LOG.debug(("Node properties for %(node)s are updated as " "%(properties)s"), {'properties': inspected_properties, 'node': task.node.uuid}) LOG.info(_LI("Node %s inspected."), task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info(_LI("The node %s was powered on for inspection. " "Powered off the node as inspection completed."), task.node.uuid) return states.MANAGEABLE
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential and additional hardware properties. It fails if any of the essential properties are not received from the node. It doesn't fail if node fails to return any capabilities as the capabilities differ from hardware to hardware mostly. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :raises: IloOperationError if system fails to get power state. :returns: The resulting state of inspection. """ power_turned_on = False ilo_object = ilo_common.get_ilo_object(task.node) try: state = task.driver.power.get_power_state(task) except exception.IloOperationError as ilo_exception: operation = (_("Inspecting hardware (get_power_state) on %s") % task.node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) if state != states.POWER_ON: LOG.info( _LI("The node %s is not powered on. Powering on the " "node for inspection."), task.node.uuid) conductor_utils.node_power_action(task, states.POWER_ON) power_turned_on = True # get the essential properties and update the node properties # with it. inspected_properties = {} result = _get_essential_properties(task.node, ilo_object) # A temporary hook for OOB inspection to not to update 'local_gb' # for hardware if the storage is a "Direct Attached Storage" or # "Dynamic Smart Array Controllers" and the operator has manually # updated the local_gb in node properties prior to node inspection. # This will be removed once we have inband inspection support for # ilo drivers. current_local_gb = task.node.properties.get('local_gb') properties = result['properties'] if current_local_gb: if properties['local_gb'] == 0 and current_local_gb > 0: properties['local_gb'] = current_local_gb LOG.warning( _LW('Could not discover size of disk on the node ' '%s. Value of `properties/local_gb` of the ' 'node is not overwritten.'), task.node.uuid) for known_property in self.ESSENTIAL_PROPERTIES: inspected_properties[known_property] = properties[known_property] node_properties = task.node.properties node_properties.update(inspected_properties) task.node.properties = node_properties # Inspect the hardware for additional hardware capabilities. # Since additional hardware capabilities may not apply to all the # hardwares, the method inspect_hardware() doesn't raise an error # for these capabilities. capabilities = _get_capabilities(task.node, ilo_object) if capabilities: valid_cap = _create_supported_capabilities_dict(capabilities) capabilities = utils.get_updated_capabilities( task.node.properties.get('capabilities'), valid_cap) if capabilities: node_properties['capabilities'] = capabilities task.node.properties = node_properties task.node.save() # Create ports for the nics detected. _create_ports_if_not_exist(task, result['macs']) LOG.debug(("Node properties for %(node)s are updated as " "%(properties)s"), { 'properties': inspected_properties, 'node': task.node.uuid }) LOG.info(_LI("Node %s inspected."), task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info( _LI("The node %s was powered on for inspection. " "Powered off the node as inspection completed."), task.node.uuid) return states.MANAGEABLE
def delete_configuration(self, task): """Delete the RAID configuration. :param task: a TaskManager instance containing the node to act on. :raises: NodeCleaningFailure, on failure to execute clean step. :raises: InstanceDeployFailure, on failure to execute deploy step. """ node = task.node LOG.debug("OOB RAID delete_configuration invoked for node %s.", node.uuid) driver_internal_info = node.driver_internal_info ilo_object = ilo_common.get_ilo_object(node) try: # Raid configuration in progress, checking status if not driver_internal_info.get('ilo_raid_delete_in_progress'): ilo_object.delete_raid_configuration() self._prepare_for_read_raid(task, 'delete_raid') return deploy_utils.get_async_step_return_state(node) else: # Raid configuration is done, updating raid_config raid_conf = ilo_object.read_raid_configuration() fields = ['ilo_raid_delete_in_progress'] if node.clean_step: fields.append('skip_current_clean_step') else: fields.append('skip_current_deploy_step') self._pop_driver_internal_values(task, *fields) if not len(raid_conf['logical_disks']): node.raid_config = {} LOG.debug("Node %(uuid)s raid delete clean step is done.", {'uuid': node.uuid}) else: # Raid configuration failed err_msg = (_("Step delete_configuration failed " "on node %(node)s with error: " "Unable to delete these logical disks: " "%(disks)s") % { 'node': node.uuid, 'disks': raid_conf['logical_disks'] }) if node.clean_step: raise exception.NodeCleaningFailure(err_msg) else: raise exception.InstanceDeployFailure(reason=err_msg) except ilo_error.IloLogicalDriveNotFoundError: LOG.info("No logical drive found to delete on node %(node)s", {'node': node.uuid}) except ilo_error.IloError as ilo_exception: operation = (_("Failed to delete raid configuration on node %s") % node.uuid) self._pop_driver_internal_values(task, 'ilo_raid_delete_in_progress', 'skip_current_clean_step') fields = ['ilo_raid_delete_in_progress'] if node.clean_step: fields.append('skip_current_clean_step') else: fields.append('skip_current_deploy_step') self._pop_driver_internal_values(task, *fields) self._set_step_failed(task, operation, ilo_exception)
def create_configuration(self, task, create_root_volume=True, create_nonroot_volumes=True): """Create a RAID configuration on a bare metal using agent ramdisk. This method creates a RAID configuration on the given node. :param task: a TaskManager instance. :param create_root_volume: If True, a root volume is created during RAID configuration. Otherwise, no root volume is created. Default is True. :param create_nonroot_volumes: If True, non-root volumes are created. If False, no non-root volumes are created. Default is True. :raises: MissingParameterValue, if node.target_raid_config is missing or was found to be empty after skipping root volume and/or non-root volumes. :raises: NodeCleaningFailure, on failure to execute step. """ node = task.node target_raid_config = raid.filter_target_raid_config( node, create_root_volume=create_root_volume, create_nonroot_volumes=create_nonroot_volumes) driver_internal_info = node.driver_internal_info driver_internal_info['target_raid_config'] = target_raid_config LOG.debug("Calling OOB RAID create_configuration for node %(node)s " "with the following target RAID configuration: %(target)s", {'node': node.uuid, 'target': target_raid_config}) ilo_object = ilo_common.get_ilo_object(node) try: # Raid configuration in progress, checking status if not driver_internal_info.get('ilo_raid_create_in_progress'): ilo_object.create_raid_configuration(target_raid_config) self._prepare_for_read_raid(task, 'create_raid') return states.CLEANWAIT else: # Raid configuration is done, updating raid_config raid_conf = ( ilo_object.read_raid_configuration( raid_config=target_raid_config)) if len(raid_conf['logical_disks']): raid.update_raid_info(node, raid_conf) LOG.debug("Node %(uuid)s raid create clean step is done.", {'uuid': node.uuid}) self._pop_driver_internal_values( task, 'ilo_raid_create_in_progress', 'cleaning_reboot', 'skip_current_clean_step') node.driver_internal_info = driver_internal_info node.save() else: # Raid configuration failed msg = "Unable to create raid" self._pop_driver_internal_values( task, 'ilo_raid_create_in_progress', 'cleaning_reboot', 'skip_current_clean_step') node.driver_internal_info = driver_internal_info node.save() raise exception.NodeCleaningFailure( "Clean step create_configuration failed " "on node %(node)s with error: %(err)s" % {'node': node.uuid, 'err': msg}) except ilo_error.IloError as ilo_exception: operation = (_("Failed to create raid configuration on node %s") % node.uuid) self._pop_driver_internal_values(task, 'ilo_raid_create_in_progress', 'cleaning_reboot', 'skip_current_clean_step') node.driver_internal_info = driver_internal_info node.save() self._set_clean_failed(task, operation, ilo_exception)
def create_configuration(self, task, create_root_volume=True, create_nonroot_volumes=True): """Create a RAID configuration on a bare metal using agent ramdisk. This method creates a RAID configuration on the given node. :param task: a TaskManager instance. :param create_root_volume: If True, a root volume is created during RAID configuration. Otherwise, no root volume is created. Default is True. :param create_nonroot_volumes: If True, non-root volumes are created. If False, no non-root volumes are created. Default is True. :raises: MissingParameterValue, if node.target_raid_config is missing or was found to be empty after skipping root volume and/or non-root volumes. :raises: NodeCleaningFailure, on failure to execute step. """ node = task.node target_raid_config = raid.filter_target_raid_config( node, create_root_volume=create_root_volume, create_nonroot_volumes=create_nonroot_volumes) driver_internal_info = node.driver_internal_info driver_internal_info['target_raid_config'] = target_raid_config node.driver_internal_info = driver_internal_info node.save() LOG.debug("Calling OOB RAID create_configuration for node %(node)s " "with the following target RAID configuration: %(target)s", {'node': node.uuid, 'target': target_raid_config}) ilo_object = ilo_common.get_ilo_object(node) try: # Raid configuration in progress, checking status if not driver_internal_info.get('ilo_raid_create_in_progress'): ilo_object.create_raid_configuration(target_raid_config) self._prepare_for_read_raid(task, 'create_raid') return states.CLEANWAIT else: # Raid configuration is done, updating raid_config raid_conf = ( ilo_object.read_raid_configuration( raid_config=target_raid_config)) if len(raid_conf['logical_disks']): raid.update_raid_info(node, raid_conf) LOG.debug("Node %(uuid)s raid create clean step is done.", {'uuid': node.uuid}) self._pop_driver_internal_values( task, 'ilo_raid_create_in_progress', 'skip_current_clean_step') else: # Raid configuration failed msg = "Unable to create raid" self._pop_driver_internal_values( task, 'ilo_raid_create_in_progress', 'skip_current_clean_step') raise exception.NodeCleaningFailure( "Clean step create_configuration failed " "on node %(node)s with error: %(err)s" % {'node': node.uuid, 'err': msg}) except ilo_error.IloError as ilo_exception: operation = (_("Failed to create raid configuration on node %s") % node.uuid) self._pop_driver_internal_values(task, 'ilo_raid_create_in_progress', 'skip_current_clean_step') self._set_clean_failed(task, operation, ilo_exception)
def erase_devices(self, task, **kwargs): """Erase all the drives on the node. This method performs out-of-band sanitize disk erase on all the supported physical drives in the node. This erase cannot be performed on logical drives. :param task: a TaskManager instance. :raises: InvalidParameterValue, if any of the arguments are invalid. :raises: IloError on an error from iLO. """ erase_pattern = kwargs.get('erase_pattern', { 'hdd': 'overwrite', 'ssd': 'block' }) node = task.node self._validate_erase_pattern(erase_pattern, node) driver_internal_info = node.driver_internal_info LOG.debug("Calling out-of-band sanitize disk erase for node %(node)s", {'node': node.uuid}) try: ilo_object = ilo_common.get_ilo_object(node) disk_types = ilo_object.get_available_disk_types() LOG.info( "Disk type detected are: %(disk_types)s. Sanitize disk " "erase are now exercised for one after another disk type " "for node %(node)s.", { 'disk_types': disk_types, 'node': node.uuid }) if disk_types: # First disk-erase will execute for HDD's and after reboot only # try for SSD, since both share same redfish api and would be # overwritten. if not driver_internal_info.get( 'ilo_disk_erase_hdd_check') and ('HDD' in disk_types): ilo_object.do_disk_erase('HDD', erase_pattern.get('hdd')) self._set_driver_internal_value( task, True, 'cleaning_reboot', 'ilo_disk_erase_hdd_check') self._set_driver_internal_value(task, False, 'skip_current_clean_step') deploy_opts = deploy_utils.build_agent_options(task.node) task.driver.boot.prepare_ramdisk(task, deploy_opts) manager_utils.node_power_action(task, states.REBOOT) return states.CLEANWAIT if not driver_internal_info.get( 'ilo_disk_erase_ssd_check') and ('SSD' in disk_types): ilo_object.do_disk_erase('SSD', erase_pattern.get('ssd')) self._set_driver_internal_value( task, True, 'ilo_disk_erase_hdd_check', 'ilo_disk_erase_ssd_check', 'cleaning_reboot') self._set_driver_internal_value(task, False, 'skip_current_clean_step') deploy_opts = deploy_utils.build_agent_options(task.node) task.driver.boot.prepare_ramdisk(task, deploy_opts) manager_utils.node_power_action(task, states.REBOOT) return states.CLEANWAIT # It will wait until disk erase will complete if self._wait_for_disk_erase_status(task.node): LOG.info( "For node %(uuid)s erase_devices clean " "step is done.", {'uuid': task.node.uuid}) self._pop_driver_internal_values( task, 'ilo_disk_erase_hdd_check', 'ilo_disk_erase_ssd_check') else: LOG.info( "No drive found to perform out-of-band sanitize " "disk erase for node %(node)s", {'node': node.uuid}) except ilo_error.IloError as ilo_exception: self._pop_driver_internal_values(task, 'ilo_disk_erase_hdd_check', 'ilo_disk_erase_ssd_check', 'cleaning_reboot', 'skip_current_clean_step') self._set_clean_failed(task, ilo_exception)
def _set_power_state(task, target_state, timeout=None): """Turns the server power on/off or do a reboot. :param task: a TaskManager instance containing the node to act on. :param target_state: target state of the node. :param timeout: timeout (in seconds) positive integer (> 0) for any power state. ``None`` indicates default timeout. :raises: InvalidParameterValue if an invalid power state was specified. :raises: IloOperationError on an error from IloClient library. :raises: PowerStateFailure if the power couldn't be set to target_state. """ node = task.node ilo_object = ilo_common.get_ilo_object(node) # Check if its soft power operation soft_power_op = target_state in [states.SOFT_POWER_OFF, states.SOFT_REBOOT] requested_state = target_state if target_state == states.SOFT_REBOOT: if _get_power_state(node) == states.POWER_OFF: target_state = states.POWER_ON # Trigger the operation based on the target state. try: if target_state == states.POWER_OFF: ilo_object.hold_pwr_btn() elif target_state == states.POWER_ON: _attach_boot_iso_if_needed(task) ilo_object.set_host_power('ON') elif target_state == states.REBOOT: _attach_boot_iso_if_needed(task) ilo_object.reset_server() target_state = states.POWER_ON elif target_state in (states.SOFT_POWER_OFF, states.SOFT_REBOOT): ilo_object.press_pwr_btn() else: msg = _("_set_power_state called with invalid power state " "'%s'") % target_state raise exception.InvalidParameterValue(msg) except ilo_error.IloError as ilo_exception: LOG.error("iLO set_power_state failed to set state to %(tstate)s " " for node %(node_id)s with error: %(error)s", {'tstate': target_state, 'node_id': node.uuid, 'error': ilo_exception}) operation = _('iLO set_power_state') raise exception.IloOperationError(operation=operation, error=ilo_exception) # Wait till the soft power state change gets reflected. time_consumed = 0 if soft_power_op: # For soft power-off, bare metal reaches final state with one # power operation. In case of soft reboot it takes two; soft # power-off followed by power-on. Also, for soft reboot we # need to ensure timeout does not expire during power-off # and power-on operation. is_final_state = target_state in (states.SOFT_POWER_OFF, states.POWER_ON) time_consumed = _wait_for_state_change( node, target_state, requested_state, is_final_state=is_final_state, timeout=timeout) if target_state == states.SOFT_REBOOT: _attach_boot_iso_if_needed(task) try: ilo_object.set_host_power('ON') except ilo_error.IloError as ilo_exception: operation = (_('Powering on failed after soft power off for ' 'node %s') % node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) # Re-calculate timeout available for power-on operation rem_timeout = timeout - time_consumed time_consumed += _wait_for_state_change( node, states.SOFT_REBOOT, requested_state, is_final_state=True, timeout=rem_timeout) else: time_consumed = _wait_for_state_change( node, target_state, requested_state, is_final_state=True, timeout=timeout) LOG.info("The node %(node_id)s operation of '%(state)s' " "is completed in %(time_consumed)s seconds.", {'node_id': node.uuid, 'state': target_state, 'time_consumed': time_consumed})