def set_power_state(self, target_state): """Turns the server power on/off or do a reboot :param target_state: target power state. Valid options are: 'POWER_ON', 'POWER_OFF' and 'REBOOT'. :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response :raises: DRACOperationFailed on error reported back by the DRAC interface :raises: DRACUnexpectedReturnValue on return value mismatch :raises: InvalidParameterValue on invalid target power state """ try: drac_requested_state = REVERSE_POWER_STATES[target_state] except KeyError: msg = ("'%(target_state)s' is not supported. " "Supported power states: %(supported_power_states)r") % { 'target_state': target_state, 'supported_power_states': list(REVERSE_POWER_STATES)} raise exceptions.InvalidParameterValue(reason=msg) selectors = {'CreationClassName': 'DCIM_ComputerSystem', 'Name': 'srv:system'} properties = {'RequestedState': drac_requested_state} self.client.invoke(uris.DCIM_ComputerSystem, 'RequestStateChange', selectors, properties)
def build_return_dict(doc, resource_uri, is_commit_required_value=None, is_reboot_required_value=None): """Builds a dictionary to be returned Build a dictionary to be returned from WSMAN operations that are not read-only. :param doc: the element tree object. :param resource_uri: the resource URI of the namespace. :param is_commit_required_value: The value to be returned for is_commit_required, or None if the value should be determined from the doc. :param is_reboot_required_value: The value to be returned for is_reboot_required, or None if the value should be determined from the doc. :returns: a dictionary containing: - is_commit_required: indicates if a commit is required. - is_reboot_required: indicates if a reboot is required. """ if is_reboot_required_value is not None and \ is_reboot_required_value not in constants.RebootRequired.all(): msg = ("is_reboot_required_value must be a member of the " "RebootRequired enumeration or None. The passed value was " "%(is_reboot_required_value)s" % { 'is_reboot_required_value': is_reboot_required_value }) raise exceptions.InvalidParameterValue(reason=msg) result = {} if is_commit_required_value is None: is_commit_required_value = is_commit_required(doc, resource_uri) result['is_commit_required'] = is_commit_required_value if is_reboot_required_value is None: is_reboot_required_value = reboot_required(doc, resource_uri) result['is_reboot_required'] = is_reboot_required_value return result
def create_reboot_job( self, reboot_type=constants.RebootJobType.reboot_forced_shutdown): """Creates a reboot job. :param reboot_type: type of reboot :returns id of the created job :raises: InvalidParameterValue on invalid reboot type :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response :raises: DRACOperationFailed on error reported back by the iDRAC interface :raises: DRACUnexpectedReturnValue on return value mismatch """ try: drac_reboot_type = REBOOT_TYPES[reboot_type] except KeyError: msg = ("'%(reboot_type)s' is not supported. " "Supported reboot types: %(supported_reboot_types)r") % { 'reboot_type': reboot_type, 'supported_reboot_types': list(REBOOT_TYPES) } raise exceptions.InvalidParameterValue(reason=msg) selectors = { 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'idrac', 'CreationClassName': 'DCIM_JobService', 'Name': 'JobService' } properties = {'RebootJobType': drac_reboot_type} doc = self.client.invoke(uris.DCIM_JobService, 'CreateRebootJob', selectors, properties, expected_return_value=utils.RET_CREATED) return self._get_job_id(doc)
def get_nic_setting(nic_id, attribute_name, drac_client): """Obtain a setting of a NIC. :param nic_id: id of the network interface controller (NIC) :param attribute_name: name of the setting :param drac_client: drac_client from python-dracclient :returns: value of the attribute on successful query, None otherwise :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response :raises: DRACOperationFailed on error reported back by the iDRAC interface :raises: InvalidParameterValue on invalid NIC attribute """ settings = drac_client.list_nic_settings(nic_id) # Were no settings found? if not settings: msg = 'Settings could not be found because nic id is invalid' raise exceptions.InvalidParameterValue(reason=msg) # Do the settings include the attribute? if attribute_name not in settings: return None return settings[attribute_name]
def set_settings(settings_type, client, namespaces, new_settings, resource_uri, cim_creation_class_name, cim_name, target, name_formatter=None): """Generically handles setting various types of settings on the iDRAC This method pulls the current list of settings from the iDRAC then compares that list against the passed new settings to determine if there are any errors. If no errors exist then the settings are sent to the iDRAC using the passed resource, target, etc. :param settings_type: a string indicating the settings type :param client: an instance of WSManClient :param namespaces: a list of URI/class pairs to retrieve. :param new_settings: a dictionary containing the proposed values, with each key being the name of attribute and the value being the proposed value. :param resource_uri: URI of resource to invoke :param cim_creation_class_name: creation class name of the CIM object :param cim_name: name of the CIM object :param target: target device :param name_formatter: a method used to format the keys in the returned dictionary. By default, attribute.name will be used. :returns: a dictionary containing: - The is_commit_required 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 for the values to be applied. Possible values are true and false. :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response :raises: DRACOperationFailed on new settings with invalid values or attempting to set read-only settings or when an error is reported back by the iDRAC interface :raises: DRACUnexpectedReturnValue on return value mismatch :raises: InvalidParameterValue on invalid new setting """ current_settings = list_settings(client, namespaces, by_name=True, name_formatter=name_formatter) unknown_keys = set(new_settings) - set(current_settings) if unknown_keys: msg = ('Unknown %(settings_type)s attributes found: %(unknown_keys)r' % {'settings_type': settings_type, 'unknown_keys': unknown_keys}) raise exceptions.InvalidParameterValue(reason=msg) read_only_keys = [] unchanged_attribs = [] invalid_attribs_msgs = [] attrib_names = [] candidates = set(new_settings) for attr in candidates: if str(new_settings[attr]) == str( current_settings[attr].current_value): unchanged_attribs.append(attr) elif current_settings[attr].read_only: read_only_keys.append(attr) else: validation_msg = current_settings[attr].validate( new_settings[attr]) if not validation_msg: attrib_names.append(attr) else: invalid_attribs_msgs.append(validation_msg) if unchanged_attribs: LOG.debug('Ignoring unchanged %(settings_type)s attributes: ' '%(unchanged_attribs)r' % {'settings_type': settings_type, 'unchanged_attribs': unchanged_attribs}) if invalid_attribs_msgs or read_only_keys: if read_only_keys: read_only_msg = ['Cannot set read-only %(settings_type)s ' 'attributes: %(read_only_keys)r.' % {'settings_type': settings_type, 'read_only_keys': read_only_keys}] else: read_only_msg = [] drac_messages = '\n'.join(invalid_attribs_msgs + read_only_msg) raise exceptions.DRACOperationFailed( drac_messages=drac_messages) if not attrib_names: return build_return_dict( None, resource_uri, is_commit_required_value=False, is_reboot_required_value=constants.RebootRequired.false) selectors = {'CreationClassName': cim_creation_class_name, 'Name': cim_name, 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem'} properties = {'Target': target, 'AttributeName': attrib_names, 'AttributeValue': [new_settings[attr] for attr in attrib_names]} doc = client.invoke(resource_uri, 'SetAttributes', selectors, properties) return build_return_dict(doc, resource_uri)
def set_bios_settings(self, new_settings): """Sets the BIOS configuration To be more precise, it sets the pending_value parameter for each of the attributes passed in. For the values to be applied, a config job must be created and the node must be rebooted. :param new_settings: a dictionary containing the proposed values, with each key being the name of attribute and the value being the proposed value. :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: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response :raises: DRACOperationFailed on error reported back by the DRAC interface :raises: DRACUnexpectedReturnValue on return value mismatch :raises: InvalidParameterValue on invalid BIOS attribute """ current_settings = self.list_bios_settings() unknown_keys = set(new_settings) - set(current_settings) if unknown_keys: msg = ('Unknown BIOS attributes found: %(unknown_keys)r' % {'unknown_keys': unknown_keys}) raise exceptions.InvalidParameterValue(reason=msg) read_only_keys = [] unchanged_attribs = [] invalid_attribs_msgs = [] attrib_names = [] candidates = set(new_settings) for attr in candidates: if str(new_settings[attr]) == str( current_settings[attr].current_value): unchanged_attribs.append(attr) elif current_settings[attr].read_only: read_only_keys.append(attr) else: validation_msg = current_settings[attr].validate( new_settings[attr]) if validation_msg is None: attrib_names.append(attr) else: invalid_attribs_msgs.append(validation_msg) if unchanged_attribs: LOG.warning('Ignoring unchanged BIOS attributes: %r', unchanged_attribs) if invalid_attribs_msgs or read_only_keys: if read_only_keys: read_only_msg = ['Cannot set read-only BIOS attributes: %r.' % read_only_keys] else: read_only_msg = [] drac_messages = '\n'.join(invalid_attribs_msgs + read_only_msg) raise exceptions.DRACOperationFailed( drac_messages=drac_messages) if not attrib_names: return {'commit_required': False} selectors = {'CreationClassName': 'DCIM_BIOSService', 'Name': 'DCIM:BIOSService', 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem'} properties = {'Target': 'BIOS.Setup.1-1', 'AttributeName': attrib_names, 'AttributeValue': [new_settings[attr] for attr in attrib_names]} doc = self.client.invoke(uris.DCIM_BIOSService, 'SetAttributes', selectors, properties) return {'commit_required': utils.is_reboot_required( doc, uris.DCIM_BIOSService)}
def build_return_dict(doc, resource_uri, is_commit_required_value=None, is_reboot_required_value=None, commit_required_value=None, include_commit_required=False): """Builds a dictionary to be returned Build a dictionary to be returned from WSMAN operations that are not read-only. :param doc: the element tree object. :param resource_uri: the resource URI of the namespace. :param is_commit_required_value: The value to be returned for is_commit_required, or None if the value should be determined from the doc. :param is_reboot_required_value: The value to be returned for is_reboot_required, or None if the value should be determined from the doc. :param commit_required_value: The value to be returned for commit_required, or None if the value should be determined from the doc. :parm include_commit_required: Indicates if the deprecated commit_required should be returned in the result. :returns: a dictionary containing: - is_commit_required: indicates if a commit is required. - is_reboot_required: indicates if a reboot is required. - commit_required: a deprecated key indicating if a commit is required. This key actually has a value that indicates if a reboot is required. """ if is_reboot_required_value is not None and \ is_reboot_required_value not in constants.RebootRequired.all(): msg = ("is_reboot_required_value must be a member of the " "RebootRequired enumeration or None. The passed value was " "%(is_reboot_required_value)s" % { 'is_reboot_required_value': is_reboot_required_value }) raise exceptions.InvalidParameterValue(reason=msg) result = {} if is_commit_required_value is None: is_commit_required_value = is_commit_required(doc, resource_uri) result['is_commit_required'] = is_commit_required_value if is_reboot_required_value is None: is_reboot_required_value = reboot_required(doc, resource_uri) result['is_reboot_required'] = is_reboot_required_value # Include commit_required in the response for backwards compatibility # TBD: Remove this parameter in the future if include_commit_required: if commit_required_value is None: commit_required_value = is_reboot_required(doc, resource_uri) result['commit_required'] = commit_required_value return result
def set_settings(client, list_settings, new_settings, resource_uri, cim_creation_class_name, cim_name, target): current_settings = list_settings() unknown_keys = set(new_settings) - set(current_settings) if unknown_keys: msg = ('Unknown attributes found: %(unknown_keys)r' % { 'unknown_keys': unknown_keys }) raise exceptions.InvalidParameterValue(reason=msg) read_only_keys = [] unchanged_attribs = [] invalid_attribs_msgs = [] attrib_names = [] candidates = set(new_settings) for attr in candidates: if str(new_settings[attr]) == str( current_settings[attr].current_value): unchanged_attribs.append(attr) elif current_settings[attr].read_only: read_only_keys.append(attr) else: validation_msg = current_settings[attr].validate( new_settings[attr]) if validation_msg: invalid_attribs_msgs.append(validation_msg) else: attrib_names.append(attr) if unchanged_attribs: LOG.debug('Ignoring unchanged attributes: %r', unchanged_attribs) if invalid_attribs_msgs or read_only_keys: if read_only_keys: read_only_msg = [ 'Cannot set read-only attributes: %r.' % read_only_keys ] else: read_only_msg = [] drac_messages = '\n'.join(invalid_attribs_msgs + read_only_msg) raise exceptions.DRACOperationFailed(drac_messages=drac_messages) if not attrib_names: return {'commit_required': False, 'reboot_required': False} selectors = { 'CreationClassName': cim_creation_class_name, 'Name': cim_name, 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem' } properties = { 'Target': target, 'AttributeName': attrib_names, 'AttributeValue': [new_settings[attr] for attr in attrib_names] } doc = client.invoke(resource_uri, 'SetAttributes', selectors, properties) return { 'commit_required': is_commit_required(doc, resource_uri), 'reboot_required': utils.is_reboot_required(doc, resource_uri) }
def create_virtual_disk(self, raid_controller, physical_disks, raid_level, size_mb, disk_name=None, span_length=None, span_depth=None): """Creates a virtual disk The created virtual disk will be in pending state. For the changes to be applied, a config job must be created and the node must be rebooted. :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 in megabytes :param disk_name: name of the virtual disk (optional) :param span_length: number of disks per span (optional) :param span_depth: number of spans in virtual disk (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: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response :raises: DRACOperationFailed on error reported back by the DRAC interface :raises: DRACUnexpectedReturnValue on return value mismatch :raises: InvalidParameterValue on invalid input parameter """ virtual_disk_prop_names = [] virtual_disk_prop_values = [] error_msgs = [] # RAID controller validation if not raid_controller: error_msgs.append("'raid_controller' is not supplied") # physical disks validation if not physical_disks: error_msgs.append("'physical_disks' is not supplied") # size validation if not size_mb: error_msgs.append("'size_mb' is not supplied") else: utils.validate_integer_value(size_mb, 'size_mb', error_msgs) virtual_disk_prop_names.append('Size') virtual_disk_prop_values.append(str(size_mb)) # RAID level validation virtual_disk_prop_names.append('RAIDLevel') try: virtual_disk_prop_values.append(RAID_LEVELS[str(raid_level)]) except KeyError: error_msgs.append("'raid_level' is invalid") if disk_name is not None: virtual_disk_prop_names.append('VirtualDiskName') virtual_disk_prop_values.append(disk_name) if span_depth is not None: utils.validate_integer_value(span_depth, 'span_depth', error_msgs) virtual_disk_prop_names.append('SpanDepth') virtual_disk_prop_values.append(str(span_depth)) if span_length is not None: utils.validate_integer_value(span_length, 'span_length', error_msgs) virtual_disk_prop_names.append('SpanLength') virtual_disk_prop_values.append(str(span_length)) if error_msgs: msg = ('The following errors were encountered while parsing ' 'the provided parameters: %r') % ','.join(error_msgs) raise exceptions.InvalidParameterValue(reason=msg) selectors = { 'SystemCreationClassName': 'DCIM_ComputerSystem', 'CreationClassName': 'DCIM_RAIDService', 'SystemName': 'DCIM:ComputerSystem', 'Name': 'DCIM:RAIDService' } properties = { 'Target': raid_controller, 'PDArray': physical_disks, 'VDPropNameArray': virtual_disk_prop_names, 'VDPropValueArray': virtual_disk_prop_values } doc = self.client.invoke(uris.DCIM_RAIDService, 'CreateVirtualDisk', selectors, properties, expected_return_value=utils.RET_SUCCESS) return { 'commit_required': utils.is_reboot_required(doc, uris.DCIM_RAIDService) }
def set_nic_settings(self, nic_id, settings): """Modify one or more settings of a NIC. If successful, the pending values of the attributes are set. For the new values to be applied, a configuration job must be created and the node must be rebooted. :param nic_id: id of the network interface controller (NIC) :param settings: dictionary containing the proposed values, with each key being the name of an attribute and the value being the proposed value :returns: dictionary containing a 'commit_required' key with a boolean value indicating whether a configuration job must be created for the new settings to be applied and also containing a 'reboot_required' key with a boolean value indicating whether or not a reboot is required :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response :raises: DRACOperationFailed on error reported back by the iDRAC interface :raises: InvalidParameterValue on invalid NIC attribute """ current_settings = self.list_nic_settings(nic_id) unknown_keys = set(settings) - set(current_settings) if unknown_keys: msg = ('Unknown NIC attributes found: %(unknown_keys)r' % { 'unknown_keys': unknown_keys }) raise ironic_exceptions.InvalidParameterValue(reason=msg) read_only_keys = [] unchanged_attribs = [] invalid_attribs_msgs = [] attrib_names = [] candidates = set(settings) for attr in candidates: if str(settings[attr]) == str( current_settings[attr].current_value): unchanged_attribs.append(attr) elif current_settings[attr].read_only: read_only_keys.append(attr) else: validation_msg = current_settings[attr].validate( settings[attr]) if validation_msg is None: attrib_names.append(attr) else: invalid_attribs_msgs.append(validation_msg) if unchanged_attribs: LOG.warning('Ignoring unchanged NIC attributes: %r' % unchanged_attribs) if invalid_attribs_msgs or read_only_keys: if read_only_keys: read_only_msg = [ 'Cannot set read-only NIC attributes: %r.' % read_only_keys ] else: read_only_msg = [] drac_messages = '\n'.join(invalid_attribs_msgs + read_only_msg) raise ironic_exceptions.DRACOperationFailed( drac_messages=drac_messages) if not attrib_names: return {'commit_required': False} selectors = { 'CreationClassName': 'DCIM_NICService', 'Name': 'DCIM:NICService', 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem' } properties = { 'Target': nic_id, 'AttributeName': attrib_names, 'AttributeValue': [settings[attr] for attr in attrib_names] } doc = self.client.invoke(uris.DCIM_NICService, 'SetAttributes', selectors, properties) return { 'reboot_required': utils.is_reboot_required(doc, uris.DCIM_NICService), 'commit_required': utils_additional.is_commit_required(doc, uris.DCIM_NICService) }
def set_settings(settings_type, client, namespaces, new_settings, resource_uri, cim_creation_class_name, cim_name, target, name_formatter=None, wait_for_idrac=True, by_name=True): """Generically handles setting various types of settings on the iDRAC This method pulls the current list of settings from the iDRAC then compares that list against the passed new settings to determine if there are any errors. If no errors exist then the settings are sent to the iDRAC using the passed resource, target, etc. :param settings_type: a string indicating the settings type :param client: an instance of WSManClient :param namespaces: a list of URI/class pairs to retrieve. :param new_settings: a dictionary containing the proposed values, with each key being the name of attribute and the value being the proposed value. :param resource_uri: URI of resource to invoke :param cim_creation_class_name: creation class name of the CIM object :param cim_name: name of the CIM object :param target: target device :param name_formatter: a method used to format the keys in the returned dictionary. By default, attribute.name will be used. :param wait_for_idrac: indicates whether or not to wait for the iDRAC to be ready to accept commands before issuing the command :param by_name: Controls whether returned dictionary uses RAID attribute name or instance_id as key. :returns: a dictionary containing: - The is_commit_required 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 for the values to be applied. Possible values are true and false. :raises: WSManRequestFailure on request failures :raises: WSManInvalidResponse when receiving invalid response :raises: DRACOperationFailed on new settings with invalid values or attempting to set read-only settings or when an error is reported back by the iDRAC interface :raises: DRACUnexpectedReturnValue on return value mismatch :raises: InvalidParameterValue on invalid new setting """ current_settings = list_settings(client, namespaces, by_name=by_name, name_formatter=name_formatter, wait_for_idrac=wait_for_idrac) unknown_keys = set(new_settings) - set(current_settings) if unknown_keys: msg = ('Unknown %(settings_type)s attributes found: %(unknown_keys)r' % { 'settings_type': settings_type, 'unknown_keys': unknown_keys }) raise exceptions.InvalidParameterValue(reason=msg) read_only_keys = [] unchanged_attribs = [] invalid_attribs_msgs = [] attrib_names = [] candidates = set(new_settings) for attr in candidates: # There are RAID settings that can have multiple values, # however these are all read-only attributes. # Filter out all read-only attributes first so that we exclude # these settings from further consideration current_setting_value = current_settings[attr].current_value if type(current_setting_value) is list: current_setting_value = current_setting_value[0] unchanged_attribute = str( new_settings[attr]) == str(current_setting_value) # check if read-only attribute is unchanged if current_settings[attr].read_only and not unchanged_attribute: read_only_keys.append(attr) if unchanged_attribute: unchanged_attribs.append(attr) else: validation_msg = current_settings[attr].validate( new_settings[attr]) if not validation_msg: attrib_names.append(attr) else: invalid_attribs_msgs.append(validation_msg) if unchanged_attribs: LOG.debug('Ignoring unchanged %(settings_type)s attributes: ' '%(unchanged_attribs)r' % { 'settings_type': settings_type, 'unchanged_attribs': unchanged_attribs }) if invalid_attribs_msgs or read_only_keys: if read_only_keys: read_only_msg = [ 'Cannot set read-only %(settings_type)s ' 'attributes: %(read_only_keys)r.' % { 'settings_type': settings_type, 'read_only_keys': read_only_keys } ] else: read_only_msg = [] drac_messages = '\n'.join(invalid_attribs_msgs + read_only_msg) raise exceptions.DRACOperationFailed(drac_messages=drac_messages) if not attrib_names: return build_return_dict( None, resource_uri, is_commit_required_value=False, is_reboot_required_value=constants.RebootRequired.false) selectors = { 'CreationClassName': cim_creation_class_name, 'Name': cim_name, 'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemName': 'DCIM:ComputerSystem' } properties = { 'Target': target, 'AttributeValue': [new_settings[attr] for attr in attrib_names] } # To set RAID settings, above we fetched list raid settings using # instance_id to retrieve attribute values. When we pass instance_id in # setattribute method for setting any new RAID settings, wsman raises # an error. So another approach to set those settings is to list raid # settings using instance_id and for settings new settings, pass the # attribute names in list to SetAttributes method along with the target. # That's the reason, we need to handle RAID specific settings like below if settings_type == 'RAID': properties['AttributeName'] = [ current_settings[attr].name for attr in attrib_names ] else: properties['AttributeName'] = attrib_names doc = client.invoke(resource_uri, 'SetAttributes', selectors, properties, wait_for_idrac=wait_for_idrac) return build_return_dict(doc, resource_uri)