Fix volume deletion on newer iDRACs
Fixes 'Unsupported parameter name @Redfish.OperationApplyTime' General error on iDRAC firmware version 6.00.02.00 or newer when deleting volumes. Handles the failure of volume.delete by retrying without operation apply time. In older iDRAC firmware versions operation apply time was silently ignored, but now in newer iDRAC firmware versions it is returning 501 Not Implemented error. Change-Id: Ia421ff025f1f0bc72252b5441589beb2e53cc25a
This commit is contained in:
parent
56efdee0ee
commit
74f95b1dd5
|
@ -0,0 +1,4 @@
|
|||
fixes:
|
||||
- |
|
||||
Fixes 'Unsupported parameter name @Redfish.OperationApplyTime' error
|
||||
on iDRAC firmware version 6.00.02.00 or newer when deleting volumes.
|
|
@ -136,8 +136,22 @@ class Volume(base.ResourceBase):
|
|||
if (payload and payload.get(_OAT_PROP)
|
||||
== res_cons.ApplyTime.IMMEDIATE.value):
|
||||
blocking = True
|
||||
r = self._conn.delete(self._path, data=payload, blocking=blocking,
|
||||
timeout=timeout)
|
||||
try:
|
||||
r = self._conn.delete(self._path, data=payload,
|
||||
blocking=blocking, timeout=timeout)
|
||||
except exceptions.ServerSideError as exc:
|
||||
if (_OAT_PROP in str(exc.message)
|
||||
and 'SYS029' in str(exc.message)):
|
||||
LOG.debug('Retry volume delete without %(prop)s for %(path)s '
|
||||
'because got error: %(err)s',
|
||||
{'prop': _OAT_PROP,
|
||||
'path': self._path,
|
||||
'err': exc})
|
||||
payload.pop(_OAT_PROP)
|
||||
r = self._conn.delete(self._path, data=payload,
|
||||
blocking=blocking, timeout=timeout)
|
||||
else:
|
||||
raise exc
|
||||
return r
|
||||
|
||||
def delete(self, payload=None, apply_time=None, timeout=500):
|
||||
|
|
|
@ -16,6 +16,7 @@ from unittest import mock
|
|||
from dateutil import parser
|
||||
|
||||
import sushy
|
||||
from sushy import exceptions
|
||||
from sushy.resources import constants as res_cons
|
||||
from sushy.resources.system.storage import constants as store_cons
|
||||
from sushy.resources.system.storage import volume
|
||||
|
@ -106,6 +107,84 @@ class VolumeTestCase(base.TestCase):
|
|||
self.assertEqual(task_mon.task_monitor_uri,
|
||||
'/redfish/v1/taskmon/4608f7e6')
|
||||
|
||||
@mock.patch.object(volume.LOG, 'debug', autospec=True)
|
||||
def test_delete_retry_on_501_sys029_apply_time(self, mock_debug):
|
||||
payload = {}
|
||||
_OAT_PROP = '@Redfish.OperationApplyTime'
|
||||
payload[_OAT_PROP] = 'Immediate'
|
||||
target_uri = '/redfish/v1/Systems/437XR1138R2/Storage/1/Volumes/1'
|
||||
response_info = {"error": {"@Message.ExtendedInfo": [
|
||||
{'Message': '@Redfish.OperationApplyTime.',
|
||||
'MessageId': 'IDRAC.2.7.SYS029'}]}}
|
||||
mock_error = mock.Mock()
|
||||
mock_error.status_code = 501
|
||||
mock_error.json.return_value = response_info
|
||||
mock_success = mock.Mock()
|
||||
mock_success.status_code = 201
|
||||
self.conn.delete.side_effect = [exceptions.ServerSideError(
|
||||
method='DELETE', url=target_uri, response=mock_error),
|
||||
mock_success]
|
||||
|
||||
resource = self.stor_volume.delete(
|
||||
payload=payload, apply_time=res_cons.ApplyTime.IMMEDIATE)
|
||||
|
||||
self.assertIsNone(resource)
|
||||
self.assertEqual(2, self.stor_volume._conn.delete.call_count)
|
||||
expected_calls = [
|
||||
mock.call(self.stor_volume._path, data=payload, blocking=True,
|
||||
timeout=500),
|
||||
mock.call(self.stor_volume._path, data={}, blocking=True,
|
||||
timeout=500)
|
||||
]
|
||||
self.stor_volume._conn.delete.assert_has_calls(expected_calls)
|
||||
mock_debug.assert_called_once()
|
||||
|
||||
@mock.patch.object(volume.LOG, 'debug', autospec=True)
|
||||
def test_delete_retry_on_501_sys029_other(self, mock_debug):
|
||||
payload = {}
|
||||
_OAT_PROP = '@Redfish.OperationApplyTime'
|
||||
payload[_OAT_PROP] = 'Immediate'
|
||||
target_uri = '/redfish/v1/Systems/437XR1138R2/Storage/1/Volumes/1'
|
||||
response_info = {"error": {"@Message.ExtendedInfo": [
|
||||
{'Message': '@Redfish.SomethingElse.',
|
||||
'MessageId': 'IDRAC.2.7.SYS029'}]}}
|
||||
mock_error = mock.Mock()
|
||||
mock_error.status_code = 501
|
||||
mock_error.json.return_value = response_info
|
||||
mock_success = mock.Mock()
|
||||
mock_success.status_code = 201
|
||||
self.conn.delete.side_effect = [exceptions.ServerSideError(
|
||||
method='DELETE', url=target_uri, response=mock_error),
|
||||
mock_success]
|
||||
|
||||
self.assertRaises(exceptions.ServerSideError, self.stor_volume.delete,
|
||||
payload=payload,
|
||||
apply_time=res_cons.ApplyTime.IMMEDIATE)
|
||||
self.stor_volume._conn.delete.assert_called_once_with(
|
||||
self.stor_volume._path, data=payload, blocking=True, timeout=500)
|
||||
mock_debug.assert_not_called()
|
||||
|
||||
@mock.patch.object(volume.LOG, 'debug', autospec=True)
|
||||
def test_delete_raise_on_501_other(self, mock_debug):
|
||||
payload = {}
|
||||
_OAT_PROP = '@Redfish.OperationApplyTime'
|
||||
payload[_OAT_PROP] = 'Immediate'
|
||||
target_uri = '/redfish/v1/Systems/437XR1138R2/Storage/1/Volumes/1'
|
||||
response_info = {"error": {"@Message.ExtendedInfo": [
|
||||
{'Message': 'Other message.'}]}}
|
||||
mock_error = mock.Mock()
|
||||
mock_error.status_code = 501
|
||||
mock_error.json.return_value = response_info
|
||||
self.conn.delete.side_effect = [exceptions.ServerSideError(
|
||||
method='DELETE', url=target_uri, response=mock_error)]
|
||||
|
||||
self.assertRaises(exceptions.ServerSideError, self.stor_volume.delete,
|
||||
payload=payload,
|
||||
apply_time=res_cons.ApplyTime.IMMEDIATE)
|
||||
self.stor_volume._conn.delete.assert_called_once_with(
|
||||
self.stor_volume._path, data=payload, blocking=True, timeout=500)
|
||||
mock_debug.assert_not_called()
|
||||
|
||||
|
||||
class VolumeCollectionTestCase(base.TestCase):
|
||||
|
||||
|
|
Loading…
Reference in New Issue