diff --git a/releasenotes/notes/fix-volume-delete-configuration-unsuported-operational_time_property-f53f650d8612a847.yaml b/releasenotes/notes/fix-volume-delete-configuration-unsuported-operational_time_property-f53f650d8612a847.yaml new file mode 100644 index 00000000..38fba8e0 --- /dev/null +++ b/releasenotes/notes/fix-volume-delete-configuration-unsuported-operational_time_property-f53f650d8612a847.yaml @@ -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. diff --git a/sushy/resources/system/storage/volume.py b/sushy/resources/system/storage/volume.py index e6c5055c..abfe1838 100644 --- a/sushy/resources/system/storage/volume.py +++ b/sushy/resources/system/storage/volume.py @@ -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): diff --git a/sushy/tests/unit/resources/system/storage/test_volume.py b/sushy/tests/unit/resources/system/storage/test_volume.py index 5d962a86..dda38ee0 100644 --- a/sushy/tests/unit/resources/system/storage/test_volume.py +++ b/sushy/tests/unit/resources/system/storage/test_volume.py @@ -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):