Image members bones

This commit is contained in:
Brian Waldon
2012-04-02 15:44:43 -07:00
parent 664f370677
commit ca8434f2c3
8 changed files with 200 additions and 14 deletions

View File

@@ -33,13 +33,6 @@ 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 to return the object's UUID first, if we have a UUID.
try:
if obj.uuid:
return obj.uuid
except AttributeError:
pass
try:
return obj.id
except AttributeError:
@@ -86,10 +79,12 @@ class Manager(object):
methods = {"PUT": self.api.put,
"POST": self.api.post}
try:
resp, body = methods[method](url, body=body)
_method = methods[method]
except KeyError:
raise exceptions.ClientException("Invalid update method: %s"
% method)
msg = "Invalid update method: %s" % method
raise exceptions.ClientException(msg)
resp, body = _method(url, body=body)
# PUT requests may not return a body
if body:
return self.resource_class(self, body[response_key])

View File

@@ -17,6 +17,7 @@ import logging
from glanceclient.common import http
from glanceclient.v1 import images
from glanceclient.v1 import image_members
logger = logging.getLogger(__name__)
@@ -36,3 +37,4 @@ class Client(http.HTTPClient):
""" Initialize a new client for the Images v1 API. """
super(Client, self).__init__(endpoint, token=token, timeout=timeout)
self.images = images.ImageManager(self)
self.image_members = image_members.ImageMemberManager(self)

View File

@@ -0,0 +1,64 @@
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from glanceclient.common import base
class ImageMember(base.Resource):
def __repr__(self):
return "<ImageMember %s>" % self._info
class ImageMemberManager(base.Manager):
resource_class = ImageMember
def get(self, image, member_id):
url = '/v1/images/%s' % (base.getid(image), member_id)
return self._get(url, 'member')
def list(self, image):
url = '/v1/images/%s/members' % base.getid(image)
return self._list(url, 'members')
def delete(self, image, member):
image_id = base.getid(image)
try:
member_id = base.getid(member)
except AttributeError:
member_id = member
self._delete("/v1/images/%s/members/%s" % (image_id, member_id))
def create(self, image, member_id, can_share=False):
"""Create an image"""
url = '/v1/images/%s/members/%s' % (base.getid(image), member_id)
body = {'member': {'can_share': can_share}}
self._update(url, body=body)
def replace(self, image, members):
memberships = []
for member in members:
try:
obj = {
'member_id': member.member_id,
'can_share': member.can_share,
}
except AttributeError:
obj = {'member_id': member['member_id']}
if 'can_share' in member:
obj['can_share'] = member['can_share']
memberships.append(obj)
url = '/v1/images/%s/members' % base.getid(image)
self.api.put(url, {}, {'memberships': memberships})

View File

@@ -22,6 +22,9 @@ class Image(base.Resource):
def __repr__(self):
return "<Image %s>" % self._info
def update(self, **fields):
self.manager.update(self, **fields)
def delete(self):
return self.manager.delete(self)
@@ -49,13 +52,13 @@ class ImageManager(base.Manager):
headers['x-image-meta-%s' % key] = value
return headers
def get(self, image):
def get(self, image_id):
"""Get the metadata for a specific image.
:param image: image object or id to look up
:rtype: :class:`Image`
"""
resp, body = self.api.head('/v1/images/%s' % base.getid(image))
resp, body = self.api.head('/v1/images/%s' % image_id)
meta = self._image_meta_from_headers(resp)
return Image(self, meta)
@@ -97,3 +100,14 @@ class ImageManager(base.Manager):
resp, body = self.api.put(url, headers=send_meta)
recv_meta = self._image_meta_from_headers(resp)
return Image(self, recv_meta)
def delete_member(self, image, image_member):
"""Remove a member from an image"""
image_id = base.getid(image)
try:
member_id = image_member.member_id
except AttributeError:
member_id = image_member
url = '/v1/images/%s/members/%s' % (image_id, member_id)
resp, body = self.api.delete(url)

0
glanceclient/v1/shell.py Executable file → Normal file
View File

View File

@@ -0,0 +1,60 @@
import unittest
from tests.v1 import utils
import glanceclient.v1.images
import glanceclient.v1.image_members
class ImageMemberManagerTest(unittest.TestCase):
def setUp(self):
self.api = utils.FakeAPI()
self.mgr = glanceclient.v1.image_members.ImageMemberManager(self.api)
self.image = glanceclient.v1.images.Image(self.api, {'id': '1'}, True)
def test_list(self):
members = self.mgr.list(self.image)
expect = [('GET', '/v1/images/1/members', {}, None)]
self.assertEqual(self.api.calls, expect)
self.assertEqual(len(members), 1)
self.assertEqual(members[0].member_id, '1')
self.assertEqual(members[0].can_share, False)
def test_delete(self):
self.mgr.delete(self.image, '1')
expect = [('DELETE', '/v1/images/1/members/1', {}, None)]
self.assertEqual(self.api.calls, expect)
def test_create(self):
self.mgr.create(self.image, '1', can_share=True)
expect_body = {'member': {'can_share': True}}
expect = [('PUT', '/v1/images/1/members/1', {}, expect_body)]
self.assertEqual(self.api.calls, expect)
def test_replace(self):
body = [
{'member_id': '2', 'can_share': False},
{'member_id': '3'},
]
self.mgr.replace(self.image, body)
expect = [('PUT', '/v1/images/1/members', {}, {'memberships': body})]
self.assertEqual(self.api.calls, expect)
def test_replace_objects(self):
body = [
glanceclient.v1.image_members.ImageMember(
self.mgr, {'member_id': '2', 'can_share': False}),
glanceclient.v1.image_members.ImageMember(
self.mgr, {'member_id': '3', 'can_share': True}),
]
self.mgr.replace(self.image, body)
expect_body = {
'memberships': [
{'member_id': '2', 'can_share': False},
{'member_id': '3', 'can_share': True},
],
}
expect = [('PUT', '/v1/images/1/members', {}, expect_body)]
self.assertEqual(self.api.calls, expect)

View File

@@ -57,3 +57,27 @@ class ImageManagerTest(unittest.TestCase):
self.assertEqual(self.api.calls, expect)
self.assertEqual(image.id, '1')
self.assertEqual(image.name, 'image-2')
class ImageTest(unittest.TestCase):
def setUp(self):
self.api = utils.FakeAPI()
self.mgr = glanceclient.v1.images.ImageManager(self.api)
def test_delete(self):
image = self.mgr.get('1')
image.delete()
expect = [
('HEAD', '/v1/images/1', {}, None),
('DELETE', '/v1/images/1', {}, None),
]
self.assertEqual(self.api.calls, expect)
def test_update(self):
image = self.mgr.get('1')
image.update(name='image-5')
expect = [
('HEAD', '/v1/images/1', {}, None),
('PUT', '/v1/images/1', {'x-image-meta-name': 'image-5'}, None),
]
self.assertEqual(self.api.calls, expect)

View File

@@ -1,4 +1,17 @@
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
fixtures = {
'/v1/images': {
@@ -39,9 +52,23 @@ fixtures = {
},
None),
'DELETE': ({}, None),
}
},
'/v1/images/1/members': {
'GET': (
{},
{'members': [
{'member_id': '1', 'can_share': False},
]},
),
'PUT': ({}, None),
},
'/v1/images/1/members/1': {
'PUT': ({}, None),
'DELETE': ({}, None),
},
}
class FakeAPI(object):
def __init__(self):