def set_config(task, **kwargs): """Sets the pending_value parameter for each of the values passed in. :param task: a TaskManager instance containing the node to act on. :param kwargs: a dictionary of {'AttributeName': 'NewValue'} :raises: DracOperationError on an error from python-dracclient. :returns: A dictionary containing the commit_required key with a boolean value indicating whether commit_bios_config() needs to be called to make the changes. """ node = task.node drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) if 'http_method' in kwargs: del kwargs['http_method'] try: return client.set_bios_settings(kwargs) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to set the BIOS settings for node ' '%(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def delete_virtual_disk(node, virtual_disk): """Delete a single virtual disk on a RAID controller. The deleted virtual disk will be in pending state. The DRAC card will do the actual configuration once the changes are applied by calling the ``commit_config`` method. :param node: an ironic node object. :param virtual_disk: id of the virtual disk. :returns: a dictionary containing the commit_needed key with a boolean value indicating whether a config job must be created for the values to be applied. :raises: DracOperationError on an error from python-dracclient. """ drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) try: return client.delete_virtual_disk(virtual_disk) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to delete virtual disk ' '%(virtual_disk_fqdd)s for node %(node_uuid)s. ' 'Reason: %(error)s.', {'virtual_disk_fqdd': virtual_disk, 'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def test_validate_job_queue(self, mock_get_drac_client): mock_client = mock.Mock() mock_get_drac_client.return_value = mock_client mock_client.list_jobs.return_value = [] drac_job.validate_job_queue(self.node) mock_client.list_jobs.assert_called_once_with(only_unfinished=True)
def set_boot_device(node, device, persistent=False): """Set the boot device for a node. Set the boot device to use on next boot of the node. :param node: an ironic node object. :param device: the boot device, one of :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: DracOperationError on an error from python-dracclient. """ drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) try: drac_boot_devices = client.list_boot_devices() current_boot_device = _get_boot_device(node, drac_boot_devices) # If we are already booting from the right device, do nothing. if current_boot_device == {'boot_device': device, 'persistent': persistent}: LOG.debug('DRAC already set to boot from %s', device) return drac_boot_device = next(drac_device.id for drac_device in drac_boot_devices[PERSISTENT_BOOT_MODE] if _BOOT_DEVICES_MAP[device] in drac_device.id) if persistent: boot_list = PERSISTENT_BOOT_MODE else: boot_list = NON_PERSISTENT_BOOT_MODE client.change_boot_device_order(boot_list, drac_boot_device) client.commit_pending_bios_changes() except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to change boot device order for ' 'node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def commit_config(task, reboot=False): """Commits pending changes added by set_config :param task: a TaskManager instance containing the node to act on. :param reboot: indicates whether a reboot job should be automatically created with the config job. :raises: DracOperationError on an error from python-dracclient. :returns: the job_id key with the id of the newly created config job. """ node = task.node drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) try: return client.commit_pending_bios_changes(reboot) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to commit the pending BIOS changes ' 'for node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def create_virtual_disk(node, raid_controller, physical_disks, raid_level, size_mb, disk_name=None, span_length=None, span_depth=None): """Create a single virtual disk on a RAID controller. The created virtual disk will be in pending state. The DRAC card will do the actual configuration once the changes are applied by calling the ``commit_config`` method. :param node: an ironic node object. :param raid_controller: id of the RAID controller. :param physical_disks: ids of the physical disks. :param raid_level: RAID level of the virtual disk. :param size_mb: size of the virtual disk. :param disk_name: name of the virtual disk. (optional) :param span_depth: Number of spans in virtual disk. (optional) :param span_length: Number of disks per span. (optional) :returns: a dictionary containing the commit_needed key with a boolean value indicating whether a config job must be created for the values to be applied. :raises: DracOperationError on an error from python-dracclient. """ drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) try: return client.create_virtual_disk(raid_controller, physical_disks, raid_level, size_mb, disk_name, span_length, span_depth) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to create virtual disk for node ' '%(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def set_boot_device(node, device, persistent=False): """Set the boot device for a node. Set the boot device to use on next boot of the node. :param node: an ironic node object. :param device: the boot device, one of :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: DracOperationError on an error from python-dracclient. """ drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) try: drac_boot_devices = client.list_boot_devices() current_boot_device = _get_boot_device(node, drac_boot_devices) # If we are already booting from the right device, do nothing. if current_boot_device == {'boot_device': device, 'persistent': persistent}: LOG.debug('DRAC already set to boot from %s', device) return persistent_boot_mode = _get_next_persistent_boot_mode(node) drac_boot_device = None for drac_device in drac_boot_devices[persistent_boot_mode]: for id_component in _BOOT_DEVICES_MAP[device]: if id_component in drac_device.id: drac_boot_device = drac_device.id break if drac_boot_device: break if drac_boot_device: if persistent: boot_list = persistent_boot_mode else: boot_list = _NON_PERSISTENT_BOOT_MODE client.change_boot_device_order(boot_list, drac_boot_device) else: # No DRAC boot device of the type requested by the argument # 'device' is present. This is normal for UEFI boot mode, # following deployment's writing of the operating system to # disk. It can also occur when a server has not been # powered on after a new boot device has been installed. # # If the boot order is flexibly programmable, use that to # attempt to detect and boot from a device of the requested # type during the next boot. That avoids the need for an # extra reboot. Otherwise, this function cannot satisfy the # request, because it was called with an invalid device. bios_settings = client.list_bios_settings(by_name=True) if _is_boot_order_flexibly_programmable(persistent, bios_settings): drac_boot_mode = bios_settings['BootMode'].current_value if drac_boot_mode not in _DRAC_BOOT_MODES: message = _("DRAC reported unknown boot mode " "'%(drac_boot_mode)s'") % { 'drac_boot_mode': drac_boot_mode} LOG.error('DRAC driver failed to change boot device order ' 'for node %(node_uuid)s. Reason: %(message)s.', {'node_uuid': node.uuid, 'message': message}) raise exception.DracOperationError(error=message) flexibly_program_settings = _flexibly_program_boot_order( device, drac_boot_mode) client.set_bios_settings(flexibly_program_settings) else: raise exception.InvalidParameterValue( _("set_boot_device called with invalid device " "'%(device)s' for node %(node_id)s.") % {'device': device, 'node_id': node.uuid}) client.commit_pending_bios_changes() except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to change boot device order for ' 'node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def set_boot_device(node, device, persistent=False): """Set the boot device for a node. Set the boot device to use on next boot of the node. :param node: an ironic node object. :param device: the boot device, one of :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: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) # If pending BIOS job or pending non-BIOS job found in job queue, # we need to clear that jobs before executing clear_job_queue or # known_good_state clean step of management interface. # Otherwise, pending BIOS config job can cause creating new config jobs # to fail and pending non-BIOS job can execute on reboot the node. validate_job_queue = True if node.driver_internal_info.get("clean_steps"): if node.driver_internal_info.get("clean_steps")[0].get( 'step') in _CLEAR_JOBS_CLEAN_STEPS: unfinished_jobs = drac_job.list_unfinished_jobs(node) if unfinished_jobs: validate_job_queue = False client.delete_jobs(job_ids=[job.id for job in unfinished_jobs]) if validate_job_queue: drac_job.validate_job_queue(node) try: drac_boot_devices = client.list_boot_devices() current_boot_device = _get_boot_device(node, drac_boot_devices) # If we are already booting from the right device, do nothing. if current_boot_device == {'boot_device': device, 'persistent': persistent}: LOG.debug('DRAC already set to boot from %s', device) return persistent_boot_mode = _get_next_persistent_boot_mode(node) drac_boot_device = None for drac_device in drac_boot_devices[persistent_boot_mode]: for id_component in _BOOT_DEVICES_MAP[device]: if id_component in drac_device.id: drac_boot_device = drac_device.id break if drac_boot_device: break if drac_boot_device: if persistent: boot_list = persistent_boot_mode else: boot_list = _NON_PERSISTENT_BOOT_MODE client.change_boot_device_order(boot_list, drac_boot_device) else: # No DRAC boot device of the type requested by the argument # 'device' is present. This is normal for UEFI boot mode, # following deployment's writing of the operating system to # disk. It can also occur when a server has not been # powered on after a new boot device has been installed. # # If the boot order is flexibly programmable, use that to # attempt to detect and boot from a device of the requested # type during the next boot. That avoids the need for an # extra reboot. Otherwise, this function cannot satisfy the # request, because it was called with an invalid device. bios_settings = client.list_bios_settings(by_name=True) if _is_boot_order_flexibly_programmable(persistent, bios_settings): drac_boot_mode = bios_settings['BootMode'].current_value if drac_boot_mode not in _DRAC_BOOT_MODES: message = _("DRAC reported unknown boot mode " "'%(drac_boot_mode)s'") % { 'drac_boot_mode': drac_boot_mode} LOG.error('DRAC driver failed to change boot device order ' 'for node %(node_uuid)s. Reason: %(message)s.', {'node_uuid': node.uuid, 'message': message}) raise exception.DracOperationError(error=message) flexibly_program_settings = _flexibly_program_boot_order( device, drac_boot_mode) client.set_bios_settings(flexibly_program_settings) else: raise exception.InvalidParameterValue( _("set_boot_device called with invalid device " "'%(device)s' for node %(node_id)s.") % {'device': device, 'node_id': node.uuid}) job_id = client.commit_pending_bios_changes() job_entry = client.get_job(job_id) timeout = CONF.drac.boot_device_job_status_timeout end_time = time.time() + timeout LOG.debug('Waiting for BIOS configuration job %(job_id)s ' 'to be scheduled for node %(node)s', {'job_id': job_id, 'node': node.uuid}) while job_entry.status != "Scheduled": if time.time() >= end_time: raise exception.DracOperationError( error=_( 'Timed out waiting BIOS configuration for job ' '%(job)s to reach Scheduled state. Job is still ' 'in %(status)s state.') % {'job': job_id, 'status': job_entry.status}) time.sleep(3) job_entry = client.get_job(job_id) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to change boot device order for ' 'node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def apply_configuration(self, task, settings): """Apply the BIOS configuration to the node :param task: a TaskManager instance containing the node to act on :param settings: List of BIOS settings to apply :raises: DRACOperationError upon an error from python-dracclient :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment) if configuration is in progress asynchronously or None if it is completed. """ LOG.debug( "Configuring node %(node_uuid)s with BIOS settings:" " %(settings)s", { "node_uuid": task.node.uuid, "settings": settings }) node = task.node # convert ironic settings list to DRAC kwsettings kwsettings = {s['name']: s['value'] for s in settings} drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) try: # Argument validation is done by the dracclient method # set_bios_settings. No need to do it here. set_result = client.set_bios_settings(kwsettings) except drac_exceptions.BaseClientException as exc: LOG.error( "Failed to apply BIOS config on node %(node_uuid)s." " Error %(error)s", { "node_uuid": task.node.uuid, "error": exc }) raise exception.DracOperationError(error=exc) # If no commit is required, we're done if not set_result['is_commit_required']: LOG.info( "Completed BIOS configuration on node %(node_uuid)s" " with BIOS settings: %(settings)s", { "node_uuid": task.node.uuid, "settings": settings }) return # Otherwise, need to reboot the node as well to commit configuration else: LOG.debug("Rebooting node %(node_uuid)s to apply BIOS settings", {"node_uuid": task.node.uuid}) reboot_needed = set_result['is_reboot_required'] try: commit_result = client.commit_pending_bios_changes( reboot=reboot_needed) except drac_exceptions.BaseClientException as exc: LOG.error( "Failed to commit BIOS changes on node %(node_uuid)s" ". Error %(error)s", { "node_uuid": task.node.uuid, "error": exc }) raise exception.DracOperationError(error=exc) # Store JobID for the async job handler _check_node_bios_jobs driver_internal_info = node.driver_internal_info driver_internal_info.setdefault('bios_config_job_ids', []).append(commit_result) node.driver_internal_info = driver_internal_info # This method calls node.save(), bios_config_job_ids will be saved # automatically # These flags are for the conductor to manage the asynchronous # jobs that have been initiated by this method deploy_utils.set_async_step_flags(node, reboot=reboot_needed, skip_current_step=True, polling=True) # Return the clean/deploy state string return deploy_utils.get_async_step_return_state(node)
def factory_reset(self, task): """Reset the BIOS settings of the node to the factory default. This uses the Lifecycle Controller configuration to perform BIOS configuration reset. Leveraging the python-dracclient methods already available. :param task: a TaskManager instance containing the node to act on :raises: DracOperationError on an error from python-dracclient :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment) if reset is in progress asynchronously or None if it is completed. """ node = task.node drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) lc_bios_reset_attrib = {"BIOS Reset To Defaults Requested": "True"} try: set_result = client.set_lifecycle_settings(lc_bios_reset_attrib) except drac_exceptions.BaseClientException as exc: LOG.error( 'Failed to reset BIOS on the node %(node_uuid)s.' ' Reason: %(error)s.', { 'node_uuid': node.uuid, 'error': exc }) raise exception.DracOperationError(error=exc) if not set_result['is_commit_required']: LOG.info("BIOS reset successful on the node " "%(node_uuid)s", {"node_uuid": node.uuid}) return else: # Rebooting the Node is compulsory, LC call returns # reboot_required=False/Optional, which is not desired reboot_needed = True try: commit_job_id = client.commit_pending_lifecycle_changes( reboot=reboot_needed) except drac_exceptions.BaseClientException as exc: LOG.error( 'Failed to commit BIOS reset on node ' '%(node_uuid)s. Reason: %(error)s.', { 'node_uuid': node.uuid, 'error': exc }) raise exception.DracOperationError(error=exc) # Store JobID for the async job handler _check_node_bios_jobs driver_internal_info = node.driver_internal_info driver_internal_info.setdefault('bios_config_job_ids', []).append(commit_job_id) node.driver_internal_info = driver_internal_info # This method calls node.save(), bios_config_job_id will be # saved automatically # These flags are for the conductor to manage the asynchronous # jobs that have been initiated by this method deploy_utils.set_async_step_flags(node, reboot=reboot_needed, skip_current_step=True, polling=True) return deploy_utils.get_async_step_return_state(task.node)