Ejemplo n.º 1
0
    def wait_until_idrac_is_ready(self, retries=24, retry_delay=10):
        """Waits until the iDRAC is in a ready state

        :param retries: The number of times to check if the iDRAC is ready
        :param retry_delay: The number of seconds to wait between retries

        :raises: WSManRequestFailure on request failures
        :raises: WSManInvalidResponse when receiving invalid response
        :raises: DRACOperationFailed on error reported back by the DRAC
                 interface or timeout
        :raises: DRACUnexpectedReturnValue on return value mismatch
        """

        # Try every 10 seconds over 4 minutes for the iDRAC to become ready
        while retries > 0:
            LOG.debug("Checking to see if the iDRAC is ready")

            if self.is_idrac_ready():
                LOG.debug("The iDRAC is ready")
                return

            LOG.debug("The iDRAC is not ready")
            retries -= 1
            if retries > 0:
                time.sleep(retry_delay)

        if retries == 0:
            err_msg = "Timed out waiting for the iDRAC to become ready"
            LOG.error(err_msg)
            raise exceptions.DRACOperationFailed(drac_messages=err_msg)
Ejemplo n.º 2
0
    def list_bios_settings(self, by_name=True):
        """List the BIOS configuration settings

        :param by_name: Controls whether returned dictionary uses BIOS
                        attribute name or instance_id as key.
        :returns: a dictionary with the BIOS settings using its name as the
                  key. The attributes are either BIOSEnumerableAttribute,
                  BIOSStringAttribute or BIOSIntegerAttribute objects.
        :raises: WSManRequestFailure on request failures
        :raises: WSManInvalidResponse when receiving invalid response
        :raises: DRACOperationFailed on error reported back by the DRAC
                 interface
        """

        result = {}
        namespaces = [(uris.DCIM_BIOSEnumeration, BIOSEnumerableAttribute),
                      (uris.DCIM_BIOSString, BIOSStringAttribute),
                      (uris.DCIM_BIOSInteger, BIOSIntegerAttribute)]
        for (namespace, attr_cls) in namespaces:
            attribs = self._get_config(namespace, attr_cls, by_name)
            if not set(result).isdisjoint(set(attribs)):
                raise exceptions.DRACOperationFailed(
                    drac_messages=('Colliding attributes %r' % (
                        set(result) & set(attribs))))
            result.update(attribs)
        return result
Ejemplo n.º 3
0
    def clear_foreign_config(self, raid_controller):
        """Free up foreign drives

        The job to clear foreign config will be in pending state.
        For the changes to be applied, a config job must be created.

        :param raid_controller: id of the RAID controller
        :returns: a dictionary containing:
                 - The is_commit_required key with the value always set to
                   True indicating that a config job must be created to
                   clear foreign configuration.
                 - The is_reboot_required key with a RebootRequired enumerated
                   value indicating whether the server must be rebooted to
                   clear foreign configuration.
        :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
        """

        selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem',
                     'CreationClassName': 'DCIM_RAIDService',
                     'SystemName': 'DCIM:ComputerSystem',
                     'Name': 'DCIM:RAIDService'}
        properties = {'Target': raid_controller}

        doc = self.client.invoke(uris.DCIM_RAIDService, 'ClearForeignConfig',
                                 selectors, properties,
                                 check_return_value=False)

        is_commit_required_value = True
        is_reboot_required_value = None

        ret_value = utils.find_xml(doc,
                                   'ReturnValue',
                                   uris.DCIM_RAIDService).text

        if ret_value == utils.RET_ERROR:
            message_id = utils.find_xml(doc,
                                        'MessageID',
                                        uris.DCIM_RAIDService).text

            # A MessageID 'STOR018' indicates no foreign drive was
            # detected. Return a value which informs the caller nothing
            # further needs to be done.
            if message_id == NO_FOREIGN_DRIVE:
                is_commit_required_value = False
                is_reboot_required_value = constants.RebootRequired.false
            else:
                message = utils.find_xml(doc,
                                         'Message',
                                         uris.DCIM_RAIDService).text
                raise exceptions.DRACOperationFailed(
                        drac_messages=message)

        return utils.build_return_dict(
                doc, uris.DCIM_RAIDService,
                is_commit_required_value=is_commit_required_value,
                is_reboot_required_value=is_reboot_required_value)
Ejemplo n.º 4
0
    def invoke(self,
               resource_uri,
               method,
               selectors=None,
               properties=None,
               expected_return_value=None,
               wait_for_idrac=True,
               check_return_value=True):
        """Invokes a remote WS-Man method

        :param resource_uri: URI of the resource
        :param method: name of the method to invoke
        :param selectors: dictionary of selectors
        :param properties: dictionary of properties
        :param expected_return_value: expected return value reported back by
            the DRAC card. For return value codes check the profile
            documentation of the resource used in the method call. If not set,
            return value checking is skipped.
        :param wait_for_idrac: indicates whether or not to wait for the
            iDRAC to be ready to accept commands before issuing the
            command
        :param check_return_value: indicates if the ReturnValue should be
            checked and an exception thrown on an unexpected value
        :returns: an lxml.etree.Element object of the response received
        :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
        """
        if wait_for_idrac:
            self.wait_until_idrac_is_ready()

        if selectors is None:
            selectors = {}

        if properties is None:
            properties = {}

        resp = super(WSManClient, self).invoke(resource_uri, method, selectors,
                                               properties)

        if check_return_value:
            return_value = utils.find_xml(resp, 'ReturnValue',
                                          resource_uri).text
            if return_value == utils.RET_ERROR:
                message_elems = utils.find_xml(resp, 'Message', resource_uri,
                                               True)
                messages = [
                    message_elem.text for message_elem in message_elems
                ]
                raise exceptions.DRACOperationFailed(drac_messages=messages)

            if (expected_return_value is not None
                    and return_value != expected_return_value):
                raise exceptions.DRACUnexpectedReturnValue(
                    expected_return_value=expected_return_value,
                    actual_return_value=return_value)

        return resp
Ejemplo n.º 5
0
def list_settings(client, namespaces, by_name=True, fqdd_filter=None,
                  name_formatter=None):
    """List the configuration settings

    :param client: an instance of WSManClient.
    :param namespaces: a list of URI/class pairs to retrieve.
    :param by_name: controls whether returned dictionary uses
                    attribute name or instance_id as key.
    :param fqdd_filter: An FQDD used to filter the instances.  Note that
                        this is only used when by_name is True.
    :param name_formatter: a method used to format the keys in the
                           returned dictionary.  By default,
                           attribute.name will be used.
    :returns: a dictionary with the settings using name or instance_id as
              the key.
    :raises: WSManRequestFailure on request failures
    :raises: WSManInvalidResponse when receiving invalid response
    :raises: DRACOperationFailed on error reported back by the DRAC
             interface
    """

    result = {}
    for (namespace, attr_cls) in namespaces:
        attribs = _get_config(client, namespace, attr_cls, by_name,
                              fqdd_filter, name_formatter)
        if not set(result).isdisjoint(set(attribs)):
            raise exceptions.DRACOperationFailed(
                drac_messages=('Colliding attributes %r' % (
                    set(result) & set(attribs))))
        result.update(attribs)
    return result
Ejemplo n.º 6
0
    def test_reboot_retries_fail(self, mock_sleep, mock_get_drac_client):
        mock_client = mock_get_drac_client.return_value
        mock_client.get_power_state.return_value = drac_constants.POWER_OFF
        exc = drac_exceptions.DRACOperationFailed(
            drac_messages=['The command failed to set RequestedState'])
        mock_client.set_power_state.side_effect = exc

        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            self.assertRaises(exception.DracOperationError,
                              task.driver.power.reboot, task)

        self.assertEqual(drac_power.POWER_STATE_TRIES,
                         mock_client.set_power_state.call_count)
Ejemplo n.º 7
0
    def test_raid_controller_jbod_ex_no_match(self, mock_requests,
                                              mock_convert_physical_disks,
                                              mock_wait_until_idrac_is_ready):

        mock_requests.post(
            'https://1.2.3.4:443/wsman',
            text=test_utils.RAIDEnumerations[uris.DCIM_PhysicalDiskView]['ok'])
        msg = "NON_MATCHING_MESSAGE"
        exc = exceptions.DRACOperationFailed(drac_messages=msg)
        mock_convert_physical_disks.side_effect = exc

        self.assertRaises(exceptions.DRACOperationFailed,
                          self.drac_client.is_jbod_capable,
                          self.raid_controller_fqdd)
Ejemplo n.º 8
0
    def test_raid_controller_jbod_not_supported(self, mock_requests,
                                                mock_convert_physical_disks,
                                                mock_wait_idrac_is_ready):

        msg = " operation is not supported on th"
        exc = exceptions.DRACOperationFailed(drac_messages=msg)
        mock_convert_physical_disks.side_effect = exc

        mock_requests.post(
            'https://1.2.3.4:443/wsman',
            text=test_utils.RAIDEnumerations[uris.DCIM_PhysicalDiskView]['ok'])

        is_jbod = self.drac_client.is_jbod_capable(self.raid_controller_fqdd)
        self.assertFalse(is_jbod, msg="is_jbod is false")
Ejemplo n.º 9
0
    def test_reboot_retries_success(self, mock_sleep, mock_get_drac_client):
        mock_client = mock_get_drac_client.return_value
        mock_client.get_power_state.return_value = drac_constants.POWER_OFF
        exc = drac_exceptions.DRACOperationFailed(
            drac_messages=['The command failed to set RequestedState'])
        mock_client.set_power_state.side_effect = [exc, None]

        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=False) as task:
            task.driver.power.reboot(task)

        drac_power_state = drac_power.REVERSE_POWER_STATES[states.POWER_ON]
        self.assertEqual(2, mock_client.set_power_state.call_count)
        mock_client.set_power_state.assert_has_calls(
            [mock.call(drac_power_state),
             mock.call(drac_power_state)])
Ejemplo n.º 10
0
    def _list_nic_settings(self, selection_expression):
        result = {}
        configurable_attributes = [
            (uris.DCIM_NICEnumeration, 'DCIM_NICEnumeration',
             NICEnumerationAttribute),
            (uris.DCIM_NICString, 'DCIM_NICString', NICStringAttribute),
            (uris.DCIM_NICInteger, 'DCIM_NICInteger', NICIntegerAttribute)
        ]

        for (resource, class_name, attr_cls) in configurable_attributes:
            attribs = self._get_config(resource, class_name,
                                       selection_expression, attr_cls)

            if not set(result).isdisjoint(set(attribs)):
                raise ironic_exceptions.DRACOperationFailed(
                    drac_messages=('Colliding attributes %r' %
                                   (set(result) & set(attribs))))

            result.update(attribs)

        return result
Ejemplo n.º 11
0
    def list_raid_settings(self, by_name=True):
        """Returns the list of RAID controllers

        :returns: a list of RAIDController objects
        :raises: WSManRequestFailure on request failures
        :raises: WSManInvalidResponse when receiving invalid response
        :raises: DRACOperationFailed on error reported back by the DRAC
                 interface
        """

        result = {}
        namespaces = [(uris.DCIM_RAIDEnumeration, RAIDEnumerableAttribute),
                      (uris.DCIM_RAIDString, RAIDStringAttribute),
                      (uris.DCIM_RAIDInteger, RAIDIntegerAttribute)]
        for (namespace, attr_cls) in namespaces:
            attribs = self._get_config(namespace, attr_cls, by_name)
            if not set(result).isdisjoint(set(attribs)):
                raise exceptions.DRACOperationFailed(
                    drac_messages=('Colliding attributes %r' %
                                   (set(result) & set(attribs))))
            result.update(attribs)
        return result
Ejemplo n.º 12
0
    def delete_jobs(self, job_ids=['JID_CLEARALL']):
        """Deletes the given jobs, or all jobs if none specified

        :raises: WSManRequestFailure on request failures
        :raises: WSManInvalidResponse when receiving invalid response
        :raises: DRACOperationFailed on error reported back by the iDRAC
                 interface
        :raises: DRACUnexpectedReturnValue on non-success
        """

        selectors = {
            'SystemCreationClassName': 'DCIM_ComputerSystem',
            'SystemName': 'idrac',
            'CreationClassName': 'DCIM_JobService',
            'Name': 'JobService'
        }

        if job_ids is None:
            return

        messages = []

        for job_id in job_ids:
            properties = {'JobID': job_id}

            try:
                self.client.invoke(uris.DCIM_JobService,
                                   'DeleteJobQueue',
                                   selectors,
                                   properties,
                                   expected_return_value=utils.RET_SUCCESS)
            except exceptions.DRACOperationFailed as dof:
                for message in dof.args:
                    messages.append(message + " " + job_id)

        if len(messages):
            raise exceptions.DRACOperationFailed(drac_messages=messages)
Ejemplo n.º 13
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)}
Ejemplo n.º 14
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)
    }
Ejemplo n.º 15
0
    def wait_until_idrac_is_reset(self, force=False):
        """Resets the iDRAC and waits for it to become ready

        :param force: does a force reset when True and a graceful reset when
               False
        :returns: None.
        :raises: WSManRequestFailure on request failures
        :raises: WSManInvalidResponse when receiving invalid response
        :raises: DRACOperationFailed on failure to reset iDRAC
        """
        return_value = self.reset_idrac(force)

        if not return_value:
            LOG.debug("iDRAC failed to reset")
            raise exceptions.DRACOperationFailed(
                drac_messages="Failed to reset iDRAC")
        else:
            LOG.debug("iDRAC was successfully reset")

        LOG.info("Waiting for the iDRAC to become not pingable")
        required_ping_fail_count = 2
        retries = 24
        ping_fail_count = 0
        while retries > 0:
            response = os.system("ping -c 1 {} 2>&1 1>/dev/null".format(
                self.client.host))
            retries -= 1
            if response != 0:
                ping_fail_count += 1
                LOG.debug("The iDRAC is not pingable, ping_fail_count="
                          "{}".format(ping_fail_count))
                if ping_fail_count == required_ping_fail_count:
                    LOG.debug("Breaking")
                    break
            else:
                ping_fail_count = 0
                LOG.debug("The iDRAC is pingable")

            sleep(10)

        if retries == 0 and ping_fail_count < required_ping_fail_count:
            raise exceptions.DRACOperationFailed(
                drac_messages="Timed out "
                "waiting for the " + self.client.host + " iDRAC to "
                "become not pingable")

        LOG.info("The iDRAC has become not pingable")

        LOG.info("Waiting for the iDRAC to become pingable")
        retries = 24
        ping_success_count = 0
        while retries > 0:
            response = os.system("ping -c 1 {} 2>&1 1>/dev/null".format(
                self.client.host))
            retries -= 1
            if response != 0:
                LOG.debug("The iDRAC is not pingable")
                ping_success_count = 0
            else:
                ping_success_count += 1
                LOG.debug("The iDRAC is pingable, ping_success_count="
                          "{}".format(ping_success_count))
                if ping_success_count == 3:
                    LOG.debug("Breaking")
                    break

            sleep(10)

        if retries == 0 and ping_success_count < 3:
            raise exceptions.DRACOperationFailed(
                drac_messages="Timed out "
                "waiting for the " + self.client.host + " iDRAC to "
                "become pingable")

        LOG.info("The iDRAC has become pingable")
        sleep(30)

        LOG.info("Waiting for the iDRAC to become ready")
        retries = 24
        while retries > 0:
            try:
                is_ready = self.is_idrac_ready()
                if is_ready:
                    LOG.info("The iDRAC is ready")
                    break
                else:
                    LOG.debug("The iDRAC is not ready")
            except:
                # It is normal to get a series of connection errors before
                # the iDRAC becomes ready
                ex = sys.exc_info()[0]
                LOG.debug("An exception occurred while checking iDRAC ready "
                          "state. Ignoring.: {}".format(str(ex)))
                pass
            retries -= 1
            sleep(10)

        if retries == 0:
            raise exceptions.DRACOperationFailed(
                drac_messages="Timed out "
                "waiting for the " + self.client.host + " iDRAC to "
                "become ready")
Ejemplo n.º 16
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)
        }
Ejemplo n.º 17
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)
Ejemplo n.º 18
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)