Richard Pioso 9915d8e16d Fix GET HTTP 202 status without Location
When a GET request is performed on an asynchronous operation task
monitor URI, the iDRAC service can correctly return the HTTP '202
Accepted' status code without a 'Location' header. [1] This has been
observed while querying the status of the long-running OEM Server
Configuration Profile (SCP) import and export system configuration
operations. [2]

sushy-oem-idrac has incorrectly required the 'Location' header to
continue to query the status of the operation, and determine when it has
completed and whether it succeeded. This fixes that by reusing the URI
provided in the 'Location' header of the response to a previous request,
instead of failing.

[1] http://redfish.dmtf.org/schemas/DSP0266_1.11.0.html#asynchronous-operations
[2] Pushkala Iyer et al., "Server Cloning with Server Configuration
Profiles", (Dell Inc., version 1.1, July 2016),
https://downloads.dell.com/solutions/dell-management-solution-resources/Server
Cloning with Server Configuration Profiles 1_1 Final.pdf.

Co-Authored-By: Derek Higgins <derekh@redhat.com>
Story: 2007640
Task: 39697
Change-Id: I07eef4bab7285d2c454a6112a43b0725e8ee5a09
2020-09-22 11:36:07 -04:00

77 lines
2.5 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from datetime import datetime
from datetime import timedelta
import logging
import time
from dateutil import parser
import sushy
LOG = logging.getLogger(__name__)
TASK_POLL_PERIOD = 1
def _to_datetime(retry_after_str):
if retry_after_str.isdigit():
# Retry-After: 120
return datetime.now() + timedelta(seconds=int(retry_after_str))
else:
# Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
return parser.parse(retry_after_str)
def http_call(conn, method, *args, **kwargs):
handle = getattr(conn, method.lower())
sleep_for = kwargs.pop('sushy_task_poll_period', TASK_POLL_PERIOD)
response = handle(*args, **kwargs)
LOG.debug('Finished HTTP %s with args %s %s, response is '
'%d', method, args or '', kwargs, response.status_code)
location = None
while response.status_code == 202:
location = response.headers.get('location', location)
if not location:
raise sushy.exceptions.ExtensionError(
error='Response %d to HTTP %s with args %s, kwargs %s '
'does not include Location: in '
'header' % (response.status_code, method.upper(),
args, kwargs))
retry_after = response.headers.get('retry-after')
if retry_after:
retry_after = _to_datetime(retry_after)
sleep_for = max(0, (retry_after - datetime.now()).total_seconds())
LOG.debug('Sleeping for %d secs before retrying HTTP GET '
'%s', sleep_for, location)
time.sleep(sleep_for)
response = conn.get(location)
LOG.debug('Finished HTTP GET %s, response is '
'%d', location, response.status_code)
if response.status_code >= 400:
raise sushy.exceptions.ExtensionError(
error='HTTP %s with args %s, kwargs %s failed '
'with code %s' % (method.upper(), args, kwargs,
response.status_code))
return response