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:
parent
ee0ca6f1f6
commit
b327b0811f
@ -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()
|
||||
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):
|
||||
"""List the Lifecycle Controller configuration settings
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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>
|
@ -13,6 +13,27 @@
|
||||
<s:Body>
|
||||
<wsen:EnumerateResponse>
|
||||
<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:AttributeDisplayName>iDRAC Type</n1:AttributeDisplayName>
|
||||
<n1:AttributeName>Type</n1:AttributeName>
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user