Add ImageDetail for extra information

The current Image can only receive the basics returned by a GET
/images, which is just name, id, and links. A call to /images/detail
gives a number of added details, some of which were already in the
Image resource but not set. This change follows with how a few other
basic/detail requests were done, and exposes it through the proxy with
details coming by default (since they're likely expected, and most
useful).

Change-Id: Iadc98cd0f679777b1c6e14711ad00787142221e2
This commit is contained in:
Brian Curtin 2015-02-22 23:58:11 -06:00
parent ee4c85d243
commit f92973d7f8
5 changed files with 93 additions and 46 deletions

View File

@ -10,3 +10,12 @@ The ``Image`` class inherits from :class:`~openstack.resource.Resource`.
.. autoclass:: openstack.compute.v2.image.Image
:members:
The ImageDetail Class
---------------------
The ``ImageDetail`` class inherits from
:class:`~openstack.compute.v2.image.Image`.
.. autoclass:: openstack.compute.v2.image.ImageDetail
:members:

View File

@ -59,9 +59,6 @@ class Proxy(object):
def update_flavor(self, **data):
return flavor.Flavor(data).update(self.session)
def create_image(self, **data):
return image.Image(data).create(self.session)
def delete_image(self, **data):
image.Image(data).delete(self.session)
@ -71,11 +68,18 @@ class Proxy(object):
def get_image(self, **data):
return image.Image(data).get(self.session)
def list_images(self):
return image.Image.list(self.session)
def list_images(self, details=True):
"""Return a generator of images
def update_image(self, **data):
return image.Image(data).update(self.session)
:param bool details: When ``True``, returns
:class:`~openstack.compute.v2.image.ImageDetail` objects,
otherwise :class:`~openstack.compute.v2.image.Image`.
*Default: ``True``*
:returns: A generator of image objects
"""
img = image.ImageDetail if details else image.Image
return img.list(self.session, paginate=False)
def create_keypair(self, **data):
return keypair.Keypair(data).create(self.session)

View File

@ -21,26 +21,24 @@ class Image(resource.Resource):
service = compute_service.ComputeService()
# capabilities
allow_create = True
allow_retrieve = True
allow_update = True
allow_delete = True
allow_list = True
# Properties
#: Timestamp when the image was created.
created = resource.prop('created')
#: Links pertaining to this image. This is a list of dictionaries,
#: each including keys ``href`` and ``rel``, and optionally ``type``.
links = resource.prop('links')
#: The name of this image.
name = resource.prop('name')
#: Timestamp when the image was created.
created = resource.prop('created')
#: Metadata pertaining to this image. *Type: dict*
metadata = resource.prop('metadata', type=dict)
#: The mimimum disk size. *Type: int*
min_disk = resource.prop('minDisk', type=int)
#: The minimum RAM size. *Type: int*
min_ram = resource.prop('minRam', type=int)
#: The name of this image.
name = resource.prop('name')
#: If this image is still building, its progress is represented here.
#: Once an image is created, progres will be 100. *Type: int*
progress = resource.prop('progress', type=int)
@ -48,3 +46,13 @@ class Image(resource.Resource):
status = resource.prop('status')
#: Timestamp when the image was updated.
updated = resource.prop('updated')
#: Size of the image in bytes. *Type: int*
size = resource.prop('OS-EXT-IMG-SIZE:size', type=int)
class ImageDetail(Image):
base_path = '/images/detail'
allow_retrieve = False
allow_delete = False
allow_list = True

View File

@ -15,19 +15,26 @@ import testtools
from openstack.compute.v2 import image
IDENTIFIER = 'IDENTIFIER'
EXAMPLE = {
'created': '1',
BASIC_EXAMPLE = {
'id': IDENTIFIER,
'links': '3',
'metadata': {'key': '4'},
'minDisk': 5,
'minRam': 6,
'name': '7',
'progress': 8,
'status': '9',
'updated': '10',
'links': '2',
'name': '3',
}
DETAILS = {
'created': '1',
'metadata': {'key': '2'},
'minDisk': 3,
'minRam': 4,
'progress': 5,
'status': '6',
'updated': '7',
'OS-EXT-IMG-SIZE:size': 8
}
DETAIL_EXAMPLE = BASIC_EXAMPLE.copy()
DETAIL_EXAMPLE.update(DETAILS)
class TestImage(testtools.TestCase):
@ -37,21 +44,40 @@ class TestImage(testtools.TestCase):
self.assertEqual('images', sot.resources_key)
self.assertEqual('/images', sot.base_path)
self.assertEqual('compute', sot.service.service_type)
self.assertTrue(sot.allow_create)
self.assertFalse(sot.allow_create)
self.assertTrue(sot.allow_retrieve)
self.assertTrue(sot.allow_update)
self.assertFalse(sot.allow_update)
self.assertTrue(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = image.Image(EXAMPLE)
self.assertEqual(EXAMPLE['created'], sot.created)
self.assertEqual(EXAMPLE['id'], sot.id)
self.assertEqual(EXAMPLE['links'], sot.links)
self.assertEqual(EXAMPLE['metadata'], sot.metadata)
self.assertEqual(EXAMPLE['minDisk'], sot.min_disk)
self.assertEqual(EXAMPLE['minRam'], sot.min_ram)
self.assertEqual(EXAMPLE['name'], sot.name)
self.assertEqual(EXAMPLE['progress'], sot.progress)
self.assertEqual(EXAMPLE['status'], sot.status)
self.assertEqual(EXAMPLE['updated'], sot.updated)
def test_make_basic(self):
sot = image.Image(BASIC_EXAMPLE)
self.assertEqual(BASIC_EXAMPLE['id'], sot.id)
self.assertEqual(BASIC_EXAMPLE['links'], sot.links)
self.assertEqual(BASIC_EXAMPLE['name'], sot.name)
def test_detail(self):
sot = image.ImageDetail()
self.assertEqual('image', sot.resource_key)
self.assertEqual('images', sot.resources_key)
self.assertEqual('/images/detail', sot.base_path)
self.assertEqual('compute', sot.service.service_type)
self.assertFalse(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_detail(self):
sot = image.ImageDetail(DETAIL_EXAMPLE)
self.assertEqual(DETAIL_EXAMPLE['created'], sot.created)
self.assertEqual(DETAIL_EXAMPLE['id'], sot.id)
self.assertEqual(DETAIL_EXAMPLE['links'], sot.links)
self.assertEqual(DETAIL_EXAMPLE['metadata'], sot.metadata)
self.assertEqual(DETAIL_EXAMPLE['minDisk'], sot.min_disk)
self.assertEqual(DETAIL_EXAMPLE['minRam'], sot.min_ram)
self.assertEqual(DETAIL_EXAMPLE['name'], sot.name)
self.assertEqual(DETAIL_EXAMPLE['progress'], sot.progress)
self.assertEqual(DETAIL_EXAMPLE['status'], sot.status)
self.assertEqual(DETAIL_EXAMPLE['updated'], sot.updated)
self.assertEqual(DETAIL_EXAMPLE['OS-EXT-IMG-SIZE:size'], sot.size)

View File

@ -59,10 +59,6 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
self.verify_update('openstack.compute.v2.flavor.Flavor.update',
self.proxy.update_flavor)
def test_image_create(self):
self.verify_create('openstack.compute.v2.image.Image.create',
self.proxy.create_image)
def test_image_delete(self):
self.verify_delete('openstack.compute.v2.image.Image.delete',
self.proxy.delete_image)
@ -75,13 +71,17 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
self.verify_get('openstack.compute.v2.image.Image.get',
self.proxy.get_image)
def test_image_list(self):
def test_image_list_basic(self):
self.verify_list('openstack.compute.v2.image.Image.list',
self.proxy.list_images)
self.proxy.list_images,
method_kwargs={"details": False},
expected_kwargs={"paginate": False})
def test_image_update(self):
self.verify_update('openstack.compute.v2.image.Image.update',
self.proxy.update_image)
def test_image_list_detail(self):
self.verify_list('openstack.compute.v2.image.ImageDetail.list',
self.proxy.list_images,
method_kwargs={"details": True},
expected_kwargs={"paginate": False})
def test_keypair_create(self):
self.verify_create('openstack.compute.v2.keypair.Keypair.create',