Refactor HTTPClient to use two request methods
Rather than depend on magic, I would prefer that we explicitly call two different request methods: json_request and raw_request. The former will encode/decode request bodies to and from JSON, while the latter will not. Change-Id: I6a429a5975993f71df85df55f11c5d51c050c289
This commit is contained in:
parent
623a0898f8
commit
3943699427
|
@ -17,9 +17,6 @@
|
|||
Base utilities to build API operation managers and objects on top of.
|
||||
"""
|
||||
|
||||
from glanceclient.common import exceptions
|
||||
|
||||
|
||||
# Python 2.4 compat
|
||||
try:
|
||||
all
|
||||
|
@ -50,11 +47,7 @@ class Manager(object):
|
|||
self.api = api
|
||||
|
||||
def _list(self, url, response_key, obj_class=None, body=None):
|
||||
resp = None
|
||||
if body:
|
||||
resp, body = self.api.post(url, body=body)
|
||||
else:
|
||||
resp, body = self.api.get(url)
|
||||
resp, body = self.api.json_request('GET', url)
|
||||
|
||||
if obj_class is None:
|
||||
obj_class = self.resource_class
|
||||
|
@ -62,29 +55,11 @@ class Manager(object):
|
|||
data = body[response_key]
|
||||
return [obj_class(self, res, loaded=True) for res in data if res]
|
||||
|
||||
def _get(self, url, response_key):
|
||||
resp, body = self.api.get(url)
|
||||
return self.resource_class(self, body[response_key])
|
||||
|
||||
def _create(self, url, body, response_key, return_raw=False):
|
||||
resp, body = self.api.post(url, body=body)
|
||||
if return_raw:
|
||||
return body[response_key]
|
||||
return self.resource_class(self, body[response_key])
|
||||
|
||||
def _delete(self, url):
|
||||
self.api.delete(url)
|
||||
self.api.raw_request('DELETE', url)
|
||||
|
||||
def _update(self, url, body, response_key=None, method="PUT"):
|
||||
methods = {"PUT": self.api.put,
|
||||
"POST": self.api.post}
|
||||
try:
|
||||
_method = methods[method]
|
||||
except KeyError:
|
||||
msg = "Invalid update method: %s" % method
|
||||
raise exceptions.ClientException(msg)
|
||||
|
||||
resp, body = _method(url, body=body)
|
||||
def _update(self, url, body, response_key=None):
|
||||
resp, body = self.api.json_request('PUT', url, body=body)
|
||||
# PUT requests may not return a body
|
||||
if body:
|
||||
return self.resource_class(self, body[response_key])
|
||||
|
|
|
@ -69,30 +69,16 @@ class HTTPClient(httplib2.Http):
|
|||
Wrapper around httplib2.Http.request to handle tasks such as
|
||||
setting headers, JSON encoding/decoding, and error handling.
|
||||
"""
|
||||
url = self.endpoint + url
|
||||
|
||||
# Copy the kwargs so we can reuse the original in case of redirects
|
||||
_kwargs = copy.copy(kwargs)
|
||||
_kwargs.setdefault('headers', kwargs.get('headers', {}))
|
||||
_kwargs['headers']['User-Agent'] = USER_AGENT
|
||||
if 'raw_body' in _kwargs:
|
||||
raw_body = _kwargs.pop('raw_body')
|
||||
if raw_body is not None:
|
||||
_kwargs['headers']['Content-Type'] = 'application/octet-stream'
|
||||
_kwargs['body'] = raw_body
|
||||
elif 'body' in kwargs and kwargs['body'] is not None:
|
||||
_kwargs['headers']['Content-Type'] = 'application/json'
|
||||
_kwargs['body'] = json.dumps(kwargs['body'])
|
||||
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
|
||||
kwargs['headers'].setdefault('User-Agent', USER_AGENT)
|
||||
if self.auth_token:
|
||||
kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
|
||||
|
||||
resp, body = super(HTTPClient, self).request(url, method, **_kwargs)
|
||||
self.http_log((url, method,), _kwargs, resp, body)
|
||||
|
||||
if body:
|
||||
try:
|
||||
body = json.loads(body)
|
||||
except ValueError:
|
||||
logger.debug("Could not decode JSON from body: %s" % body)
|
||||
else:
|
||||
logger.debug("No body was returned.")
|
||||
body = None
|
||||
resp, body = super(HTTPClient, self).request(url, method, **kwargs)
|
||||
self.http_log((url, method,), kwargs, resp, body)
|
||||
|
||||
if 400 <= resp.status < 600:
|
||||
logger.exception("Request returned failure status.")
|
||||
|
@ -103,26 +89,28 @@ class HTTPClient(httplib2.Http):
|
|||
|
||||
return resp, body
|
||||
|
||||
def request(self, url, method, **kwargs):
|
||||
def json_request(self, method, url, **kwargs):
|
||||
kwargs.setdefault('headers', {})
|
||||
if self.auth_token:
|
||||
kwargs['headers']['X-Auth-Token'] = self.auth_token
|
||||
kwargs['headers'].setdefault('Content-Type', 'application/json')
|
||||
|
||||
if 'body' in kwargs:
|
||||
kwargs['body'] = json.dumps(kwargs['body'])
|
||||
|
||||
resp, body = self._http_request(url, method, **kwargs)
|
||||
|
||||
if body:
|
||||
try:
|
||||
body = json.loads(body)
|
||||
except ValueError:
|
||||
logger.debug("Could not decode JSON from body: %s" % body)
|
||||
else:
|
||||
logger.debug("No body was returned.")
|
||||
body = None
|
||||
|
||||
req_url = self.endpoint + url
|
||||
resp, body = self._http_request(req_url, method, **kwargs)
|
||||
return resp, body
|
||||
|
||||
def head(self, url, **kwargs):
|
||||
return self.request(url, 'HEAD', **kwargs)
|
||||
|
||||
def get(self, url, **kwargs):
|
||||
return self.request(url, 'GET', **kwargs)
|
||||
|
||||
def post(self, url, **kwargs):
|
||||
return self.request(url, 'POST', **kwargs)
|
||||
|
||||
def put(self, url, **kwargs):
|
||||
return self.request(url, 'PUT', **kwargs)
|
||||
|
||||
def delete(self, url, **kwargs):
|
||||
return self.request(url, 'DELETE', **kwargs)
|
||||
def raw_request(self, method, url, **kwargs):
|
||||
kwargs.setdefault('headers', {})
|
||||
kwargs['headers'].setdefault('Content-Type',
|
||||
'application/octet-stream')
|
||||
return self._http_request(url, method, **kwargs)
|
||||
|
|
|
@ -34,7 +34,7 @@ class ImageMemberManager(base.Manager):
|
|||
def get(self, image, member_id):
|
||||
image_id = base.getid(image)
|
||||
url = '/v1/images/%s/members/%s' % (image_id, member_id)
|
||||
resp, body = self.api.get(url)
|
||||
resp, body = self.api.json_request('GET', url)
|
||||
member = body['member']
|
||||
member['image_id'] = image_id
|
||||
return ImageMember(self, member, loaded=True)
|
||||
|
@ -59,7 +59,8 @@ class ImageMemberManager(base.Manager):
|
|||
|
||||
def _list_by_image(self, image):
|
||||
image_id = base.getid(image)
|
||||
resp, body = self.api.get('/v1/images/%s/members' % image_id)
|
||||
url = '/v1/images/%s/members' % image_id
|
||||
resp, body = self.api.json_request('GET', url)
|
||||
out = []
|
||||
for member in body['members']:
|
||||
member['image_id'] = image_id
|
||||
|
@ -68,7 +69,8 @@ class ImageMemberManager(base.Manager):
|
|||
|
||||
def _list_by_member(self, member):
|
||||
member_id = base.getid(member)
|
||||
resp, body = self.api.get('/v1/shared-images/%s' % member_id)
|
||||
url = '/v1/shared-images/%s' % member_id
|
||||
resp, body = self.api.json_request('GET', url)
|
||||
out = []
|
||||
for member in body['shared_images']:
|
||||
member['member_id'] = member_id
|
||||
|
@ -98,4 +100,4 @@ class ImageMemberManager(base.Manager):
|
|||
obj['can_share'] = member['can_share']
|
||||
memberships.append(obj)
|
||||
url = '/v1/images/%s/members' % base.getid(image)
|
||||
self.api.put(url, {}, {'memberships': memberships})
|
||||
self.api.json_request('PUT', url, {}, {'memberships': memberships})
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import copy
|
||||
import errno
|
||||
import json
|
||||
import os
|
||||
import urllib
|
||||
|
||||
|
@ -67,7 +68,7 @@ class ImageManager(base.Manager):
|
|||
:param image: image object or id to look up
|
||||
:rtype: :class:`Image`
|
||||
"""
|
||||
resp, body = self.api.head('/v1/images/%s' % image_id)
|
||||
resp, body = self.api.raw_request('HEAD', '/v1/images/%s' % image_id)
|
||||
meta = self._image_meta_from_headers(resp)
|
||||
return Image(self, meta)
|
||||
|
||||
|
@ -142,9 +143,9 @@ class ImageManager(base.Manager):
|
|||
if copy_from is not None:
|
||||
hdrs['x-glance-api-copy-from'] = copy_from
|
||||
|
||||
resp, body = self.api.post('/v1/images', headers=hdrs,
|
||||
raw_body=image_data)
|
||||
return Image(self, body['image'])
|
||||
resp, body = self.api.raw_request(
|
||||
'POST', '/v1/images', headers=hdrs, body=image_data)
|
||||
return Image(self, json.loads(body)['image'])
|
||||
|
||||
def update(self, image, **kwargs):
|
||||
"""Update an image
|
||||
|
@ -172,7 +173,7 @@ class ImageManager(base.Manager):
|
|||
if copy_from is not None:
|
||||
hdrs['x-glance-api-copy-from'] = copy_from
|
||||
|
||||
image_id = base.getid(image)
|
||||
resp, body = self.api.put('/v1/images/%s' % image_id, headers=hdrs,
|
||||
raw_body=image_data)
|
||||
return Image(self, body['image'])
|
||||
url = '/v1/images/%s' % base.getid(image)
|
||||
resp, body = self.api.raw_request(
|
||||
'PUT', url, headers=hdrs, body=image_data)
|
||||
return Image(self, json.loads(body)['image'])
|
||||
|
|
|
@ -13,23 +13,29 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
|
||||
|
||||
fixtures = {
|
||||
'/v1/images': {
|
||||
'POST': (
|
||||
{
|
||||
'location': '/v1/images/1',
|
||||
},
|
||||
{'image': {
|
||||
'id': '1',
|
||||
'name': 'image-1',
|
||||
'container_format': 'ovf',
|
||||
'disk_format': 'vhd',
|
||||
'owner': 'asdf',
|
||||
'size': '1024',
|
||||
'min_ram': '512',
|
||||
'min_disk': '10',
|
||||
'properties': {'a': 'b', 'c': 'd'},
|
||||
}}),
|
||||
json.dumps(
|
||||
{'image': {
|
||||
'id': '1',
|
||||
'name': 'image-1',
|
||||
'container_format': 'ovf',
|
||||
'disk_format': 'vhd',
|
||||
'owner': 'asdf',
|
||||
'size': '1024',
|
||||
'min_ram': '512',
|
||||
'min_disk': '10',
|
||||
'properties': {'a': 'b', 'c': 'd'},
|
||||
}},
|
||||
),
|
||||
),
|
||||
},
|
||||
'/v1/images/detail': {
|
||||
'GET': (
|
||||
|
@ -53,17 +59,19 @@ fixtures = {
|
|||
None),
|
||||
'PUT': (
|
||||
{},
|
||||
{'image': {
|
||||
'id': '1',
|
||||
'name': 'image-2',
|
||||
'container_format': 'ovf',
|
||||
'disk_format': 'vhd',
|
||||
'owner': 'asdf',
|
||||
'size': '1024',
|
||||
'min_ram': '512',
|
||||
'min_disk': '10',
|
||||
'properties': {'a': 'b', 'c': 'd'},
|
||||
}},
|
||||
json.dumps(
|
||||
{'image': {
|
||||
'id': '1',
|
||||
'name': 'image-2',
|
||||
'container_format': 'ovf',
|
||||
'disk_format': 'vhd',
|
||||
'owner': 'asdf',
|
||||
'size': '1024',
|
||||
'min_ram': '512',
|
||||
'min_disk': '10',
|
||||
'properties': {'a': 'b', 'c': 'd'},
|
||||
}},
|
||||
),
|
||||
),
|
||||
'DELETE': ({}, None),
|
||||
},
|
||||
|
@ -110,17 +118,8 @@ class FakeAPI(object):
|
|||
url = url.split('?', 1)[0]
|
||||
return fixtures[url][method]
|
||||
|
||||
def get(self, url):
|
||||
return self._request('GET', url)
|
||||
def raw_request(self, *args, **kwargs):
|
||||
return self._request(*args, **kwargs)
|
||||
|
||||
def head(self, url):
|
||||
return self._request('HEAD', url)
|
||||
|
||||
def post(self, url, headers=None, body=None, raw_body=None):
|
||||
return self._request('POST', url, headers, body or raw_body)
|
||||
|
||||
def put(self, url, headers=None, body=None, raw_body=None):
|
||||
return self._request('PUT', url, headers, body or raw_body)
|
||||
|
||||
def delete(self, url):
|
||||
return self._request('DELETE', url)
|
||||
def json_request(self, *args, **kwargs):
|
||||
return self._request(*args, **kwargs)
|
||||
|
|
Loading…
Reference in New Issue