Adds a retry workaround for power ON failure
Closes: 2021995 Change-Id: Id98474c334840ecc86371aa6b6395f7b4d6b1714
This commit is contained in:
parent
56c507344d
commit
5839129389
@ -23,6 +23,7 @@ import tempfile
|
||||
|
||||
from OpenSSL.crypto import FILETYPE_ASN1
|
||||
from OpenSSL.crypto import load_certificate
|
||||
import retrying
|
||||
from six.moves.urllib import parse
|
||||
import sushy
|
||||
from sushy import utils
|
||||
@ -46,11 +47,14 @@ from proliantutils import utils as common_utils
|
||||
Class specific for Redfish APIs.
|
||||
"""
|
||||
|
||||
MAX_RETRY_ATTEMPTS = 3 # Maximum number of attempts to be retried
|
||||
MAX_TIME_BEFORE_RETRY = 7 * 1000 # wait time in milliseconds before retry
|
||||
|
||||
GET_POWER_STATE_MAP = {
|
||||
sushy.SYSTEM_POWER_STATE_ON: 'ON',
|
||||
sushy.SYSTEM_POWER_STATE_POWERING_ON: 'ON',
|
||||
sushy.SYSTEM_POWER_STATE_POWERING_ON: 'PoweringOn',
|
||||
sushy.SYSTEM_POWER_STATE_OFF: 'OFF',
|
||||
sushy.SYSTEM_POWER_STATE_POWERING_OFF: 'OFF'
|
||||
sushy.SYSTEM_POWER_STATE_POWERING_OFF: 'PoweringOff'
|
||||
}
|
||||
|
||||
POWER_RESET_MAP = {
|
||||
@ -264,6 +268,46 @@ class RedfishOperations(operations.IloOperations):
|
||||
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||
return GET_POWER_STATE_MAP.get(sushy_system.power_state)
|
||||
|
||||
def _perform_power_op(self, power):
|
||||
"""This method performs power operation.
|
||||
|
||||
:param: power : target power state
|
||||
"""
|
||||
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||
try:
|
||||
sushy_system.reset_system(POWER_RESET_MAP[power])
|
||||
except sushy.exceptions.SushyError as e:
|
||||
msg = (self._('The Redfish controller failed to set power state '
|
||||
'of server to %(target_value)s. Error %(error)s') %
|
||||
{'target_value': power, 'error': str(e)})
|
||||
LOG.debug(msg)
|
||||
raise exception.IloError(msg)
|
||||
|
||||
@retrying.retry(
|
||||
stop_max_attempt_number=MAX_RETRY_ATTEMPTS,
|
||||
retry_on_result=lambda state: state != 'ON',
|
||||
wait_fixed=MAX_TIME_BEFORE_RETRY
|
||||
)
|
||||
def _retry_until_powered_on(self, power):
|
||||
"""This method retries power on operation.
|
||||
|
||||
:param: power : target power state
|
||||
"""
|
||||
# If the system is in the same power state as
|
||||
# requested by the user, it gives the error
|
||||
# InvalidOperationForSystemState. To avoid this error
|
||||
# the power state is checked before power on
|
||||
# operation is performed.
|
||||
status = self.get_host_power_status()
|
||||
allowed_states = ['ON', 'PoweringOn']
|
||||
if power == "OFF":
|
||||
allowed_states = ['OFF', 'PoweringOff']
|
||||
if (status != power or status not in allowed_states or status is None):
|
||||
self._perform_power_op(power)
|
||||
return self.get_host_power_status()
|
||||
else:
|
||||
return status
|
||||
|
||||
def reset_server(self):
|
||||
"""Resets the server.
|
||||
|
||||
@ -302,9 +346,8 @@ class RedfishOperations(operations.IloOperations):
|
||||
"state."), {'target_value': target_value})
|
||||
return
|
||||
|
||||
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||
try:
|
||||
sushy_system.reset_system(POWER_RESET_MAP[target_value])
|
||||
self._retry_until_powered_on(target_value)
|
||||
except sushy.exceptions.SushyError as e:
|
||||
msg = (self._('The Redfish controller failed to set power state '
|
||||
'of server to %(target_value)s. Error %(error)s') %
|
||||
|
@ -139,16 +139,55 @@ class RedfishOperationsTestCase(testtools.TestCase):
|
||||
'The Redfish controller failed to set power state of server to ON',
|
||||
self.rf_client.set_host_power, 'ON')
|
||||
|
||||
def test_set_host_power_invalid_input(self):
|
||||
@mock.patch.object(redfish.RedfishOperations, 'get_host_power_status')
|
||||
def test_set_host_power_invalid_input(self, host_power_status_mock):
|
||||
self.assertRaisesRegex(
|
||||
exception.InvalidInputError,
|
||||
'The parameter "target_value" value "Off" is invalid.',
|
||||
self.rf_client.set_host_power, 'Off')
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, 'get_host_power_status')
|
||||
def test_set_host_power_change(self, get_host_power_status_mock):
|
||||
get_host_power_status_mock.return_value = 'OFF'
|
||||
def test_set_host_power_exc(self, host_power_status_mock):
|
||||
self.assertRaises(exception.InvalidInputError,
|
||||
self.rf_client.set_host_power, 'invalid')
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, 'get_host_power_status')
|
||||
@mock.patch.object(redfish.RedfishOperations, '_retry_until_powered_on')
|
||||
def test_set_host_power_off(self, retry_mock, host_power_status_mock):
|
||||
host_power_status_mock.return_value = 'ON'
|
||||
self.rf_client.set_host_power('OFF')
|
||||
host_power_status_mock.assert_called_once_with()
|
||||
self.assertTrue(retry_mock.called)
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_perform_power_op')
|
||||
@mock.patch.object(redfish.RedfishOperations, 'get_host_power_status')
|
||||
@mock.patch.object(redfish.RedfishOperations, '_retry_until_powered_on')
|
||||
def test_set_host_power_on(self, retry_mock, host_power_status_mock,
|
||||
perform_power_op_mock):
|
||||
host_power_status_mock.return_value = 'OFF'
|
||||
self.rf_client.set_host_power('ON')
|
||||
host_power_status_mock.assert_called_once_with()
|
||||
self.assertFalse(perform_power_op_mock.called)
|
||||
self.assertTrue(retry_mock.called)
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_perform_power_op')
|
||||
@mock.patch.object(redfish.RedfishOperations, 'get_host_power_status')
|
||||
def test_retry_until_powered_on_3times(self, host_power_status_mock,
|
||||
perform_power_mock):
|
||||
host_power_status_mock.side_effect = ['OFF', 'OFF', 'ON']
|
||||
self.rf_client._retry_until_powered_on('ON')
|
||||
self.assertEqual(3, host_power_status_mock.call_count)
|
||||
|
||||
@mock.patch.object(redfish.RedfishOperations, '_perform_power_op')
|
||||
@mock.patch.object(redfish.RedfishOperations, 'get_host_power_status')
|
||||
def test_retry_until_powered_on(self, host_power_status_mock,
|
||||
perform_power_mock):
|
||||
host_power_status_mock.return_value = 'ON'
|
||||
self.rf_client._retry_until_powered_on('ON')
|
||||
self.assertEqual(1, host_power_status_mock.call_count)
|
||||
|
||||
def test_perform_power_op(self):
|
||||
self.rf_client._perform_power_op("ON")
|
||||
self.sushy.get_system().reset_system.assert_called_once_with(
|
||||
sushy.RESET_ON)
|
||||
|
||||
|
@ -11,5 +11,5 @@ pyasn1-lextudio>=1.1.0 # BSD
|
||||
pyasn1-modules-lextudio>=0.2.0 # BSD
|
||||
|
||||
# Redfish communication uses the Sushy library
|
||||
sushy>=4.4.0
|
||||
sushy>=4.5.0
|
||||
pyOpenSSL>=19.1.0
|
||||
|
Loading…
Reference in New Issue
Block a user