def _create_config_job(node): """Create a configuration job. This method is used to apply the pending values created by set_boot_device(). :param node: an ironic node object. :raises: DracClientError if the client received unexpected response. :raises: DracOperationFailed if the client received response with an error message. :raises: DracUnexpectedReturnValue if the client received a response with unexpected return value. """ client = drac_client.get_wsman_client(node) selectors = { 'CreationClassName': 'DCIM_BIOSService', 'Name': 'DCIM:BIOSService', 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem' } properties = {'Target': TARGET_DEVICE, 'ScheduledStartTime': 'TIME_NOW'} try: client.wsman_invoke(resource_uris.DCIM_BIOSService, 'CreateTargetedConfigJob', selectors, properties, drac_client.RET_CREATED) except exception.DracRequestFailed as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to create config job for node ' '%(node_uuid)s. The changes are not applied. ' 'Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc })
def _get_lifecycle_controller_version(node): """Returns the Lifecycle controller version of the DRAC card of the node :param node: the node. :returns: the Lifecycle controller version. :raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are missing. """ client = drac_client.get_wsman_client(node) filter_query = ('select LifecycleControllerVersion from DCIM_SystemView') try: doc = client.wsman_enumerate(resource_uris.DCIM_SystemView, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to get power state for node ' '%(node_uuid)s. Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc }) version = drac_common.find_xml(doc, 'LifecycleControllerVersion', resource_uris.DCIM_SystemView).text return version
def _set_power_state(node, target_state): """Turns the server power on/off or do a reboot. :param node: an ironic node object. :param target_state: target state of the node. :raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if an invalid power state was specified. """ client = drac_client.get_wsman_client(node) selectors = {'CreationClassName': 'DCIM_ComputerSystem', 'Name': 'srv:system'} properties = {'RequestedState': REVERSE_POWER_STATES[target_state]} try: client.wsman_invoke(resource_uris.DCIM_ComputerSystem, 'RequestStateChange', selectors, properties) except exception.DracRequestFailed as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to set power state for node ' '%(node_uuid)s to %(target_power_state)s. ' 'Reason: %(error)s.'), {'node_uuid': node.uuid, 'target_power_state': target_state, 'error': exc})
def _create_config_job(node): """Create a configuration job. This method is used to apply the pending values created by set_boot_device(). :param node: an ironic node object. :raises: DracClientError if the client received unexpected response. :raises: DracOperationFailed if the client received response with an error message. :raises: DracUnexpectedReturnValue if the client received a response with unexpected return value. """ client = drac_client.get_wsman_client(node) selectors = {'CreationClassName': 'DCIM_BIOSService', 'Name': 'DCIM:BIOSService', 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem'} properties = {'Target': TARGET_DEVICE, 'ScheduledStartTime': 'TIME_NOW'} try: client.wsman_invoke(resource_uris.DCIM_BIOSService, 'CreateTargetedConfigJob', selectors, properties, drac_client.RET_CREATED) except exception.DracRequestFailed as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to create config job for node ' '%(node_uuid)s. The changes are not applied. ' 'Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc})
def _set_power_state(node, target_state): """Turns the server power on/off or do a reboot. :param node: an ironic node object. :param target_state: target state of the node. :raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if an invalid power state was specified. """ client = drac_client.get_wsman_client(node) selectors = { 'CreationClassName': 'DCIM_ComputerSystem', 'Name': 'srv:system' } properties = {'RequestedState': REVERSE_POWER_STATES[target_state]} try: client.wsman_invoke(resource_uris.DCIM_ComputerSystem, 'RequestStateChange', selectors, properties) except exception.DracRequestFailed as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to set power state for node ' '%(node_uuid)s to %(target_power_state)s. ' 'Reason: %(error)s.'), { 'node_uuid': node.uuid, 'target_power_state': target_state, 'error': exc })
def _check_for_config_job(node): """Check if a configuration job is already created. :param node: an ironic node object. :raises: DracClientError on an error from pywsman library. :raises: DracPendingConfigJobExists if the job is already created. """ client = drac_client.get_wsman_client(node) try: doc = client.wsman_enumerate(resource_uris.DCIM_LifecycleJob) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to list the configuration jobs ' 'for node %(node_uuid)s. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) items = drac_common.find_xml(doc, 'DCIM_LifecycleJob', resource_uris.DCIM_LifecycleJob, find_all=True) for i in items: name = drac_common.find_xml(i, 'Name', resource_uris.DCIM_LifecycleJob) if TARGET_DEVICE not in name.text: continue job_status = drac_common.find_xml(i, 'JobStatus', resource_uris.DCIM_LifecycleJob).text # If job is already completed or failed we can # create another one. # Job Control Documentation: http://goo.gl/o1dDD3 (Section 7.2.3.2) if job_status.lower() not in ('completed', 'failed'): job_id = drac_common.find_xml(i, 'InstanceID', resource_uris.DCIM_LifecycleJob).text raise exception.DracPendingConfigJobExists(job_id=job_id, target=TARGET_DEVICE)
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: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are missing. """ client = drac_client.get_wsman_client(node) filter_query = ('select EnabledState,ElementName from DCIM_ComputerSystem ' 'where Name="srv:system"') try: doc = client.wsman_enumerate(resource_uris.DCIM_ComputerSystem, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to get power state for node ' '%(node_uuid)s. Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc }) enabled_state = drac_common.find_xml(doc, 'EnabledState', resource_uris.DCIM_ComputerSystem) return POWER_STATES[enabled_state.text]
def _check_for_config_job(node): """Check if a configuration job is already created. :param node: an ironic node object. :raises: DracClientError on an error from pywsman library. :raises: DracPendingConfigJobExists if the job is already created. """ client = drac_client.get_wsman_client(node) try: doc = client.wsman_enumerate(resource_uris.DCIM_LifecycleJob) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE("DRAC driver failed to list the configuration jobs " "for node %(node_uuid)s. Reason: %(error)s."), {"node_uuid": node.uuid, "error": exc}, ) items = drac_common.find_xml(doc, "DCIM_LifecycleJob", resource_uris.DCIM_LifecycleJob, find_all=True) for i in items: name = drac_common.find_xml(i, "Name", resource_uris.DCIM_LifecycleJob) if TARGET_DEVICE not in name.text: continue job_status = drac_common.find_xml(i, "JobStatus", resource_uris.DCIM_LifecycleJob).text # If job is already completed or failed we can # create another one. # The 'Completed with Errors' JobStatus can be returned by # configuration jobs that set NIC or BIOS attributes. # Job Control Documentation: http://goo.gl/o1dDD3 (Section 7.2.3.2) if job_status.lower() not in ("completed", "completed with errors", "failed"): job_id = drac_common.find_xml(i, "InstanceID", resource_uris.DCIM_LifecycleJob).text raise exception.DracPendingConfigJobExists(job_id=job_id, target=TARGET_DEVICE)
def _get_boot_list_for_boot_device(node, device, controller_version): """Get the boot list for a given boot device. The DCIM_BootConfigSetting resource represents each boot list (eg. IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot). The DCIM_BootSourceSetting resource represents each of the boot list boot devices or sources that are shown under their corresponding boot list. :param node: ironic node object. :param device: boot device. :param controller_version: version of the Lifecycle controller. :raises: DracClientError on an error from pywsman library. :returns: dictionary containing: :boot_list: boot list. :boot_device_id: boot device id. """ client = drac_client.get_wsman_client(node) if controller_version < '2.0.0': filter_query = None else: filter_query = ("select * from DCIM_BootSourceSetting where " "InstanceID like '%%#%s%%'" % _BOOT_DEVICES_MAP[device]) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to set the boot device ' 'for node %(node_uuid)s. Can\'t find the ID ' 'for the %(device)s type. Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc, 'device': device }) if controller_version < '2.0.0': boot_devices = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting, find_all=True) for boot_device in boot_devices: if _BOOT_DEVICES_MAP[device] in boot_device.text: boot_device_id = boot_device.text boot_list = boot_device_id.split(':')[0] break else: boot_device_id = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text boot_list = drac_common.find_xml( doc, 'BootSourceType', resource_uris.DCIM_BootSourceSetting).text return {'boot_list': boot_list, 'boot_device_id': boot_device_id}
def _get_boot_list_for_boot_device(node, device, controller_version): """Get the boot list for a given boot device. The DCIM_BootConfigSetting resource represents each boot list (eg. IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot). The DCIM_BootSourceSetting resource represents each of the boot list boot devices or sources that are shown under their corresponding boot list. :param node: ironic node object. :param device: boot device. :param controller_version: version of the Lifecycle controller. :raises: DracClientError on an error from pywsman library. :returns: dictionary containing: :boot_list: boot list. :boot_device_id: boot device id. """ client = drac_client.get_wsman_client(node) if controller_version < '2.0.0': filter_query = None else: filter_query = ("select * from DCIM_BootSourceSetting where " "InstanceID like '%%#%s%%'" % _BOOT_DEVICES_MAP[device]) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to set the boot device ' 'for node %(node_uuid)s. Can\'t find the ID ' 'for the %(device)s type. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc, 'device': device}) if controller_version < '2.0.0': boot_devices = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting, find_all=True) for boot_device in boot_devices: if _BOOT_DEVICES_MAP[device] in boot_device.text: boot_device_id = boot_device.text boot_list = boot_device_id.split(':')[0] break else: boot_device_id = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text boot_list = drac_common.find_xml( doc, 'BootSourceType', resource_uris.DCIM_BootSourceSetting).text return {'boot_list': boot_list, 'boot_device_id': boot_device_id}
def _get_next_boot_list(node): """Get the next boot list. The DCIM_BootConfigSetting resource represents each boot list (eg. IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot). The DCIM_BootSourceSetting resource represents each of the boot list boot devices or sources that are shown under their corresponding boot list. :param node: an ironic node object. :raises: DracClientError on an error from pywsman library. :returns: a dictionary containing: :instance_id: the instance id of the boot list. :is_next: whether it's the next device to boot or not. One of PERSISTENT, ONE_TIME_BOOT constants. """ client = drac_client.get_wsman_client(node) filter_query = ('select * from DCIM_BootConfigSetting where IsNext=%s ' 'or IsNext=%s' % (PERSISTENT, ONE_TIME_BOOT)) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootConfigSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to get next boot mode for ' 'node %(node_uuid)s. Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc }) items = drac_common.find_xml(doc, 'DCIM_BootConfigSetting', resource_uris.DCIM_BootConfigSetting, find_all=True) # This list will have 2 items maximum, one for the persistent element # and another one for the OneTime if set boot_list = None for i in items: boot_list_id = drac_common.find_xml( i, 'InstanceID', resource_uris.DCIM_BootConfigSetting).text is_next = drac_common.find_xml( i, 'IsNext', resource_uris.DCIM_BootConfigSetting).text boot_list = {'instance_id': boot_list_id, 'is_next': is_next} # If OneTime is set we should return it, because that's # where the next boot device is if is_next == ONE_TIME_BOOT: break return boot_list
def _get_boot_device_for_boot_list(node, boot_list_id, controller_version): """Get the next boot device for a given boot list. The DCIM_BootConfigSetting resource represents each boot list (eg. IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot). The DCIM_BootSourceSetting resource represents each of the boot list boot devices or sources that are shown under their corresponding boot list. :param node: ironic node object. :param boot_list_id: boot list id. :param controller_version: version of the Lifecycle controller. :raises: DracClientError on an error from pywsman library. :returns: boot device id. """ client = drac_client.get_wsman_client(node) if controller_version < '2.0.0': filter_query = ('select * from DCIM_BootSourceSetting where ' 'PendingAssignedSequence=0') else: filter_query = ('select * from DCIM_BootSourceSetting where ' 'PendingAssignedSequence=0 and ' 'BootSourceType="%s"' % boot_list_id) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to get the current boot ' 'device for node %(node_uuid)s. ' 'Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc }) if controller_version < '2.0.0': boot_devices = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting, find_all=True) for device in boot_devices: if device.text.startswith(boot_list_id): boot_device_id = device.text break else: boot_device_id = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text return boot_device_id
def _get_next_boot_mode(node): """Get the next boot mode. To see a list of supported boot modes see: http://goo.gl/aEsvUH (Section 7.2) :param node: an ironic node object. :raises: DracClientError on an error from pywsman library. :returns: a dictionary containing: :instance_id: the instance id of the boot device. :is_next: whether it's the next device to boot or not. One of PERSISTENT, NOT_NEXT, ONE_TIME_BOOT constants. """ client = drac_client.get_wsman_client(node) filter_query = ('select * from DCIM_BootConfigSetting where IsNext=%s ' 'or IsNext=%s' % (PERSISTENT, ONE_TIME_BOOT)) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootConfigSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to get next boot mode for ' 'node %(node_uuid)s. Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc }) items = drac_common.find_xml(doc, 'DCIM_BootConfigSetting', resource_uris.DCIM_BootConfigSetting, find_all=True) # This list will have 2 items maximum, one for the persistent element # and another one for the OneTime if set boot_mode = None for i in items: instance_id = drac_common.find_xml( i, 'InstanceID', resource_uris.DCIM_BootConfigSetting).text is_next = drac_common.find_xml( i, 'IsNext', resource_uris.DCIM_BootConfigSetting).text boot_mode = {'instance_id': instance_id, 'is_next': is_next} # If OneTime is set we should return it, because that's # where the next boot device is if is_next == ONE_TIME_BOOT: break return boot_mode
def _get_next_boot_list(node): """Get the next boot list. The DCIM_BootConfigSetting resource represents each boot list (eg. IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot). The DCIM_BootSourceSetting resource represents each of the boot list boot devices or sources that are shown under their corresponding boot list. :param node: an ironic node object. :raises: DracClientError on an error from pywsman library. :returns: a dictionary containing: :instance_id: the instance id of the boot list. :is_next: whether it's the next device to boot or not. One of PERSISTENT, ONE_TIME_BOOT constants. """ client = drac_client.get_wsman_client(node) filter_query = ('select * from DCIM_BootConfigSetting where IsNext=%s ' 'or IsNext=%s' % (PERSISTENT, ONE_TIME_BOOT)) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootConfigSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to get next boot mode for ' 'node %(node_uuid)s. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) items = drac_common.find_xml(doc, 'DCIM_BootConfigSetting', resource_uris.DCIM_BootConfigSetting, find_all=True) # This list will have 2 items maximum, one for the persistent element # and another one for the OneTime if set boot_list = None for i in items: boot_list_id = drac_common.find_xml( i, 'InstanceID', resource_uris.DCIM_BootConfigSetting).text is_next = drac_common.find_xml( i, 'IsNext', resource_uris.DCIM_BootConfigSetting).text boot_list = {'instance_id': boot_list_id, 'is_next': is_next} # If OneTime is set we should return it, because that's # where the next boot device is if is_next == ONE_TIME_BOOT: break return boot_list
def _get_next_boot_mode(node): """Get the next boot mode. To see a list of supported boot modes see: http://goo.gl/aEsvUH (Section 7.2) :param node: an ironic node object. :raises: DracClientError on an error from pywsman library. :returns: a dictionary containing: :instance_id: the instance id of the boot device. :is_next: whether it's the next device to boot or not. One of PERSISTENT, NOT_NEXT, ONE_TIME_BOOT constants. """ client = drac_client.get_wsman_client(node) filter_query = ('select * from DCIM_BootConfigSetting where IsNext=%s ' 'or IsNext=%s' % (PERSISTENT, ONE_TIME_BOOT)) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootConfigSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to get next boot mode for ' 'node %(node_uuid)s. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) items = drac_common.find_xml(doc, 'DCIM_BootConfigSetting', resource_uris.DCIM_BootConfigSetting, find_all=True) # This list will have 2 items maximum, one for the persistent element # and another one for the OneTime if set boot_mode = None for i in items: instance_id = drac_common.find_xml(i, 'InstanceID', resource_uris.DCIM_BootConfigSetting).text is_next = drac_common.find_xml(i, 'IsNext', resource_uris.DCIM_BootConfigSetting).text boot_mode = {'instance_id': instance_id, 'is_next': is_next} # If OneTime is set we should return it, because that's # where the next boot device is if is_next == ONE_TIME_BOOT: break return boot_mode
def _get_boot_device_for_boot_list(node, boot_list_id, controller_version): """Get the next boot device for a given boot list. The DCIM_BootConfigSetting resource represents each boot list (eg. IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot). The DCIM_BootSourceSetting resource represents each of the boot list boot devices or sources that are shown under their corresponding boot list. :param node: ironic node object. :param boot_list_id: boot list id. :param controller_version: version of the Lifecycle controller. :raises: DracClientError on an error from pywsman library. :returns: boot device id. """ client = drac_client.get_wsman_client(node) if controller_version < '2.0.0': filter_query = ('select * from DCIM_BootSourceSetting where ' 'PendingAssignedSequence=0') else: filter_query = ('select * from DCIM_BootSourceSetting where ' 'PendingAssignedSequence=0 and ' 'BootSourceType="%s"' % boot_list_id) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to get the current boot ' 'device for node %(node_uuid)s. ' 'Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) if controller_version < '2.0.0': boot_devices = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting, find_all=True) for device in boot_devices: if device.text.startswith(boot_list_id): boot_device_id = device.text break else: boot_device_id = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text return boot_device_id
def _get_config(node, resource): """Helper for get_config. Handles getting BIOS config values for a single namespace :param node: an ironic node object. :param resource: the namespace. :returns: a dictionary that maps the name of each attribute to a dictionary of values of that attribute. :raises: InvalidParameterValue if some information required to connnect to the DRAC is missing on the node or the value of one or more required parameters is invalid. :raises: DracClientError on an error from pywsman library. :raises: DracOperationFailed if the specified resource is unknown. """ res = {} client = wsman_client.get_wsman_client(node) try: doc = client.wsman_enumerate(resource) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to get BIOS settings ' 'for resource %(resource)s ' 'from node %(node_uuid)s. ' 'Reason: %(error)s.'), { 'node_uuid': node.uuid, 'resource': resource, 'error': exc }) items = doc.find('.//{%s}Items' % resource_uris.CIM_WSMAN) for item in items: if resource == resource_uris.DCIM_BIOSEnumeration: attribute = parse_enumeration(item, resource) elif resource == resource_uris.DCIM_BIOSString: attribute = parse_string(item, resource) elif resource == resource_uris.DCIM_BIOSInteger: attribute = parse_integer(item, resource) else: raise exception.DracOperationFailed( message=_('Unknown namespace %(ns)s for item: "%(item)s"') % { 'item': ET.tostring(item), 'ns': resource }) res[attribute['name']] = attribute return res
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: DracClientError on an error from pywsman library. :returns: a dictionary containing: :boot_device: the boot device, one of :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. """ client = drac_client.get_wsman_client(task.node) boot_mode = _get_next_boot_mode(task.node) persistent = boot_mode['is_next'] == PERSISTENT instance_id = boot_mode['instance_id'] filter_query = ('select * from DCIM_BootSourceSetting where ' 'PendingAssignedSequence=0 and ' 'BootSourceType="%s"' % instance_id) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to get the current boot ' 'device for node %(node_uuid)s. ' 'Reason: %(error)s.'), { 'node_uuid': task.node.uuid, 'error': exc }) instance_id = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text boot_device = next((key for (key, value) in _BOOT_DEVICES_MAP.items() if value in instance_id), None) return {'boot_device': boot_device, 'persistent': persistent}
def _check_for_config_job(node): """Check if a configuration job is already created. :param node: an ironic node object. :raises: DracClientError on an error from pywsman library. :raises: DracPendingConfigJobExists if the job is already created. """ client = drac_client.get_wsman_client(node) try: doc = client.wsman_enumerate(resource_uris.DCIM_LifecycleJob) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to list the configuration jobs ' 'for node %(node_uuid)s. Reason: %(error)s.'), { 'node_uuid': node.uuid, 'error': exc }) items = drac_common.find_xml(doc, 'DCIM_LifecycleJob', resource_uris.DCIM_LifecycleJob, find_all=True) for i in items: name = drac_common.find_xml(i, 'Name', resource_uris.DCIM_LifecycleJob) if TARGET_DEVICE not in name.text: continue job_status = drac_common.find_xml(i, 'JobStatus', resource_uris.DCIM_LifecycleJob).text # If job is already completed or failed we can # create another one. # The 'Completed with Errors' JobStatus can be returned by # configuration jobs that set NIC or BIOS attributes. # Job Control Documentation: http://goo.gl/o1dDD3 (Section 7.2.3.2) if job_status.lower() not in ('completed', 'completed with errors', 'failed'): job_id = drac_common.find_xml(i, 'InstanceID', resource_uris.DCIM_LifecycleJob).text raise exception.DracPendingConfigJobExists(job_id=job_id, target=TARGET_DEVICE)
def _get_config(node, resource): """Helper for get_config. Handles getting BIOS config values for a single namespace :param node: an ironic node object. :param resource: the namespace. :returns: a dictionary that maps the name of each attribute to a dictionary of values of that attribute. :raises: InvalidParameterValue if some information required to connnect to the DRAC is missing on the node or the value of one or more required parameters is invalid. :raises: DracClientError on an error from pywsman library. :raises: DracOperationFailed if the specified resource is unknown. """ res = {} client = wsman_client.get_wsman_client(node) try: doc = client.wsman_enumerate(resource) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to get BIOS settings ' 'for resource %(resource)s ' 'from node %(node_uuid)s. ' 'Reason: %(error)s.'), {'node_uuid': node.uuid, 'resource': resource, 'error': exc}) items = doc.find('.//{%s}Items' % resource_uris.CIM_WSMAN) for item in items: if resource == resource_uris.DCIM_BIOSEnumeration: attribute = parse_enumeration(item, resource) elif resource == resource_uris.DCIM_BIOSString: attribute = parse_string(item, resource) elif resource == resource_uris.DCIM_BIOSInteger: attribute = parse_integer(item, resource) else: raise exception.DracOperationFailed( message=_('Unknown namespace %(ns)s for item: "%(item)s"') % { 'item': ET.tostring(item), 'ns': resource}) res[attribute['name']] = attribute return res
def _get_lifecycle_controller_version(node): """Returns the Lifecycle controller version of the DRAC card of the node :param node: the node. :returns: the Lifecycle controller version. :raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are missing. """ client = drac_client.get_wsman_client(node) filter_query = "select LifecycleControllerVersion from DCIM_SystemView" try: doc = client.wsman_enumerate(resource_uris.DCIM_SystemView, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE("DRAC driver failed to get power state for node " "%(node_uuid)s. Reason: %(error)s."), {"node_uuid": node.uuid, "error": exc}, ) version = drac_common.find_xml(doc, "LifecycleControllerVersion", resource_uris.DCIM_SystemView).text return version
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: DracClientError on an error from pywsman library. :returns: a dictionary containing: :boot_device: the boot device, one of :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. """ client = drac_client.get_wsman_client(task.node) boot_mode = _get_next_boot_mode(task.node) persistent = boot_mode['is_next'] == PERSISTENT instance_id = boot_mode['instance_id'] filter_query = ('select * from DCIM_BootSourceSetting where ' 'PendingAssignedSequence=0 and ' 'BootSourceType="%s"' % instance_id) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to get the current boot ' 'device for node %(node_uuid)s. ' 'Reason: %(error)s.'), {'node_uuid': task.node.uuid, 'error': exc}) instance_id = drac_common.find_xml(doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text boot_device = next((key for (key, value) in _BOOT_DEVICES_MAP.items() if value in instance_id), None) return {'boot_device': boot_device, 'persistent': persistent}
def abandon_config(task): """Abandons uncommitted changes added by set_config :param task: is the ironic task for abandoning the changes. :raises: DracClientError on an error from pywsman library. :raises: DracOperationFailed on error reported back by DRAC. :raises: DracUnexpectedReturnValue if the drac did not report success. """ node = task.node client = wsman_client.get_wsman_client(node) selectors = { 'CreationClassName': 'DCIM_BIOSService', 'Name': 'DCIM:BIOSService', 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem' } properties = {'Target': 'BIOS.Setup.1-1'} client.wsman_invoke(resource_uris.DCIM_BIOSService, 'DeletePendingConfiguration', selectors, properties, wsman_client.RET_SUCCESS)
def abandon_config(task): """Abandons uncommitted changes added by set_config :param task: is the ironic task for abandoning the changes. :raises: DracClientError on an error from pywsman library. :raises: DracOperationFailed on error reported back by DRAC. :raises: DracUnexpectedReturnValue if the drac did not report success. """ node = task.node client = wsman_client.get_wsman_client(node) selectors = {'CreationClassName': 'DCIM_BIOSService', 'Name': 'DCIM:BIOSService', 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem'} properties = {'Target': 'BIOS.Setup.1-1'} client.wsman_invoke(resource_uris.DCIM_BIOSService, 'DeletePendingConfiguration', selectors, properties, wsman_client.RET_SUCCESS)
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: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are missing. """ client = drac_client.get_wsman_client(node) filter_query = ('select EnabledState,ElementName from DCIM_ComputerSystem ' 'where Name="srv:system"') try: doc = client.wsman_enumerate(resource_uris.DCIM_ComputerSystem, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to get power state for node ' '%(node_uuid)s. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) enabled_state = drac_common.find_xml(doc, 'EnabledState', resource_uris.DCIM_ComputerSystem) return POWER_STATES[enabled_state.text]
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 :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: DracClientError if the client received unexpected response. :raises: DracOperationFailed if the client received response with an error message. :raises: DracUnexpectedReturnValue if the client received a response with unexpected return value. :raises: InvalidParameterValue if an invalid boot device is specified. :raises: DracPendingConfigJobExists on an error when creating the job. """ # Check for an existing configuration job _check_for_config_job(task.node) client = drac_client.get_wsman_client(task.node) filter_query = ("select * from DCIM_BootSourceSetting where " "InstanceID like '%%#%s%%'" % _BOOT_DEVICES_MAP[device]) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to set the boot device ' 'for node %(node_uuid)s. Can\'t find the ID ' 'for the %(device)s type. Reason: %(error)s.'), {'node_uuid': task.node.uuid, 'error': exc, 'device': device}) instance_id = drac_common.find_xml(doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text source = 'OneTime' if persistent: source = drac_common.find_xml(doc, 'BootSourceType', resource_uris.DCIM_BootSourceSetting).text # NOTE(lucasagomes): Don't ask me why 'BootSourceType' is set # for 'InstanceID' and 'InstanceID' is set for 'source'! You # know enterprisey... selectors = {'InstanceID': source} properties = {'source': instance_id} try: client.wsman_invoke(resource_uris.DCIM_BootConfigSetting, 'ChangeBootOrderByInstanceID', selectors, properties) except exception.DracRequestFailed as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to set the boot device for ' 'node %(node_uuid)s to %(target_boot_device)s. ' 'Reason: %(error)s.'), {'node_uuid': task.node.uuid, 'target_boot_device': device, 'error': exc}) # Create a configuration job _create_config_job(task.node)
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 :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: DracClientError if the client received unexpected response. :raises: DracOperationFailed if the client received response with an error message. :raises: DracUnexpectedReturnValue if the client received a response with unexpected return value. :raises: InvalidParameterValue if an invalid boot device is specified. :raises: DracPendingConfigJobExists on an error when creating the job. """ client = drac_client.get_wsman_client(task.node) controller_version = _get_lifecycle_controller_version(task.node) current_boot_device = _get_boot_device(task.node, controller_version) # 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 # Check for an existing configuration job _check_for_config_job(task.node) # Querying the boot device attributes boot_device = _get_boot_list_for_boot_device(task.node, device, controller_version) boot_list = boot_device['boot_list'] boot_device_id = boot_device['boot_device_id'] if not persistent: boot_list = 'OneTime' # Send the request to DRAC selectors = {'InstanceID': boot_list} properties = {'source': boot_device_id} try: client.wsman_invoke(resource_uris.DCIM_BootConfigSetting, 'ChangeBootOrderByInstanceID', selectors, properties) except exception.DracRequestFailed as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to set the boot device for ' 'node %(node_uuid)s to %(target_boot_device)s. ' 'Reason: %(error)s.'), { 'node_uuid': task.node.uuid, 'target_boot_device': device, 'error': exc }) # Create a configuration job _create_config_job(task.node)
def set_config(task, **kwargs): """Sets the pending_value parameter for each of the values passed in. :param task: an ironic task object. :param kwargs: a dictionary of {'AttributeName': 'NewValue'} :raises: DracOperationFailed if any new values are invalid. :raises: DracOperationFailed if any of the attributes are read-only. :raises: DracOperationFailed if any of the attributes cannot be set for any other reason. :raises: DracClientError on an error from the pywsman library. :returns: A boolean indicating whether commit_config needs to be called to make the changes. """ node = task.node management.check_for_config_job(node) current = get_config(node) unknown_keys = set(kwargs) - set(current) if unknown_keys: LOG.warning(_LW('Ignoring unknown BIOS attributes "%r"'), unknown_keys) candidates = set(kwargs) - unknown_keys read_only_keys = [] unchanged_attribs = [] invalid_attribs_msgs = [] attrib_names = [] for k in candidates: if str(kwargs[k]) == str(current[k]['current_value']): unchanged_attribs.append(k) elif current[k]['read_only']: read_only_keys.append(k) else: if 'possible_values' in current[k]: if str(kwargs[k]) not in current[k]['possible_values']: m = _('Attribute %(attr)s cannot be set to value %(val)s.' ' It must be in %(ok)r') % { 'attr': k, 'val': kwargs[k], 'ok': current[k]['possible_values'] } invalid_attribs_msgs.append(m) continue if ('pcre_regex' in current[k] and current[k]['pcre_regex'] is not None): regex = re.compile(current[k]['pcre_regex']) if regex.search(str(kwargs[k])) is None: # TODO(victor-lowther) # Leave untranslated for now until the unicode # issues that the test suite exposes are straightened out. m = ('Attribute %(attr)s cannot be set to value %(val)s.' ' It must match regex %(re)s.') % { 'attr': k, 'val': kwargs[k], 're': current[k]['pcre_regex'] } invalid_attribs_msgs.append(m) continue if 'lower_bound' in current[k]: lower = current[k]['lower_bound'] upper = current[k]['upper_bound'] val = int(kwargs[k]) if val < lower or val > upper: m = _('Attribute %(attr)s cannot be set to value %(val)d.' ' It must be between %(lower)d and %(upper)d.') % { 'attr': k, 'val': val, 'lower': lower, 'upper': upper } invalid_attribs_msgs.append(m) continue attrib_names.append(k) if unchanged_attribs: LOG.warning(_LW('Ignoring unchanged BIOS settings %r'), unchanged_attribs) if invalid_attribs_msgs or read_only_keys: raise exception.DracOperationFailed( _format_error_msg(invalid_attribs_msgs, read_only_keys)) if not attrib_names: return False client = wsman_client.get_wsman_client(node) selectors = { 'CreationClassName': 'DCIM_BIOSService', 'Name': 'DCIM:BIOSService', 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem' } properties = { 'Target': 'BIOS.Setup.1-1', 'AttributeName': attrib_names, 'AttributeValue': map(lambda k: kwargs[k], attrib_names) } doc = client.wsman_invoke(resource_uris.DCIM_BIOSService, 'SetAttributes', selectors, properties) # Yes, we look for RebootRequired. In this context, that actually means # that we need to create a lifecycle controller config job and then reboot # so that the lifecycle controller can commit the BIOS config changes that # we have proposed. set_results = doc.findall('.//{%s}RebootRequired' % resource_uris.DCIM_BIOSService) return any(str(res.text) == 'Yes' for res in set_results)
def set_config(task, **kwargs): """Sets the pending_value parameter for each of the values passed in. :param task: an ironic task object. :param kwargs: a dictionary of {'AttributeName': 'NewValue'} :raises: DracOperationFailed if any new values are invalid. :raises: DracOperationFailed if any of the attributes are read-only. :raises: DracOperationFailed if any of the attributes cannot be set for any other reason. :raises: DracClientError on an error from the pywsman library. :returns: A boolean indicating whether commit_config needs to be called to make the changes. """ node = task.node management.check_for_config_job(node) current = get_config(node) unknown_keys = set(kwargs) - set(current) if unknown_keys: LOG.warning(_LW('Ignoring unknown BIOS attributes "%r"'), unknown_keys) candidates = set(kwargs) - unknown_keys read_only_keys = [] unchanged_attribs = [] invalid_attribs_msgs = [] attrib_names = [] for k in candidates: if str(kwargs[k]) == str(current[k]['current_value']): unchanged_attribs.append(k) elif current[k]['read_only']: read_only_keys.append(k) else: if 'possible_values' in current[k]: if str(kwargs[k]) not in current[k]['possible_values']: m = _('Attribute %(attr)s cannot be set to value %(val)s.' ' It must be in %(ok)r') % { 'attr': k, 'val': kwargs[k], 'ok': current[k]['possible_values']} invalid_attribs_msgs.append(m) continue if ('pcre_regex' in current[k] and current[k]['pcre_regex'] is not None): regex = re.compile(current[k]['pcre_regex']) if regex.search(str(kwargs[k])) is None: # TODO(victor-lowther) # Leave untranslated for now until the unicode # issues that the test suite exposes are straightened out. m = ('Attribute %(attr)s cannot be set to value %(val)s.' ' It must match regex %(re)s.') % { 'attr': k, 'val': kwargs[k], 're': current[k]['pcre_regex']} invalid_attribs_msgs.append(m) continue if 'lower_bound' in current[k]: lower = current[k]['lower_bound'] upper = current[k]['upper_bound'] val = int(kwargs[k]) if val < lower or val > upper: m = _('Attribute %(attr)s cannot be set to value %(val)d.' ' It must be between %(lower)d and %(upper)d.') % { 'attr': k, 'val': val, 'lower': lower, 'upper': upper} invalid_attribs_msgs.append(m) continue attrib_names.append(k) if unchanged_attribs: LOG.warning(_LW('Ignoring unchanged BIOS settings %r'), unchanged_attribs) if invalid_attribs_msgs or read_only_keys: raise exception.DracOperationFailed( _format_error_msg(invalid_attribs_msgs, read_only_keys)) if not attrib_names: return False client = wsman_client.get_wsman_client(node) selectors = {'CreationClassName': 'DCIM_BIOSService', 'Name': 'DCIM:BIOSService', 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem'} properties = {'Target': 'BIOS.Setup.1-1', 'AttributeName': attrib_names, 'AttributeValue': map(lambda k: kwargs[k], attrib_names)} doc = client.wsman_invoke(resource_uris.DCIM_BIOSService, 'SetAttributes', selectors, properties) # Yes, we look for RebootRequired. In this context, that actually means # that we need to create a lifecycle controller config job and then reboot # so that the lifecycle controller can commit the BIOS config changes that # we have proposed. set_results = doc.findall( './/{%s}RebootRequired' % resource_uris.DCIM_BIOSService) return any(str(res.text) == 'Yes' for res in set_results)
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 :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: DracClientError if the client received unexpected response. :raises: DracOperationFailed if the client received response with an error message. :raises: DracUnexpectedReturnValue if the client received a response with unexpected return value. :raises: InvalidParameterValue if an invalid boot device is specified. :raises: DracPendingConfigJobExists on an error when creating the job. """ client = drac_client.get_wsman_client(task.node) controller_version = _get_lifecycle_controller_version(task.node) current_boot_device = _get_boot_device(task.node, controller_version) # 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 # Check for an existing configuration job check_for_config_job(task.node) # Querying the boot device attributes boot_device = _get_boot_list_for_boot_device(task.node, device, controller_version) boot_list = boot_device['boot_list'] boot_device_id = boot_device['boot_device_id'] if not persistent: boot_list = 'OneTime' # Send the request to DRAC selectors = {'InstanceID': boot_list} properties = {'source': boot_device_id} try: client.wsman_invoke(resource_uris.DCIM_BootConfigSetting, 'ChangeBootOrderByInstanceID', selectors, properties, drac_client.RET_SUCCESS) except exception.DracRequestFailed as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to set the boot device for ' 'node %(node_uuid)s to %(target_boot_device)s. ' 'Reason: %(error)s.'), {'node_uuid': task.node.uuid, 'target_boot_device': device, 'error': exc}) # Create a configuration job create_config_job(task.node)
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 :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: DracClientError if the client received unexpected response. :raises: DracOperationFailed if the client received response with an error message. :raises: DracUnexpectedReturnValue if the client received a response with unexpected return value. :raises: InvalidParameterValue if an invalid boot device is specified. :raises: DracPendingConfigJobExists on an error when creating the job. """ # Check for an existing configuration job _check_for_config_job(task.node) client = drac_client.get_wsman_client(task.node) filter_query = ("select * from DCIM_BootSourceSetting where " "InstanceID like '%%#%s%%'" % _BOOT_DEVICES_MAP[device]) try: doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to set the boot device ' 'for node %(node_uuid)s. Can\'t find the ID ' 'for the %(device)s type. Reason: %(error)s.'), { 'node_uuid': task.node.uuid, 'error': exc, 'device': device }) instance_id = drac_common.find_xml( doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text source = 'OneTime' if persistent: source = drac_common.find_xml( doc, 'BootSourceType', resource_uris.DCIM_BootSourceSetting).text # NOTE(lucasagomes): Don't ask me why 'BootSourceType' is set # for 'InstanceID' and 'InstanceID' is set for 'source'! You # know enterprisey... selectors = {'InstanceID': source} properties = {'source': instance_id} try: client.wsman_invoke(resource_uris.DCIM_BootConfigSetting, 'ChangeBootOrderByInstanceID', selectors, properties) except exception.DracRequestFailed as exc: with excutils.save_and_reraise_exception(): LOG.error( _LE('DRAC driver failed to set the boot device for ' 'node %(node_uuid)s to %(target_boot_device)s. ' 'Reason: %(error)s.'), { 'node_uuid': task.node.uuid, 'target_boot_device': device, 'error': exc }) # Create a configuration job _create_config_job(task.node)