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.
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

View File

@@ -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):
"""

View File

@@ -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})

View File

@@ -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)

View File

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

View File

@@ -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,

View File

@@ -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),
]