예제 #1
0
    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)
예제 #2
0
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
예제 #3
0
    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)
예제 #4
0
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]
예제 #5
0
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)
예제 #6
0
    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)}
예제 #7
0
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
예제 #8
0
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)
    }
예제 #9
0
    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)
        }
예제 #10
0
    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)
        }
예제 #11
0
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)