Add minDisk and minRam to OSAPI image details
Change-Id: I4bf1920a245de85c88c38ec3ad82dc0e93cc671c
This commit is contained in:
@@ -187,6 +187,10 @@ class CreateInstanceHelper(object):
|
||||
config_drive=config_drive,))
|
||||
except quota.QuotaError as error:
|
||||
self._handle_quota_error(error)
|
||||
except exception.InstanceTypeMemoryTooSmall as error:
|
||||
raise exc.HTTPBadRequest(explanation=unicode(error))
|
||||
except exception.InstanceTypeDiskTooSmall as error:
|
||||
raise exc.HTTPBadRequest(explanation=unicode(error))
|
||||
except exception.ImageNotFound as error:
|
||||
msg = _("Can not find requested image")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
@@ -41,6 +41,8 @@ SUPPORTED_FILTERS = {
|
||||
'changes-since': 'changes-since',
|
||||
'server': 'property-instance_ref',
|
||||
'type': 'property-image_type',
|
||||
'minRam': 'min_ram',
|
||||
'minDisk': 'min_disk',
|
||||
}
|
||||
|
||||
|
||||
@@ -239,6 +241,10 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer):
|
||||
image_elem.set('status', str(image_dict['status']))
|
||||
if 'progress' in image_dict:
|
||||
image_elem.set('progress', str(image_dict['progress']))
|
||||
if 'minRam' in image_dict:
|
||||
image_elem.set('minRam', str(image_dict['minRam']))
|
||||
if 'minDisk' in image_dict:
|
||||
image_elem.set('minDisk', str(image_dict['minDisk']))
|
||||
if 'server' in image_dict:
|
||||
server_elem = self._create_server_node(image_dict['server'])
|
||||
image_elem.append(server_elem)
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
<optional>
|
||||
<attribute name="progress"> <text/> </attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="minDisk"> <text/> </attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="minRam"> <text/> </attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="server">
|
||||
<attribute name="id"> <text/> </attribute>
|
||||
|
||||
@@ -161,6 +161,11 @@ class ViewBuilderV11(ViewBuilder):
|
||||
|
||||
if detail:
|
||||
image["metadata"] = image_obj.get("properties", {})
|
||||
if 'min_ram' in image_obj:
|
||||
image["minRam"] = image_obj.get("min_ram") or 0
|
||||
|
||||
if 'min_disk' in image_obj:
|
||||
image["minDisk"] = image_obj.get("min_disk") or 0
|
||||
|
||||
return image
|
||||
|
||||
|
||||
@@ -219,6 +219,11 @@ class API(base.Base):
|
||||
image_href)
|
||||
image = image_service.show(context, image_id)
|
||||
|
||||
if instance_type['memory_mb'] < int(image.get('min_ram', 0)):
|
||||
raise exception.InstanceTypeMemoryTooSmall()
|
||||
if instance_type['local_gb'] < int(image.get('min_disk', 0)):
|
||||
raise exception.InstanceTypeDiskTooSmall()
|
||||
|
||||
config_drive_id = None
|
||||
if config_drive and config_drive is not True:
|
||||
# config_drive is volume id
|
||||
|
||||
@@ -810,3 +810,11 @@ class ZoneRequestError(Error):
|
||||
if message is None:
|
||||
message = _("1 or more Zones could not complete the request")
|
||||
super(ZoneRequestError, self).__init__(message=message)
|
||||
|
||||
|
||||
class InstanceTypeMemoryTooSmall(NovaException):
|
||||
message = _("Instance type's memory is too small for requested image.")
|
||||
|
||||
|
||||
class InstanceTypeDiskTooSmall(NovaException):
|
||||
message = _("Instance type's disk is too small for requested image.")
|
||||
|
||||
@@ -404,7 +404,7 @@ def _limit_attributes(image_meta):
|
||||
'container_format', 'checksum', 'id',
|
||||
'name', 'created_at', 'updated_at',
|
||||
'deleted_at', 'deleted', 'status',
|
||||
'is_public']
|
||||
'min_disk', 'min_ram', 'is_public']
|
||||
output = {}
|
||||
for attr in IMAGE_ATTRIBUTES:
|
||||
output[attr] = image_meta.get(attr)
|
||||
|
||||
@@ -113,6 +113,7 @@ class ImagesTest(test.TestCase):
|
||||
self.assertDictMatch(expected_image, actual_image)
|
||||
|
||||
def test_get_image_v1_1(self):
|
||||
self.maxDiff = None
|
||||
request = webob.Request.blank('/v1.1/fake/images/124')
|
||||
app = fakes.wsgi_app(fake_auth_context=self._get_fake_context())
|
||||
response = request.get_response(app)
|
||||
@@ -133,6 +134,8 @@ class ImagesTest(test.TestCase):
|
||||
"created": NOW_API_FORMAT,
|
||||
"status": "SAVING",
|
||||
"progress": 0,
|
||||
"minDisk": 0,
|
||||
"minRam": 0,
|
||||
'server': {
|
||||
'id': '42',
|
||||
"links": [{
|
||||
@@ -542,6 +545,8 @@ class ImagesTest(test.TestCase):
|
||||
'created': NOW_API_FORMAT,
|
||||
'status': 'ACTIVE',
|
||||
'progress': 100,
|
||||
'minDisk': 0,
|
||||
'minRam': 0,
|
||||
"links": [{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v1.1/fake/images/123",
|
||||
@@ -567,6 +572,8 @@ class ImagesTest(test.TestCase):
|
||||
'created': NOW_API_FORMAT,
|
||||
'status': 'SAVING',
|
||||
'progress': 0,
|
||||
'minDisk': 0,
|
||||
'minRam': 0,
|
||||
'server': {
|
||||
'id': '42',
|
||||
"links": [{
|
||||
@@ -603,6 +610,8 @@ class ImagesTest(test.TestCase):
|
||||
'created': NOW_API_FORMAT,
|
||||
'status': 'SAVING',
|
||||
'progress': 0,
|
||||
'minDisk': 0,
|
||||
'minRam': 0,
|
||||
'server': {
|
||||
'id': '42',
|
||||
"links": [{
|
||||
@@ -639,6 +648,8 @@ class ImagesTest(test.TestCase):
|
||||
'created': NOW_API_FORMAT,
|
||||
'status': 'ACTIVE',
|
||||
'progress': 100,
|
||||
'minDisk': 0,
|
||||
'minRam': 0,
|
||||
'server': {
|
||||
'id': '42',
|
||||
"links": [{
|
||||
@@ -675,6 +686,8 @@ class ImagesTest(test.TestCase):
|
||||
'created': NOW_API_FORMAT,
|
||||
'status': 'ERROR',
|
||||
'progress': 0,
|
||||
'minDisk': 0,
|
||||
'minRam': 0,
|
||||
'server': {
|
||||
'id': '42',
|
||||
"links": [{
|
||||
@@ -711,6 +724,8 @@ class ImagesTest(test.TestCase):
|
||||
'created': NOW_API_FORMAT,
|
||||
'status': 'DELETED',
|
||||
'progress': 0,
|
||||
'minDisk': 0,
|
||||
'minRam': 0,
|
||||
'server': {
|
||||
'id': '42',
|
||||
"links": [{
|
||||
@@ -747,6 +762,8 @@ class ImagesTest(test.TestCase):
|
||||
'created': NOW_API_FORMAT,
|
||||
'status': 'DELETED',
|
||||
'progress': 0,
|
||||
'minDisk': 0,
|
||||
'minRam': 0,
|
||||
'server': {
|
||||
'id': '42',
|
||||
"links": [{
|
||||
@@ -780,6 +797,8 @@ class ImagesTest(test.TestCase):
|
||||
'created': NOW_API_FORMAT,
|
||||
'status': 'ACTIVE',
|
||||
'progress': 100,
|
||||
'minDisk': 0,
|
||||
'minRam': 0,
|
||||
"links": [{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v1.1/fake/images/130",
|
||||
@@ -810,6 +829,30 @@ class ImagesTest(test.TestCase):
|
||||
controller.index(request)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_image_filter_with_min_ram(self):
|
||||
image_service = self.mox.CreateMockAnything()
|
||||
context = self._get_fake_context()
|
||||
filters = {'min_ram': '0'}
|
||||
image_service.index(context, filters=filters).AndReturn([])
|
||||
self.mox.ReplayAll()
|
||||
request = webob.Request.blank('/v1.1/images?minRam=0')
|
||||
request.environ['nova.context'] = context
|
||||
controller = images.ControllerV11(image_service=image_service)
|
||||
controller.index(request)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_image_filter_with_min_disk(self):
|
||||
image_service = self.mox.CreateMockAnything()
|
||||
context = self._get_fake_context()
|
||||
filters = {'min_disk': '7'}
|
||||
image_service.index(context, filters=filters).AndReturn([])
|
||||
self.mox.ReplayAll()
|
||||
request = webob.Request.blank('/v1.1/images?minDisk=7')
|
||||
request.environ['nova.context'] = context
|
||||
controller = images.ControllerV11(image_service=image_service)
|
||||
controller.index(request)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_image_filter_with_status(self):
|
||||
image_service = self.mox.CreateMockAnything()
|
||||
context = self._get_fake_context()
|
||||
@@ -1369,6 +1412,152 @@ class ImageXMLSerializationTest(test.TestCase):
|
||||
server_root = root.find('{0}server'.format(NS))
|
||||
self.assertEqual(server_root, None)
|
||||
|
||||
def test_show_with_min_ram(self):
|
||||
serializer = images.ImageXMLSerializer()
|
||||
|
||||
fixture = {
|
||||
'image': {
|
||||
'id': 1,
|
||||
'name': 'Image1',
|
||||
'created': self.TIMESTAMP,
|
||||
'updated': self.TIMESTAMP,
|
||||
'status': 'ACTIVE',
|
||||
'progress': 80,
|
||||
'minRam': 256,
|
||||
'server': {
|
||||
'id': '1',
|
||||
'links': [
|
||||
{
|
||||
'href': self.SERVER_HREF,
|
||||
'rel': 'self',
|
||||
},
|
||||
{
|
||||
'href': self.SERVER_BOOKMARK,
|
||||
'rel': 'bookmark',
|
||||
},
|
||||
],
|
||||
},
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
},
|
||||
'links': [
|
||||
{
|
||||
'href': self.IMAGE_HREF % 1,
|
||||
'rel': 'self',
|
||||
},
|
||||
{
|
||||
'href': self.IMAGE_BOOKMARK % 1,
|
||||
'rel': 'bookmark',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
output = serializer.serialize(fixture, 'show')
|
||||
print output
|
||||
root = etree.XML(output)
|
||||
xmlutil.validate_schema(root, 'image')
|
||||
image_dict = fixture['image']
|
||||
|
||||
for key in ['name', 'id', 'updated', 'created', 'status', 'progress',
|
||||
'minRam']:
|
||||
self.assertEqual(root.get(key), str(image_dict[key]))
|
||||
|
||||
link_nodes = root.findall('{0}link'.format(ATOMNS))
|
||||
self.assertEqual(len(link_nodes), 2)
|
||||
for i, link in enumerate(image_dict['links']):
|
||||
for key, value in link.items():
|
||||
self.assertEqual(link_nodes[i].get(key), value)
|
||||
|
||||
metadata_root = root.find('{0}metadata'.format(NS))
|
||||
metadata_elems = metadata_root.findall('{0}meta'.format(NS))
|
||||
self.assertEqual(len(metadata_elems), 1)
|
||||
for i, metadata_elem in enumerate(metadata_elems):
|
||||
(meta_key, meta_value) = image_dict['metadata'].items()[i]
|
||||
self.assertEqual(str(metadata_elem.get('key')), str(meta_key))
|
||||
self.assertEqual(str(metadata_elem.text).strip(), str(meta_value))
|
||||
|
||||
server_root = root.find('{0}server'.format(NS))
|
||||
self.assertEqual(server_root.get('id'), image_dict['server']['id'])
|
||||
link_nodes = server_root.findall('{0}link'.format(ATOMNS))
|
||||
self.assertEqual(len(link_nodes), 2)
|
||||
for i, link in enumerate(image_dict['server']['links']):
|
||||
for key, value in link.items():
|
||||
self.assertEqual(link_nodes[i].get(key), value)
|
||||
|
||||
def test_show_with_min_disk(self):
|
||||
serializer = images.ImageXMLSerializer()
|
||||
|
||||
fixture = {
|
||||
'image': {
|
||||
'id': 1,
|
||||
'name': 'Image1',
|
||||
'created': self.TIMESTAMP,
|
||||
'updated': self.TIMESTAMP,
|
||||
'status': 'ACTIVE',
|
||||
'progress': 80,
|
||||
'minDisk': 5,
|
||||
'server': {
|
||||
'id': '1',
|
||||
'links': [
|
||||
{
|
||||
'href': self.SERVER_HREF,
|
||||
'rel': 'self',
|
||||
},
|
||||
{
|
||||
'href': self.SERVER_BOOKMARK,
|
||||
'rel': 'bookmark',
|
||||
},
|
||||
],
|
||||
},
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
},
|
||||
'links': [
|
||||
{
|
||||
'href': self.IMAGE_HREF % 1,
|
||||
'rel': 'self',
|
||||
},
|
||||
{
|
||||
'href': self.IMAGE_BOOKMARK % 1,
|
||||
'rel': 'bookmark',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
output = serializer.serialize(fixture, 'show')
|
||||
print output
|
||||
root = etree.XML(output)
|
||||
xmlutil.validate_schema(root, 'image')
|
||||
image_dict = fixture['image']
|
||||
|
||||
for key in ['name', 'id', 'updated', 'created', 'status', 'progress',
|
||||
'minDisk']:
|
||||
self.assertEqual(root.get(key), str(image_dict[key]))
|
||||
|
||||
link_nodes = root.findall('{0}link'.format(ATOMNS))
|
||||
self.assertEqual(len(link_nodes), 2)
|
||||
for i, link in enumerate(image_dict['links']):
|
||||
for key, value in link.items():
|
||||
self.assertEqual(link_nodes[i].get(key), value)
|
||||
|
||||
metadata_root = root.find('{0}metadata'.format(NS))
|
||||
metadata_elems = metadata_root.findall('{0}meta'.format(NS))
|
||||
self.assertEqual(len(metadata_elems), 1)
|
||||
for i, metadata_elem in enumerate(metadata_elems):
|
||||
(meta_key, meta_value) = image_dict['metadata'].items()[i]
|
||||
self.assertEqual(str(metadata_elem.get('key')), str(meta_key))
|
||||
self.assertEqual(str(metadata_elem.text).strip(), str(meta_value))
|
||||
|
||||
server_root = root.find('{0}server'.format(NS))
|
||||
self.assertEqual(server_root.get('id'), image_dict['server']['id'])
|
||||
link_nodes = server_root.findall('{0}link'.format(ATOMNS))
|
||||
self.assertEqual(len(link_nodes), 2)
|
||||
for i, link in enumerate(image_dict['server']['links']):
|
||||
for key, value in link.items():
|
||||
self.assertEqual(link_nodes[i].get(key), value)
|
||||
|
||||
def test_index(self):
|
||||
serializer = images.ImageXMLSerializer()
|
||||
|
||||
|
||||
@@ -127,6 +127,8 @@ class TestGlanceImageService(test.TestCase):
|
||||
'name': 'test image',
|
||||
'is_public': False,
|
||||
'size': None,
|
||||
'min_disk': None,
|
||||
'min_ram': None,
|
||||
'location': None,
|
||||
'disk_format': None,
|
||||
'container_format': None,
|
||||
@@ -157,6 +159,8 @@ class TestGlanceImageService(test.TestCase):
|
||||
'name': 'test image',
|
||||
'is_public': False,
|
||||
'size': None,
|
||||
'min_disk': None,
|
||||
'min_ram': None,
|
||||
'location': None,
|
||||
'disk_format': None,
|
||||
'container_format': None,
|
||||
@@ -287,6 +291,8 @@ class TestGlanceImageService(test.TestCase):
|
||||
'name': 'TestImage %d' % (i),
|
||||
'properties': {},
|
||||
'size': None,
|
||||
'min_disk': None,
|
||||
'min_ram': None,
|
||||
'location': None,
|
||||
'disk_format': None,
|
||||
'container_format': None,
|
||||
@@ -330,6 +336,8 @@ class TestGlanceImageService(test.TestCase):
|
||||
'name': 'TestImage %d' % (i),
|
||||
'properties': {},
|
||||
'size': None,
|
||||
'min_disk': None,
|
||||
'min_ram': None,
|
||||
'location': None,
|
||||
'disk_format': None,
|
||||
'container_format': None,
|
||||
@@ -382,6 +390,8 @@ class TestGlanceImageService(test.TestCase):
|
||||
'name': 'image1',
|
||||
'is_public': True,
|
||||
'size': None,
|
||||
'min_disk': None,
|
||||
'min_ram': None,
|
||||
'location': None,
|
||||
'disk_format': None,
|
||||
'container_format': None,
|
||||
@@ -416,6 +426,8 @@ class TestGlanceImageService(test.TestCase):
|
||||
'name': 'image10',
|
||||
'is_public': True,
|
||||
'size': None,
|
||||
'min_disk': None,
|
||||
'min_ram': None,
|
||||
'location': None,
|
||||
'disk_format': None,
|
||||
'container_format': None,
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
Tests For Compute
|
||||
"""
|
||||
|
||||
from copy import copy
|
||||
|
||||
from nova import compute
|
||||
from nova import context
|
||||
from nova import db
|
||||
@@ -1394,3 +1396,92 @@ class ComputeTestCase(test.TestCase):
|
||||
self.assertEqual(self.compute_api._volume_size(inst_type,
|
||||
'swap'),
|
||||
swap_size)
|
||||
|
||||
|
||||
class ComputeTestMinRamMinDisk(test.TestCase):
|
||||
def setUp(self):
|
||||
super(ComputeTestMinRamMinDisk, self).setUp()
|
||||
self.compute = utils.import_object(FLAGS.compute_manager)
|
||||
self.compute_api = compute.API()
|
||||
self.context = context.RequestContext('fake', 'fake')
|
||||
self.fake_image = {
|
||||
'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
|
||||
|
||||
def test_create_with_too_little_ram(self):
|
||||
"""Test an instance type with too little memory"""
|
||||
|
||||
inst_type = instance_types.get_default_instance_type()
|
||||
inst_type['memory_mb'] = 1
|
||||
|
||||
def fake_show(*args):
|
||||
img = copy(self.fake_image)
|
||||
img['min_ram'] = 2
|
||||
return img
|
||||
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
|
||||
|
||||
self.assertRaises(exception.InstanceTypeMemoryTooSmall,
|
||||
self.compute_api.create, self.context, inst_type, None)
|
||||
|
||||
# Now increase the inst_type memory and make sure all is fine.
|
||||
inst_type['memory_mb'] = 2
|
||||
ref = self.compute_api.create(self.context, inst_type, None)
|
||||
self.assertTrue(ref)
|
||||
|
||||
db.instance_destroy(self.context, ref[0]['id'])
|
||||
|
||||
def test_create_with_too_little_disk(self):
|
||||
"""Test an instance type with too little disk space"""
|
||||
|
||||
inst_type = instance_types.get_default_instance_type()
|
||||
inst_type['local_gb'] = 1
|
||||
|
||||
def fake_show(*args):
|
||||
img = copy(self.fake_image)
|
||||
img['min_disk'] = 2
|
||||
return img
|
||||
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
|
||||
|
||||
self.assertRaises(exception.InstanceTypeDiskTooSmall,
|
||||
self.compute_api.create, self.context, inst_type, None)
|
||||
|
||||
# Now increase the inst_type disk space and make sure all is fine.
|
||||
inst_type['local_gb'] = 2
|
||||
ref = self.compute_api.create(self.context, inst_type, None)
|
||||
self.assertTrue(ref)
|
||||
|
||||
db.instance_destroy(self.context, ref[0]['id'])
|
||||
|
||||
def test_create_just_enough_ram_and_disk(self):
|
||||
"""Test an instance type with just enough ram and disk space"""
|
||||
|
||||
inst_type = instance_types.get_default_instance_type()
|
||||
inst_type['local_gb'] = 2
|
||||
inst_type['memory_mb'] = 2
|
||||
|
||||
def fake_show(*args):
|
||||
img = copy(self.fake_image)
|
||||
img['min_ram'] = 2
|
||||
img['min_disk'] = 2
|
||||
return img
|
||||
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
|
||||
|
||||
ref = self.compute_api.create(self.context, inst_type, None)
|
||||
self.assertTrue(ref)
|
||||
|
||||
db.instance_destroy(self.context, ref[0]['id'])
|
||||
|
||||
def test_create_with_no_ram_and_disk_reqs(self):
|
||||
"""Test an instance type with no min_ram or min_disk"""
|
||||
|
||||
inst_type = instance_types.get_default_instance_type()
|
||||
inst_type['local_gb'] = 1
|
||||
inst_type['memory_mb'] = 1
|
||||
|
||||
def fake_show(*args):
|
||||
return copy(self.fake_image)
|
||||
self.stubs.Set(fake_image._FakeImageService, 'show', fake_show)
|
||||
|
||||
ref = self.compute_api.create(self.context, inst_type, None)
|
||||
self.assertTrue(ref)
|
||||
|
||||
db.instance_destroy(self.context, ref[0]['id'])
|
||||
|
||||
Reference in New Issue
Block a user