diff --git a/dracclient/client.py b/dracclient/client.py
index f325a24..36b1908 100644
--- a/dracclient/client.py
+++ b/dracclient/client.py
@@ -40,6 +40,7 @@ class DRACClient(object):
"""Client for managing DRAC nodes"""
BIOS_DEVICE_FQDD = 'BIOS.Setup.1-1'
+ IDRAC_FQDD = 'iDRAC.Embedded.1'
def __init__(
self, host, username, password, port=443, path='/wsman',
@@ -191,19 +192,94 @@ class DRACClient(object):
"""
return self._bios_cfg.set_bios_settings(settings)
- def list_idrac_settings(self):
+ def list_idrac_settings(self, by_name=False, fqdd_filter=IDRAC_FQDD):
"""List the iDRAC configuration settings
- :returns: a dictionary with the iDRAC settings using InstanceID as the
- key. The attributes are either iDRACCArdEnumerableAttribute,
- iDRACCardStringAttribute or iDRACCardIntegerAttribute
- objects.
+ :param by_name: Controls whether returned dictionary uses iDRAC card
+ attribute name as key. If set to False, instance_id
+ will be used. If set to True the keys will be of the
+ form "group_id#name".
+ :param fqdd_filter: An FQDD used to filter the instances. Note that
+ this is only used when by_name is True.
+ :returns: a dictionary with the iDRAC settings using instance_id as the
+ key except when by_name is True. The attributes are either
+ iDRACCardEnumerableAttribute, iDRACCardStringAttribute or
+ iDRACCardIntegerAttribute objects.
+ :raises: WSManRequestFailure on request failures
+ :raises: WSManInvalidResponse when receiving invalid response
+ :raises: DRACOperationFailed on error reported back by the DRAC
+ interface
+ """
+ return self._idrac_cfg.list_idrac_settings(by_name=by_name,
+ fqdd_filter=fqdd_filter)
+
+ def set_idrac_settings(self, settings, idrac_fqdd=IDRAC_FQDD):
+ """Sets the iDRAC configuration settings
+
+ 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 may
+ need to be created and the node may need to be rebooted.
+
+ :param settings: a dictionary containing the proposed values, with
+ each key being the name of attribute qualified with
+ the group ID in the form "group_id#name" and the value
+ being the proposed value.
+ :param idrac_fqdd: the FQDD of the iDRAC.
+ :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 error reported back by the DRAC
interface
+ :raises: DRACUnexpectedReturnValue on return value mismatch
+ :raises: InvalidParameterValue on invalid attribute
"""
- return self._idrac_cfg.list_idrac_settings()
+ return self._idrac_cfg.set_idrac_settings(settings, idrac_fqdd)
+
+ def commit_pending_idrac_changes(
+ self,
+ idrac_fqdd=IDRAC_FQDD,
+ reboot=False):
+ """Creates a config job for applying all pending changes to an iDRAC
+
+ :param idrac_fqdd: the FQDD of the iDRAC.
+ :param reboot: indication of whether to also create a reboot job
+ :returns: id of the created configuration job
+ :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
+ """
+ return self._job_mgmt.create_config_job(
+ resource_uri=uris.DCIM_iDRACCardService,
+ cim_creation_class_name='DCIM_iDRACCardService',
+ cim_name='DCIM:iDRACCardService',
+ target=idrac_fqdd,
+ reboot=reboot)
+
+ def abandon_pending_idrac_changes(self, idrac_fqdd=IDRAC_FQDD):
+ """Abandon all pending changes to an iDRAC
+
+ Once a config job has been submitted, it can no longer be abandoned.
+
+ :param idrac_fqdd: the FQDD of the iDRAC.
+ :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
+ """
+ self._job_mgmt.delete_pending_config(
+ resource_uri=uris.DCIM_iDRACCardService,
+ cim_creation_class_name='DCIM_iDRACCardService',
+ cim_name='DCIM:iDRACCardService',
+ target=idrac_fqdd)
def list_lifecycle_settings(self):
"""List the Lifecycle Controller configuration settings
diff --git a/dracclient/resources/bios.py b/dracclient/resources/bios.py
index eacb835..978e882 100644
--- a/dracclient/resources/bios.py
+++ b/dracclient/resources/bios.py
@@ -526,4 +526,5 @@ class BIOSConfiguration(object):
uris.DCIM_BIOSService,
"DCIM_BIOSService",
"DCIM:BIOSService",
- 'BIOS.Setup.1-1')
+ 'BIOS.Setup.1-1',
+ include_commit_required=True)
diff --git a/dracclient/resources/idrac_card.py b/dracclient/resources/idrac_card.py
index b8c5561..e412cbf 100644
--- a/dracclient/resources/idrac_card.py
+++ b/dracclient/resources/idrac_card.py
@@ -15,34 +15,6 @@ from dracclient.resources import uris
from dracclient import utils
-class iDRACCardConfiguration(object):
-
- def __init__(self, client):
- """Creates iDRACCardManagement object
-
- :param client: an instance of WSManClient
- """
- self.client = client
-
- def list_idrac_settings(self):
- """List the iDRACCard configuration settings
-
- :returns: a dictionary with the iDRACCard settings using its name as
- the key. The attributes are either
- iDRACCardEnumerableAttribute, iDRACCardStringAttribute
- or iDRACCardIntegerAttribute objects.
- :raises: WSManRequestFailure on request failures
- :raises: WSManInvalidResponse when receiving invalid response
- :raises: DRACOperationFailed on error reported back by the DRAC
- interface
- """
- namespaces = [(uris.DCIM_iDRACCardEnumeration,
- iDRACCardEnumerableAttribute),
- (uris.DCIM_iDRACCardString, iDRACCardStringAttribute),
- (uris.DCIM_iDRACCardInteger, iDRACCardIntegerAttribute)]
- return utils.list_settings(self.client, namespaces, by_name=False)
-
-
class iDRACCardAttribute(object):
"""Generic iDRACCard attribute class"""
@@ -195,6 +167,20 @@ class iDRACCardStringAttribute(iDRACCardAttribute):
idrac_attr.read_only, idrac_attr.fqdd, idrac_attr.group_id,
min_length, max_length)
+ def validate(self, new_value):
+ """Validates new value"""
+
+ val_len = len(new_value)
+ if val_len < self.min_length or val_len > self.max_length:
+ msg = ("Attribute '%(attr)s' cannot be set to value '%(val)s'."
+ " It must be between %(lower)d and %(upper)d characters in "
+ "length.") % {
+ 'attr': self.name,
+ 'val': new_value,
+ 'lower': self.min_length,
+ 'upper': self.max_length}
+ return msg
+
class iDRACCardIntegerAttribute(iDRACCardAttribute):
"""Integer iDRACCard attribute class"""
@@ -258,3 +244,83 @@ class iDRACCardIntegerAttribute(iDRACCardAttribute):
'lower': self.lower_bound,
'upper': self.upper_bound}
return msg
+
+
+class iDRACCardConfiguration(object):
+
+ NAMESPACES = [(uris.DCIM_iDRACCardEnumeration,
+ iDRACCardEnumerableAttribute),
+ (uris.DCIM_iDRACCardString, iDRACCardStringAttribute),
+ (uris.DCIM_iDRACCardInteger, iDRACCardIntegerAttribute)]
+
+ def __init__(self, client):
+ """Creates an iDRACCardConfiguration object
+
+ :param client: an instance of WSManClient
+ """
+ self.client = client
+
+ def list_idrac_settings(self, by_name=False, fqdd_filter=None):
+ """List the iDRACCard configuration settings
+
+ :param by_name: Controls whether returned dictionary uses iDRAC card
+ attribute name as key. If set to False, instance_id
+ will be used. If set to True the keys will be of the
+ form "group_id#name".
+ :param fqdd_filter: An FQDD used to filter the instances. Note that
+ this is only used when by_name is True.
+ :returns: a dictionary with the iDRAC settings using instance_id as the
+ key except when by_name is True. The attributes are either
+ iDRACCArdEnumerableAttribute, iDRACCardStringAttribute or
+ iDRACCardIntegerAttribute objects.
+ :raises: WSManRequestFailure on request failures
+ :raises: WSManInvalidResponse when receiving invalid response
+ :raises: DRACOperationFailed on error reported back by the DRAC
+ interface
+ """
+
+ return utils.list_settings(self.client,
+ self.NAMESPACES,
+ by_name=by_name,
+ fqdd_filter=fqdd_filter,
+ name_formatter=_name_formatter)
+
+ def set_idrac_settings(self, new_settings, idrac_fqdd):
+ """Set the iDRACCard configuration settings
+
+ 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 may
+ need to be created and the node may need to be rebooted.
+
+ :param new_settings: a dictionary containing the proposed values, with
+ each key being the name of attribute qualified
+ with the group ID in the form "group_id#name" and
+ the value being the proposed value.
+ :param idrac_fqdd: the FQDD of the iDRAC.
+ :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 error reported back by the DRAC
+ interface
+ :raises: DRACUnexpectedReturnValue on return value mismatch
+ :raises: InvalidParameterValue on invalid attribute
+ """
+ return utils.set_settings('iDRAC Card',
+ self.client,
+ self.NAMESPACES,
+ new_settings,
+ uris.DCIM_iDRACCardService,
+ "DCIM_iDRACCardService",
+ "DCIM:iDRACCardService",
+ idrac_fqdd,
+ name_formatter=_name_formatter)
+
+
+def _name_formatter(attribute):
+ return "{}#{}".format(attribute.group_id, attribute.name)
diff --git a/dracclient/resources/raid.py b/dracclient/resources/raid.py
index b8fccc4..5d085d8 100644
--- a/dracclient/resources/raid.py
+++ b/dracclient/resources/raid.py
@@ -345,6 +345,7 @@ class RAIDManagement(object):
expected_return_value=utils.RET_SUCCESS)
return utils.build_return_dict(doc, uris.DCIM_RAIDService,
+ include_commit_required=True,
is_commit_required_value=True)
def create_virtual_disk(self, raid_controller, physical_disks, raid_level,
@@ -445,6 +446,7 @@ class RAIDManagement(object):
expected_return_value=utils.RET_SUCCESS)
return utils.build_return_dict(doc, uris.DCIM_RAIDService,
+ include_commit_required=True,
is_commit_required_value=True)
def delete_virtual_disk(self, virtual_disk):
@@ -484,4 +486,5 @@ class RAIDManagement(object):
expected_return_value=utils.RET_SUCCESS)
return utils.build_return_dict(doc, uris.DCIM_RAIDService,
+ include_commit_required=True,
is_commit_required_value=True)
diff --git a/dracclient/tests/test_idrac_card.py b/dracclient/tests/test_idrac_card.py
index db49ab5..f6fcb98 100644
--- a/dracclient/tests/test_idrac_card.py
+++ b/dracclient/tests/test_idrac_card.py
@@ -11,16 +11,25 @@
# License for the specific language governing permissions and limitations
# under the License.
+import lxml.etree
import mock
+import re
import requests_mock
import dracclient.client
+from dracclient import constants
+from dracclient import exceptions
from dracclient.resources import idrac_card
+from dracclient.resources import job
from dracclient.resources import uris
from dracclient.tests import base
from dracclient.tests import utils as test_utils
+@requests_mock.Mocker()
+@mock.patch.object(dracclient.client.WSManClient,
+ 'wait_until_idrac_is_ready', spec_set=True,
+ autospec=True)
class ClientiDRACCardConfigurationTestCase(base.BaseTest):
def setUp(self):
@@ -28,12 +37,8 @@ class ClientiDRACCardConfigurationTestCase(base.BaseTest):
self.drac_client = dracclient.client.DRACClient(
**test_utils.FAKE_ENDPOINT)
- @requests_mock.Mocker()
- @mock.patch.object(dracclient.client.WSManClient,
- 'wait_until_idrac_is_ready', spec_set=True,
- autospec=True)
- def test_list_idrac_settings(self, mock_requests,
- mock_wait_until_idrac_is_ready):
+ def test_list_idrac_settings_by_instance_id(
+ self, mock_requests, mock_wait_until_idrac_is_ready):
expected_enum_attr = idrac_card.iDRACCardEnumerableAttribute(
name='Type',
instance_id='iDRAC.Embedded.1#Info.1#Type',
@@ -75,7 +80,7 @@ class ClientiDRACCardConfigurationTestCase(base.BaseTest):
idrac_settings = self.drac_client.list_idrac_settings()
- self.assertEqual(630, len(idrac_settings))
+ self.assertEqual(631, len(idrac_settings))
# enumerable attribute
self.assertIn('iDRAC.Embedded.1#Info.1#Type', idrac_settings)
self.assertEqual(expected_enum_attr, idrac_settings[
@@ -88,3 +93,222 @@ class ClientiDRACCardConfigurationTestCase(base.BaseTest):
self.assertIn('iDRAC.Embedded.1#SSH.1#Port', idrac_settings)
self.assertEqual(expected_integer_attr, idrac_settings[
'iDRAC.Embedded.1#SSH.1#Port'])
+
+ def test_list_idrac_settings_by_name(
+ self, mock_requests, mock_wait_until_idrac_is_ready):
+ expected_enum_attr = idrac_card.iDRACCardEnumerableAttribute(
+ name='Type',
+ instance_id='iDRAC.Embedded.1#Info.1#Type',
+ read_only=True,
+ current_value='13G Monolithic',
+ pending_value=None,
+ fqdd='iDRAC.Embedded.1',
+ group_id='Info.1',
+ possible_values=['12G/13G', '12G Monolithic', '12G Modular',
+ '13G Monolithic', '13G Modular', '12G DCS',
+ '13G DCS'])
+ expected_string_attr = idrac_card.iDRACCardStringAttribute(
+ name='Version',
+ instance_id='iDRAC.Embedded.1#Info.1#Version',
+ read_only=True,
+ current_value='2.40.40.40',
+ pending_value=None,
+ fqdd='iDRAC.Embedded.1',
+ group_id='Info.1',
+ min_length=0,
+ max_length=63)
+ expected_integer_attr = idrac_card.iDRACCardIntegerAttribute(
+ name='Port',
+ instance_id='iDRAC.Embedded.1#SSH.1#Port',
+ read_only=False,
+ current_value=22,
+ pending_value=None,
+ fqdd='iDRAC.Embedded.1',
+ group_id='SSH.1',
+ lower_bound=1,
+ upper_bound=65535)
+ mock_requests.post('https://1.2.3.4:443/wsman', [
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardEnumeration]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardString]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardInteger]['ok']}])
+
+ idrac_settings = self.drac_client.list_idrac_settings(by_name=True)
+
+ self.assertEqual(630, len(idrac_settings))
+
+ # enumerable attribute
+ self.assertIn('Info.1#Type', idrac_settings)
+ self.assertEqual(expected_enum_attr, idrac_settings[
+ 'Info.1#Type'])
+ # string attribute
+ self.assertIn('Info.1#Version', idrac_settings)
+ self.assertEqual(expected_string_attr,
+ idrac_settings['Info.1#Version'])
+ # integer attribute
+ self.assertIn('SSH.1#Port', idrac_settings)
+ self.assertEqual(expected_integer_attr, idrac_settings[
+ 'SSH.1#Port'])
+
+ def test_list_multi_idrac_settings_by_name(
+ self, mock_requests, mock_wait_until_idrac_is_ready):
+ expected_enum_attr = idrac_card.iDRACCardEnumerableAttribute(
+ name='Type',
+ instance_id='iDRAC.Embedded.2#Info.1#Type',
+ read_only=True,
+ current_value='13G Monolithic',
+ pending_value=None,
+ fqdd='iDRAC.Embedded.2',
+ group_id='Info.1',
+ possible_values=['12G/13G', '12G Monolithic', '12G Modular',
+ '13G Monolithic', '13G Modular', '12G DCS',
+ '13G DCS'])
+ mock_requests.post('https://1.2.3.4:443/wsman', [
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardEnumeration]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardString]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardInteger]['ok']}])
+
+ idrac_settings = self.drac_client.list_idrac_settings(
+ by_name=True, fqdd_filter='iDRAC.Embedded.2')
+
+ self.assertEqual(1, len(idrac_settings))
+
+ # enumerable attribute
+ self.assertIn('Info.1#Type', idrac_settings)
+ self.assertEqual(expected_enum_attr, idrac_settings[
+ 'Info.1#Type'])
+
+ @mock.patch.object(dracclient.client.WSManClient, 'invoke',
+ spec_set=True, autospec=True)
+ def test_set_idrac_settings(
+ self, mock_requests, mock_invoke, mock_wait_until_idrac_is_ready):
+ expected_selectors = {'CreationClassName': 'DCIM_iDRACCardService',
+ 'SystemName': 'DCIM:ComputerSystem',
+ 'Name': 'DCIM:iDRACCardService',
+ 'SystemCreationClassName': 'DCIM_ComputerSystem'}
+ expected_properties = {'Target': 'iDRAC.Embedded.1',
+ 'AttributeName': ['LDAP.1#GroupAttributeIsDN'],
+ 'AttributeValue': ['Disabled']}
+ mock_requests.post('https://1.2.3.4:443/wsman', [
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardEnumeration]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardString]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardInteger]['ok']}])
+ mock_invoke.return_value = lxml.etree.fromstring(
+ test_utils.iDracCardInvocations[uris.DCIM_iDRACCardService][
+ 'SetAttributes']['ok'])
+
+ result = self.drac_client.set_idrac_settings(
+ {'LDAP.1#GroupAttributeIsDN': 'Disabled'})
+
+ self.assertEqual({'is_commit_required': True,
+ 'is_reboot_required':
+ constants.RebootRequired.false},
+ result)
+ mock_invoke.assert_called_once_with(
+ mock.ANY, uris.DCIM_iDRACCardService, 'SetAttributes',
+ expected_selectors, expected_properties)
+
+ @mock.patch.object(dracclient.client.WSManClient, 'invoke',
+ spec_set=True, autospec=True)
+ def test_set_idrac_settings_with_valid_length_string(
+ self, mock_requests, mock_invoke, mock_wait_until_idrac_is_ready):
+ expected_selectors = {'CreationClassName': 'DCIM_iDRACCardService',
+ 'SystemName': 'DCIM:ComputerSystem',
+ 'Name': 'DCIM:iDRACCardService',
+ 'SystemCreationClassName': 'DCIM_ComputerSystem'}
+ expected_properties = {'Target': 'iDRAC.Embedded.1',
+ 'AttributeName': ['Users.16#Password'],
+ 'AttributeValue': ['12345678901234567890']}
+ mock_requests.post('https://1.2.3.4:443/wsman', [
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardEnumeration]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardString]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardInteger]['ok']}])
+ mock_invoke.return_value = lxml.etree.fromstring(
+ test_utils.iDracCardInvocations[uris.DCIM_iDRACCardService][
+ 'SetAttributes']['ok'])
+ result = self.drac_client.set_idrac_settings(
+ {'Users.16#Password': '12345678901234567890'})
+ self.assertEqual({'is_commit_required': True,
+ 'is_reboot_required':
+ constants.RebootRequired.false},
+ result)
+ mock_invoke.assert_called_once_with(
+ mock.ANY, uris.DCIM_iDRACCardService, 'SetAttributes',
+ expected_selectors, expected_properties)
+
+ def test_set_idrac_settings_with_too_long_string(
+ self, mock_requests, mock_wait_until_idrac_is_ready):
+ expected_message = ("Attribute 'Password' cannot be set to "
+ "value '123456789012345678901'. It must be "
+ "between 0 and 20 characters in length.")
+ mock_requests.post('https://1.2.3.4:443/wsman', [
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardEnumeration]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardString]['ok']},
+ {'text': test_utils.iDracCardEnumerations[
+ uris.DCIM_iDRACCardInteger]['ok']}])
+ self.assertRaisesRegexp(
+ exceptions.DRACOperationFailed, re.escape(expected_message),
+ self.drac_client.set_idrac_settings,
+ {'Users.16#Password': '123456789012345678901'})
+
+
+class ClientiDRACCardChangesTestCase(base.BaseTest):
+
+ def setUp(self):
+ super(ClientiDRACCardChangesTestCase, self).setUp()
+ self.drac_client = dracclient.client.DRACClient(
+ **test_utils.FAKE_ENDPOINT)
+
+ @mock.patch.object(job.JobManagement, 'create_config_job', spec_set=True,
+ autospec=True)
+ def test_commit_pending_idrac_changes(self, mock_create_config_job):
+ self.drac_client.commit_pending_idrac_changes()
+
+ mock_create_config_job.assert_called_once_with(
+ mock.ANY,
+ resource_uri=uris.DCIM_iDRACCardService,
+ cim_creation_class_name='DCIM_iDRACCardService',
+ cim_name='DCIM:iDRACCardService',
+ target=dracclient.client.DRACClient.IDRAC_FQDD,
+ reboot=False)
+
+ @mock.patch.object(job.JobManagement, 'create_config_job', spec_set=True,
+ autospec=True)
+ def test_commit_pending_idrac_changes_with_reboot(
+ self, mock_create_config_job):
+
+ self.drac_client.commit_pending_idrac_changes(
+ reboot=True)
+
+ mock_create_config_job.assert_called_once_with(
+ mock.ANY,
+ resource_uri=uris.DCIM_iDRACCardService,
+ cim_creation_class_name='DCIM_iDRACCardService',
+ cim_name='DCIM:iDRACCardService',
+ target=dracclient.client.DRACClient.IDRAC_FQDD,
+ reboot=True)
+
+ @mock.patch.object(job.JobManagement, 'delete_pending_config',
+ spec_set=True, autospec=True)
+ def test_abandon_pending_idrac_changes(self, mock_delete_pending_config):
+ self.drac_client.abandon_pending_idrac_changes()
+
+ mock_delete_pending_config.assert_called_once_with(
+ mock.ANY,
+ resource_uri=uris.DCIM_iDRACCardService,
+ cim_creation_class_name='DCIM_iDRACCardService',
+ cim_name='DCIM:iDRACCardService',
+ target=dracclient.client.DRACClient.IDRAC_FQDD)
diff --git a/dracclient/tests/utils.py b/dracclient/tests/utils.py
index e82e944..8eef943 100644
--- a/dracclient/tests/utils.py
+++ b/dracclient/tests/utils.py
@@ -147,6 +147,15 @@ iDracCardEnumerations = {
},
}
+iDracCardInvocations = {
+ uris.DCIM_iDRACCardService: {
+ 'SetAttributes': {
+ 'ok': load_wsman_xml(
+ 'idrac_service-invoke-set_attributes-ok')
+ }
+ }
+}
+
LifecycleControllerEnumerations = {
uris.DCIM_SystemView: {
'ok': load_wsman_xml('system_view-enum-ok')
diff --git a/dracclient/tests/wsman_mocks/idrac_service-invoke-set_attributes-ok.xml b/dracclient/tests/wsman_mocks/idrac_service-invoke-set_attributes-ok.xml
new file mode 100644
index 0000000..3f52ea8
--- /dev/null
+++ b/dracclient/tests/wsman_mocks/idrac_service-invoke-set_attributes-ok.xml
@@ -0,0 +1,19 @@
+
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+ http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_iDRACCardService/SetAttributesResponse
+ uuid:305ed6f4-2269-43c5-8c28-00b85e81099d
+ uuid:683c34bc-53c4-13c4-8064-17c1f0d9bed4
+
+
+
+ The command was successful
+ RAC001
+ No
+ 0
+ Set PendingValue
+
+
+
diff --git a/dracclient/tests/wsman_mocks/idraccard_enumeration-enum-ok.xml b/dracclient/tests/wsman_mocks/idraccard_enumeration-enum-ok.xml
index a5a537d..35bc8b9 100644
--- a/dracclient/tests/wsman_mocks/idraccard_enumeration-enum-ok.xml
+++ b/dracclient/tests/wsman_mocks/idraccard_enumeration-enum-ok.xml
@@ -13,6 +13,27 @@
+
+ iDRAC Type
+ Type
+ 13G Monolithic
+ 12G/13G
+
+ 6
+ iDRAC.Embedded.2
+ RAC Information
+ Info.1
+ iDRAC.Embedded.2#Info.1#Type
+ true
+
+ 12G/13G
+ 12G Monolithic
+ 12G Modular
+ 13G Monolithic
+ 13G Modular
+ 12G DCS
+ 13G DCS
+
iDRAC Type
Type
diff --git a/dracclient/utils.py b/dracclient/utils.py
index 5ed16e8..dbf4588 100644
--- a/dracclient/utils.py
+++ b/dracclient/utils.py
@@ -133,7 +133,8 @@ def get_all_wsman_resource_attrs(doc, resource_uri, attr_name, nullable=False):
def build_return_dict(doc, resource_uri,
is_commit_required_value=None,
is_reboot_required_value=None,
- commit_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
@@ -150,6 +151,8 @@ def build_return_dict(doc, resource_uri,
: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.
@@ -179,10 +182,11 @@ def build_return_dict(doc, resource_uri,
# Include commit_required in the response for backwards compatibility
# TBD: Remove this parameter in the future
- if commit_required_value is None:
- commit_required_value = is_reboot_required(doc, resource_uri)
+ 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
+ result['commit_required'] = commit_required_value
return result
@@ -246,13 +250,19 @@ def validate_integer_value(value, attr_name, error_msgs):
error_msgs.append("'%s' is not an integer value" % attr_name)
-def list_settings(client, namespaces, by_name=True):
+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
@@ -263,7 +273,8 @@ def list_settings(client, namespaces, by_name=True):
result = {}
for (namespace, attr_cls) in namespaces:
- attribs = _get_config(client, namespace, attr_cls, by_name)
+ 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' % (
@@ -272,7 +283,8 @@ def list_settings(client, namespaces, by_name=True):
return result
-def _get_config(client, resource, attr_cls, by_name):
+def _get_config(client, resource, attr_cls, by_name, fqdd_filter,
+ name_formatter):
result = {}
doc = client.enumerate(resource)
@@ -281,7 +293,14 @@ def _get_config(client, resource, attr_cls, by_name):
for item in items:
attribute = attr_cls.parse(item)
if by_name:
- result[attribute.name] = attribute
+ # Filter out all instances without a matching FQDD
+ if fqdd_filter is None or fqdd_filter == attribute.fqdd:
+ if name_formatter is None:
+ name = attribute.name
+ else:
+ name = name_formatter(attribute)
+
+ result[name] = attribute
else:
result[attribute.instance_id] = attribute
@@ -295,7 +314,9 @@ def set_settings(settings_type,
resource_uri,
cim_creation_class_name,
cim_name,
- target):
+ target,
+ name_formatter=None,
+ include_commit_required=False):
"""Generically handles setting various types of settings on the iDRAC
This method pulls the current list of settings from the iDRAC then compares
@@ -313,6 +334,11 @@ def set_settings(settings_type,
: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.
+ :parm include_commit_required: Indicates if the deprecated commit_required
+ should be returned in the result.
:returns: a dictionary containing:
- The commit_required key with a boolean value indicating
whether a config job must be created for the values to be
@@ -334,7 +360,8 @@ def set_settings(settings_type,
:raises: InvalidParameterValue on invalid new setting
"""
- current_settings = list_settings(client, namespaces, by_name=True)
+ current_settings = list_settings(client, namespaces, by_name=True,
+ name_formatter=name_formatter)
unknown_keys = set(new_settings) - set(current_settings)
if unknown_keys:
@@ -383,7 +410,10 @@ def set_settings(settings_type,
if not attrib_names:
return build_return_dict(
- None, resource_uri, is_commit_required_value=False,
+ None,
+ resource_uri,
+ include_commit_required=include_commit_required,
+ is_commit_required_value=False,
is_reboot_required_value=constants.RebootRequired.false,
commit_required_value=False)
@@ -398,4 +428,5 @@ def set_settings(settings_type,
doc = client.invoke(resource_uri, 'SetAttributes',
selectors, properties)
- return build_return_dict(doc, resource_uri)
+ return build_return_dict(doc, resource_uri,
+ include_commit_required=include_commit_required)