awesomeness. merging into trunk since my parallax-api is already in trunk I believe. :)

This commit is contained in:
jaypipes@gmail.com 2010-10-18 14:36:46 +00:00 committed by Tarmac
commit a6962d618e
7 changed files with 157 additions and 35 deletions

View File

@ -1,3 +1,4 @@
*.pyc
glance.egg-info
glance.sqlite
*.glance-venv

View File

@ -74,8 +74,16 @@ class ImageController(wsgi.Controller):
return dict(image=self._make_image_dict(image))
def delete(self, req, id):
"""Delete is not currently supported """
raise exc.HTTPNotImplemented()
"""Deletes an existing image with the registry.
:param req: Request body. Ignored.
:param id: The opaque internal identifier for the image
:retval Returns 200 if delete was successful, a fault if not.
"""
context = None
updated_image = db.image_destroy(context, id)
def create(self, req):
"""Registers a new image with the registry.
@ -88,33 +96,43 @@ class ImageController(wsgi.Controller):
in the 'id' field
"""
image_data = json.loads(req.body)
image_data = json.loads(req.body)['image']
# Ensure the image has a status set
image_data.setdefault('status', 'available')
context = None
new_image = db.image_create(context, image_data)
return dict(new_image)
return dict(image=new_image)
def update(self, req, id):
"""Update is not currently supported """
raise exc.HTTPNotImplemented()
"""Updates an existing image with the registry.
:param req: Request body. A JSON-ified dict of information about
the image. This will replace the information in the
registry about this image
:param id: The opaque internal identifier for the image
:retval Returns the updated image information as a mapping,
"""
image_data = json.loads(req.body)['image']
context = None
updated_image = db.image_update(context, id, image_data)
return dict(image=updated_image)
@staticmethod
def _make_image_dict(image):
""" Create a dict represenation of an image which we can use to
"""Create a dict representation of an image which we can use to
serialize the image.
"""
def _fetch_attrs(d, attrs):
return dict([(a, d[a]) for a in attrs])
# attributes common to all models
base_attrs = set(['id', 'created_at', 'updated_at', 'deleted_at',
'deleted'])
file_attrs = base_attrs | set(['location', 'size'])
files = [_fetch_attrs(f, file_attrs) for f in image['files']]
files = [_fetch_attrs(f, db.IMAGE_FILE_ATTRS) for f in image['files']]
# TODO(sirp): should this be a dict, or a list of dicts?
# A plain dict is more convenient, but list of dicts would provide
@ -122,8 +140,7 @@ class ImageController(wsgi.Controller):
metadata = dict((m['key'], m['value']) for m in image['metadata']
if not m['deleted'])
image_attrs = base_attrs | set(['name', 'image_type', 'status', 'is_public'])
image_dict = _fetch_attrs(image, image_attrs)
image_dict = _fetch_attrs(image, db.IMAGE_ATTRS)
image_dict['files'] = files
image_dict['metadata'] = metadata

View File

@ -21,3 +21,12 @@ DB abstraction for Nova and Glance
"""
from glance.parallax.db.api import *
# attributes common to all models
BASE_MODEL_ATTRS = set(['id', 'created_at', 'updated_at', 'deleted_at',
'deleted'])
IMAGE_FILE_ATTRS = BASE_MODEL_ATTRS | set(['location', 'size'])
IMAGE_ATTRS = BASE_MODEL_ATTRS | set(['name', 'image_type', 'status',
'is_public'])

View File

@ -53,8 +53,7 @@ def _deleted(context):
def image_create(_context, values):
image_ref = models.Image()
for (key, value) in values.iteritems():
image_ref[key] = value
image_ref.update(values)
image_ref.save()
return image_ref
@ -107,8 +106,7 @@ def image_update(_context, image_id, values):
session = get_session()
with session.begin():
image_ref = models.Image.find(image_id, session=session)
for (key, value) in values.iteritems():
image_ref[key] = value
image_ref.update(values)
image_ref.save(session=session)

View File

@ -106,6 +106,11 @@ class ModelBase(object):
self.deleted_at = datetime.datetime.utcnow()
self.save(session=session)
def update(self, values):
"""dict.update() behaviour."""
for k, v in values.iteritems():
self[k] = v
def __setitem__(self, key, value):
setattr(self, key, value)

View File

@ -20,6 +20,7 @@
import datetime
import httplib
import StringIO
import sys
import stubout
@ -191,7 +192,7 @@ def stub_out_parallax_db_image_api(stubs):
VALID_STATUSES = ('available', 'disabled', 'pending')
def __init__(self):
self.images = self.FIXTURES
self.images = FakeDatastore.FIXTURES
self.next_id = 3
def image_create(self, _context, values):
@ -201,25 +202,32 @@ def stub_out_parallax_db_image_api(stubs):
values['status'] = 'available'
else:
if not values['status'] in self.VALID_STATUSES:
raise exception.Invalid("Invalid status '%s' for image" % values['status'])
raise exception.Invalid("Invalid status '%s' for image" %
values['status'])
self.next_id += 1
self.images.extend(values)
self.images.append(values)
return values
def image_update(self, _context, image_id, values):
image = self.image_get(_context, image_id)
image.update(values)
return image
def image_destroy(self, _context, image_id):
try:
del self.images[image_id]
except KeyError:
new_exc = exception.NotFound("No model for id %s" % image_id)
raise new_exc.__class__, new_exc, sys.exc_info()[2]
image = self.image_get(_context, image_id)
self.images.remove(image)
def image_get(self, _context, image_id):
if image_id not in self.images.keys() or self.images[image_id]['deleted']:
new_exc = exception.NotFound("No model for id %s" % image_id)
images = [i for i in self.images if str(i['id']) == str(image_id)]
if len(images) != 1 or images[0]['deleted']:
new_exc = exception.NotFound("No model for id %s %s" %
(image_id, str(self.images)))
raise new_exc.__class__, new_exc, sys.exc_info()[2]
else:
return self.images[image_id]
return images[0]
def image_get_all_public(self, _context, public):
return [f for f in self.images
@ -228,6 +236,8 @@ def stub_out_parallax_db_image_api(stubs):
fake_datastore = FakeDatastore()
stubs.Set(glance.parallax.db.sqlalchemy.api, 'image_create',
fake_datastore.image_create)
stubs.Set(glance.parallax.db.sqlalchemy.api, 'image_update',
fake_datastore.image_update)
stubs.Set(glance.parallax.db.sqlalchemy.api, 'image_destroy',
fake_datastore.image_destroy)
stubs.Set(glance.parallax.db.sqlalchemy.api, 'image_get',

View File

@ -105,7 +105,7 @@ class TestImageController(unittest.TestCase):
req = webob.Request.blank('/images')
req.method = 'POST'
req.body = json.dumps(fixture)
req.body = json.dumps(dict(image=fixture))
res = req.get_response(controllers.API())
@ -114,13 +114,13 @@ class TestImageController(unittest.TestCase):
res_dict = json.loads(res.body)
for k,v in fixture.iteritems():
self.assertEquals(v, res_dict[k])
self.assertEquals(v, res_dict['image'][k])
# Test ID auto-assigned properly
self.assertEquals(3, res_dict['id'])
self.assertEquals(3, res_dict['image']['id'])
# Test status was updated properly
self.assertEquals('available', res_dict['status'])
self.assertEquals('available', res_dict['image']['status'])
def test_create_image_with_bad_status(self):
"""Tests proper exception is raised if a bad status is set"""
@ -134,9 +134,91 @@ class TestImageController(unittest.TestCase):
req = webob.Request.blank('/images')
req.method = 'POST'
req.body = json.dumps(fixture)
req.body = json.dumps(dict(image=fixture))
# TODO(jaypipes): Port Nova's Fault infrastructure
# over to Glance to support exception catching into
# standard HTTP errors.
self.assertRaises(exception.Invalid, req.get_response, controllers.API())
def test_update_image(self):
"""Tests that the /images PUT parallax API updates the image"""
fixture = {'name': 'fake public image #2',
'image_type': 'ramdisk'
}
req = webob.Request.blank('/images/2')
req.method = 'PUT'
req.body = json.dumps(dict(image=fixture))
res = req.get_response(controllers.API())
self.assertEquals(res.status_int, 200)
res_dict = json.loads(res.body)
for k,v in fixture.iteritems():
self.assertEquals(v, res_dict['image'][k])
def test_update_image_not_existing(self):
"""Tests proper exception is raised if attempt to update non-existing
image"""
fixture = {'id': 3,
'name': 'fake public image',
'is_public': True,
'image_type': 'kernel',
'status': 'bad status'
}
req = webob.Request.blank('/images/3')
req.method = 'PUT'
req.body = json.dumps(dict(image=fixture))
# TODO(jaypipes): Port Nova's Fault infrastructure
# over to Glance to support exception catching into
# standard HTTP errors.
self.assertRaises(exception.NotFound, req.get_response, controllers.API())
def test_delete_image(self):
"""Tests that the /images DELETE parallax API deletes the image"""
# Grab the original number of images
req = webob.Request.blank('/images')
res = req.get_response(controllers.API())
res_dict = json.loads(res.body)
self.assertEquals(res.status_int, 200)
orig_num_images = len(res_dict['images'])
# Delete image #2
req = webob.Request.blank('/images/2')
req.method = 'DELETE'
res = req.get_response(controllers.API())
self.assertEquals(res.status_int, 200)
# Verify one less image
req = webob.Request.blank('/images')
res = req.get_response(controllers.API())
res_dict = json.loads(res.body)
self.assertEquals(res.status_int, 200)
new_num_images = len(res_dict['images'])
self.assertEquals(new_num_images, orig_num_images - 1)
def test_delete_image_not_existing(self):
"""Tests proper exception is raised if attempt to delete non-existing
image"""
req = webob.Request.blank('/images/3')
req.method = 'DELETE'
# TODO(jaypipes): Port Nova's Fault infrastructure
# over to Glance to support exception catching into
# standard HTTP errors.
self.assertRaises(exception.NotFound, req.get_response, controllers.API())