From 79d94202ed784e7c1a497475abb7208c1efa7786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aija=20Jaunt=C4=93va?= Date: Fri, 22 Jan 2021 09:53:05 -0500 Subject: [PATCH] Fix ExtendedInfo error handling for non-list item Conflicts: sushy/exceptions.py Change-Id: I2d082762fcf59d7b9bc8adc7c5159520ac628043 (cherry picked from commit 7ec04224b4e80225ff3db51d860210e8e160f02f) (cherry picked from commit dfe6b33fa66072e9ead784d06fa30946ef922cb8) --- ...nded-info-error-handling-73fecb6bf5c852ff.yaml | 7 +++++++ sushy/exceptions.py | 13 +++++++------ .../unit/json_samples/error_single_ext_info.json | 13 +++++++++++++ sushy/tests/unit/test_connector.py | 15 +++++++++++++++ 4 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/fix-extended-info-error-handling-73fecb6bf5c852ff.yaml create mode 100644 sushy/tests/unit/json_samples/error_single_ext_info.json diff --git a/releasenotes/notes/fix-extended-info-error-handling-73fecb6bf5c852ff.yaml b/releasenotes/notes/fix-extended-info-error-handling-73fecb6bf5c852ff.yaml new file mode 100644 index 00000000..8ee28a4f --- /dev/null +++ b/releasenotes/notes/fix-extended-info-error-handling-73fecb6bf5c852ff.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes ``AttributeError: 'str' object has no attribute 'get'`` during error + handling. This occurs when BMC does not return a list of messages inside + ``@Message.ExtendedInfo``, but a single item. This has been observed with + iDRAC. diff --git a/sushy/exceptions.py b/sushy/exceptions.py index 3d6d18b7..3d24b21e 100644 --- a/sushy/exceptions.py +++ b/sushy/exceptions.py @@ -104,9 +104,9 @@ class HTTPError(SushyError): self.code = self.body.get('code', 'Base.1.0.GeneralError') self.detail = self.body.get('message') ext_info = self.body.get('@Message.ExtendedInfo', [{}]) - index = self._get_most_severe_msg_index(ext_info) - self.detail = ext_info[index].get('Message', self.detail) - error = '%s: %s' % (self.code, self.detail or 'unknown error') + message = self._get_most_severe_msg(ext_info) + self.detail = message or self.detail + error = '%s: %s' % (self.code, self.detail or 'unknown error.') kwargs = {'method': method, 'url': url, 'code': self.status_code, 'error': error} @@ -115,13 +115,14 @@ class HTTPError(SushyError): super(HTTPError, self).__init__(**kwargs) @staticmethod - def _get_most_severe_msg_index(extended_info): + def _get_most_severe_msg(extended_info): + if not isinstance(extended_info, list): + return extended_info.get('Message', None) if len(extended_info) > 0: for sev in ['Critical', 'Warning']: for i, m in enumerate(extended_info): if m.get('Severity') == sev: - return i - return 0 + return m.get('Message') class BadRequestError(HTTPError): diff --git a/sushy/tests/unit/json_samples/error_single_ext_info.json b/sushy/tests/unit/json_samples/error_single_ext_info.json new file mode 100644 index 00000000..d56a0a6e --- /dev/null +++ b/sushy/tests/unit/json_samples/error_single_ext_info.json @@ -0,0 +1,13 @@ +{ + "error": { + "code": "Base.1.5.GeneralError", + "message": "A general error has occurred. See ExtendedInfo for more information.", + "@Message.ExtendedInfo": { + "@odata.type": "#Message.v1_0_0.Message", + "MessageId": "Base.1.5.GeneralError", + "Message": "A general error has occurred. See Resolution for information on how to resolve the error.", + "Resolution": "Redfish request contains unsupported media type. Correct the request body and resubmit.", + "Severity": "Warning" + } + } +} \ No newline at end of file diff --git a/sushy/tests/unit/test_connector.py b/sushy/tests/unit/test_connector.py index 1c81afa9..f1b85c47 100644 --- a/sushy/tests/unit/test_connector.py +++ b/sushy/tests/unit/test_connector.py @@ -316,6 +316,21 @@ class ConnectorOpTestCase(base.TestCase): self.assertIsNotNone(exc.body) self.assertIn('body submitted was malformed JSON', exc.detail) + def test_known_http_error_nonlist_ext_info(self): + self.request.return_value.status_code =\ + http_client.UNSUPPORTED_MEDIA_TYPE + with open('sushy/tests/unit/json_samples/' + 'error_single_ext_info.json') as f: + self.request.return_value.json.return_value = json.load(f) + + with self.assertRaisesRegex(exceptions.HTTPError, + 'See Resolution for information') as cm: + self.conn._op('POST', 'http://foo.bar') + exc = cm.exception + self.assertEqual(http_client.UNSUPPORTED_MEDIA_TYPE, exc.status_code) + self.assertIsNotNone(exc.body) + self.assertIn('See Resolution for information', exc.detail) + def test_not_found_error(self): self.request.return_value.status_code = http_client.NOT_FOUND self.request.return_value.json.side_effect = ValueError('no json')