Requests must always have a read/connect timeout
Before this change, a request to a nonresponsive BMC may have hung forever waiting for requests to connect and read. Now, we ensure all calls have a timeout unless explicitly disabled by the API user. Because timeout was already an argument for _op, anyone attempting to previously override the requests timeout (send one at all) wouldn't have been able to for pythonic reasons. Closes-Bug: 2021995 Change-Id: I76c543c6e39bab81f46a7aa00f1d38328c30cf7b
This commit is contained in:
parent
f03d77ebcb
commit
7c2a2511fc
|
@ -0,0 +1,5 @@
|
|||
fixes:
|
||||
- Fixes bug where sushy would not pass a read/connect timeout through to
|
||||
requests when making requests to a redfish service. This means that an
|
||||
ill-timed failure could cause python processes calling sushy to freeze
|
||||
indefinately.
|
|
@ -117,7 +117,11 @@ class Connector(object):
|
|||
:param data: Optional JSON data.
|
||||
:param headers: Optional dictionary of headers.
|
||||
:param blocking: Whether to block for asynchronous operations.
|
||||
:param timeout: Max time in seconds to wait for blocking async call.
|
||||
:param timeout: Max time in seconds to wait for blocking async call or
|
||||
for requests library to connect and read. If a custom
|
||||
timeout for requests is provided in
|
||||
extra_session_req_kwargs, it will be used instead for
|
||||
those calls.
|
||||
:param server_side_retries_left: Remaining retries. If not provided
|
||||
will use limit provided by instance's server_side_retries
|
||||
:param extra_session_req_kwargs: Optional keyword argument to pass
|
||||
|
@ -127,7 +131,6 @@ class Connector(object):
|
|||
:raises: ConnectionError
|
||||
:raises: HTTPError
|
||||
"""
|
||||
|
||||
if server_side_retries_left is None:
|
||||
server_side_retries_left = self._server_side_retries
|
||||
|
||||
|
@ -153,6 +156,7 @@ class Connector(object):
|
|||
response = self._session.request(method, url, json=data,
|
||||
headers=headers,
|
||||
verify=self._verify,
|
||||
timeout=timeout,
|
||||
**extra_session_req_kwargs)
|
||||
except requests.exceptions.RequestException as e:
|
||||
# Capture any general exception by looking for the parent
|
||||
|
@ -211,6 +215,7 @@ class Connector(object):
|
|||
method, url, json=data,
|
||||
headers=headers,
|
||||
verify=self._verify,
|
||||
timeout=timeout,
|
||||
**extra_session_req_kwargs)
|
||||
except exceptions.HTTPError as retry_exc:
|
||||
LOG.error("Failure occured while attempting to retry "
|
||||
|
|
|
@ -178,7 +178,7 @@ class ConnectorOpTestCase(base.TestCase):
|
|||
self.conn._op('GET', path='fake/path', headers=self.headers)
|
||||
self.request.assert_called_once_with(
|
||||
'GET', 'http://foo.bar:1234/fake/path',
|
||||
headers=self.headers, json=None, verify=True)
|
||||
headers=self.headers, json=None, verify=True, timeout=60)
|
||||
|
||||
def test_response_callback(self):
|
||||
mock_response_callback = mock.MagicMock()
|
||||
|
@ -193,21 +193,21 @@ class ConnectorOpTestCase(base.TestCase):
|
|||
self.request.assert_called_once_with(
|
||||
'GET', 'http://foo.bar:1234/fake/path',
|
||||
headers=self.headers, json=None, allow_redirects=False,
|
||||
verify=True)
|
||||
verify=True, timeout=60)
|
||||
|
||||
def test_ok_post(self):
|
||||
self.conn._op('POST', path='fake/path', data=self.data.copy(),
|
||||
headers=self.headers)
|
||||
self.request.assert_called_once_with(
|
||||
'POST', 'http://foo.bar:1234/fake/path',
|
||||
json=self.data, headers=self.headers, verify=True)
|
||||
json=self.data, headers=self.headers, verify=True, timeout=60)
|
||||
|
||||
def test_ok_put(self):
|
||||
self.conn._op('PUT', path='fake/path', data=self.data.copy(),
|
||||
headers=self.headers)
|
||||
self.request.assert_called_once_with(
|
||||
'PUT', 'http://foo.bar:1234/fake/path',
|
||||
json=self.data, headers=self.headers, verify=True)
|
||||
json=self.data, headers=self.headers, verify=True, timeout=60)
|
||||
|
||||
def test_ok_delete(self):
|
||||
expected_headers = self.headers.copy()
|
||||
|
@ -215,7 +215,7 @@ class ConnectorOpTestCase(base.TestCase):
|
|||
self.conn._op('DELETE', path='fake/path', headers=self.headers.copy())
|
||||
self.request.assert_called_once_with(
|
||||
'DELETE', 'http://foo.bar:1234/fake/path',
|
||||
headers=expected_headers, json=None, verify=True)
|
||||
headers=expected_headers, json=None, verify=True, timeout=60)
|
||||
|
||||
def test_ok_post_with_session(self):
|
||||
self.conn._session.headers = {}
|
||||
|
@ -227,7 +227,7 @@ class ConnectorOpTestCase(base.TestCase):
|
|||
data=self.data)
|
||||
self.request.assert_called_once_with(
|
||||
'POST', 'http://foo.bar:1234/fake/path',
|
||||
json=self.data, headers=expected_headers, verify=True)
|
||||
json=self.data, headers=expected_headers, verify=True, timeout=60)
|
||||
self.assertEqual(self.conn._session.headers,
|
||||
{'X-Auth-Token': 'asdf1234'})
|
||||
|
||||
|
@ -240,7 +240,7 @@ class ConnectorOpTestCase(base.TestCase):
|
|||
self.conn._op('GET', path=path, headers=headers)
|
||||
self.request.assert_called_once_with(
|
||||
'GET', 'http://foo.bar:1234' + path,
|
||||
headers=expected_headers, json=None, verify=True)
|
||||
headers=expected_headers, json=None, verify=True, timeout=60)
|
||||
|
||||
def test_odata_version_header_redfish_no_headers(self):
|
||||
path = '/redfish/v1/bar'
|
||||
|
@ -248,7 +248,7 @@ class ConnectorOpTestCase(base.TestCase):
|
|||
self.conn._op('GET', path=path)
|
||||
self.request.assert_called_once_with(
|
||||
'GET', 'http://foo.bar:1234' + path,
|
||||
headers=expected_headers, json=None, verify=True)
|
||||
headers=expected_headers, json=None, verify=True, timeout=60)
|
||||
|
||||
def test_odata_version_header_redfish_existing_header(self):
|
||||
path = '/redfish/v1/foo'
|
||||
|
@ -257,7 +257,7 @@ class ConnectorOpTestCase(base.TestCase):
|
|||
self.conn._op('GET', path=path, headers=headers)
|
||||
self.request.assert_called_once_with(
|
||||
'GET', 'http://foo.bar:1234' + path,
|
||||
headers=expected_headers, json=None, verify=True)
|
||||
headers=expected_headers, json=None, verify=True, timeout=60)
|
||||
|
||||
def test_timed_out_session_unable_to_create_session(self):
|
||||
self.conn._auth.can_refresh_session.return_value = False
|
||||
|
|
Loading…
Reference in New Issue