Re-trying InsertMedia call with TransferProtocolType if required

Adding extra logic to handle InsertMedia API call failure due to
TransferProtocolType parameter missing. This is a non-standard
parameter (rarely) required on some servers. If such failure mode
is detected, TransferProtocolType value is automatically added
to the request payload prior to re-trying the InsertMedia call.

Story: 2009871
Task: 44574
Change-Id: I608a776f1849621f00e17d1edca16a54906d05fd
This commit is contained in:
Jacob Anders 2022-02-22 18:24:39 +10:00
parent 4f45f13dd7
commit ecf1bcc80b
4 changed files with 70 additions and 1 deletions

View File

@ -0,0 +1,7 @@
---
fixes:
- |
Resolved virtualmedia attach failures caused by the lack of
TransferProtocolType parameter in the request payload which is required
by certain BMCs (e.g. on Nokia servers). This is done by adding capability
to retry virtualmedia InsertMedia with the updated payload in such cases.

View File

@ -106,6 +106,18 @@ class VirtualMedia(base.ResourceBase):
eject_uri = eject_media.target_uri
return eject_uri, use_patch
def is_transfer_protocol_required(self, response=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
def insert_media(self, image, inserted=True, write_protected=True,
username=None, password=None, transfer_method=None):
"""Attach remote media to virtual media
@ -161,7 +173,19 @@ class VirtualMedia(base.ResourceBase):
payload['Inserted'] = False
if not write_protected:
payload['WriteProtected'] = False
self._conn.post(target_uri, data=payload)
# NOTE(janders) attempting to detect whether attachment failure is
# 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):
if payload['Image'].startswith('https://'):
payload['TransferProtocolType'] = "HTTPS"
elif payload['Image'].startswith('http://'):
payload['TransferProtocolType'] = "HTTP"
self._conn.post(target_uri, data=payload)
else:
raise
self.invalidate()
def eject_media(self):

View File

@ -0,0 +1,22 @@
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_0_8.Message",
"Message": "The action /redfish/v1/Managers/Self/VirtualMedia/CD1/Actions/VirtualMedia.InsertMedia requires the parameter TransferProtocolType to be present in the request body.",
"MessageArgs": [
"/redfish/v1/Managers/Self/VirtualMedia/CD1/Actions/VirtualMedia.InsertMedia",
"TransferProtocolType"
],
"MessageId": "Base.1.5.ActionParameterMissing",
"RelatedProperties": [
"#/TransferProtocolType"
],
"Resolution": "Supply the action with the required parameter in the request body when the request is resubmitted.",
"Severity": "Critical"
}
],
"code": "Base.1.5.ActionParameterMissing",
"message": "The action /redfish/v1/Managers/Self/VirtualMedia/CD1/Actions/VirtualMedia.InsertMedia requires the parameter TransferProtocolType to be present in the request body."
}
}

View File

@ -16,6 +16,8 @@ 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
@ -176,6 +178,20 @@ class VirtualMediaTestCase(base.TestCase):
headers={"If-Match": 'W/"3d7b8a7360bf2941d","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):
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)
self.assertTrue(retval)
def test_eject_media_none(self):
self.sys_virtual_media._actions.eject_media = None
self.assertRaisesRegex(