From d54faad0425d95f31913b81dd8c0fbd97605fe4e Mon Sep 17 00:00:00 2001 From: Andrey Kurilin Date: Wed, 16 Apr 2014 17:29:15 +0300 Subject: [PATCH] Reuse class Manager from common code Class `Managers` from `glanceclient.common.base` module is similar to class `apiclient:ManagerWithFind` from common code. In this patch: - class glanceclient.common.base:Managers replaced by apiclient:ManagerWithFind - module glanceclient.common.base marked as 'deprecated' Related to bp common-client-library-2 Change-Id: I41da4a9188e97ca2c07b6234fc2ac0a877553d3f --- glanceclient/common/base.py | 72 +++++--------------------------- glanceclient/common/http.py | 30 +++++++++++++ glanceclient/v1/image_members.py | 14 +++---- glanceclient/v1/images.py | 29 ++++++++----- tests/test_base.py | 2 +- tests/utils.py | 26 ++++++++++++ tests/v1/test_images.py | 5 +++ 7 files changed, 99 insertions(+), 79 deletions(-) diff --git a/glanceclient/common/base.py b/glanceclient/common/base.py index 85229a0c..55f265e4 100644 --- a/glanceclient/common/base.py +++ b/glanceclient/common/base.py @@ -15,71 +15,21 @@ """ Base utilities to build API operation managers and objects on top of. + +DEPRECATED post v.0.12.0. Use 'glanceclient.openstack.common.apiclient.base' +instead of this module." """ -import copy +import warnings from glanceclient.openstack.common.apiclient import base -# Python 2.4 compat -try: - all -except NameError: - def all(iterable): - return True not in (not x for x in iterable) + +warnings.warn("The 'glanceclient.common.base' module is deprecated post " + "v.0.12.0. Use 'glanceclient.openstack.common.apiclient.base' " + "instead of this one.", DeprecationWarning) -def getid(obj): - """ - Abstracts the common pattern of allowing both an object or an object's ID - (UUID) as a parameter when dealing with relationships. - """ - try: - return obj.id - except AttributeError: - return obj - - -class Manager(object): - """ - Managers interact with a particular type of API (servers, flavors, images, - etc.) and provide CRUD operations for them. - """ - resource_class = None - - def __init__(self, api): - self.api = api - - def _list(self, url, response_key, obj_class=None, body=None): - resp, body = self.api.json_request('GET', url) - - if obj_class is None: - obj_class = self.resource_class - - data = body[response_key] - return ([obj_class(self, res, loaded=True) for res in data if res], - resp) - - def _delete(self, url): - resp = self.api.raw_request('DELETE', url) - return resp[0] - - 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]) - - -class Resource(base.Resource): - """ - A resource represents a particular instance of an object (tenant, user, - etc). This is pretty much just a bag for attributes. - """ - - def to_dict(self): - # Note(akurilin): There is a patch in Oslo, that adds to_dict() method - # to common Resource - I1db6c12a1f798de7f7fafd0c34fb0ef523610153. - # When Oslo code comes, we will be in able to remove this method - # and class at all. - return copy.deepcopy(self._info) +getid = base.getid +Manager = base.ManagerWithFind +Resource = base.Resource diff --git a/glanceclient/common/http.py b/glanceclient/common/http.py index 2bc62573..f0165928 100644 --- a/glanceclient/common/http.py +++ b/glanceclient/common/http.py @@ -327,6 +327,36 @@ class HTTPClient(object): return self._http_request(url, method, **kwargs) + def client_request(self, method, url, **kwargs): + # NOTE(akurilin): this method provides compatibility with methods which + # expects requests.Response object(for example - methods of + # class Managers from common code). + if 'json' in kwargs and 'body' not in kwargs: + kwargs['body'] = kwargs.pop('json') + resp, body = self.json_request(method, url, **kwargs) + resp.json = lambda: body + resp.content = bool(body) + resp.status_code = resp.status + return resp + + def head(self, url, **kwargs): + return self.client_request("HEAD", url, **kwargs) + + def get(self, url, **kwargs): + return self.client_request("GET", url, **kwargs) + + def post(self, url, **kwargs): + return self.client_request("POST", url, **kwargs) + + def put(self, url, **kwargs): + return self.client_request("PUT", url, **kwargs) + + def delete(self, url, **kwargs): + return self.raw_request("DELETE", url, **kwargs) + + def patch(self, url, **kwargs): + return self.client_request("PATCH", url, **kwargs) + class OpenSSLConnectionDelegator(object): """ diff --git a/glanceclient/v1/image_members.py b/glanceclient/v1/image_members.py index 77c7f65b..f464fb8c 100644 --- a/glanceclient/v1/image_members.py +++ b/glanceclient/v1/image_members.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from glanceclient.common import base +from glanceclient.openstack.common.apiclient import base class ImageMember(base.Resource): @@ -28,13 +28,13 @@ class ImageMember(base.Resource): self.manager.delete(self) -class ImageMemberManager(base.Manager): +class ImageMemberManager(base.ManagerWithFind): resource_class = ImageMember 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.json_request('GET', url) + resp, body = self.client.json_request('GET', url) member = body['member'] member['image_id'] = image_id return ImageMember(self, member, loaded=True) @@ -60,7 +60,7 @@ class ImageMemberManager(base.Manager): def _list_by_image(self, image): image_id = base.getid(image) url = '/v1/images/%s/members' % image_id - resp, body = self.api.json_request('GET', url) + resp, body = self.client.json_request('GET', url) out = [] for member in body['members']: member['image_id'] = image_id @@ -70,7 +70,7 @@ class ImageMemberManager(base.Manager): def _list_by_member(self, member): member_id = base.getid(member) url = '/v1/shared-images/%s' % member_id - resp, body = self.api.json_request('GET', url) + resp, body = self.client.json_request('GET', url) out = [] for member in body['shared_images']: member['member_id'] = member_id @@ -84,7 +84,7 @@ class ImageMemberManager(base.Manager): """Creates an image.""" url = '/v1/images/%s/members/%s' % (base.getid(image), member_id) body = {'member': {'can_share': can_share}} - self._update(url, body=body) + self._put(url, json=body) def replace(self, image, members): memberships = [] @@ -100,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.json_request('PUT', url, {}, {'memberships': memberships}) + self.client.json_request('PUT', url, {}, {'memberships': memberships}) diff --git a/glanceclient/v1/images.py b/glanceclient/v1/images.py index 19b24329..ca4ff1e9 100644 --- a/glanceclient/v1/images.py +++ b/glanceclient/v1/images.py @@ -19,8 +19,8 @@ import json import six from six.moves.urllib import parse -from glanceclient.common import base from glanceclient.common import utils +from glanceclient.openstack.common.apiclient import base from glanceclient.openstack.common import strutils UPDATE_PARAMS = ('name', 'disk_format', 'container_format', 'min_disk', @@ -56,9 +56,19 @@ class Image(base.Resource): return self.manager.data(self, **kwargs) -class ImageManager(base.Manager): +class ImageManager(base.ManagerWithFind): resource_class = Image + def _list(self, url, response_key, obj_class=None, body=None): + resp = self.client.get(url) + + if obj_class is None: + obj_class = self.resource_class + + data = resp.json()[response_key] + return ([obj_class(self, res, loaded=True) for res in data if res], + resp) + def _image_meta_from_headers(self, headers): meta = {'properties': {}} safe_decode = strutils.safe_decode @@ -112,10 +122,9 @@ class ImageManager(base.Manager): :param image: image object or id to look up :rtype: :class:`Image` """ - image_id = base.getid(image) - resp, body = self.api.raw_request('HEAD', '/v1/images/%s' - % parse.quote(str(image_id))) + resp, body = self.client.raw_request( + 'HEAD', '/v1/images/%s' % parse.quote(str(image_id))) meta = self._image_meta_from_headers(dict(resp.getheaders())) return_request_id = kwargs.get('return_req_id', None) if return_request_id is not None: @@ -131,8 +140,8 @@ class ImageManager(base.Manager): :rtype: iterable containing image data """ image_id = base.getid(image) - resp, body = self.api.raw_request('GET', '/v1/images/%s' - % parse.quote(str(image_id))) + resp, body = self.client.raw_request( + 'GET', '/v1/images/%s' % parse.quote(str(image_id))) checksum = resp.getheader('x-image-meta-checksum', None) if do_checksum and checksum is not None: body.set_checksum(checksum) @@ -244,7 +253,7 @@ class ImageManager(base.Manager): def delete(self, image, **kwargs): """Delete an image.""" - resp = self._delete("/v1/images/%s" % base.getid(image)) + resp = self._delete("/v1/images/%s" % base.getid(image))[0] return_request_id = kwargs.get('return_req_id', None) if return_request_id is not None: return_request_id.append(resp.getheader(OS_REQ_ID_HDR, None)) @@ -275,7 +284,7 @@ class ImageManager(base.Manager): if copy_from is not None: hdrs['x-glance-api-copy-from'] = copy_from - resp, body_iter = self.api.raw_request( + resp, body_iter = self.client.raw_request( 'POST', '/v1/images', headers=hdrs, body=image_data) body = json.loads(''.join([c for c in body_iter])) return_request_id = kwargs.get('return_req_id', None) @@ -319,7 +328,7 @@ class ImageManager(base.Manager): hdrs['x-glance-api-copy-from'] = copy_from url = '/v1/images/%s' % base.getid(image) - resp, body_iter = self.api.raw_request( + resp, body_iter = self.client.raw_request( 'PUT', url, headers=hdrs, body=image_data) body = json.loads(''.join([c for c in body_iter])) return_request_id = kwargs.get('return_req_id', None) diff --git a/tests/test_base.py b/tests/test_base.py index fb51ae67..4a97de80 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -16,7 +16,7 @@ import testtools -from glanceclient.common import base +from glanceclient.openstack.common.apiclient import base class TestBase(testtools.TestCase): diff --git a/tests/utils.py b/tests/utils.py index b7ca3aff..2eb5f02d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -44,6 +44,32 @@ class FakeAPI(object): fixture = self._request(*args, **kwargs) return FakeResponse(fixture[0]), fixture[1] + def client_request(self, method, url, **kwargs): + if 'json' in kwargs and 'body' not in kwargs: + kwargs['body'] = kwargs.pop('json') + resp, body = self.json_request(method, url, **kwargs) + resp.json = lambda: body + resp.content = bool(body) + return resp + + def head(self, url, **kwargs): + return self.client_request("HEAD", url, **kwargs) + + def get(self, url, **kwargs): + return self.client_request("GET", url, **kwargs) + + def post(self, url, **kwargs): + return self.client_request("POST", url, **kwargs) + + def put(self, url, **kwargs): + return self.client_request("PUT", url, **kwargs) + + def delete(self, url, **kwargs): + return self.raw_request("DELETE", url, **kwargs) + + def patch(self, url, **kwargs): + return self.client_request("PATCH", url, **kwargs) + class FakeResponse(object): def __init__(self, headers, body=None, diff --git a/tests/v1/test_images.py b/tests/v1/test_images.py index 63df002e..6c0fde15 100644 --- a/tests/v1/test_images.py +++ b/tests/v1/test_images.py @@ -837,6 +837,7 @@ class ImageTest(testtools.TestCase): image = self.mgr.get('1') image.delete() expect = [ + ('HEAD', '/v1/images/1', {}, None), ('HEAD', '/v1/images/1', {}, None), ('DELETE', '/v1/images/1', {}, None), ] @@ -846,6 +847,7 @@ class ImageTest(testtools.TestCase): image = self.mgr.get('1') image.update(name='image-5') expect = [ + ('HEAD', '/v1/images/1', {}, None), ('HEAD', '/v1/images/1', {}, None), ('PUT', '/v1/images/1', {'x-image-meta-name': 'image-5'}, None), ] @@ -855,6 +857,7 @@ class ImageTest(testtools.TestCase): image = self.mgr.get('1') data = ''.join([b for b in image.data()]) expect = [ + ('HEAD', '/v1/images/1', {}, None), ('HEAD', '/v1/images/1', {}, None), ('GET', '/v1/images/1', {}, None), ] @@ -870,6 +873,7 @@ class ImageTest(testtools.TestCase): image = self.mgr.get('2') data = ''.join([b for b in image.data(do_checksum=False)]) expect = [ + ('HEAD', '/v1/images/2', {}, None), ('HEAD', '/v1/images/2', {}, None), ('GET', '/v1/images/2', {}, None), ] @@ -891,6 +895,7 @@ class ImageTest(testtools.TestCase): image = self.mgr.get('3') data = ''.join([b for b in image.data(do_checksum=False)]) expect = [ + ('HEAD', '/v1/images/3', {}, None), ('HEAD', '/v1/images/3', {}, None), ('GET', '/v1/images/3', {}, None), ]