From 74f95b1dd511cbbf36b0e85a596212d96047e345 Mon Sep 17 00:00:00 2001 From: Kamlesh Chauvhan Date: Thu, 17 Nov 2022 07:46:29 +0000 Subject: [PATCH] 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 --- ...tional_time_property-f53f650d8612a847.yaml | 4 + sushy/resources/system/storage/volume.py | 18 ++++- .../resources/system/storage/test_volume.py | 79 +++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/fix-volume-delete-configuration-unsuported-operational_time_property-f53f650d8612a847.yaml 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):