Add ability to set iDRAC card settings

This patch adds the ability to set iDRAC card settings.

Change-Id: I117e5ed0af1469dc316a30abcfa384d3c773ed1f
This commit is contained in:
Christopher Dearborn 2017-11-17 15:41:29 -05:00
parent ee0ca6f1f6
commit b327b0811f
9 changed files with 504 additions and 54 deletions

View File

@ -40,6 +40,7 @@ class DRACClient(object):
"""Client for managing DRAC nodes""" """Client for managing DRAC nodes"""
BIOS_DEVICE_FQDD = 'BIOS.Setup.1-1' BIOS_DEVICE_FQDD = 'BIOS.Setup.1-1'
IDRAC_FQDD = 'iDRAC.Embedded.1'
def __init__( def __init__(
self, host, username, password, port=443, path='/wsman', self, host, username, password, port=443, path='/wsman',
@ -191,19 +192,94 @@ class DRACClient(object):
""" """
return self._bios_cfg.set_bios_settings(settings) 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 """List the iDRAC configuration settings
:returns: a dictionary with the iDRAC settings using InstanceID as the :param by_name: Controls whether returned dictionary uses iDRAC card
key. The attributes are either iDRACCArdEnumerableAttribute, attribute name as key. If set to False, instance_id
iDRACCardStringAttribute or iDRACCardIntegerAttribute will be used. If set to True the keys will be of the
objects. 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: WSManRequestFailure on request failures
:raises: WSManInvalidResponse when receiving invalid response :raises: WSManInvalidResponse when receiving invalid response
:raises: DRACOperationFailed on error reported back by the DRAC :raises: DRACOperationFailed on error reported back by the DRAC
interface interface
""" """
return self._idrac_cfg.list_idrac_settings() 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.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): def list_lifecycle_settings(self):
"""List the Lifecycle Controller configuration settings """List the Lifecycle Controller configuration settings

View File

@ -526,4 +526,5 @@ class BIOSConfiguration(object):
uris.DCIM_BIOSService, uris.DCIM_BIOSService,
"DCIM_BIOSService", "DCIM_BIOSService",
"DCIM:BIOSService", "DCIM:BIOSService",
'BIOS.Setup.1-1') 'BIOS.Setup.1-1',
include_commit_required=True)

View File

@ -15,34 +15,6 @@ from dracclient.resources import uris
from dracclient import utils 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): class iDRACCardAttribute(object):
"""Generic iDRACCard attribute class""" """Generic iDRACCard attribute class"""
@ -195,6 +167,20 @@ class iDRACCardStringAttribute(iDRACCardAttribute):
idrac_attr.read_only, idrac_attr.fqdd, idrac_attr.group_id, idrac_attr.read_only, idrac_attr.fqdd, idrac_attr.group_id,
min_length, max_length) 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): class iDRACCardIntegerAttribute(iDRACCardAttribute):
"""Integer iDRACCard attribute class""" """Integer iDRACCard attribute class"""
@ -258,3 +244,83 @@ class iDRACCardIntegerAttribute(iDRACCardAttribute):
'lower': self.lower_bound, 'lower': self.lower_bound,
'upper': self.upper_bound} 'upper': self.upper_bound}
return msg 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)

View File

@ -345,6 +345,7 @@ class RAIDManagement(object):
expected_return_value=utils.RET_SUCCESS) expected_return_value=utils.RET_SUCCESS)
return utils.build_return_dict(doc, uris.DCIM_RAIDService, return utils.build_return_dict(doc, uris.DCIM_RAIDService,
include_commit_required=True,
is_commit_required_value=True) is_commit_required_value=True)
def create_virtual_disk(self, raid_controller, physical_disks, raid_level, def create_virtual_disk(self, raid_controller, physical_disks, raid_level,
@ -445,6 +446,7 @@ class RAIDManagement(object):
expected_return_value=utils.RET_SUCCESS) expected_return_value=utils.RET_SUCCESS)
return utils.build_return_dict(doc, uris.DCIM_RAIDService, return utils.build_return_dict(doc, uris.DCIM_RAIDService,
include_commit_required=True,
is_commit_required_value=True) is_commit_required_value=True)
def delete_virtual_disk(self, virtual_disk): def delete_virtual_disk(self, virtual_disk):
@ -484,4 +486,5 @@ class RAIDManagement(object):
expected_return_value=utils.RET_SUCCESS) expected_return_value=utils.RET_SUCCESS)
return utils.build_return_dict(doc, uris.DCIM_RAIDService, return utils.build_return_dict(doc, uris.DCIM_RAIDService,
include_commit_required=True,
is_commit_required_value=True) is_commit_required_value=True)

View File

@ -11,16 +11,25 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import lxml.etree
import mock import mock
import re
import requests_mock import requests_mock
import dracclient.client import dracclient.client
from dracclient import constants
from dracclient import exceptions
from dracclient.resources import idrac_card from dracclient.resources import idrac_card
from dracclient.resources import job
from dracclient.resources import uris from dracclient.resources import uris
from dracclient.tests import base from dracclient.tests import base
from dracclient.tests import utils as test_utils 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): class ClientiDRACCardConfigurationTestCase(base.BaseTest):
def setUp(self): def setUp(self):
@ -28,12 +37,8 @@ class ClientiDRACCardConfigurationTestCase(base.BaseTest):
self.drac_client = dracclient.client.DRACClient( self.drac_client = dracclient.client.DRACClient(
**test_utils.FAKE_ENDPOINT) **test_utils.FAKE_ENDPOINT)
@requests_mock.Mocker() def test_list_idrac_settings_by_instance_id(
@mock.patch.object(dracclient.client.WSManClient, self, mock_requests, mock_wait_until_idrac_is_ready):
'wait_until_idrac_is_ready', spec_set=True,
autospec=True)
def test_list_idrac_settings(self, mock_requests,
mock_wait_until_idrac_is_ready):
expected_enum_attr = idrac_card.iDRACCardEnumerableAttribute( expected_enum_attr = idrac_card.iDRACCardEnumerableAttribute(
name='Type', name='Type',
instance_id='iDRAC.Embedded.1#Info.1#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() idrac_settings = self.drac_client.list_idrac_settings()
self.assertEqual(630, len(idrac_settings)) self.assertEqual(631, len(idrac_settings))
# enumerable attribute # enumerable attribute
self.assertIn('iDRAC.Embedded.1#Info.1#Type', idrac_settings) self.assertIn('iDRAC.Embedded.1#Info.1#Type', idrac_settings)
self.assertEqual(expected_enum_attr, 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.assertIn('iDRAC.Embedded.1#SSH.1#Port', idrac_settings)
self.assertEqual(expected_integer_attr, idrac_settings[ self.assertEqual(expected_integer_attr, idrac_settings[
'iDRAC.Embedded.1#SSH.1#Port']) '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)

View File

@ -147,6 +147,15 @@ iDracCardEnumerations = {
}, },
} }
iDracCardInvocations = {
uris.DCIM_iDRACCardService: {
'SetAttributes': {
'ok': load_wsman_xml(
'idrac_service-invoke-set_attributes-ok')
}
}
}
LifecycleControllerEnumerations = { LifecycleControllerEnumerations = {
uris.DCIM_SystemView: { uris.DCIM_SystemView: {
'ok': load_wsman_xml('system_view-enum-ok') 'ok': load_wsman_xml('system_view-enum-ok')

View File

@ -0,0 +1,19 @@
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_iDRACCardService">
<s:Header>
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsa:Action>http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_iDRACCardService/SetAttributesResponse</wsa:Action>
<wsa:RelatesTo>uuid:305ed6f4-2269-43c5-8c28-00b85e81099d</wsa:RelatesTo>
<wsa:MessageID>uuid:683c34bc-53c4-13c4-8064-17c1f0d9bed4</wsa:MessageID>
</s:Header>
<s:Body>
<n1:SetAttributes_OUTPUT>
<n1:Message>The command was successful</n1:Message>
<n1:MessageID>RAC001</n1:MessageID>
<n1:RebootRequired>No</n1:RebootRequired>
<n1:ReturnValue>0</n1:ReturnValue>
<n1:SetResult>Set PendingValue</n1:SetResult>
</n1:SetAttributes_OUTPUT>
</s:Body>
</s:Envelope>

View File

@ -13,6 +13,27 @@
<s:Body> <s:Body>
<wsen:EnumerateResponse> <wsen:EnumerateResponse>
<wsman:Items> <wsman:Items>
<n1:DCIM_iDRACCardEnumeration>
<n1:AttributeDisplayName>iDRAC Type</n1:AttributeDisplayName>
<n1:AttributeName>Type</n1:AttributeName>
<n1:CurrentValue>13G Monolithic</n1:CurrentValue>
<n1:DefaultValue>12G/13G</n1:DefaultValue>
<n1:Dependency xsi:nil="true"/>
<n1:DisplayOrder>6</n1:DisplayOrder>
<n1:FQDD>iDRAC.Embedded.2</n1:FQDD>
<n1:GroupDisplayName>RAC Information</n1:GroupDisplayName>
<n1:GroupID>Info.1</n1:GroupID>
<n1:InstanceID>iDRAC.Embedded.2#Info.1#Type</n1:InstanceID>
<n1:IsReadOnly>true</n1:IsReadOnly>
<n1:PendingValue xsi:nil="true"/>
<n1:PossibleValues>12G/13G</n1:PossibleValues>
<n1:PossibleValues>12G Monolithic</n1:PossibleValues>
<n1:PossibleValues>12G Modular</n1:PossibleValues>
<n1:PossibleValues>13G Monolithic</n1:PossibleValues>
<n1:PossibleValues>13G Modular</n1:PossibleValues>
<n1:PossibleValues>12G DCS</n1:PossibleValues>
<n1:PossibleValues>13G DCS</n1:PossibleValues>
</n1:DCIM_iDRACCardEnumeration>
<n1:DCIM_iDRACCardEnumeration> <n1:DCIM_iDRACCardEnumeration>
<n1:AttributeDisplayName>iDRAC Type</n1:AttributeDisplayName> <n1:AttributeDisplayName>iDRAC Type</n1:AttributeDisplayName>
<n1:AttributeName>Type</n1:AttributeName> <n1:AttributeName>Type</n1:AttributeName>

View File

@ -133,7 +133,8 @@ def get_all_wsman_resource_attrs(doc, resource_uri, attr_name, nullable=False):
def build_return_dict(doc, resource_uri, def build_return_dict(doc, resource_uri,
is_commit_required_value=None, is_commit_required_value=None,
is_reboot_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 """Builds a dictionary to be returned
Build a dictionary to be returned from WSMAN operations that are not 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 :param commit_required_value: The value to be returned for
commit_required, or None if the value should be determined commit_required, or None if the value should be determined
from the doc. from the doc.
:parm include_commit_required: Indicates if the deprecated commit_required
should be returned in the result.
:returns: a dictionary containing: :returns: a dictionary containing:
- is_commit_required: indicates if a commit is required. - is_commit_required: indicates if a commit is required.
- is_reboot_required: indicates if a reboot 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 # Include commit_required in the response for backwards compatibility
# TBD: Remove this parameter in the future # TBD: Remove this parameter in the future
if commit_required_value is None: if include_commit_required:
commit_required_value = is_reboot_required(doc, resource_uri) 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 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) 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 """List the configuration settings
:param client: an instance of WSManClient. :param client: an instance of WSManClient.
:param namespaces: a list of URI/class pairs to retrieve. :param namespaces: a list of URI/class pairs to retrieve.
:param by_name: controls whether returned dictionary uses :param by_name: controls whether returned dictionary uses
attribute name or instance_id as key. 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 :returns: a dictionary with the settings using name or instance_id as
the key. the key.
:raises: WSManRequestFailure on request failures :raises: WSManRequestFailure on request failures
@ -263,7 +273,8 @@ def list_settings(client, namespaces, by_name=True):
result = {} result = {}
for (namespace, attr_cls) in namespaces: 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)): if not set(result).isdisjoint(set(attribs)):
raise exceptions.DRACOperationFailed( raise exceptions.DRACOperationFailed(
drac_messages=('Colliding attributes %r' % ( drac_messages=('Colliding attributes %r' % (
@ -272,7 +283,8 @@ def list_settings(client, namespaces, by_name=True):
return result 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 = {} result = {}
doc = client.enumerate(resource) doc = client.enumerate(resource)
@ -281,7 +293,14 @@ def _get_config(client, resource, attr_cls, by_name):
for item in items: for item in items:
attribute = attr_cls.parse(item) attribute = attr_cls.parse(item)
if by_name: 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: else:
result[attribute.instance_id] = attribute result[attribute.instance_id] = attribute
@ -295,7 +314,9 @@ def set_settings(settings_type,
resource_uri, resource_uri,
cim_creation_class_name, cim_creation_class_name,
cim_name, cim_name,
target): target,
name_formatter=None,
include_commit_required=False):
"""Generically handles setting various types of settings on the iDRAC """Generically handles setting various types of settings on the iDRAC
This method pulls the current list of settings from the iDRAC then compares 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_creation_class_name: creation class name of the CIM object
:param cim_name: name of the CIM object :param cim_name: name of the CIM object
:param target: target device :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: :returns: a dictionary containing:
- The commit_required key with a boolean value indicating - The commit_required key with a boolean value indicating
whether a config job must be created for the values to be 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 :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) unknown_keys = set(new_settings) - set(current_settings)
if unknown_keys: if unknown_keys:
@ -383,7 +410,10 @@ def set_settings(settings_type,
if not attrib_names: if not attrib_names:
return build_return_dict( 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, is_reboot_required_value=constants.RebootRequired.false,
commit_required_value=False) commit_required_value=False)
@ -398,4 +428,5 @@ def set_settings(settings_type,
doc = client.invoke(resource_uri, 'SetAttributes', doc = client.invoke(resource_uri, 'SetAttributes',
selectors, properties) selectors, properties)
return build_return_dict(doc, resource_uri) return build_return_dict(doc, resource_uri,
include_commit_required=include_commit_required)