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
This commit is contained in:
Andrey Kurilin
2014-04-16 17:29:15 +03:00
parent dbefc1a3b1
commit d54faad042
7 changed files with 99 additions and 79 deletions

View File

@@ -15,71 +15,21 @@
""" """
Base utilities to build API operation managers and objects on top of. 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 from glanceclient.openstack.common.apiclient import base
# Python 2.4 compat
try: warnings.warn("The 'glanceclient.common.base' module is deprecated post "
all "v.0.12.0. Use 'glanceclient.openstack.common.apiclient.base' "
except NameError: "instead of this one.", DeprecationWarning)
def all(iterable):
return True not in (not x for x in iterable)
def getid(obj): getid = base.getid
""" Manager = base.ManagerWithFind
Abstracts the common pattern of allowing both an object or an object's ID Resource = base.Resource
(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)

View File

@@ -327,6 +327,36 @@ class HTTPClient(object):
return self._http_request(url, method, **kwargs) 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): class OpenSSLConnectionDelegator(object):
""" """

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from glanceclient.common import base from glanceclient.openstack.common.apiclient import base
class ImageMember(base.Resource): class ImageMember(base.Resource):
@@ -28,13 +28,13 @@ class ImageMember(base.Resource):
self.manager.delete(self) self.manager.delete(self)
class ImageMemberManager(base.Manager): class ImageMemberManager(base.ManagerWithFind):
resource_class = ImageMember resource_class = ImageMember
def get(self, image, member_id): def get(self, image, member_id):
image_id = base.getid(image) image_id = base.getid(image)
url = '/v1/images/%s/members/%s' % (image_id, member_id) 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 = body['member']
member['image_id'] = image_id member['image_id'] = image_id
return ImageMember(self, member, loaded=True) return ImageMember(self, member, loaded=True)
@@ -60,7 +60,7 @@ class ImageMemberManager(base.Manager):
def _list_by_image(self, image): def _list_by_image(self, image):
image_id = base.getid(image) image_id = base.getid(image)
url = '/v1/images/%s/members' % image_id url = '/v1/images/%s/members' % image_id
resp, body = self.api.json_request('GET', url) resp, body = self.client.json_request('GET', url)
out = [] out = []
for member in body['members']: for member in body['members']:
member['image_id'] = image_id member['image_id'] = image_id
@@ -70,7 +70,7 @@ class ImageMemberManager(base.Manager):
def _list_by_member(self, member): def _list_by_member(self, member):
member_id = base.getid(member) member_id = base.getid(member)
url = '/v1/shared-images/%s' % member_id url = '/v1/shared-images/%s' % member_id
resp, body = self.api.json_request('GET', url) resp, body = self.client.json_request('GET', url)
out = [] out = []
for member in body['shared_images']: for member in body['shared_images']:
member['member_id'] = member_id member['member_id'] = member_id
@@ -84,7 +84,7 @@ class ImageMemberManager(base.Manager):
"""Creates an image.""" """Creates an image."""
url = '/v1/images/%s/members/%s' % (base.getid(image), member_id) url = '/v1/images/%s/members/%s' % (base.getid(image), member_id)
body = {'member': {'can_share': can_share}} body = {'member': {'can_share': can_share}}
self._update(url, body=body) self._put(url, json=body)
def replace(self, image, members): def replace(self, image, members):
memberships = [] memberships = []
@@ -100,4 +100,4 @@ class ImageMemberManager(base.Manager):
obj['can_share'] = member['can_share'] obj['can_share'] = member['can_share']
memberships.append(obj) memberships.append(obj)
url = '/v1/images/%s/members' % base.getid(image) url = '/v1/images/%s/members' % base.getid(image)
self.api.json_request('PUT', url, {}, {'memberships': memberships}) self.client.json_request('PUT', url, {}, {'memberships': memberships})

View File

@@ -19,8 +19,8 @@ import json
import six import six
from six.moves.urllib import parse from six.moves.urllib import parse
from glanceclient.common import base
from glanceclient.common import utils from glanceclient.common import utils
from glanceclient.openstack.common.apiclient import base
from glanceclient.openstack.common import strutils from glanceclient.openstack.common import strutils
UPDATE_PARAMS = ('name', 'disk_format', 'container_format', 'min_disk', UPDATE_PARAMS = ('name', 'disk_format', 'container_format', 'min_disk',
@@ -56,9 +56,19 @@ class Image(base.Resource):
return self.manager.data(self, **kwargs) return self.manager.data(self, **kwargs)
class ImageManager(base.Manager): class ImageManager(base.ManagerWithFind):
resource_class = Image 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): def _image_meta_from_headers(self, headers):
meta = {'properties': {}} meta = {'properties': {}}
safe_decode = strutils.safe_decode safe_decode = strutils.safe_decode
@@ -112,10 +122,9 @@ class ImageManager(base.Manager):
:param image: image object or id to look up :param image: image object or id to look up
:rtype: :class:`Image` :rtype: :class:`Image`
""" """
image_id = base.getid(image) image_id = base.getid(image)
resp, body = self.api.raw_request('HEAD', '/v1/images/%s' resp, body = self.client.raw_request(
% parse.quote(str(image_id))) 'HEAD', '/v1/images/%s' % parse.quote(str(image_id)))
meta = self._image_meta_from_headers(dict(resp.getheaders())) meta = self._image_meta_from_headers(dict(resp.getheaders()))
return_request_id = kwargs.get('return_req_id', None) return_request_id = kwargs.get('return_req_id', None)
if return_request_id is not None: if return_request_id is not None:
@@ -131,8 +140,8 @@ class ImageManager(base.Manager):
:rtype: iterable containing image data :rtype: iterable containing image data
""" """
image_id = base.getid(image) image_id = base.getid(image)
resp, body = self.api.raw_request('GET', '/v1/images/%s' resp, body = self.client.raw_request(
% parse.quote(str(image_id))) 'GET', '/v1/images/%s' % parse.quote(str(image_id)))
checksum = resp.getheader('x-image-meta-checksum', None) checksum = resp.getheader('x-image-meta-checksum', None)
if do_checksum and checksum is not None: if do_checksum and checksum is not None:
body.set_checksum(checksum) body.set_checksum(checksum)
@@ -244,7 +253,7 @@ class ImageManager(base.Manager):
def delete(self, image, **kwargs): def delete(self, image, **kwargs):
"""Delete an image.""" """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) return_request_id = kwargs.get('return_req_id', None)
if return_request_id is not None: if return_request_id is not None:
return_request_id.append(resp.getheader(OS_REQ_ID_HDR, 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: if copy_from is not None:
hdrs['x-glance-api-copy-from'] = copy_from 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) 'POST', '/v1/images', headers=hdrs, body=image_data)
body = json.loads(''.join([c for c in body_iter])) body = json.loads(''.join([c for c in body_iter]))
return_request_id = kwargs.get('return_req_id', None) 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 hdrs['x-glance-api-copy-from'] = copy_from
url = '/v1/images/%s' % base.getid(image) 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) 'PUT', url, headers=hdrs, body=image_data)
body = json.loads(''.join([c for c in body_iter])) body = json.loads(''.join([c for c in body_iter]))
return_request_id = kwargs.get('return_req_id', None) return_request_id = kwargs.get('return_req_id', None)

View File

@@ -16,7 +16,7 @@
import testtools import testtools
from glanceclient.common import base from glanceclient.openstack.common.apiclient import base
class TestBase(testtools.TestCase): class TestBase(testtools.TestCase):

View File

@@ -44,6 +44,32 @@ class FakeAPI(object):
fixture = self._request(*args, **kwargs) fixture = self._request(*args, **kwargs)
return FakeResponse(fixture[0]), fixture[1] 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): class FakeResponse(object):
def __init__(self, headers, body=None, def __init__(self, headers, body=None,

View File

@@ -837,6 +837,7 @@ class ImageTest(testtools.TestCase):
image = self.mgr.get('1') image = self.mgr.get('1')
image.delete() image.delete()
expect = [ expect = [
('HEAD', '/v1/images/1', {}, None),
('HEAD', '/v1/images/1', {}, None), ('HEAD', '/v1/images/1', {}, None),
('DELETE', '/v1/images/1', {}, None), ('DELETE', '/v1/images/1', {}, None),
] ]
@@ -846,6 +847,7 @@ class ImageTest(testtools.TestCase):
image = self.mgr.get('1') image = self.mgr.get('1')
image.update(name='image-5') image.update(name='image-5')
expect = [ expect = [
('HEAD', '/v1/images/1', {}, None),
('HEAD', '/v1/images/1', {}, None), ('HEAD', '/v1/images/1', {}, None),
('PUT', '/v1/images/1', {'x-image-meta-name': 'image-5'}, 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') image = self.mgr.get('1')
data = ''.join([b for b in image.data()]) data = ''.join([b for b in image.data()])
expect = [ expect = [
('HEAD', '/v1/images/1', {}, None),
('HEAD', '/v1/images/1', {}, None), ('HEAD', '/v1/images/1', {}, None),
('GET', '/v1/images/1', {}, None), ('GET', '/v1/images/1', {}, None),
] ]
@@ -870,6 +873,7 @@ class ImageTest(testtools.TestCase):
image = self.mgr.get('2') image = self.mgr.get('2')
data = ''.join([b for b in image.data(do_checksum=False)]) data = ''.join([b for b in image.data(do_checksum=False)])
expect = [ expect = [
('HEAD', '/v1/images/2', {}, None),
('HEAD', '/v1/images/2', {}, None), ('HEAD', '/v1/images/2', {}, None),
('GET', '/v1/images/2', {}, None), ('GET', '/v1/images/2', {}, None),
] ]
@@ -891,6 +895,7 @@ class ImageTest(testtools.TestCase):
image = self.mgr.get('3') image = self.mgr.get('3')
data = ''.join([b for b in image.data(do_checksum=False)]) data = ''.join([b for b in image.data(do_checksum=False)])
expect = [ expect = [
('HEAD', '/v1/images/3', {}, None),
('HEAD', '/v1/images/3', {}, None), ('HEAD', '/v1/images/3', {}, None),
('GET', '/v1/images/3', {}, None), ('GET', '/v1/images/3', {}, None),
] ]