Merge "Requests session keyword arguments for sushy connector"

This commit is contained in:
Zuul 2018-10-24 11:32:05 +00:00 committed by Gerrit Code Review
commit 6e2d337a9b
3 changed files with 73 additions and 57 deletions

View File

@ -0,0 +1,5 @@
---
features:
- |
Adds functionality to pass different requests library session
arguments to sushy connector.

View File

@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import logging
import requests
@ -55,7 +54,8 @@ class Connector(object):
"""Close this connector and the associated HTTP session."""
self._session.close()
def _op(self, method, path='', data=None, headers=None):
def _op(self, method, path='', data=None, headers=None,
**extra_session_req_kwargs):
"""Generic RESTful request handler.
:param method: The HTTP method to be used, e.g: GET, POST,
@ -63,29 +63,24 @@ class Connector(object):
:param path: The sub-URI path to the resource.
:param data: Optional JSON data.
:param headers: Optional dictionary of headers.
:param extra_session_req_kwargs: Optional keyword argument to pass
requests library arguments which would pass on to requests session
object.
:returns: The response object from the requests library.
:raises: ConnectionError
:raises: HTTPError
"""
json_data = None
if headers is None:
headers = {}
if data is not None:
json_data = json.dumps(data)
headers['Content-Type'] = 'application/json'
url = parse.urljoin(self._url, path)
# TODO(lucasagomes): We should mask the data to remove sensitive
# information
LOG.debug('HTTP request: %(method)s %(url)s; '
'headers: %(headers)s; body: %(data)s',
LOG.debug('HTTP request: %(method)s %(url)s; headers: %(headers)s; '
'body: %(data)s; session arguments: %(session)s;',
{'method': method, 'url': url, 'headers': headers,
'data': json_data})
'data': data, 'session': extra_session_req_kwargs})
try:
response = self._session.request(method, url,
data=json_data,
headers=headers)
response = self._session.request(method, url, json=data,
headers=headers,
**extra_session_req_kwargs)
except requests.ConnectionError as e:
raise exceptions.ConnectionError(url=url, error=e)
# If we received an AccessError, and we
@ -99,9 +94,9 @@ class Connector(object):
self._auth.refresh_session()
LOG.debug("Authentication refreshed successfully, "
"retrying the call.")
response = self._session.request(method, url,
data=json_data,
headers=headers)
response = self._session.request(method, url, json=data,
headers=headers,
**extra_session_req_kwargs)
else:
raise
@ -112,65 +107,90 @@ class Connector(object):
return response
def get(self, path='', data=None, headers=None):
def get(self, path='', data=None, headers=None,
**extra_session_req_kwargs):
"""HTTP GET method.
:param path: Optional sub-URI path to the resource.
:param data: Optional JSON data.
:param headers: Optional dictionary of headers.
:param extra_session_req_kwargs: Optional keyword argument to pass
requests library arguments which would pass on to requests session
object.
:returns: The response object from the requests library.
:raises: ConnectionError
:raises: HTTPError
"""
return self._op('GET', path, data, headers)
return self._op('GET', path, data=data, headers=headers,
**extra_session_req_kwargs)
def post(self, path='', data=None, headers=None):
def post(self, path='', data=None, headers=None,
**extra_session_req_kwargs):
"""HTTP POST method.
:param path: Optional sub-URI path to the resource.
:param data: Optional JSON data.
:param headers: Optional dictionary of headers.
:param extra_session_req_kwargs: Optional keyword argument to pass
requests library arguments which would pass on to requests session
object.
:returns: The response object from the requests library.
:raises: ConnectionError
:raises: HTTPError
"""
return self._op('POST', path, data, headers)
return self._op('POST', path, data=data, headers=headers,
**extra_session_req_kwargs)
def patch(self, path='', data=None, headers=None):
def patch(self, path='', data=None, headers=None,
**extra_session_req_kwargs):
"""HTTP PATCH method.
:param path: Optional sub-URI path to the resource.
:param data: Optional JSON data.
:param headers: Optional dictionary of headers.
:param extra_session_req_kwargs: Optional keyword argument to pass
requests library arguments which would pass on to requests session
object.
:returns: The response object from the requests library.
:raises: ConnectionError
:raises: HTTPError
"""
return self._op('PATCH', path, data, headers)
return self._op('PATCH', path, data=data, headers=headers,
**extra_session_req_kwargs)
def put(self, path='', data=None, headers=None):
def put(self, path='', data=None, headers=None,
**extra_session_req_kwargs):
"""HTTP PUT method.
:param path: Optional sub-URI path to the resource.
:param data: Optional JSON data.
:param headers: Optional dictionary of headers.
:param extra_session_req_kwargs: Optional keyword argument to pass
requests library arguments which would pass on to requests session
object.
:returns: The response object from the requests library.
:raises: ConnectionError
:raises: HTTPError
"""
return self._op('PUT', path, data, headers)
return self._op('PUT', path, data=data, headers=headers,
**extra_session_req_kwargs)
def delete(self, path='', data=None, headers=None):
def delete(self, path='', data=None, headers=None,
**extra_session_req_kwargs):
"""HTTP DELETE method.
:param path: Optional sub-URI path to the resource.
:param data: Optional JSON data.
:param headers: Optional dictionary of headers.
:param extra_session_req_kwargs: Optional keyword argument to pass
requests library arguments which would pass on to requests session
object.
:returns: The response object from the requests library.
:raises: ConnectionError
:raises: HTTPError
"""
return self._op('DELETE', path, data, headers)
return self._op('DELETE', path, data=data, headers=headers,
**extra_session_req_kwargs)
def __enter__(self):
return self

View File

@ -48,35 +48,35 @@ class ConnectorMethodsTestCase(base.TestCase):
self.conn.get(path='fake/path', data=self.data.copy(),
headers=self.headers.copy())
mock__op.assert_called_once_with(mock.ANY, 'GET', 'fake/path',
self.data, self.headers)
data=self.data, headers=self.headers)
@mock.patch.object(connector.Connector, '_op', autospec=True)
def test_post(self, mock__op):
self.conn.post(path='fake/path', data=self.data.copy(),
headers=self.headers.copy())
mock__op.assert_called_once_with(mock.ANY, 'POST', 'fake/path',
self.data, self.headers)
data=self.data, headers=self.headers)
@mock.patch.object(connector.Connector, '_op', autospec=True)
def test_patch(self, mock__op):
self.conn.patch(path='fake/path', data=self.data.copy(),
headers=self.headers.copy())
mock__op.assert_called_once_with(mock.ANY, 'PATCH', 'fake/path',
self.data, self.headers)
data=self.data, headers=self.headers)
@mock.patch.object(connector.Connector, '_op', autospec=True)
def test_put(self, mock__op):
self.conn.put(path='fake/path', data=self.data.copy(),
headers=self.headers.copy())
mock__op.assert_called_once_with(mock.ANY, 'PUT', 'fake/path',
self.data, self.headers)
data=self.data, headers=self.headers)
@mock.patch.object(connector.Connector, '_op', autospec=True)
def test_delete(self, mock__op):
self.conn.delete(path='fake/path', data=self.data.copy(),
headers=self.headers.copy())
mock__op.assert_called_once_with(mock.ANY, 'DELETE', 'fake/path',
self.data, self.headers)
data=self.data, headers=self.headers)
def test_set_auth(self):
mock_auth = mock.MagicMock()
@ -119,59 +119,52 @@ class ConnectorOpTestCase(base.TestCase):
self.request.return_value.status_code = http_client.OK
def test_ok_get(self):
expected_headers = self.headers.copy()
self.conn._op('GET', path='fake/path', headers=self.headers)
self.request.assert_called_once_with(
'GET', 'http://foo.bar:1234/fake/path',
data=None, headers=expected_headers)
headers=self.headers, json=None)
def test_ok_get_url_redirect_false(self):
self.conn._op('GET', path='fake/path', headers=self.headers,
allow_redirects=False)
self.request.assert_called_once_with(
'GET', 'http://foo.bar:1234/fake/path',
headers=self.headers, json=None, allow_redirects=False)
def test_ok_post(self):
expected_headers = self.headers.copy()
expected_headers['Content-Type'] = 'application/json'
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',
data=json.dumps(self.data), headers=expected_headers)
json=self.data, headers=self.headers)
def test_ok_put(self):
expected_headers = self.headers.copy()
expected_headers['Content-Type'] = 'application/json'
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',
data=json.dumps(self.data), headers=expected_headers)
json=self.data, headers=self.headers)
def test_ok_delete(self):
expected_headers = self.headers.copy()
self.conn._op('DELETE', path='fake/path', headers=self.headers.copy())
self.request.assert_called_once_with(
'DELETE', 'http://foo.bar:1234/fake/path',
data=None, headers=expected_headers)
headers=self.headers, json=None)
def test_ok_post_with_session(self):
self.conn._session.headers = {}
self.conn._session.headers['X-Auth-Token'] = 'asdf1234'
expected_headers = self.headers.copy()
expected_headers['Content-Type'] = 'application/json'
self.conn._op('POST', path='fake/path', data=self.data,
headers=self.headers)
self.conn._op('POST', path='fake/path', headers=self.headers,
data=self.data)
self.request.assert_called_once_with(
'POST', 'http://foo.bar:1234/fake/path',
data=json.dumps(self.data), headers=expected_headers)
json=self.data, headers=expected_headers)
self.assertEqual(self.conn._session.headers,
{'X-Auth-Token': 'asdf1234'})
def test_timed_out_session_unable_to_create_session(self):
self.conn._auth.can_refresh_session.return_value = False
expected_headers = self.headers.copy()
expected_headers['Content-Type'] = 'application/json'
self.conn._session = self.session
self.request = self.session.request
self.request.return_value.status_code = http_client.FORBIDDEN
@ -190,8 +183,6 @@ class ConnectorOpTestCase(base.TestCase):
self.session = mock.Mock(spec=requests.Session)
self.conn._session = self.session
self.request = self.session.request
first_expected_headers = self.headers.copy()
first_expected_headers['Content-Type'] = 'application/json'
first_response = mock.Mock()
first_response.status_code = http_client.FORBIDDEN
second_response = mock.Mock()