awesomeness. merging into trunk since my parallax-api is already in trunk I believe. :)
This commit is contained in:
commit
a6962d618e
@ -1,3 +1,4 @@
|
||||
*.pyc
|
||||
glance.egg-info
|
||||
glance.sqlite
|
||||
*.glance-venv
|
||||
|
@ -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
|
||||
|
@ -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'])
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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())
|
||||
|
Loading…
x
Reference in New Issue
Block a user