diff options
-rw-r--r-- | glanceclient/v1/images.py | 59 | ||||
-rwxr-xr-x | run_tests.sh | 2 | ||||
-rw-r--r-- | tests/__init__.py | 0 | ||||
-rw-r--r-- | tests/test_test.py | 5 | ||||
-rw-r--r-- | tests/v1/__init__.py | 0 | ||||
-rw-r--r-- | tests/v1/test_images.py | 59 | ||||
-rw-r--r-- | tests/v1/utils.py | 70 |
7 files changed, 174 insertions, 21 deletions
diff --git a/glanceclient/v1/images.py b/glanceclient/v1/images.py index 442b23e..97e2ce4 100644 --- a/glanceclient/v1/images.py +++ b/glanceclient/v1/images.py @@ -29,21 +29,34 @@ class Image(base.Resource): class ImageManager(base.Manager): resource_class = Image - def get(self, image): - """Get the metadata for a specific image. - - :param image: image object or id to look up - :rtype: :class:`Image` - """ - resp, body = self.api.head("/images/%s" % base.getid(image)) + def _image_meta_from_headers(self, headers): meta = {'properties': {}} - for key, value in resp.iteritems(): + for key, value in headers.iteritems(): if key.startswith('x-image-meta-property-'): _key = key[22:] meta['properties'][_key] = value elif key.startswith('x-image-meta-'): _key = key[13:] meta[_key] = value + return meta + + def _image_meta_to_headers(self, fields): + headers = {} + for key, value in fields.iteritems(): + if key == 'properties': + headers['x-image-meta-property-%s' % key] = value + else: + headers['x-image-meta-%s' % key] = value + return headers + + def get(self, image): + """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)) + meta = self._image_meta_from_headers(resp) return Image(self, meta) def list(self, limit=None, marker=None): @@ -58,13 +71,29 @@ class ImageManager(base.Manager): params['limit'] = int(limit) if marker: params['marker'] = int(marker) - - query = "" - if params: - query = "?" + urllib.urlencode(params) - - return self._list("/images/detail%s" % query, "images") + query = '?%s' % urllib.urlencode(params) if params else '' + return self._list('/v1/images/detail%s' % query, "images") def delete(self, image): """Delete an image.""" - self._delete("/images/%s" % base.getid(image)) + self._delete("/v1/images/%s" % base.getid(image)) + + def create(self, **kwargs): + """Create an image""" + fields = {} + if 'name' in kwargs: + fields['name'] = kwargs['name'] + resp, body = self.api.post('/v1/images', body={'image': fields}) + meta = self._image_meta_from_headers(resp) + return Image(self, meta) + + def update(self, image, **kwargs): + """Update an image""" + fields = {} + if 'name' in kwargs: + fields['name'] = kwargs['name'] + send_meta = self._image_meta_to_headers(fields) + url = '/v1/images/%s' % base.getid(image) + resp, body = self.api.put(url, headers=send_meta) + recv_meta = self._image_meta_from_headers(resp) + return Image(self, recv_meta) diff --git a/run_tests.sh b/run_tests.sh index d0d3645..2ecbc4a 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -50,7 +50,7 @@ function run_tests { function run_pep8 { echo "Running pep8..." PEP8_OPTIONS="--exclude=$PEP8_EXCLUDE --repeat" - PEP8_INCLUDE="glanceclient/* setup.py run_tests.py tools/install_venv.py" + PEP8_INCLUDE="glanceclient/*.py setup.py run_tests.py tools/install_venv.py" ${wrapper} pep8 $PEP8_OPTIONS $PEP8_INCLUDE } diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/__init__.py diff --git a/tests/test_test.py b/tests/test_test.py deleted file mode 100644 index ec6a6e5..0000000 --- a/tests/test_test.py +++ /dev/null @@ -1,5 +0,0 @@ -import unittest - -class TestCase(unittest.TestCase): - def test_one(self): - self.assertTrue(True) diff --git a/tests/v1/__init__.py b/tests/v1/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/v1/__init__.py diff --git a/tests/v1/test_images.py b/tests/v1/test_images.py new file mode 100644 index 0000000..f8d904f --- /dev/null +++ b/tests/v1/test_images.py @@ -0,0 +1,59 @@ + +import unittest + +from tests.v1 import utils + +import glanceclient.v1.images + + +class ImageManagerTest(unittest.TestCase): + + def setUp(self): + self.api = utils.FakeAPI() + self.mgr = glanceclient.v1.images.ImageManager(self.api) + + def test_list(self): + images = self.mgr.list() + expect = [('GET', '/v1/images/detail', {}, None)] + self.assertEqual(self.api.calls, expect) + self.assertEqual(len(images), 1) + self.assertEqual(images[0].id, '1') + self.assertEqual(images[0].name, 'image-1') + self.assertEqual(images[0].properties, {'arch': 'x86_64'}) + + def test_list_with_limit(self): + self.mgr.list(limit=10) + expect = [('GET', '/v1/images/detail?limit=10', {}, None)] + self.assertEqual(self.api.calls, expect) + + def test_list_with_marker(self): + self.mgr.list(marker=20) + expect = [('GET', '/v1/images/detail?marker=20', {}, None)] + self.assertEqual(self.api.calls, expect) + + def test_get(self): + image = self.mgr.get('1') + expect = [('HEAD', '/v1/images/1', {}, None)] + self.assertEqual(self.api.calls, expect) + self.assertEqual(image.id, '1') + self.assertEqual(image.name, 'image-1') + + def test_delete(self): + self.mgr.delete('1') + expect = [('DELETE', '/v1/images/1', {}, None)] + self.assertEqual(self.api.calls, expect) + + def test_create(self): + image = self.mgr.create(name='image-1') + expect = [('POST', '/v1/images', {}, {'image': {'name': 'image-1'}})] + self.assertEqual(self.api.calls, expect) + self.assertEqual(image.id, '1') + self.assertEqual(image.name, 'image-1') + + def test_update(self): + image = self.mgr.update('1', name='image-2') + expect_hdrs = {'x-image-meta-name': 'image-2'} + expect = [('PUT', '/v1/images/1', expect_hdrs, None)] + self.assertEqual(self.api.calls, expect) + self.assertEqual(image.id, '1') + self.assertEqual(image.name, 'image-2') diff --git a/tests/v1/utils.py b/tests/v1/utils.py new file mode 100644 index 0000000..42acd27 --- /dev/null +++ b/tests/v1/utils.py @@ -0,0 +1,70 @@ + + +fixtures = { + '/v1/images': { + 'POST': ( + { + 'location': '/v1/images/1', + 'x-image-meta-id': '1', + 'x-image-meta-name': 'image-1', + 'x-image-meta-property-arch': 'x86_64', + }, + None), + }, + '/v1/images/detail': { + 'GET': ( + {}, + {'images': [ + { + 'id': '1', + 'name': 'image-1', + 'properties': {'arch': 'x86_64'}, + }, + ]}, + ), + }, + '/v1/images/1': { + 'HEAD': ( + { + 'x-image-meta-id': '1', + 'x-image-meta-name': 'image-1', + 'x-image-meta-property-arch': 'x86_64', + }, + None), + 'PUT': ( + { + 'x-image-meta-id': '1', + 'x-image-meta-name': 'image-2', + 'x-image-meta-property-arch': 'x86_64', + }, + None), + 'DELETE': ({}, None), + } +} + +class FakeAPI(object): + + def __init__(self): + self.calls = [] + + def _request(self, method, url, headers=None, body=None): + call = (method, url, headers or {}, body) + self.calls.append(call) + # drop any query params + url = url.split('?', 1)[0] + return fixtures[url][method] + + def get(self, url): + return self._request('GET', url) + + def head(self, url): + return self._request('HEAD', url) + + def post(self, url, headers=None, body=None): + return self._request('POST', url, headers, body) + + def put(self, url, headers=None, body=None): + return self._request('PUT', url, headers, body) + + def delete(self, url): + return self._request('DELETE', url) |