Merge "Handle a different error code for missing TransferProtocolType"
This commit is contained in:
commit
c52873796f
@ -0,0 +1,6 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Correctly handles error code ``Base.1.5.PropertyMissing`` when dealing with
|
||||
hardware that requires ``TransferProtocolType`` for virtual media
|
||||
operations.
|
@ -96,6 +96,9 @@ class HTTPError(SushyError):
|
||||
message = ('HTTP %(method)s %(url)s returned code %(code)s. %(error)s '
|
||||
'Extended information: %(ext_info)s')
|
||||
|
||||
extended_info = None
|
||||
"""Extended information provided in the response."""
|
||||
|
||||
def __init__(self, method, url, response):
|
||||
self.status_code = response.status_code
|
||||
try:
|
||||
@ -106,17 +109,16 @@ class HTTPError(SushyError):
|
||||
{'method': method, 'url': url, 'code':
|
||||
self.status_code})
|
||||
error = 'unknown error'
|
||||
ext_info = 'none'
|
||||
else:
|
||||
self.body = body.get('error', {})
|
||||
self.code = self.body.get('code', 'Base.1.0.GeneralError')
|
||||
self.detail = self.body.get('message')
|
||||
ext_info = self.body.get('@Message.ExtendedInfo', [{}])
|
||||
message = self._get_most_severe_msg(ext_info)
|
||||
self.extended_info = self.body.get('@Message.ExtendedInfo')
|
||||
message = self._get_most_severe_msg(self.extended_info or [{}])
|
||||
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, 'ext_info': ext_info}
|
||||
'error': error, 'ext_info': self.extended_info}
|
||||
LOG.debug('HTTP response for %(method)s %(url)s: '
|
||||
'status code: %(code)s, error: %(error)s, '
|
||||
'extended: %(ext_info)s', kwargs)
|
||||
@ -132,6 +134,14 @@ class HTTPError(SushyError):
|
||||
if m.get('Severity') == sev:
|
||||
return m.get('Message')
|
||||
|
||||
@property
|
||||
def related_properties(self):
|
||||
"""List of properties related to the error."""
|
||||
try:
|
||||
return self.extended_info[0]['RelatedProperties']
|
||||
except (IndexError, KeyError, TypeError):
|
||||
return []
|
||||
|
||||
|
||||
class BadRequestError(HTTPError):
|
||||
pass
|
||||
|
@ -106,17 +106,16 @@ class VirtualMedia(base.ResourceBase):
|
||||
eject_uri = eject_media.target_uri
|
||||
return eject_uri, use_patch
|
||||
|
||||
def is_transfer_protocol_required(self, response=None):
|
||||
def is_transfer_protocol_required(self, error=None):
|
||||
"""Check the response code and body and in case of failure
|
||||
|
||||
Try to determine if it happened due to missing TransferProtocolType.
|
||||
"""
|
||||
if (response.code == "Base.1.5.ActionParameterMissing"
|
||||
and response.body is not None):
|
||||
if ("#/TransferProtocolType" in response.body
|
||||
["@Message.ExtendedInfo"][0]['RelatedProperties']):
|
||||
return True
|
||||
return False
|
||||
return (
|
||||
(error.code.endswith(".ActionParameterMissing")
|
||||
or error.code.endswith(".PropertyMissing"))
|
||||
and "#/TransferProtocolType" in error.related_properties
|
||||
)
|
||||
|
||||
def insert_media(self, image, inserted=True, write_protected=True,
|
||||
username=None, password=None, transfer_method=None):
|
||||
@ -177,8 +176,8 @@ class VirtualMedia(base.ResourceBase):
|
||||
# due to absence of TransferProtocolType param and if so adding it
|
||||
try:
|
||||
self._conn.post(target_uri, data=payload)
|
||||
except exceptions.HTTPError as response:
|
||||
if self.is_transfer_protocol_required(response):
|
||||
except exceptions.HTTPError as error:
|
||||
if self.is_transfer_protocol_required(error):
|
||||
if payload['Image'].startswith('https://'):
|
||||
payload['TransferProtocolType'] = "HTTPS"
|
||||
elif payload['Image'].startswith('http://'):
|
||||
|
@ -16,8 +16,6 @@ from http import client as http_client
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
import requests
|
||||
|
||||
import sushy
|
||||
from sushy import exceptions
|
||||
from sushy.resources.certificateservice import certificate
|
||||
@ -178,18 +176,25 @@ class VirtualMediaTestCase(base.TestCase):
|
||||
headers={"If-Match": 'W/"3d7b8a7360bf2941d"'})
|
||||
self.assertTrue(self.sys_virtual_media._is_stale)
|
||||
|
||||
@mock.patch.object(requests, 'post', autospec=True)
|
||||
def test_is_transfer_protocol_required(self, mock_post):
|
||||
def test_is_transfer_protocol_required(self):
|
||||
with open('sushy/tests/unit/json_samples/'
|
||||
'transfer_proto_required_error.json') as f:
|
||||
response_obj = json.load(f)
|
||||
response = mock.MagicMock()
|
||||
response.status_code = 400
|
||||
response.code = "Base.1.5.ActionParameterMissing"
|
||||
response.body = response_obj['error']
|
||||
response.raise_for_status.side_effect = requests.exceptions.HTTPError
|
||||
mock_post.return_value = response
|
||||
retval = self.sys_virtual_media.is_transfer_protocol_required(response)
|
||||
response = mock.Mock(spec=['json', 'status_code'])
|
||||
response.json.return_value = response_obj
|
||||
error = exceptions.HTTPError('GET', 'VirtualMedia', response)
|
||||
retval = self.sys_virtual_media.is_transfer_protocol_required(error)
|
||||
self.assertTrue(retval)
|
||||
|
||||
def test_is_transfer_protocol_required_alt_code(self):
|
||||
with open('sushy/tests/unit/json_samples/'
|
||||
'transfer_proto_required_error.json') as f:
|
||||
response_obj = json.load(f)
|
||||
response_obj['error']['code'] = 'Base.1.5.PropertyMissing'
|
||||
response = mock.Mock(spec=['json', 'status_code'])
|
||||
response.json.return_value = response_obj
|
||||
error = exceptions.HTTPError('GET', 'VirtualMedia', response)
|
||||
retval = self.sys_virtual_media.is_transfer_protocol_required(error)
|
||||
self.assertTrue(retval)
|
||||
|
||||
def test_eject_media_none(self):
|
||||
|
@ -362,6 +362,7 @@ class ConnectorOpTestCase(base.TestCase):
|
||||
self.assertEqual(http_client.BAD_REQUEST, exc.status_code)
|
||||
self.assertIsNotNone(exc.body)
|
||||
self.assertIn('body submitted was malformed JSON', exc.detail)
|
||||
self.assertEqual(len(exc.extended_info), 3, exc.extended_info)
|
||||
|
||||
def test_known_http_error_nonlist_ext_info(self):
|
||||
self.request.return_value.status_code =\
|
||||
@ -377,6 +378,8 @@ class ConnectorOpTestCase(base.TestCase):
|
||||
self.assertEqual(http_client.UNSUPPORTED_MEDIA_TYPE, exc.status_code)
|
||||
self.assertIsNotNone(exc.body)
|
||||
self.assertIn('See Resolution for information', exc.detail)
|
||||
self.assertIn('unsupported media type',
|
||||
exc.extended_info['Resolution'])
|
||||
|
||||
@mock.patch('time.sleep', autospec=True)
|
||||
def test_not_found_error(self, mock_sleep):
|
||||
@ -504,7 +507,7 @@ class ConnectorOpTestCase(base.TestCase):
|
||||
'this is expected prior to authentication', 'HTTP GET '
|
||||
'http://redfish/v1/SessionService returned code '
|
||||
'%s. unknown error Extended information: '
|
||||
'none' % http_client.FORBIDDEN)
|
||||
'None' % http_client.FORBIDDEN)
|
||||
self.assertEqual(http_client.FORBIDDEN, exc.status_code)
|
||||
|
||||
def test_blocking_no_location_header(self):
|
||||
@ -557,7 +560,7 @@ class ConnectorOpTestCase(base.TestCase):
|
||||
"authentication. %(err)s",
|
||||
{'err': 'HTTP GET http://redfish/v1/SessionService '
|
||||
'returned code HTTPStatus.FORBIDDEN. unknown error '
|
||||
'Extended information: none'}
|
||||
'Extended information: None'}
|
||||
)
|
||||
|
||||
def test__op_raises_connection_error(self):
|
||||
|
Loading…
Reference in New Issue
Block a user