Refactor iDRAC is ready functionality
Web Services Management (WS-Management and WS-Man) requests/commands can
fail or return invalid results when issued to an Integrated Dell Remote
Access Controller (iDRAC) whose Lifecycle Controller remote service is
not "ready". Specifically, that applies to the WS-Man Enumerate and
Invoke operations.
A Dell technical white paper [0], "Lifecycle Controller Integration --
Best Practices Guide", states that for Lifecycle Controller firmware
1.5.0 and later "The Lifecycle Controller remote service must be in a
'ready' state before running any other WSMAN commands." That applies to
almost all of the workflows and use cases documented by that paper and
supported by this project, openstack/python-dracclient. That document
describes how to determine the readiness of the Lifecycle Controller
remote service. A project commit [1] implements that.
This refactors that patch in preparation for changing the internal
implementation of the project's APIs so that they follow that best
practice. The implementation of is_idrac_ready() and
wait_until_idrac_is_ready() have been relocated further down the call
stack, to the iDRAC specialization of the WS-Man client defined by class
dracclient.client.WSManClient. Those methods continue to be available
through the API provided by class dracclient.client.Client.
No changes have been made to this project's APIs nor to any functional
behavior.
[0]
http://en.community.dell.com/techcenter/extras/m/white_papers/20442332
[1]
39253bb272
Change-Id: I87996bbca129995f6c84848ebdb0c33cfedeea53
Partial-Bug: #1697558
Related-Bug: #1691808
This commit is contained in:
parent
00c9fe4fde
commit
10df06f6c3
|
@ -29,6 +29,8 @@ from dracclient.resources import uris
|
|||
from dracclient import utils
|
||||
from dracclient import wsman
|
||||
|
||||
IDRAC_IS_READY = "LC061"
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -513,7 +515,7 @@ class DRACClient(object):
|
|||
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||
"""
|
||||
|
||||
return self._lifecycle_cfg.is_idrac_ready()
|
||||
return self.client.is_idrac_ready()
|
||||
|
||||
def wait_until_idrac_is_ready(self, retries=24, retry_delay=10):
|
||||
"""Waits until the iDRAC is in a ready state
|
||||
|
@ -528,23 +530,7 @@ class DRACClient(object):
|
|||
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||
"""
|
||||
|
||||
# Try every 10 seconds over 4 minutes for the iDRAC to become ready
|
||||
while retries > 0:
|
||||
LOG.debug("Checking to see if the iDRAC is ready")
|
||||
|
||||
if self.is_idrac_ready():
|
||||
LOG.debug("The iDRAC is ready")
|
||||
return
|
||||
|
||||
LOG.debug("The iDRAC is not ready")
|
||||
retries -= 1
|
||||
if retries > 0:
|
||||
time.sleep(retry_delay)
|
||||
|
||||
if retries == 0:
|
||||
err_msg = "Timed out waiting for the iDRAC to become ready"
|
||||
LOG.error(err_msg)
|
||||
raise exceptions.DRACOperationFailed(drac_messages=err_msg)
|
||||
return self.client.wait_until_idrac_is_ready(retries, retry_delay)
|
||||
|
||||
|
||||
class WSManClient(wsman.Client):
|
||||
|
@ -591,3 +577,65 @@ class WSManClient(wsman.Client):
|
|||
actual_return_value=return_value)
|
||||
|
||||
return resp
|
||||
|
||||
def is_idrac_ready(self):
|
||||
"""Indicates if the iDRAC is ready to accept commands
|
||||
|
||||
Returns a boolean indicating if the iDRAC is ready to accept
|
||||
commands.
|
||||
|
||||
:returns: Boolean indicating iDRAC readiness
|
||||
:raises: WSManRequestFailure on request failures
|
||||
:raises: WSManInvalidResponse when receiving invalid response
|
||||
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||
interface
|
||||
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||
"""
|
||||
|
||||
selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem',
|
||||
'SystemName': 'DCIM:ComputerSystem',
|
||||
'CreationClassName': 'DCIM_LCService',
|
||||
'Name': 'DCIM:LCService'}
|
||||
|
||||
result = self.invoke(uris.DCIM_LCService,
|
||||
'GetRemoteServicesAPIStatus',
|
||||
selectors,
|
||||
{},
|
||||
expected_return_value=utils.RET_SUCCESS)
|
||||
|
||||
message_id = utils.find_xml(result,
|
||||
'MessageID',
|
||||
uris.DCIM_LCService).text
|
||||
|
||||
return message_id == IDRAC_IS_READY
|
||||
|
||||
def wait_until_idrac_is_ready(self, retries=24, retry_delay=10):
|
||||
"""Waits until the iDRAC is in a ready state
|
||||
|
||||
:param retries: The number of times to check if the iDRAC is ready
|
||||
:param retry_delay: The number of seconds to wait between retries
|
||||
|
||||
:raises: WSManRequestFailure on request failures
|
||||
:raises: WSManInvalidResponse when receiving invalid response
|
||||
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||
interface or timeout
|
||||
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||
"""
|
||||
|
||||
# Try every 10 seconds over 4 minutes for the iDRAC to become ready
|
||||
while retries > 0:
|
||||
LOG.debug("Checking to see if the iDRAC is ready")
|
||||
|
||||
if self.is_idrac_ready():
|
||||
LOG.debug("The iDRAC is ready")
|
||||
return
|
||||
|
||||
LOG.debug("The iDRAC is not ready")
|
||||
retries -= 1
|
||||
if retries > 0:
|
||||
time.sleep(retry_delay)
|
||||
|
||||
if retries == 0:
|
||||
err_msg = "Timed out waiting for the iDRAC to become ready"
|
||||
LOG.error(err_msg)
|
||||
raise exceptions.DRACOperationFailed(drac_messages=err_msg)
|
||||
|
|
|
@ -15,8 +15,6 @@ from dracclient.resources import uris
|
|||
from dracclient import utils
|
||||
from dracclient import wsman
|
||||
|
||||
IDRAC_IS_READY = "LC061"
|
||||
|
||||
|
||||
class LifecycleControllerManagement(object):
|
||||
|
||||
|
@ -87,37 +85,6 @@ class LCConfiguration(object):
|
|||
|
||||
return result
|
||||
|
||||
def is_idrac_ready(self):
|
||||
"""Indicates if the iDRAC is ready to accept commands
|
||||
|
||||
Returns a boolean indicating if the iDRAC is ready to accept
|
||||
commands.
|
||||
|
||||
:returns: Boolean indicating iDRAC readiness
|
||||
:raises: WSManRequestFailure on request failures
|
||||
:raises: WSManInvalidResponse when receiving invalid response
|
||||
:raises: DRACOperationFailed on error reported back by the DRAC
|
||||
interface
|
||||
:raises: DRACUnexpectedReturnValue on return value mismatch
|
||||
"""
|
||||
|
||||
selectors = {'SystemCreationClassName': 'DCIM_ComputerSystem',
|
||||
'SystemName': 'DCIM:ComputerSystem',
|
||||
'CreationClassName': 'DCIM_LCService',
|
||||
'Name': 'DCIM:LCService'}
|
||||
|
||||
result = self.client.invoke(uris.DCIM_LCService,
|
||||
'GetRemoteServicesAPIStatus',
|
||||
selectors,
|
||||
{},
|
||||
expected_return_value=utils.RET_SUCCESS)
|
||||
|
||||
message_id = utils.find_xml(result,
|
||||
'MessageID',
|
||||
uris.DCIM_LCService).text
|
||||
|
||||
return message_id == IDRAC_IS_READY
|
||||
|
||||
|
||||
class LCAttribute(object):
|
||||
"""Generic LC attribute class"""
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import requests_mock
|
||||
|
||||
import dracclient.client
|
||||
from dracclient import exceptions
|
||||
from dracclient.resources import uris
|
||||
from dracclient.tests import base
|
||||
from dracclient.tests import utils as test_utils
|
||||
|
||||
|
@ -83,3 +85,48 @@ class WSManClientTestCase(base.BaseTest):
|
|||
self.assertRaises(exceptions.DRACUnexpectedReturnValue, client.invoke,
|
||||
'http://resource', 'Foo',
|
||||
expected_return_value='4242')
|
||||
|
||||
def test_is_idrac_ready_ready(self, mock_requests):
|
||||
expected_text = test_utils.LifecycleControllerInvocations[
|
||||
uris.DCIM_LCService]['GetRemoteServicesAPIStatus']['is_ready']
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
text=expected_text)
|
||||
|
||||
client = dracclient.client.WSManClient(**test_utils.FAKE_ENDPOINT)
|
||||
self.assertTrue(client.is_idrac_ready())
|
||||
|
||||
def test_is_idrac_ready_not_ready(self, mock_requests):
|
||||
expected_text = test_utils.LifecycleControllerInvocations[
|
||||
uris.DCIM_LCService]['GetRemoteServicesAPIStatus']['is_not_ready']
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
text=expected_text)
|
||||
|
||||
client = dracclient.client.WSManClient(**test_utils.FAKE_ENDPOINT)
|
||||
self.assertFalse(client.is_idrac_ready())
|
||||
|
||||
def test_wait_until_idrac_is_ready_ready(self, mock_requests):
|
||||
expected_text = test_utils.LifecycleControllerInvocations[
|
||||
uris.DCIM_LCService]['GetRemoteServicesAPIStatus']['is_ready']
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
text=expected_text)
|
||||
|
||||
client = dracclient.client.WSManClient(**test_utils.FAKE_ENDPOINT)
|
||||
|
||||
try:
|
||||
client.wait_until_idrac_is_ready()
|
||||
except exceptions.DRACOperationFailed:
|
||||
self.fail('wait_until_idrac_is_ready() timed out when it should '
|
||||
'not have!')
|
||||
|
||||
@mock.patch('time.sleep', autospec=True)
|
||||
def test_wait_until_idrac_is_ready_timeout(self,
|
||||
mock_requests,
|
||||
mock_ts):
|
||||
expected_text = test_utils.LifecycleControllerInvocations[
|
||||
uris.DCIM_LCService]['GetRemoteServicesAPIStatus']['is_not_ready']
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
text=expected_text)
|
||||
|
||||
client = dracclient.client.WSManClient(**test_utils.FAKE_ENDPOINT)
|
||||
self.assertRaises(exceptions.DRACOperationFailed,
|
||||
client.wait_until_idrac_is_ready)
|
||||
|
|
|
@ -11,11 +11,9 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import requests_mock
|
||||
|
||||
import dracclient.client
|
||||
from dracclient import exceptions
|
||||
from dracclient.resources import lifecycle_controller
|
||||
from dracclient.resources import uris
|
||||
from dracclient.tests import base
|
||||
|
@ -86,47 +84,3 @@ class ClientLCConfigurationTestCase(base.BaseTest):
|
|||
lifecycle_settings)
|
||||
self.assertEqual(expected_string_attr,
|
||||
lifecycle_settings['LifecycleController.Embedded.1#LCAttributes.1#SystemID']) # noqa
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_is_idrac_ready_ready(self, mock_requests):
|
||||
expected_text = test_utils.LifecycleControllerInvocations[
|
||||
uris.DCIM_LCService]['GetRemoteServicesAPIStatus']['is_ready']
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
text=expected_text)
|
||||
|
||||
self.assertTrue(self.drac_client.is_idrac_ready())
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_is_idrac_ready_not_ready(self, mock_requests):
|
||||
expected_text = test_utils.LifecycleControllerInvocations[
|
||||
uris.DCIM_LCService]['GetRemoteServicesAPIStatus']['is_not_ready']
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
text=expected_text)
|
||||
|
||||
self.assertFalse(self.drac_client.is_idrac_ready())
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_wait_until_idrac_is_ready_ready(self, mock_requests):
|
||||
expected_text = test_utils.LifecycleControllerInvocations[
|
||||
uris.DCIM_LCService]['GetRemoteServicesAPIStatus']['is_ready']
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
text=expected_text)
|
||||
|
||||
try:
|
||||
self.drac_client.wait_until_idrac_is_ready()
|
||||
except exceptions.DRACOperationFailed:
|
||||
self.fail('wait_until_idrac_is_ready() timed out when it should '
|
||||
'not have!')
|
||||
|
||||
@requests_mock.Mocker()
|
||||
@mock.patch('time.sleep', autospec=True)
|
||||
def test_wait_until_idrac_is_ready_timeout(self,
|
||||
mock_requests,
|
||||
mock_ts):
|
||||
expected_text = test_utils.LifecycleControllerInvocations[
|
||||
uris.DCIM_LCService]['GetRemoteServicesAPIStatus']['is_not_ready']
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
text=expected_text)
|
||||
|
||||
self.assertRaises(exceptions.DRACOperationFailed,
|
||||
self.drac_client.wait_until_idrac_is_ready)
|
||||
|
|
Loading…
Reference in New Issue