def test_get_drac_client(self, mock_dracclient): expected_call = mock.call('1.2.3.4', 'admin', 'fake', 443, '/wsman', 'https') node = obj_utils.create_test_node(self.context, driver='fake_drac', driver_info=INFO_DICT) drac_common.get_drac_client(node) self.assertEqual(mock_dracclient.mock_calls, [expected_call])
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 'is_commit_required' key with a boolean value indicating whether commit_config() needs to be called to make the changes, and the 'is_reboot_required' key which has a value of 'true' or 'false'. This key is used to indicate to the commit_config() call if a reboot should be performed. """ 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 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 commit_config(node, raid_controller, reboot=False, realtime=False): """Apply all pending changes on a RAID controller. :param node: an ironic node object. :param raid_controller: id of the RAID controller. :param reboot: indicates whether a reboot job should be automatically created with the config job. (optional, defaults to False) :param realtime: indicates RAID controller supports realtime. (optional, defaults to False) :returns: id of the created job :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: return client.commit_pending_raid_changes( raid_controller=raid_controller, reboot=reboot, realtime=realtime) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to commit pending RAID config for' ' controller %(raid_controller_fqdd)s on node ' '%(node_uuid)s. Reason: %(error)s.', {'raid_controller_fqdd': raid_controller, 'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def _is_raid_controller(node, raid_controller_fqdd, raid_controllers=None): """Find out if object's fqdd is for a raid controller or not :param node: an ironic node object :param raid_controller_fqdd: The object's fqdd we are testing to see if it is a raid controller or not. :param raid_controllers: A list of RAIDControllers used to check for the presence of BOSS cards. If None, the iDRAC will be queried for the list of controllers. :returns: boolean, True if the device is a RAID controller, False if not. """ client = drac_common.get_drac_client(node) try: return client.is_raid_controller(raid_controller_fqdd, raid_controllers) except drac_exceptions.BaseClientException as exc: LOG.error('Unable to determine if controller %(raid_controller_fqdd)s ' 'on node %(node_uuid)s is a RAID controller. ' 'Reason: %(error)s. ', {'raid_controller_fqdd': raid_controller_fqdd, 'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def clear_foreign_config(node, raid_controller): """Free up the foreign drives. :param node: an ironic node object. :param raid_controller: id of the RAID controller. :returns: a dictionary containing - The is_commit_required needed key with a boolean value indicating whether a config job must be created for the values to be applied. - The is_reboot_required key with a RebootRequired enumerated value indicating whether the server must be rebooted to clear foreign configuration. :raises: DracOperationError on an error from python-dracclient. """ try: drac_job.validate_job_queue(node) client = drac_common.get_drac_client(node) return client.clear_foreign_config(raid_controller) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to free foreign driver ' 'on %(raid_controller_fqdd)s ' 'for node %(node_uuid)s. ' 'Reason: %(error)s.', {'raid_controller_fqdd': raid_controller, 'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def validate_job_queue(node): """Validates the job queue on the node. It raises an exception if an unfinished configuration job exists. :param node: an ironic node object. :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: unfinished_jobs = client.list_jobs(only_unfinished=True) except drac_exceptions.BaseClientException as exc: LOG.error( _LE('DRAC driver failed to get the list of unfinished jobs ' 'for node %(node_uuid)s. Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc }) raise exception.DracOperationError(error=exc) if unfinished_jobs: msg = _('Unfinished config jobs found: %(jobs)r. Make sure they are ' 'completed before retrying.') % { 'jobs': unfinished_jobs } raise exception.DracOperationError(error=msg)
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 _get_next_persistent_boot_mode(node): client = drac_common.get_drac_client(node) try: boot_modes = client.list_boot_modes() except drac_exceptions.BaseClientException as exc: LOG.error( 'DRAC driver failed to get next persistent boot mode for ' 'node %(node_uuid)s. Reason: %(error)s', { 'node_uuid': node.uuid, 'error': exc }) raise exception.DracOperationError(error=exc) next_persistent_boot_mode = None for mode in boot_modes: if mode.is_next and mode.id != _NON_PERSISTENT_BOOT_MODE: next_persistent_boot_mode = mode.id break if not next_persistent_boot_mode: message = _('List of boot modes, %(list_boot_modes)s, does not ' 'contain a persistent mode') % { 'list_boot_modes': boot_modes } LOG.error( 'DRAC driver failed to get next persistent boot mode for ' 'node %(node_uuid)s. Reason: %(message)s', { 'node_uuid': node.uuid, 'message': message }) raise exception.DracOperationError(error=message) return next_persistent_boot_mode
def _get_next_persistent_boot_mode(node): client = drac_common.get_drac_client(node) try: boot_modes = client.list_boot_modes() except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to get next persistent boot mode for ' 'node %(node_uuid)s. Reason: %(error)s', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc) next_persistent_boot_mode = None for mode in boot_modes: if mode.is_next and mode.id != _NON_PERSISTENT_BOOT_MODE: next_persistent_boot_mode = mode.id break if not next_persistent_boot_mode: message = _('List of boot modes, %(list_boot_modes)s, does not ' 'contain a persistent mode') % { 'list_boot_modes': boot_modes} LOG.error('DRAC driver failed to get next persistent boot mode for ' 'node %(node_uuid)s. Reason: %(message)s', {'node_uuid': node.uuid, 'message': message}) raise exception.DracOperationError(error=message) return next_persistent_boot_mode
def _set_power_state(node, power_state): """Turns the server power on/off or do a reboot. :param node: an ironic node object. :param power_state: a power state from :mod:`ironic.common.states`. :raises: InvalidParameterValue if required DRAC credentials are missing. :raises: DracOperationError on an error from python-dracclient """ # NOTE(ifarkas): DRAC interface doesn't allow changing the boot device # multiple times in a row without a reboot. This is # because a change need to be committed via a # configuration job, and further configuration jobs # cannot be created until the previous one is processed # at the next boot. As a workaround, it is saved to # driver_internal_info during set_boot_device and committing # it here. _commit_boot_list_change(node) client = drac_common.get_drac_client(node) target_power_state = REVERSE_POWER_STATES[power_state] try: client.set_power_state(target_power_state) except drac_exceptions.BaseClientException as exc: LOG.error(_LE('DRAC driver failed to set power state for node ' '%(node_uuid)s to %(power_state)s. ' 'Reason: %(error)s.'), {'node_uuid': node.uuid, 'power_state': power_state, 'error': exc}) raise exception.DracOperationError(error=exc)
def _get_boot_device(node, drac_boot_devices=None): client = drac_common.get_drac_client(node) try: boot_modes = client.list_boot_modes() next_boot_modes = [mode.id for mode in boot_modes if mode.is_next] if NON_PERSISTENT_BOOT_MODE in next_boot_modes: next_boot_mode = NON_PERSISTENT_BOOT_MODE else: next_boot_mode = next_boot_modes[0] if drac_boot_devices is None: drac_boot_devices = client.list_boot_devices() drac_boot_device = drac_boot_devices[next_boot_mode][0] boot_device = next(key for (key, value) in _BOOT_DEVICES_MAP.items() if value in drac_boot_device.id) return { 'boot_device': boot_device, 'persistent': next_boot_mode == PERSISTENT_BOOT_MODE } except (drac_exceptions.BaseClientException, IndexError) as exc: LOG.error( 'DRAC driver failed to get next boot mode 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 commit_config(node, raid_controller, reboot=False, realtime=False): """Apply all pending changes on a RAID controller. :param node: an ironic node object. :param raid_controller: id of the RAID controller. :param reboot: indicates whether a reboot job should be automatically created with the config job. (optional, defaults to False) :param realtime: indicates RAID controller supports realtime. (optional, defaults to False) :returns: id of the created job :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: return client.commit_pending_raid_changes( raid_controller=raid_controller, reboot=reboot, realtime=realtime) except drac_exceptions.BaseClientException as exc: LOG.error( 'DRAC driver failed to commit pending RAID config for' ' controller %(raid_controller_fqdd)s on node ' '%(node_uuid)s. Reason: %(error)s.', { 'raid_controller_fqdd': raid_controller, 'node_uuid': node.uuid, 'error': exc }) raise exception.DracOperationError(error=exc)
def _set_power_state(node, power_state): """Turns the server power on/off or do a reboot. :param node: an ironic node object. :param power_state: a power state from :mod:`ironic.common.states`. :raises: InvalidParameterValue if required DRAC credentials are missing. :raises: DracOperationError on an error from python-dracclient """ # NOTE(ifarkas): DRAC interface doesn't allow changing the boot device # multiple times in a row without a reboot. This is # because a change need to be committed via a # configuration job, and further configuration jobs # cannot be created until the previous one is processed # at the next boot. As a workaround, it is saved to # driver_internal_info during set_boot_device and committing # it here. _commit_boot_list_change(node) client = drac_common.get_drac_client(node) target_power_state = REVERSE_POWER_STATES[power_state] try: client.set_power_state(target_power_state) except drac_exceptions.BaseClientException as exc: LOG.error( 'DRAC driver failed to set power state for node ' '%(node_uuid)s to %(power_state)s. ' 'Reason: %(error)s.', { 'node_uuid': node.uuid, 'power_state': power_state, 'error': exc }) raise exception.DracOperationError(error=exc)
def reset_idrac(self, task): """Reset the iDRAC. :param task: a TaskManager instance containing the node to act on. :returns: None if it is completed. :raises: DracOperationError on an error from python-dracclient. """ node = task.node client = drac_common.get_drac_client(node) client.reset_idrac(force=True, wait=True)
def known_good_state(self, task): """Reset the iDRAC, Clear the job queue. :param task: a TaskManager instance containing the node to act on. :returns: None if it is completed. :raises: DracOperationError on an error from python-dracclient. """ node = task.node client = drac_common.get_drac_client(node) client.reset_idrac(force=True, wait=True) client.delete_jobs(job_ids=[_CLEAR_JOB_IDS])
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 list_raid_controllers(node): """List the RAID controllers of the node. :param node: an ironic node object. :returns: a list of RAIDController objects from dracclient. :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: return client.list_raid_controllers() except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to get the list of RAID controllers ' 'for node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def clear_job_queue(self, task): """Clear the job queue. :param task: a TaskManager instance containing the node to act on. :returns: None if it is completed. :raises: DracOperationError on an error from python-dracclient. """ try: node = task.node client = drac_common.get_drac_client(node) client.delete_jobs(job_ids=[_CLEAR_JOB_IDS]) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to clear the job queue for node ' '%(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def list_unfinished_jobs(node): """List unfinished config jobs of the node. :param node: an ironic node object. :returns: a list of Job objects from dracclient. :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: return client.list_jobs(only_unfinished=True) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to get the list of unfinished jobs ' 'for node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def cache_bios_settings(self, task): """Store or update the current BIOS settings for the node. Get the current BIOS settings and store them in the bios_settings database table. :param task: a TaskManager instance containing the node to act on. :raises: DracOperationError on an error from python-dracclient """ node = task.node node_id = node.id node_uuid = node.uuid client = drac_common.get_drac_client(node) try: kwsettings = client.list_bios_settings() except drac_exceptions.BaseClientException as exc: LOG.error( 'DRAC driver failed to get the BIOS settings for node ' '%(node_uuid)s. Reason: %(error)s.', { 'node_uuid': node.uuid, 'error': exc }) raise exception.DracOperationError(error=exc) # convert dracclient BIOS settings into ironic settings list settings = [{ "name": name, "value": attrib.current_value } for name, attrib in kwsettings.items()] # Store them in the database table LOG.debug('Caching BIOS settings for node %(node_uuid)s', {'node_uuid': node_uuid}) create_list, update_list, delete_list, nochange_list = ( objects.BIOSSettingList.sync_node_setting(task.context, node_id, settings)) if create_list: objects.BIOSSettingList.create(task.context, node_id, create_list) if update_list: objects.BIOSSettingList.save(task.context, node_id, update_list) if delete_list: delete_names = [d['name'] for d in delete_list] objects.BIOSSettingList.delete(task.context, node_id, delete_names)
def abandon_config(task): """Abandons uncommitted changes added by set_config :param task: a TaskManager instance containing the node to act on. :raises: DracOperationError on an error from python-dracclient. """ node = task.node client = drac_common.get_drac_client(node) try: client.abandon_pending_bios_changes() except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to delete the pending BIOS ' 'settings for node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def get_job(node, job_id): """Get the details of a Lifecycle job of the node. :param node: an ironic node object. :param job_id: ID of the Lifecycle job. :returns: a Job object from dracclient. :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: return client.get_job(job_id) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to get the job %(job_id)s ' '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 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 list_physical_disks(node): """List the physical disks of the node. :param node: an ironic node object. :returns: a list of PhysicalDisk objects from dracclient. :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: return client.list_physical_disks() except drac_exceptions.BaseClientException as exc: LOG.error( _LE('DRAC driver failed to get the list of physical disks ' '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( _LE('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 abandon_config(node, raid_controller): """Deletes all pending changes on a RAID controller. :param node: an ironic node object. :param raid_controller: id of the RAID controller. :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: client.abandon_pending_raid_changes(raid_controller) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to delete pending RAID config ' 'for controller %(raid_controller_fqdd)s on node ' '%(node_uuid)s. Reason: %(error)s.', {'raid_controller_fqdd': raid_controller, 'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def _get_power_state(node): """Returns the current power state of the node. :param node: an ironic node object. :returns: the power state, one of :mod:`ironic.common.states`. :raises: InvalidParameterValue if required DRAC credentials are missing. :raises: DracOperationError on an error from python-dracclient """ client = drac_common.get_drac_client(node) try: drac_power_state = client.get_power_state() except drac_exceptions.BaseClientException as exc: LOG.error(_LE('DRAC driver failed to get power state for node ' '%(node_uuid)s. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc) return POWER_STATES[drac_power_state]
def _get_boot_device(node, drac_boot_devices=None): client = drac_common.get_drac_client(node) try: boot_modes = client.list_boot_modes() next_boot_modes = [mode.id for mode in boot_modes if mode.is_next] if _NON_PERSISTENT_BOOT_MODE in next_boot_modes: next_boot_mode = _NON_PERSISTENT_BOOT_MODE else: next_boot_mode = next_boot_modes[0] if drac_boot_devices is None: drac_boot_devices = client.list_boot_devices() # It is possible for there to be no boot device. boot_device = None if next_boot_mode in drac_boot_devices: drac_boot_device = drac_boot_devices[next_boot_mode][0] for key, value in _BOOT_DEVICES_MAP.items(): for id_component in value: if id_component in drac_boot_device.id: boot_device = key break if boot_device: break return { 'boot_device': boot_device, 'persistent': next_boot_mode != _NON_PERSISTENT_BOOT_MODE } except (drac_exceptions.BaseClientException, IndexError) as exc: LOG.error( 'DRAC driver failed to get next boot mode for ' 'node %(node_uuid)s. Reason: %(error)s.', { 'node_uuid': node.uuid, 'error': exc }) raise exception.DracOperationError(error=exc)
def delete_configuration(self, task): """Delete the RAID configuration. :param task: a TaskManager instance containing the node to act on. :returns: states.CLEANWAIT if deletion is in progress asynchronously or None if it is completed. :raises: DracOperationError on an error from python-dracclient. """ node = task.node # The node is rebooting at this point, so wait for the iDRAC to # enter the ready state before proceeding with cleaning client = drac_common.get_drac_client(node) client.wait_until_idrac_is_ready() controllers = set() for disk in list_virtual_disks(node): controllers.add(disk.controller) delete_virtual_disk(node, disk.id) return _commit_to_controllers(node, list(controllers))
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 _get_boot_device(node, drac_boot_devices=None): client = drac_common.get_drac_client(node) try: boot_modes = client.list_boot_modes() next_boot_modes = [mode.id for mode in boot_modes if mode.is_next] if _NON_PERSISTENT_BOOT_MODE in next_boot_modes: next_boot_mode = _NON_PERSISTENT_BOOT_MODE else: next_boot_mode = next_boot_modes[0] if drac_boot_devices is None: drac_boot_devices = client.list_boot_devices() # It is possible for there to be no boot device. boot_device = None if next_boot_mode in drac_boot_devices: drac_boot_device = drac_boot_devices[next_boot_mode][0] for key, value in _BOOT_DEVICES_MAP.items(): for id_component in value: if id_component in drac_boot_device.id: boot_device = key break if boot_device: break return {'boot_device': boot_device, 'persistent': next_boot_mode != _NON_PERSISTENT_BOOT_MODE} except (drac_exceptions.BaseClientException, IndexError) as exc: LOG.error('DRAC driver failed to get next boot mode for ' 'node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def validate_job_queue(node): """Validates the job queue on the node. It raises an exception if an unfinished configuration job exists. :param node: an ironic node object. :raises: DracOperationError on an error from python-dracclient. """ client = drac_common.get_drac_client(node) try: unfinished_jobs = client.list_jobs(only_unfinished=True) except drac_exceptions.BaseClientException as exc: LOG.error( _LE("DRAC driver failed to get the list of unfinished jobs " "for node %(node_uuid)s. Reason: %(error)s."), {"node_uuid": node.uuid, "error": exc}, ) raise exception.DracOperationError(error=exc) if unfinished_jobs: msg = _("Unfinished config jobs found: %(jobs)r. Make sure they are " "completed before retrying.") % { "jobs": unfinished_jobs } raise exception.DracOperationError(error=msg)
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 _get_boot_device(node, drac_boot_devices=None): client = drac_common.get_drac_client(node) try: boot_modes = client.list_boot_modes() next_boot_modes = [mode.id for mode in boot_modes if mode.is_next] if NON_PERSISTENT_BOOT_MODE in next_boot_modes: next_boot_mode = NON_PERSISTENT_BOOT_MODE else: next_boot_mode = next_boot_modes[0] if drac_boot_devices is None: drac_boot_devices = client.list_boot_devices() drac_boot_device = drac_boot_devices[next_boot_mode][0] boot_device = next(key for (key, value) in _BOOT_DEVICES_MAP.items() if value in drac_boot_device.id) return {'boot_device': boot_device, 'persistent': next_boot_mode == PERSISTENT_BOOT_MODE} except (drac_exceptions.BaseClientException, IndexError) as exc: LOG.error('DRAC driver failed to get next boot mode for ' 'node %(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
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: factory_reset_time_before_reboot =\ client.get_system().last_system_inventory_time LOG.debug( "Factory resetting node %(node_uuid)s " "last inventory reboot time before factory reset " "%(factory_reset_time_before_reboot)s", { "node_uuid": task.node.uuid, "factory_reset_time_before_reboot": factory_reset_time_before_reboot }) commit_job_id = client.commit_pending_lifecycle_changes( reboot=reboot_needed) LOG.info( "Commit job id of a node %(node_uuid)s." "%(commit_job_id)s", { 'node_uuid': node.uuid, "commit_job_id": commit_job_id }) 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 the last inventory time on reboot for async job handler # _check_last_system_inventory_changed driver_internal_info = node.driver_internal_info driver_internal_info['factory_reset_time_before_reboot'] = \ factory_reset_time_before_reboot # Store the current time to later check if factory reset times out driver_internal_info['factory_reset_time'] = str( timeutils.utcnow(with_timezone=True)) node.driver_internal_info = driver_internal_info # rebooting the server to apply factory reset value client.set_power_state('REBOOT') # 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)
def get_config(node): """Get the BIOS configuration. The BIOS settings look like:: {'EnumAttrib': {'name': 'EnumAttrib', 'current_value': 'Value', 'pending_value': 'New Value', # could also be None 'read_only': False, 'possible_values': ['Value', 'New Value', 'None']}, 'StringAttrib': {'name': 'StringAttrib', 'current_value': 'Information', 'pending_value': None, 'read_only': False, 'min_length': 0, 'max_length': 255, 'pcre_regex': '^[0-9A-Za-z]{0,255}$'}, 'IntegerAttrib': {'name': 'IntegerAttrib', 'current_value': 0, 'pending_value': None, 'read_only': True, 'lower_bound': 0, 'upper_bound': 65535}} :param node: an ironic node object. :raises: DracOperationError on an error from python-dracclient. :returns: a dictionary containing BIOS settings The above values are only examples, of course. BIOS attributes exposed via this API will always be either an enumerated attribute, a string attribute, or an integer attribute. All attributes have the following parameters: :param name: is the name of the BIOS attribute. :param current_value: is the current value of the attribute. It will always be either an integer or a string. :param pending_value: is the new value that we want the attribute to have. None means that there is no pending value. :param read_only: indicates whether this attribute can be changed. Trying to change a read-only value will result in an error. The read-only flag can change depending on other attributes. A future version of this call may expose the dependencies that indicate when that may happen. Enumerable attributes also have the following parameters: :param possible_values: is an array of values it is permissible to set the attribute to. String attributes also have the following parameters: :param min_length: is the minimum length of the string. :param max_length: is the maximum length of the string. :param pcre_regex: is a PCRE compatible regular expression that the string must match. It may be None if the string is read only or if the string does not have to match any particular regular expression. Integer attributes also have the following parameters: :param lower_bound: is the minimum value the attribute can have. :param upper_bound: is the maximum value the attribute can have. """ client = drac_common.get_drac_client(node) try: return client.list_bios_settings() except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to get the BIOS settings for node ' '%(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.DracOperationError(error=exc)
def inspect_hardware(self, task): """Inspect hardware. Inspect hardware to obtain the essential & additional hardware properties. :param task: a TaskManager instance containing the node to act on. :raises: HardwareInspectionFailure, if unable to get essential hardware properties. :returns: states.MANAGEABLE """ node = task.node client = drac_common.get_drac_client(node) properties = {} try: properties['memory_mb'] = sum( [memory.size_mb for memory in client.list_memory()]) cpus = client.list_cpus() if cpus: properties['cpus'] = sum( [self._calculate_cpus(cpu) for cpu in cpus]) properties['cpu_arch'] = 'x86_64' if cpus[0].arch64 else 'x86' bios_settings = client.list_bios_settings() current_capabilities = node.properties.get('capabilities', '') new_capabilities = { 'boot_mode': bios_settings["BootMode"].current_value.lower()} capabilties = utils.get_updated_capabilities(current_capabilities, new_capabilities) properties['capabilities'] = capabilties virtual_disks = client.list_virtual_disks() root_disk = self._guess_root_disk(virtual_disks) if root_disk: properties['local_gb'] = int(root_disk.size_mb / units.Ki) else: physical_disks = client.list_physical_disks() root_disk = self._guess_root_disk(physical_disks) if root_disk: properties['local_gb'] = int( root_disk.size_mb / units.Ki) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to introspect node ' '%(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.HardwareInspectionFailure(error=exc) valid_keys = self.ESSENTIAL_PROPERTIES missing_keys = valid_keys - set(properties) if missing_keys: error = (_('Failed to discover the following properties: ' '%(missing_keys)s') % {'missing_keys': ', '.join(missing_keys)}) raise exception.HardwareInspectionFailure(error=error) node.properties = dict(node.properties, **properties) node.save() try: nics = client.list_nics() except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to introspect node ' '%(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.HardwareInspectionFailure(error=exc) pxe_dev_nics = self._get_pxe_dev_nics(client, nics, node) if pxe_dev_nics is None: LOG.warning('No PXE enabled NIC was found for node ' '%(node_uuid)s.', {'node_uuid': node.uuid}) for nic in nics: try: port = objects.Port(task.context, address=nic.mac, node_id=node.id, pxe_enabled=(nic.id in pxe_dev_nics)) port.create() LOG.info('Port created with MAC address %(mac)s ' 'for node %(node_uuid)s during inspection', {'mac': nic.mac, 'node_uuid': node.uuid}) except exception.MACAlreadyExists: LOG.warning('Failed to create a port with MAC address ' '%(mac)s when inspecting the node ' '%(node_uuid)s because the address is already ' 'registered', {'mac': nic.mac, 'node_uuid': node.uuid}) LOG.info('Node %s successfully inspected.', node.uuid) return states.MANAGEABLE
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 create_configuration(self, task, create_root_volume=True, create_nonroot_volumes=True): """Create the RAID configuration. This method creates the RAID configuration on the given node. :param task: a TaskManager instance containing the node to act on. :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. :returns: states.CLEANWAIT if creation is in progress asynchronously or None if it is completed. :raises: MissingParameterValue, if node.target_raid_config is missing or empty. :raises: DracOperationError on an error from python-dracclient. """ node = task.node # The node is rebooting at this point, so wait for the iDRAC to # enter the ready state before proceeding with cleaning client = drac_common.get_drac_client(node) client.wait_until_idrac_is_ready() logical_disks = node.target_raid_config['logical_disks'] for disk in logical_disks: if (disk['size_gb'] == 'MAX' and 'physical_disks' not in disk): raise exception.InvalidParameterValue( _("create_configuration called with invalid " "target_raid_configuration for node %(node_id)s. " "'physical_disks' is missing from logical_disk while " "'size_gb'='MAX' was requested: " "%(logical_disk)s") % {'node_id': node.uuid, 'logical_disk': disk}) if disk['size_gb'] == 'MAX': disk['size_mb'] = 'MAX' else: disk['size_mb'] = disk['size_gb'] * units.Ki del disk['size_gb'] physical_disks = list_physical_disks(node) logical_disks = _find_configuration(logical_disks, physical_disks) logical_disks_to_create = _filter_logical_disks( logical_disks, create_root_volume, create_nonroot_volumes) controllers = set() for logical_disk in logical_disks_to_create: controllers.add(logical_disk['controller']) create_virtual_disk( node, raid_controller=logical_disk['controller'], physical_disks=logical_disk['physical_disks'], raid_level=logical_disk['raid_level'], size_mb=logical_disk['size_mb'], disk_name=logical_disk.get('name'), span_length=logical_disk.get('span_length'), span_depth=logical_disk.get('span_depth')) return _commit_to_controllers(node, list(controllers))
def create_configuration(self, task, create_root_volume=True, create_nonroot_volumes=True): """Create the RAID configuration. This method creates the RAID configuration on the given node. :param task: a TaskManager instance containing the node to act on. :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. :returns: states.CLEANWAIT if creation is in progress asynchronously or None if it is completed. :raises: MissingParameterValue, if node.target_raid_config is missing or empty. :raises: DracOperationError on an error from python-dracclient. """ node = task.node # The node is rebooting at this point, so wait for the iDRAC to # enter the ready state before proceeding with cleaning client = drac_common.get_drac_client(node) client.wait_until_idrac_is_ready() logical_disks = node.target_raid_config['logical_disks'] for disk in logical_disks: if (disk['size_gb'] == 'MAX' and 'physical_disks' not in disk): raise exception.InvalidParameterValue( _("create_configuration called with invalid " "target_raid_configuration for node %(node_id)s. " "'physical_disks' is missing from logical_disk while " "'size_gb'='MAX' was requested: " "%(logical_disk)s") % { 'node_id': node.uuid, 'logical_disk': disk }) if disk['size_gb'] == 'MAX': disk['size_mb'] = 'MAX' else: disk['size_mb'] = disk['size_gb'] * units.Ki del disk['size_gb'] physical_disks = list_physical_disks(node) logical_disks = _find_configuration(logical_disks, physical_disks) logical_disks_to_create = _filter_logical_disks( logical_disks, create_root_volume, create_nonroot_volumes) controllers = set() for logical_disk in logical_disks_to_create: controllers.add(logical_disk['controller']) create_virtual_disk(node, raid_controller=logical_disk['controller'], physical_disks=logical_disk['physical_disks'], raid_level=logical_disk['raid_level'], size_mb=logical_disk['size_mb'], disk_name=logical_disk.get('name'), span_length=logical_disk.get('span_length'), span_depth=logical_disk.get('span_depth')) return _commit_to_controllers(node, list(controllers))
def get_config(node): """Get the BIOS configuration. :param node: an ironic node object. :raises: DracOperationError on an error from python-dracclient. :returns: a dictionary containing BIOS settings in the form of: {'EnumAttrib': {'name': 'EnumAttrib', 'current_value': 'Value', 'pending_value': 'New Value', # could also be None 'read_only': False, 'possible_values': ['Value', 'New Value', 'None']}, 'StringAttrib': {'name': 'StringAttrib', 'current_value': 'Information', 'pending_value': None, 'read_only': False, 'min_length': 0, 'max_length': 255, 'pcre_regex': '^[0-9A-Za-z]{0,255}$'}, 'IntegerAttrib': {'name': 'IntegerAttrib', 'current_value': 0, 'pending_value': None, 'read_only': True, 'lower_bound': 0, 'upper_bound': 65535} } The above values are only examples, of course. BIOS attributes exposed via this API will always be either an enumerated attribute, a string attribute, or an integer attribute. All attributes have the following parameters: :name: is the name of the BIOS attribute. :current_value: is the current value of the attribute. It will always be either an integer or a string. :pending_value: is the new value that we want the attribute to have. None means that there is no pending value. :read_only: indicates whether this attribute can be changed. Trying to change a read-only value will result in an error. The read-only flag can change depending on other attributes. A future version of this call may expose the dependencies that indicate when that may happen. Enumerable attributes also have the following parameters: :possible_values: is an array of values it is permissible to set the attribute to. String attributes also have the following parameters: :min_length: is the minimum length of the string. :max_length: is the maximum length of the string. :pcre_regex: is a PCRE compatible regular expression that the string must match. It may be None if the string is read only or if the string does not have to match any particular regular expression. Integer attributes also have the following parameters: :lower_bound: is the minimum value the attribute can have. :upper_bound: is the maximum value the attribute can have. """ client = drac_common.get_drac_client(node) try: return client.list_bios_settings() except drac_exceptions.BaseClientException as exc: LOG.error( _LE('DRAC driver failed to get the BIOS settings 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 _check_last_system_inventory_changed(self, task): """Check the progress of last system inventory time of a node. This handles jobs for BIOS factory reset. Handle means, it checks for job status to not only signify completed jobs but also handle failures by invoking the 'fail' event, allowing the conductor to put the node into clean/deploy FAIL state. :param task: a TaskManager instance with the node to act on """ node = task.node client = drac_common.get_drac_client(node) # Get the last system inventory time from node before reboot factory_reset_time_before_reboot = node.driver_internal_info.get( 'factory_reset_time_before_reboot') # Get the factory reset start time factory_reset_time = node.driver_internal_info.get( 'factory_reset_time') LOG.debug( "Factory resetting node %(node_uuid)s factory reset time " " %(factory_reset_time)s", { "node_uuid": task.node.uuid, "factory_reset_time": factory_reset_time }) # local variable to track difference between current time and factory # reset start time time_difference = 0 # Get the last system inventory time after reboot factory_reset_time_endof_reboot = ( client.get_system().last_system_inventory_time) LOG.debug( "Factory resetting node %(node_uuid)s " "last inventory reboot time after factory reset " "%(factory_reset_time_endof_reboot)s", { "node_uuid": task.node.uuid, "factory_reset_time_endof_reboot": factory_reset_time_endof_reboot }) if factory_reset_time_before_reboot != factory_reset_time_endof_reboot: # from the database cleanup with factory reset time self._delete_cached_reboot_time(node) # Cache the new BIOS settings, self.cache_bios_settings(task) self._resume_current_operation(task) else: # Calculate difference between current time and factory reset # start time if it is more than configured timeout then set # the node to fail state time = timeutils.utcnow( with_timezone=True) - timeutils.parse_isotime( str(factory_reset_time)) time_difference = time.total_seconds() LOG.debug( "Factory resetting node %(node_uuid)s " "time difference %(time_difference)s ", { "node_uuid": task.node.uuid, "time_difference": time_difference }) if time_difference > CONF.drac.bios_factory_reset_timeout: task.upgrade_lock() self._delete_cached_reboot_time(node) error_message = ("BIOS factory reset was not completed within " "{} seconds, unable to cache updated bios " "setting").format( CONF.drac.bios_factory_reset_timeout) self._set_failed(task, error_message) else: LOG.debug( "Factory reset for a node %(node)s is not done " "will check again later", {'node': task.node.uuid})
def inspect_hardware(self, task): """Inspect hardware. Inspect hardware to obtain the essential & additional hardware properties. :param task: a TaskManager instance containing the node to act on. :raises: HardwareInspectionFailure, if unable to get essential hardware properties. :returns: states.MANAGEABLE """ node = task.node client = drac_common.get_drac_client(node) properties = {} try: properties['memory_mb'] = sum( [memory.size_mb for memory in client.list_memory()]) cpus = client.list_cpus() if cpus: properties['cpus'] = len(cpus) properties['cpu_arch'] = 'x86_64' if cpus[0].arch64 else 'x86' virtual_disks = client.list_virtual_disks() root_disk = self._guess_root_disk(virtual_disks) if root_disk: properties['local_gb'] = int(root_disk.size_mb / units.Ki) else: physical_disks = client.list_physical_disks() root_disk = self._guess_root_disk(physical_disks) if root_disk: properties['local_gb'] = int( root_disk.size_mb / units.Ki) except drac_exceptions.BaseClientException as exc: LOG.error(_LE('DRAC driver failed to introspect node ' '%(node_uuid)s. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) raise exception.HardwareInspectionFailure(error=exc) valid_keys = self.ESSENTIAL_PROPERTIES missing_keys = valid_keys - set(properties) if missing_keys: error = (_('Failed to discover the following properties: ' '%(missing_keys)s') % {'missing_keys': ', '.join(missing_keys)}) raise exception.HardwareInspectionFailure(error=error) node.properties = dict(node.properties, **properties) node.save() try: nics = client.list_nics() except drac_exceptions.BaseClientException as exc: LOG.error(_LE('DRAC driver failed to introspect node ' '%(node_uuid)s. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) raise exception.HardwareInspectionFailure(error=exc) for nic in nics: try: port = objects.Port(task.context, address=nic.mac, node_id=node.id) port.create() LOG.info(_LI('Port created with MAC address %(mac)s ' 'for node %(node_uuid)s during inspection'), {'mac': nic.mac, 'node_uuid': node.uuid}) except exception.MACAlreadyExists: LOG.warning(_LW('Failed to create a port with MAC address ' '%(mac)s when inspecting the node ' '%(node_uuid)s because the address is already ' 'registered'), {'mac': nic.mac, 'node_uuid': node.uuid}) LOG.info(_LI('Node %s successfully inspected.'), node.uuid) return states.MANAGEABLE