Take Lifecycle Controller out of recovery mode

This patch is to check if a node is in recovery mode and take it
out of recovery mode by setting LifecycleControllerState attribute
value to 'Enabled'.

Modified list_lifecycle_settings() method to use
utils.list_settings() for retrieving lifecycle settings.

Change-Id: I4287f317b2413b70cd00fd4cf8aa69bff6ae5e2f
(cherry picked from commit ceef78a938)
This commit is contained in:
mpardhi23 2019-05-29 03:03:05 -04:00 committed by Christopher Dearborn
parent f49840cfe0
commit 48f6133383
16 changed files with 642 additions and 75 deletions

View File

@ -388,9 +388,12 @@ class DRACClient(object):
cim_name='DCIM:iDRACCardService', cim_name='DCIM:iDRACCardService',
target=idrac_fqdd) target=idrac_fqdd)
def list_lifecycle_settings(self): def list_lifecycle_settings(self, by_name=False):
"""List the Lifecycle Controller configuration settings """List the Lifecycle Controller configuration settings
:param by_name: Controls whether returned dictionary uses Lifecycle
attribute name as key. If set to False, instance_id
will be used.
:returns: a dictionary with the Lifecycle Controller settings using its :returns: a dictionary with the Lifecycle Controller settings using its
InstanceID as the key. The attributes are either InstanceID as the key. The attributes are either
LCEnumerableAttribute or LCStringAttribute objects. LCEnumerableAttribute or LCStringAttribute objects.
@ -399,7 +402,49 @@ class DRACClient(object):
:raises: DRACOperationFailed on error reported back by the DRAC :raises: DRACOperationFailed on error reported back by the DRAC
interface interface
""" """
return self._lifecycle_cfg.list_lifecycle_settings() return self._lifecycle_cfg.list_lifecycle_settings(by_name)
def is_lifecycle_in_recovery(self):
"""Checks if Lifecycle Controller in recovery mode or not
This method checks the LCStatus value to determine if lifecycle
controller is in recovery mode by invoking GetRemoteServicesAPIStatus
from iDRAC.
:returns: a boolean indicating if lifecycle controller is in recovery
:raises: WSManRequestFailure on request failures
:raises: WSManInvalidResponse when receiving invalid response
:raises: DRACOperationFailed on error reported back by the DRAC
interface
"""
return self._lifecycle_cfg.is_lifecycle_in_recovery()
def set_lifecycle_settings(self, settings):
"""Sets lifecycle controller configuration
It sets the pending_value parameter for each of the attributes
passed in. For the values to be applied, a config job must
be created.
:param settings: a dictionary containing the proposed values, with
each key being the name of attribute and the value
being the proposed value.
:returns: a dictionary containing:
- The 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 Lifecycle attribute
"""
return self._lifecycle_cfg.set_lifecycle_settings(settings)
def list_system_settings(self): def list_system_settings(self):
"""List the System configuration settings """List the System configuration settings
@ -469,7 +514,9 @@ class DRACClient(object):
cim_system_name='DCIM:ComputerSystem', cim_system_name='DCIM:ComputerSystem',
reboot=False, reboot=False,
start_time='TIME_NOW', start_time='TIME_NOW',
realtime=False): realtime=False,
wait_for_idrac=True,
method_name='CreateTargetedConfigJob'):
"""Creates a configuration job. """Creates a configuration job.
In CIM (Common Information Model), weak association is used to name an In CIM (Common Information Model), weak association is used to name an
@ -495,6 +542,10 @@ class DRACClient(object):
schedule_job_execution is called schedule_job_execution is called
:param realtime: Indicates if reatime mode should be used. :param realtime: Indicates if reatime mode should be used.
Valid values are True and False. Valid values are True and False.
:param wait_for_idrac: indicates whether or not to wait for the
iDRAC to be ready to accept commands before
issuing the command.
:param method_name: method of CIM object to invoke
:returns: id of the created job :returns: id of the created job
:raises: WSManRequestFailure on request failures :raises: WSManRequestFailure on request failures
:raises: WSManInvalidResponse when receiving invalid response :raises: WSManInvalidResponse when receiving invalid response
@ -512,7 +563,9 @@ class DRACClient(object):
cim_system_name=cim_system_name, cim_system_name=cim_system_name,
reboot=reboot, reboot=reboot,
start_time=start_time, start_time=start_time,
realtime=realtime) realtime=realtime,
wait_for_idrac=wait_for_idrac,
method_name=method_name)
def create_nic_config_job( def create_nic_config_job(
self, self,
@ -651,6 +704,37 @@ class DRACClient(object):
cim_creation_class_name='DCIM_BIOSService', cim_creation_class_name='DCIM_BIOSService',
cim_name='DCIM:BIOSService', target=self.BIOS_DEVICE_FQDD) cim_name='DCIM:BIOSService', target=self.BIOS_DEVICE_FQDD)
def commit_pending_lifecycle_changes(
self,
reboot=False,
start_time='TIME_NOW'):
"""Applies all pending changes on Lifecycle by creating a config job
:param reboot: indicates whether a RebootJob should also be
created or not
:param start_time: start time for job execution in format
yyyymmddhhmmss, the string 'TIME_NOW' which
means execute immediately or None which means
the job will not execute until
schedule_job_execution is called
:returns: id of the created job
:raises: WSManRequestFailure on request failures
:raises: WSManInvalidResponse when receiving invalid response
:raises: DRACOperationFailed on error reported back by the DRAC
interface, including start_time being in the past or
badly formatted start_time
:raises: DRACUnexpectedReturnValue on return value mismatch
"""
return self._job_mgmt.create_config_job(
resource_uri=uris.DCIM_LCService,
cim_creation_class_name='DCIM_LCService',
cim_name='DCIM:LCService',
target='',
reboot=reboot,
start_time=start_time,
wait_for_idrac=False,
method_name='CreateConfigJob')
def get_lifecycle_controller_version(self): def get_lifecycle_controller_version(self):
"""Returns the Lifecycle controller version """Returns the Lifecycle controller version

View File

@ -37,6 +37,9 @@ PRIMARY_STATUS = {
# binary unit constants # binary unit constants
UNITS_KI = 2 ** 10 UNITS_KI = 2 ** 10
# Lifecycle Controller status constant
LC_IN_RECOVERY = '4'
# Reboot required indicator # Reboot required indicator
# Note: When the iDRAC returns optional for this value, this indicates that # Note: When the iDRAC returns optional for this value, this indicates that

View File

@ -118,7 +118,9 @@ class JobManagement(object):
cim_system_name='DCIM:ComputerSystem', cim_system_name='DCIM:ComputerSystem',
reboot=False, reboot=False,
start_time='TIME_NOW', start_time='TIME_NOW',
realtime=False): realtime=False,
wait_for_idrac=True,
method_name='CreateTargetedConfigJob'):
"""Creates a config job """Creates a config job
In CIM (Common Information Model), weak association is used to name an In CIM (Common Information Model), weak association is used to name an
@ -145,6 +147,10 @@ class JobManagement(object):
job id. job id.
:param realtime: Indicates if reatime mode should be used. :param realtime: Indicates if reatime mode should be used.
Valid values are True and False. Default value is False. Valid values are True and False. Default value is False.
:param wait_for_idrac: indicates whether or not to wait for the
iDRAC to be ready to accept commands before
issuing the command.
:param method_name: method of CIM object to invoke
:returns: id of the created job :returns: id of the created job
:raises: WSManRequestFailure on request failures :raises: WSManRequestFailure on request failures
:raises: WSManInvalidResponse when receiving invalid response :raises: WSManInvalidResponse when receiving invalid response
@ -169,10 +175,10 @@ class JobManagement(object):
if start_time is not None: if start_time is not None:
properties['ScheduledStartTime'] = start_time properties['ScheduledStartTime'] = start_time
doc = self.client.invoke(resource_uri, 'CreateTargetedConfigJob', doc = self.client.invoke(resource_uri, method_name,
selectors, properties, selectors, properties,
expected_return_value=utils.RET_CREATED) expected_return_value=utils.RET_CREATED,
wait_for_idrac=wait_for_idrac)
return self._get_job_id(doc) return self._get_job_id(doc)
def create_reboot_job( def create_reboot_job(

View File

@ -11,9 +11,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from dracclient import constants
from dracclient.resources import uris from dracclient.resources import uris
from dracclient import utils from dracclient import utils
from dracclient import wsman
class LifecycleControllerManagement(object): class LifecycleControllerManagement(object):
@ -42,47 +42,6 @@ class LifecycleControllerManagement(object):
return tuple(map(int, (lc_version_str.split('.')))) return tuple(map(int, (lc_version_str.split('.'))))
class LCConfiguration(object):
def __init__(self, client):
"""Creates LifecycleControllerManagement object
:param client: an instance of WSManClient
"""
self.client = client
def list_lifecycle_settings(self):
"""List the LC configuration settings
:returns: a dictionary with the LC settings using InstanceID as the
key. The attributes are either LCEnumerableAttribute,
LCStringAttribute or LCIntegerAttribute objects.
:raises: WSManRequestFailure on request failures
:raises: WSManInvalidResponse when receiving invalid response
:raises: DRACOperationFailed on error reported back by the DRAC
interface
"""
result = {}
namespaces = [(uris.DCIM_LCEnumeration, LCEnumerableAttribute),
(uris.DCIM_LCString, LCStringAttribute)]
for (namespace, attr_cls) in namespaces:
attribs = self._get_config(namespace, attr_cls)
result.update(attribs)
return result
def _get_config(self, resource, attr_cls):
result = {}
doc = self.client.enumerate(resource)
items = doc.find('.//{%s}Items' % wsman.NS_WSMAN)
for item in items:
attribute = attr_cls.parse(item)
result[attribute.instance_id] = attribute
return result
class LCAttribute(object): class LCAttribute(object):
"""Generic LC attribute class""" """Generic LC attribute class"""
@ -161,6 +120,17 @@ class LCEnumerableAttribute(LCAttribute):
lifecycle_attr.current_value, lifecycle_attr.pending_value, lifecycle_attr.current_value, lifecycle_attr.pending_value,
lifecycle_attr.read_only, possible_values) lifecycle_attr.read_only, possible_values)
def validate(self, new_value):
"""Validates new value"""
if str(new_value) not in self.possible_values:
msg = ("Attribute '%(attr)s' cannot be set to value '%(val)s'."
" It must be in %(possible_values)r.") % {
'attr': self.name,
'val': new_value,
'possible_values': self.possible_values}
return msg
class LCStringAttribute(LCAttribute): class LCStringAttribute(LCAttribute):
"""String LC attribute class""" """String LC attribute class"""
@ -199,3 +169,96 @@ class LCStringAttribute(LCAttribute):
return cls(lifecycle_attr.name, lifecycle_attr.instance_id, return cls(lifecycle_attr.name, lifecycle_attr.instance_id,
lifecycle_attr.current_value, lifecycle_attr.pending_value, lifecycle_attr.current_value, lifecycle_attr.pending_value,
lifecycle_attr.read_only, min_length, max_length) lifecycle_attr.read_only, min_length, max_length)
class LCConfiguration(object):
NAMESPACES = [(uris.DCIM_LCEnumeration, LCEnumerableAttribute),
(uris.DCIM_LCString, LCStringAttribute)]
def __init__(self, client):
"""Creates LifecycleControllerManagement object
:param client: an instance of WSManClient
"""
self.client = client
def list_lifecycle_settings(self, by_name=False):
"""List the LC configuration settings
:param by_name: Controls whether returned dictionary uses Lifecycle
attribute name or instance_id as key.
:returns: a dictionary with the LC settings using InstanceID as the
key. The attributes are either LCEnumerableAttribute,
LCStringAttribute or LCIntegerAttribute 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)
def is_lifecycle_in_recovery(self):
"""Check if Lifecycle Controller in recovery mode or not
This method checks the LCStatus value to determine if lifecycle
controller is in recovery mode by invoking GetRemoteServicesAPIStatus
from iDRAC.
:returns: a boolean indicating if lifecycle controller is in recovery
:raises: WSManRequestFailure on request failures
:raises: WSManInvalidResponse when receiving invalid response
:raises: DRACOperationFailed on error reported back by the DRAC
interface
"""
selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem',
'SystemName': 'DCIM:ComputerSystem',
'CreationClassName': 'DCIM_LCService',
'Name': 'DCIM:LCService'}
doc = self.client.invoke(uris.DCIM_LCService,
'GetRemoteServicesAPIStatus',
selectors,
{},
expected_return_value=utils.RET_SUCCESS,
wait_for_idrac=False)
lc_status = utils.find_xml(doc,
'LCStatus',
uris.DCIM_LCService).text
return lc_status == constants.LC_IN_RECOVERY
def set_lifecycle_settings(self, settings):
"""Sets the Lifecycle Controller configuration
It sets the pending_value parameter for each of the attributes
passed in. For the values to be applied, a config job must
be created.
:param settings: a dictionary containing the proposed values, with
each key being the name of attribute and the value
being the proposed value.
:returns: a dictionary containing:
- The 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
"""
return utils.set_settings('Lifecycle',
self.client,
self.NAMESPACES,
settings,
uris.DCIM_LCService,
"DCIM_LCService",
"DCIM:LCService",
'',
wait_for_idrac=False)

View File

@ -354,7 +354,8 @@ class ClientBIOSConfigurationTestCase(base.BaseTest):
result) result)
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_BIOSService, 'SetAttributes', mock.ANY, uris.DCIM_BIOSService, 'SetAttributes',
expected_selectors, expected_properties) expected_selectors, expected_properties,
wait_for_idrac=True)
def test_set_bios_settings_error(self, mock_requests, def test_set_bios_settings_error(self, mock_requests,
mock_wait_until_idrac_is_ready): mock_wait_until_idrac_is_ready):

View File

@ -214,7 +214,8 @@ class ClientiDRACCardConfigurationTestCase(base.BaseTest):
result) result)
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_iDRACCardService, 'SetAttributes', mock.ANY, uris.DCIM_iDRACCardService, 'SetAttributes',
expected_selectors, expected_properties) expected_selectors, expected_properties,
wait_for_idrac=True)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', @mock.patch.object(dracclient.client.WSManClient, 'invoke',
spec_set=True, autospec=True) spec_set=True, autospec=True)
@ -245,7 +246,8 @@ class ClientiDRACCardConfigurationTestCase(base.BaseTest):
result) result)
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_iDRACCardService, 'SetAttributes', mock.ANY, uris.DCIM_iDRACCardService, 'SetAttributes',
expected_selectors, expected_properties) expected_selectors, expected_properties,
wait_for_idrac=True)
def test_set_idrac_settings_with_too_long_string( def test_set_idrac_settings_with_too_long_string(
self, mock_requests, mock_wait_until_idrac_is_ready): self, mock_requests, mock_wait_until_idrac_is_ready):

View File

@ -226,12 +226,43 @@ class ClientJobManagementTestCase(base.BaseTest):
self.assertEqual(mock_requests.call_count, 2) self.assertEqual(mock_requests.call_count, 2)
@mock.patch.object(dracclient.client.WSManClient, 'invoke',
spec_set=True, autospec=True)
def test_create_config_job_for_lifecycle(self, mock_invoke):
cim_creation_class_name = 'DCIM_LCService'
cim_name = 'DCIM:LCService'
target = ''
expected_selectors = {'CreationClassName': cim_creation_class_name,
'Name': cim_name,
'SystemCreationClassName': 'DCIM_ComputerSystem',
'SystemName': 'DCIM:ComputerSystem'}
expected_properties = {'Target': target,
'ScheduledStartTime': 'TIME_NOW'}
mock_invoke.return_value = lxml.etree.fromstring(
test_utils.JobInvocations[uris.DCIM_LCService][
'CreateConfigJob']['ok'])
job_id = self.drac_client.create_config_job(
uris.DCIM_LCService, cim_creation_class_name, cim_name, target,
start_time='TIME_NOW',
wait_for_idrac=False, method_name='CreateConfigJob')
mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_LCService, 'CreateConfigJob',
expected_selectors, expected_properties,
expected_return_value=utils.RET_CREATED,
wait_for_idrac=False)
self.assertEqual('JID_442507917525', job_id)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', @mock.patch.object(dracclient.client.WSManClient, 'invoke',
spec_set=True, autospec=True) spec_set=True, autospec=True)
def test_create_config_job(self, mock_invoke): def test_create_config_job(self, mock_invoke):
cim_creation_class_name = 'DCIM_BIOSService' cim_creation_class_name = 'DCIM_BIOSService'
cim_name = 'DCIM:BIOSService' cim_name = 'DCIM:BIOSService'
target = 'BIOS.Setup.1-1' target = 'BIOS.Setup.1-1'
wait_for_idrac = True
expected_selectors = {'CreationClassName': cim_creation_class_name, expected_selectors = {'CreationClassName': cim_creation_class_name,
'Name': cim_name, 'Name': cim_name,
'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemCreationClassName': 'DCIM_ComputerSystem',
@ -249,7 +280,8 @@ class ClientJobManagementTestCase(base.BaseTest):
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob', mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob',
expected_selectors, expected_properties, expected_selectors, expected_properties,
expected_return_value=utils.RET_CREATED) expected_return_value=utils.RET_CREATED,
wait_for_idrac=wait_for_idrac)
self.assertEqual('JID_442507917525', job_id) self.assertEqual('JID_442507917525', job_id)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', @mock.patch.object(dracclient.client.WSManClient, 'invoke',
@ -259,6 +291,7 @@ class ClientJobManagementTestCase(base.BaseTest):
cim_name = 'DCIM:BIOSService' cim_name = 'DCIM:BIOSService'
target = 'BIOS.Setup.1-1' target = 'BIOS.Setup.1-1'
start_time = "20140924120105" start_time = "20140924120105"
wait_for_idrac = True
expected_selectors = {'CreationClassName': cim_creation_class_name, expected_selectors = {'CreationClassName': cim_creation_class_name,
'Name': cim_name, 'Name': cim_name,
'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemCreationClassName': 'DCIM_ComputerSystem',
@ -276,7 +309,8 @@ class ClientJobManagementTestCase(base.BaseTest):
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob', mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob',
expected_selectors, expected_properties, expected_selectors, expected_properties,
expected_return_value=utils.RET_CREATED) expected_return_value=utils.RET_CREATED,
wait_for_idrac=wait_for_idrac)
self.assertEqual('JID_442507917525', job_id) self.assertEqual('JID_442507917525', job_id)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', @mock.patch.object(dracclient.client.WSManClient, 'invoke',
@ -286,6 +320,7 @@ class ClientJobManagementTestCase(base.BaseTest):
cim_name = 'DCIM:BIOSService' cim_name = 'DCIM:BIOSService'
target = 'BIOS.Setup.1-1' target = 'BIOS.Setup.1-1'
start_time = None start_time = None
wait_for_idrac = True
expected_selectors = {'CreationClassName': cim_creation_class_name, expected_selectors = {'CreationClassName': cim_creation_class_name,
'Name': cim_name, 'Name': cim_name,
'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemCreationClassName': 'DCIM_ComputerSystem',
@ -302,7 +337,8 @@ class ClientJobManagementTestCase(base.BaseTest):
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob', mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob',
expected_selectors, expected_properties, expected_selectors, expected_properties,
expected_return_value=utils.RET_CREATED) expected_return_value=utils.RET_CREATED,
wait_for_idrac=wait_for_idrac)
self.assertEqual('JID_442507917525', job_id) self.assertEqual('JID_442507917525', job_id)
@requests_mock.Mocker() @requests_mock.Mocker()
@ -323,12 +359,32 @@ class ClientJobManagementTestCase(base.BaseTest):
exceptions.DRACOperationFailed, self.drac_client.create_config_job, exceptions.DRACOperationFailed, self.drac_client.create_config_job,
uris.DCIM_BIOSService, cim_creation_class_name, cim_name, target) uris.DCIM_BIOSService, cim_creation_class_name, cim_name, target)
@requests_mock.Mocker()
@mock.patch.object(dracclient.client.WSManClient,
'wait_until_idrac_is_ready', spec_set=True,
autospec=True)
def test_create_config_job_for_lifecycle_failed(
self, mock_requests,
mock_wait_until_idrac_is_ready):
cim_creation_class_name = 'DCIM_LCService'
cim_name = 'DCIM:LCService'
target = ''
mock_requests.post(
'https://1.2.3.4:443/wsman',
text=test_utils.JobInvocations[uris.DCIM_LCService][
'CreateConfigJob']['error'])
self.assertRaises(
exceptions.DRACOperationFailed, self.drac_client.create_config_job,
uris.DCIM_LCService, cim_creation_class_name, cim_name, target)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True, @mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True,
autospec=True) autospec=True)
def test_create_config_job_with_reboot(self, mock_invoke): def test_create_config_job_with_reboot(self, mock_invoke):
cim_creation_class_name = 'DCIM_BIOSService' cim_creation_class_name = 'DCIM_BIOSService'
cim_name = 'DCIM:BIOSService' cim_name = 'DCIM:BIOSService'
target = 'BIOS.Setup.1-1' target = 'BIOS.Setup.1-1'
wait_for_idrac = True
expected_selectors = {'CreationClassName': cim_creation_class_name, expected_selectors = {'CreationClassName': cim_creation_class_name,
'Name': cim_name, 'Name': cim_name,
'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemCreationClassName': 'DCIM_ComputerSystem',
@ -347,7 +403,8 @@ class ClientJobManagementTestCase(base.BaseTest):
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob', mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob',
expected_selectors, expected_properties, expected_selectors, expected_properties,
expected_return_value=utils.RET_CREATED) expected_return_value=utils.RET_CREATED,
wait_for_idrac=wait_for_idrac)
self.assertEqual('JID_442507917525', job_id) self.assertEqual('JID_442507917525', job_id)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True, @mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True,
@ -356,6 +413,7 @@ class ClientJobManagementTestCase(base.BaseTest):
cim_creation_class_name = 'DCIM_BIOSService' cim_creation_class_name = 'DCIM_BIOSService'
cim_name = 'DCIM:BIOSService' cim_name = 'DCIM:BIOSService'
target = 'BIOS.Setup.1-1' target = 'BIOS.Setup.1-1'
wait_for_idrac = True
expected_selectors = {'CreationClassName': cim_creation_class_name, expected_selectors = {'CreationClassName': cim_creation_class_name,
'Name': cim_name, 'Name': cim_name,
'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemCreationClassName': 'DCIM_ComputerSystem',
@ -374,7 +432,8 @@ class ClientJobManagementTestCase(base.BaseTest):
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob', mock.ANY, uris.DCIM_BIOSService, 'CreateTargetedConfigJob',
expected_selectors, expected_properties, expected_selectors, expected_properties,
expected_return_value=utils.RET_CREATED) expected_return_value=utils.RET_CREATED,
wait_for_idrac=wait_for_idrac)
self.assertEqual('JID_442507917525', job_id) self.assertEqual('JID_442507917525', job_id)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True, @mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True,

View File

@ -11,14 +11,20 @@
# 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
import dracclient.resources.job
from dracclient.resources import lifecycle_controller from dracclient.resources import lifecycle_controller
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
from dracclient import utils
class ClientLifecycleControllerManagementTestCase(base.BaseTest): class ClientLifecycleControllerManagementTestCase(base.BaseTest):
@ -40,6 +46,7 @@ class ClientLifecycleControllerManagementTestCase(base.BaseTest):
self.assertEqual((2, 1, 0), version) self.assertEqual((2, 1, 0), version)
@requests_mock.Mocker()
class ClientLCConfigurationTestCase(base.BaseTest): class ClientLCConfigurationTestCase(base.BaseTest):
def setUp(self): def setUp(self):
@ -47,12 +54,12 @@ class ClientLCConfigurationTestCase(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()
@mock.patch.object(dracclient.client.WSManClient, @mock.patch.object(dracclient.client.WSManClient,
'wait_until_idrac_is_ready', spec_set=True, 'wait_until_idrac_is_ready', spec_set=True,
autospec=True) autospec=True)
def test_list_lifecycle_settings(self, mock_requests, def test_list_lifecycle_settings_by_instance_id(
mock_wait_until_idrac_is_ready): self, mock_requests,
mock_wait_until_idrac_is_ready):
expected_enum_attr = lifecycle_controller.LCEnumerableAttribute( expected_enum_attr = lifecycle_controller.LCEnumerableAttribute(
name='Lifecycle Controller State', name='Lifecycle Controller State',
instance_id='LifecycleController.Embedded.1#LCAttributes.1#LifecycleControllerState', # noqa instance_id='LifecycleController.Embedded.1#LCAttributes.1#LifecycleControllerState', # noqa
@ -74,7 +81,8 @@ class ClientLCConfigurationTestCase(base.BaseTest):
{'text': test_utils.LifecycleControllerEnumerations[ {'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCString]['ok']}]) uris.DCIM_LCString]['ok']}])
lifecycle_settings = self.drac_client.list_lifecycle_settings() lifecycle_settings = self.drac_client.list_lifecycle_settings(
by_name=False)
self.assertEqual(14, len(lifecycle_settings)) self.assertEqual(14, len(lifecycle_settings))
# enumerable attribute # enumerable attribute
@ -89,3 +97,203 @@ class ClientLCConfigurationTestCase(base.BaseTest):
lifecycle_settings) lifecycle_settings)
self.assertEqual(expected_string_attr, self.assertEqual(expected_string_attr,
lifecycle_settings['LifecycleController.Embedded.1#LCAttributes.1#SystemID']) # noqa lifecycle_settings['LifecycleController.Embedded.1#LCAttributes.1#SystemID']) # noqa
@mock.patch.object(dracclient.client.WSManClient,
'wait_until_idrac_is_ready', spec_set=True,
autospec=True)
def test_list_lifecycle_settings_by_name(
self, mock_requests,
mock_wait_until_idrac_is_ready):
expected_enum_attr = lifecycle_controller.LCEnumerableAttribute(
name='Lifecycle Controller State',
instance_id='LifecycleController.Embedded.1#LCAttributes.1#LifecycleControllerState', # noqa
read_only=False,
current_value='Enabled',
pending_value=None,
possible_values=['Disabled', 'Enabled', 'Recovery'])
expected_string_attr = lifecycle_controller.LCStringAttribute(
name='SYSID',
instance_id='LifecycleController.Embedded.1#LCAttributes.1#SystemID', # noqa
read_only=True,
current_value='639',
pending_value=None,
min_length=0,
max_length=3)
mock_requests.post('https://1.2.3.4:443/wsman', [
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCEnumeration]['ok']},
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCString]['ok']}])
lifecycle_settings = self.drac_client.list_lifecycle_settings(
by_name=True)
self.assertEqual(14, len(lifecycle_settings))
# enumerable attribute
self.assertIn(
'Lifecycle Controller State',
lifecycle_settings)
self.assertEqual(expected_enum_attr, lifecycle_settings[
'Lifecycle Controller State'])
# string attribute
self.assertIn(
'SYSID',
lifecycle_settings)
self.assertEqual(expected_string_attr,
lifecycle_settings['SYSID'])
@mock.patch.object(dracclient.client.WSManClient, 'invoke',
spec_set=True, autospec=True)
def test_is_lifecycle_in_recovery(self, mock_requests,
mock_invoke):
expected_selectors = {'CreationClassName': 'DCIM_LCService',
'SystemName': 'DCIM:ComputerSystem',
'Name': 'DCIM:LCService',
'SystemCreationClassName': 'DCIM_ComputerSystem'}
mock_invoke.return_value = lxml.etree.fromstring(
test_utils.LifecycleControllerInvocations[uris.DCIM_LCService][
'GetRemoteServicesAPIStatus']['is_recovery'])
result = self.drac_client.is_lifecycle_in_recovery()
mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_LCService, 'GetRemoteServicesAPIStatus',
expected_selectors, {},
expected_return_value=utils.RET_SUCCESS,
wait_for_idrac=False)
self.assertEqual(True, result)
@mock.patch.object(dracclient.client.WSManClient,
'invoke', spec_set=True,
autospec=True)
def test_set_lifecycle_settings(self, mock_requests,
mock_invoke):
mock_requests.post('https://1.2.3.4:443/wsman', [
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCEnumeration]['ok']},
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCString]['ok']}])
mock_invoke.return_value = lxml.etree.fromstring(
test_utils.LifecycleControllerInvocations[uris.DCIM_LCService][
'SetAttributes']['ok'])
result = self.drac_client.set_lifecycle_settings(
{'Collect System Inventory on Restart': 'Disabled'})
self.assertEqual({'is_commit_required': True,
'is_reboot_required': constants.RebootRequired.false
},
result)
@mock.patch.object(dracclient.client.WSManClient,
'wait_until_idrac_is_ready', spec_set=True,
autospec=True)
def test_set_lifecycle_settings_with_unknown_attr(
self, mock_requests, mock_wait_until_idrac_is_ready):
mock_requests.post('https://1.2.3.4:443/wsman', [
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCEnumeration]['ok']},
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCString]['ok']},
{'text': test_utils.LifecycleControllerInvocations[
uris.DCIM_LCService]['SetAttributes']['error']}])
self.assertRaises(exceptions.InvalidParameterValue,
self.drac_client.set_lifecycle_settings,
{'foo': 'bar'})
@mock.patch.object(dracclient.client.WSManClient,
'wait_until_idrac_is_ready', spec_set=True,
autospec=True)
def test_set_lifecycle_settings_with_unchanged_attr(
self, mock_requests, mock_wait_until_idrac_is_ready):
mock_requests.post('https://1.2.3.4:443/wsman', [
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCEnumeration]['ok']},
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCString]['ok']}])
result = self.drac_client.set_lifecycle_settings(
{'Lifecycle Controller State': 'Enabled'})
self.assertEqual({'is_commit_required': False,
'is_reboot_required':
constants.RebootRequired.false},
result)
@mock.patch.object(dracclient.client.WSManClient,
'wait_until_idrac_is_ready', spec_set=True,
autospec=True)
def test_set_lifecycle_settings_with_readonly_attr(
self, mock_requests, mock_wait_until_idrac_is_ready):
expected_message = ("Cannot set read-only Lifecycle attributes: "
"['Licensed'].")
mock_requests.post('https://1.2.3.4:443/wsman', [
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCEnumeration]['ok']},
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCString]['ok']}])
self.assertRaisesRegexp(
exceptions.DRACOperationFailed, re.escape(expected_message),
self.drac_client.set_lifecycle_settings, {'Licensed': 'yes'})
@mock.patch.object(dracclient.client.WSManClient,
'wait_until_idrac_is_ready', spec_set=True,
autospec=True)
def test_set_lifecycle_settings_with_incorrect_enum_value(
self, mock_requests, mock_wait_until_idrac_is_ready):
expected_message = ("Attribute 'Lifecycle Controller State' cannot "
"be set to value 'foo'. It must be in "
"['Disabled', 'Enabled', 'Recovery'].")
mock_requests.post('https://1.2.3.4:443/wsman', [
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCEnumeration]['ok']},
{'text': test_utils.LifecycleControllerEnumerations[
uris.DCIM_LCString]['ok']}])
self.assertRaisesRegexp(
exceptions.DRACOperationFailed, re.escape(expected_message),
self.drac_client.set_lifecycle_settings,
{'Lifecycle Controller State': 'foo'})
class ClientLCChangesTestCase(base.BaseTest):
def setUp(self):
super(ClientLCChangesTestCase, self).setUp()
self.drac_client = dracclient.client.DRACClient(
**test_utils.FAKE_ENDPOINT)
@mock.patch.object(dracclient.resources.job.JobManagement,
'create_config_job', spec_set=True, autospec=True)
def test_commit_pending_lifecycle_changes(self, mock_create_config_job):
self.drac_client.commit_pending_lifecycle_changes()
mock_create_config_job.assert_called_once_with(
mock.ANY, resource_uri=uris.DCIM_LCService,
cim_creation_class_name='DCIM_LCService',
cim_name='DCIM:LCService', target='',
reboot=False, start_time='TIME_NOW',
wait_for_idrac=False,
method_name='CreateConfigJob')
@mock.patch.object(dracclient.resources.job.JobManagement,
'create_config_job', spec_set=True, autospec=True)
def test_commit_pending_lifecycle_changes_with_time(
self, mock_create_config_job):
timestamp = '20140924140201'
self.drac_client.commit_pending_lifecycle_changes(
start_time=timestamp)
mock_create_config_job.assert_called_once_with(
mock.ANY, resource_uri=uris.DCIM_LCService,
cim_creation_class_name='DCIM_LCService',
cim_name='DCIM:LCService', target='',
reboot=False, start_time=timestamp,
wait_for_idrac=False,
method_name='CreateConfigJob')

View File

@ -214,7 +214,8 @@ class ClientNICTestCase(base.BaseTest):
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_NICService, 'SetAttributes', mock.ANY, uris.DCIM_NICService, 'SetAttributes',
expected_selectors, expected_properties) expected_selectors, expected_properties,
wait_for_idrac=True)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True, @mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True,
autospec=True) autospec=True)
@ -250,7 +251,8 @@ class ClientNICTestCase(base.BaseTest):
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_NICService, 'SetAttributes', mock.ANY, uris.DCIM_NICService, 'SetAttributes',
expected_selectors, expected_properties) expected_selectors, expected_properties,
wait_for_idrac=True)
@mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True, @mock.patch.object(dracclient.client.WSManClient, 'invoke', spec_set=True,
autospec=True) autospec=True)
@ -286,7 +288,8 @@ class ClientNICTestCase(base.BaseTest):
mock_invoke.assert_called_once_with( mock_invoke.assert_called_once_with(
mock.ANY, uris.DCIM_NICService, 'SetAttributes', mock.ANY, uris.DCIM_NICService, 'SetAttributes',
expected_selectors, expected_properties) expected_selectors, expected_properties,
wait_for_idrac=True)
def test_set_nic_settings_error(self, mock_requests, def test_set_nic_settings_error(self, mock_requests,
mock_wait_until_idrac_is_ready): mock_wait_until_idrac_is_ready):

View File

@ -133,6 +133,14 @@ JobInvocations = {
'error': load_wsman_xml( 'error': load_wsman_xml(
'bios_service-invoke-delete_pending_configuration-error'), 'bios_service-invoke-delete_pending_configuration-error'),
}, },
},
uris.DCIM_LCService: {
'CreateConfigJob': {
'ok': load_wsman_xml(
'lc_service-invoke-create_config_job-ok'),
'error': load_wsman_xml(
'lc_service-invoke-create_config_job-error'),
},
} }
} }
@ -192,7 +200,15 @@ LifecycleControllerInvocations = {
'GetRemoteServicesAPIStatus': { 'GetRemoteServicesAPIStatus': {
'is_ready': load_wsman_xml('lc_getremoteservicesapistatus_ready'), 'is_ready': load_wsman_xml('lc_getremoteservicesapistatus_ready'),
'is_not_ready': load_wsman_xml( 'is_not_ready': load_wsman_xml(
'lc_getremoteservicesapistatus_not_ready') 'lc_getremoteservicesapistatus_not_ready'),
'is_recovery': load_wsman_xml(
'lc_getremoteservicesapistatus_recovery'),
},
'SetAttributes': {
'ok': load_wsman_xml(
'lc_service-invoke-set_attributes-ok'),
'error': load_wsman_xml(
'lc_service-invoke-set_attributes-error'),
} }
} }
} }

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_LCService">
<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_LCService/GetRemoteServicesAPIStatusResponse</wsa:Action>
<wsa:RelatesTo>uuid:18745811-2782-4d30-a288-8f001a895215</wsa:RelatesTo>
<wsa:MessageID>uuid:9ec203ba-4fc0-1fc0-8094-98d61742a844</wsa:MessageID>
</s:Header>
<s:Body>
<n1:GetRemoteServicesAPIStatus_OUTPUT>
<n1:LCStatus>4</n1:LCStatus>
<n1:Message>Lifecycle Controller Remote Services is not ready.</n1:Message>
<n1:MessageID>LC060</n1:MessageID>
<n1:RTStatus>0</n1:RTStatus>
<n1:ReturnValue>0</n1:ReturnValue>
<n1:ServerStatus>7</n1:ServerStatus>
<n1:Status>1</n1:Status>
</n1:GetRemoteServicesAPIStatus_OUTPUT>
</s:Body>
</s:Envelope>

View File

@ -0,0 +1,17 @@
<s:Envelope xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_LCService"
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<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_LCService/CreateConfigJobResponse</wsa:Action>
<wsa:RelatesTo>uuid:80cf5e1b-b109-4ef5-87c8-5b03ce6ba117</wsa:RelatesTo>
<wsa:MessageID>uuid:e57fa514-2189-1189-8ec1-a36fc6fe83b0</wsa:MessageID>
</s:Header>
<s:Body>
<n1:CreateConfigJob_OUTPUT>
<n1:Message>Configuration job already created, cannot create another config job on specified target until existing job is completed or is cancelled</n1:Message>
<n1:MessageID>LC007</n1:MessageID>
<n1:ReturnValue>2</n1:ReturnValue>
</n1:CreateConfigJob_OUTPUT>
</s:Body>
</s:Envelope>

View File

@ -0,0 +1,28 @@
<s:Envelope xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_LCService"
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
<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_LCService/CreateConfigJobResponse</wsa:Action>
<wsa:RelatesTo>uuid:fc2fdae5-6ac2-4338-9b2e-e69b813af829</wsa:RelatesTo>
<wsa:MessageID>uuid:d7d89957-2189-1189-8ec0-a36fc6fe83b0</wsa:MessageID>
</s:Header>
<s:Body>
<n1:CreateConfigJob_OUTPUT>
<n1:Job>
<wsa:EndpointReference>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
<wsa:ReferenceParameters>
<wsman:ResourceURI>http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_LifecycleJob</wsman:ResourceURI>
<wsman:SelectorSet>
<wsman:Selector Name="InstanceID">JID_442507917525</wsman:Selector>
<wsman:Selector Name="__cimnamespace">root/dcim</wsman:Selector>
</wsman:SelectorSet>
</wsa:ReferenceParameters>
</wsa:EndpointReference>
</n1:Job>
<n1:ReturnValue>4096</n1:ReturnValue>
</n1:CreateConfigJob_OUTPUT>
</s:Body>
</s:Envelope>

View File

@ -0,0 +1,21 @@
<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_LCService">
<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_LCService/SetAttributesResponse
</wsa:Action>
<wsa:RelatesTo>uuid:bf8adefe-6fc0-456d-b97c-fd8d4aca2d6c
</wsa:RelatesTo>
<wsa:MessageID>uuid:84abf7b9-7176-1176-a11c-a53ffbd9bed4
</wsa:MessageID>
</s:Header>
<s:Body>
<n1:SetAttributes_OUTPUT>
<n1:Message>Invalid AttributeName.</n1:Message>
<n1:MessageID>LC057</n1:MessageID>
<n1:ReturnValue>2</n1:ReturnValue>
</n1:SetAttributes_OUTPUT>
</s:Body>
</s:Envelope>

View File

@ -0,0 +1,24 @@
<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_LCService">
<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_LCService/SetAttributesResponse
</wsa:Action>
<wsa:RelatesTo>uuid:bf8adefe-6fc0-456d-b97c-fd8d4aca2d6c
</wsa:RelatesTo>
<wsa:MessageID>uuid:84abf7b9-7176-1176-a11c-a53ffbd9bed4
</wsa:MessageID>
</s:Header>
<s:Body>
<n1:SetAttributes_OUTPUT>
<n1:MessageID>LC001</n1:MessageID>
<n1:Message>The command was successful</n1:Message>
<n1:ReturnValue>0</n1:ReturnValue>
<n1:RebootRequired>No</n1:RebootRequired>
<n1:SetResult>Set PendingValue</n1:SetResult>
</n1:SetAttributes_OUTPUT>
</s:Body>
</s:Envelope>

View File

@ -251,7 +251,7 @@ def validate_integer_value(value, attr_name, error_msgs):
def list_settings(client, namespaces, by_name=True, fqdd_filter=None, def list_settings(client, namespaces, by_name=True, fqdd_filter=None,
name_formatter=None): name_formatter=None, wait_for_idrac=True):
"""List the configuration settings """List the configuration settings
:param client: an instance of WSManClient. :param client: an instance of WSManClient.
@ -263,6 +263,9 @@ def list_settings(client, namespaces, by_name=True, fqdd_filter=None,
:param name_formatter: a method used to format the keys in the :param name_formatter: a method used to format the keys in the
returned dictionary. By default, returned dictionary. By default,
attribute.name will be used. attribute.name will be used.
:param wait_for_idrac: indicates whether or not to wait for the
iDRAC to be ready to accept commands before
issuing the command.
: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
@ -274,7 +277,7 @@ def list_settings(client, namespaces, by_name=True, fqdd_filter=None,
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) fqdd_filter, name_formatter, wait_for_idrac)
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' % (
@ -284,10 +287,10 @@ def list_settings(client, namespaces, by_name=True, fqdd_filter=None,
def _get_config(client, resource, attr_cls, by_name, fqdd_filter, def _get_config(client, resource, attr_cls, by_name, fqdd_filter,
name_formatter): name_formatter, wait_for_idrac):
result = {} result = {}
doc = client.enumerate(resource) doc = client.enumerate(resource, wait_for_idrac=wait_for_idrac)
items = doc.find('.//{%s}Items' % wsman.NS_WSMAN) items = doc.find('.//{%s}Items' % wsman.NS_WSMAN)
for item in items: for item in items:
@ -316,7 +319,8 @@ def set_settings(settings_type,
cim_name, cim_name,
target, target,
name_formatter=None, name_formatter=None,
include_commit_required=False): include_commit_required=False,
wait_for_idrac=True):
"""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
@ -339,6 +343,9 @@ def set_settings(settings_type,
attribute.name will be used. attribute.name will be used.
:parm include_commit_required: Indicates if the deprecated commit_required :parm include_commit_required: Indicates if the deprecated commit_required
should be returned in the result. should be returned in the result.
:param wait_for_idrac: indicates whether or not to wait for the
iDRAC to be ready to accept commands before issuing
the command
: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
@ -361,12 +368,15 @@ def set_settings(settings_type,
""" """
current_settings = list_settings(client, namespaces, by_name=True, current_settings = list_settings(client, namespaces, by_name=True,
name_formatter=name_formatter) name_formatter=name_formatter,
wait_for_idrac=wait_for_idrac)
unknown_keys = set(new_settings) - set(current_settings) unknown_keys = set(new_settings) - set(current_settings)
if unknown_keys: if unknown_keys:
msg = ('Unknown %(settings_type)s attributes found: %(unknown_keys)r' % msg = ('Unknown %(settings_type)s attributes found: '
{'settings_type': settings_type, 'unknown_keys': unknown_keys}) '%(unknown_keys)r' %
{'settings_type': settings_type,
'unknown_keys': unknown_keys})
raise exceptions.InvalidParameterValue(reason=msg) raise exceptions.InvalidParameterValue(reason=msg)
read_only_keys = [] read_only_keys = []
@ -421,12 +431,15 @@ def set_settings(settings_type,
'Name': cim_name, 'Name': cim_name,
'SystemCreationClassName': 'DCIM_ComputerSystem', 'SystemCreationClassName': 'DCIM_ComputerSystem',
'SystemName': 'DCIM:ComputerSystem'} 'SystemName': 'DCIM:ComputerSystem'}
properties = {'Target': target, properties = {'Target': target,
'AttributeName': attrib_names, 'AttributeName': attrib_names,
'AttributeValue': [new_settings[attr] for attr 'AttributeValue': [new_settings[attr] for attr
in attrib_names]} in attrib_names]}
doc = client.invoke(resource_uri, 'SetAttributes', doc = client.invoke(resource_uri, 'SetAttributes',
selectors, properties) selectors, properties,
wait_for_idrac=wait_for_idrac)
return build_return_dict(doc, resource_uri, return build_return_dict(doc, resource_uri,
include_commit_required=include_commit_required) include_commit_required=include_commit_required)