From 45d28dfb035b4e219845d44e00073d70211e8175 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 21:14:26 -0700 Subject: Fixed up unit tests and direct api that was also calling _serialize (naughty!) --- nova/api/direct.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/direct.py b/nova/api/direct.py index dfca250e0..153871e9f 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -206,7 +206,9 @@ class ServiceWrapper(wsgi.Controller): params = dict([(str(k), v) for (k, v) in params.iteritems()]) result = method(context, **params) if type(result) is dict or type(result) is list: - return self._serialize(result, req.best_match_content_type()) + content_type = req.best_match_content_type() + default_xmlns = self.get_default_xmlns(req) + return self._serialize(result, content_type, default_xmlns) else: return result -- cgit From 12505c6bae61e72b6c75ac647323c3ec15997df1 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Wed, 30 Mar 2011 10:05:06 -0700 Subject: Add XML namespaces to the OpenStack API --- nova/api/openstack/accounts.py | 5 ++--- nova/api/openstack/backup_schedules.py | 4 ++-- nova/api/openstack/common.py | 11 +++++++++++ nova/api/openstack/consoles.py | 4 ++-- nova/api/openstack/extensions.py | 7 ++++--- nova/api/openstack/flavors.py | 7 +++++-- nova/api/openstack/image_metadata.py | 3 ++- nova/api/openstack/images.py | 5 ++++- nova/api/openstack/limits.py | 4 ++-- nova/api/openstack/server_metadata.py | 3 ++- nova/api/openstack/servers.py | 5 ++++- nova/api/openstack/shared_ip_groups.py | 4 ++-- nova/api/openstack/users.py | 3 +-- nova/api/openstack/zones.py | 6 ++---- 14 files changed, 45 insertions(+), 26 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/accounts.py b/nova/api/openstack/accounts.py index 86066fa20..6e3763e47 100644 --- a/nova/api/openstack/accounts.py +++ b/nova/api/openstack/accounts.py @@ -13,15 +13,14 @@ # License for the specific language governing permissions and limitations # under the License. -import common import webob.exc from nova import exception from nova import flags from nova import log as logging -from nova import wsgi from nova.auth import manager +from nova.api.openstack import common from nova.api.openstack import faults FLAGS = flags.FLAGS @@ -35,7 +34,7 @@ def _translate_keys(account): manager=account.project_manager_id) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): _serialization_metadata = { 'application/xml': { diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py index f2d2d86e8..4bf744046 100644 --- a/nova/api/openstack/backup_schedules.py +++ b/nova/api/openstack/backup_schedules.py @@ -19,7 +19,7 @@ import time from webob import exc -from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults import nova.image.service @@ -29,7 +29,7 @@ def _translate_keys(inst): return dict(backupSchedule=inst) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """ The backup schedule API controller for the Openstack API """ _serialization_metadata = { diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 75aeb0a5f..234f921ab 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -22,6 +22,7 @@ import webob from nova import exception from nova import flags from nova import log as logging +from nova import wsgi LOG = logging.getLogger('common') @@ -30,6 +31,10 @@ LOG = logging.getLogger('common') FLAGS = flags.FLAGS +XML_NS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0' +XML_NS_V11 = 'http://docs.openstack.org/compute/api/v1.1' + + def limited(items, request, max_limit=FLAGS.osapi_max_limit): """ Return a slice of items according to requested offset and limit. @@ -128,3 +133,9 @@ def get_id_from_href(href): except: LOG.debug(_("Error extracting id from href: %s") % href) raise webob.exc.HTTPBadRequest(_('could not parse id from href')) + + +class OpenstackController(wsgi.Controller): + def get_default_xmlns(self, req): + # Use V10 by default + return XML_NS_V10 diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py index 8c291c2eb..1a77f25d7 100644 --- a/nova/api/openstack/consoles.py +++ b/nova/api/openstack/consoles.py @@ -19,7 +19,7 @@ from webob import exc from nova import console from nova import exception -from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults @@ -43,7 +43,7 @@ def _translate_detail_keys(cons): return dict(console=info) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """The Consoles Controller for the Openstack API""" _serialization_metadata = { diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index fb1dccb28..75f369cac 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -28,6 +28,7 @@ from nova import exception from nova import flags from nova import log as logging from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults @@ -115,7 +116,7 @@ class ExtensionDescriptor(object): return response_exts -class ActionExtensionController(wsgi.Controller): +class ActionExtensionController(common.OpenstackController): def __init__(self, application): @@ -136,7 +137,7 @@ class ActionExtensionController(wsgi.Controller): return res -class ResponseExtensionController(wsgi.Controller): +class ResponseExtensionController(common.OpenstackController): def __init__(self, application): self.application = application @@ -163,7 +164,7 @@ class ResponseExtensionController(wsgi.Controller): return res -class ExtensionController(wsgi.Controller): +class ExtensionController(common.OpenstackController): def __init__(self, extension_manager): self.extension_manager = extension_manager diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 5b99b5a6f..40787bd17 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -19,11 +19,11 @@ import webob from nova import db from nova import exception -from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import views -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """Flavor controller for the OpenStack API.""" _serialization_metadata = { @@ -76,3 +76,6 @@ class ControllerV11(Controller): def _get_view_builder(self, req): base_url = req.application_url return views.flavors.ViewBuilderV11(base_url) + + def get_default_xmlns(self, req): + return common.XML_NS_V11 diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index c9d6ac532..e673e5f7b 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -20,13 +20,14 @@ from webob import exc from nova import flags from nova import utils from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults FLAGS = flags.FLAGS -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """The image metadata API controller for the Openstack API""" def __init__(self): diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index e77100d7b..5550ee532 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -32,7 +32,7 @@ LOG = log.getLogger('nova.api.openstack.images') FLAGS = flags.FLAGS -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """Base `wsgi.Controller` for retrieving/displaying images.""" _serialization_metadata = { @@ -153,3 +153,6 @@ class ControllerV11(Controller): """Property to get the ViewBuilder class we need to use.""" base_url = request.application_url return images_view.ViewBuilderV11(base_url) + + def get_default_xmlns(self, req): + return common.XML_NS_V11 diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py index efc7d193d..9877af191 100644 --- a/nova/api/openstack/limits.py +++ b/nova/api/openstack/limits.py @@ -31,8 +31,8 @@ from collections import defaultdict from webob.dec import wsgify from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults -from nova.wsgi import Controller from nova.wsgi import Middleware @@ -43,7 +43,7 @@ PER_HOUR = 60 * 60 PER_DAY = 60 * 60 * 24 -class LimitsController(Controller): +class LimitsController(common.OpenstackController): """ Controller for accessing limits in the OpenStack API. """ diff --git a/nova/api/openstack/server_metadata.py b/nova/api/openstack/server_metadata.py index 45bbac99d..5c1390b9c 100644 --- a/nova/api/openstack/server_metadata.py +++ b/nova/api/openstack/server_metadata.py @@ -19,10 +19,11 @@ from webob import exc from nova import compute from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """ The server metadata API controller for the Openstack API """ def __init__(self): diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index f7696d918..64090d830 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -44,7 +44,7 @@ LOG = logging.getLogger('server') FLAGS = flags.FLAGS -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """ The Server API controller for the OpenStack API """ _serialization_metadata = { @@ -632,6 +632,9 @@ class ControllerV11(Controller): def _limit_items(self, items, req): return common.limited_by_marker(items, req) + def get_default_xmlns(self, req): + return common.XML_NS_V11 + class ServerCreateRequestXMLDeserializer(object): """ diff --git a/nova/api/openstack/shared_ip_groups.py b/nova/api/openstack/shared_ip_groups.py index ee7991d7f..996db3648 100644 --- a/nova/api/openstack/shared_ip_groups.py +++ b/nova/api/openstack/shared_ip_groups.py @@ -17,7 +17,7 @@ from webob import exc -from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults @@ -32,7 +32,7 @@ def _translate_detail_keys(inst): return dict(sharedIpGroups=inst) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """ The Shared IP Groups Controller for the Openstack API """ _serialization_metadata = { diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py index d3ab3d553..077ccfc79 100644 --- a/nova/api/openstack/users.py +++ b/nova/api/openstack/users.py @@ -18,7 +18,6 @@ from webob import exc from nova import exception from nova import flags from nova import log as logging -from nova import wsgi from nova.api.openstack import common from nova.api.openstack import faults from nova.auth import manager @@ -35,7 +34,7 @@ def _translate_keys(user): admin=user.admin) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): _serialization_metadata = { 'application/xml': { diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 846cb48a1..227ffecdc 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -13,12 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. -import common - from nova import db from nova import flags from nova import log as logging -from nova import wsgi +from nova.api.openstack import common from nova.scheduler import api @@ -43,7 +41,7 @@ def _scrub_zone(zone): 'deleted', 'deleted_at', 'updated_at')) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): _serialization_metadata = { 'application/xml': { -- cgit From ee709d39e6c66ec4aad722fb20951ae9d714f259 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Wed, 30 Mar 2011 10:41:22 -0700 Subject: Some tests actually tested for the lack of a namespace :-) --- nova/api/openstack/extensions.py | 3 ++- nova/api/openstack/faults.py | 5 +++-- nova/api/openstack/images.py | 3 --- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 75f369cac..7ea7afef6 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -156,7 +156,8 @@ class ResponseExtensionController(common.OpenstackController): body = res.body headers = res.headers except AttributeError: - body = self._serialize(res, content_type) + default_xmlns = None + body = self._serialize(res, content_type, default_xmlns) headers = {"Content-Type": content_type} res = webob.Response() res.body = body diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index 0e9c4b26f..a05e97021 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -20,10 +20,10 @@ import webob.dec import webob.exc from nova import wsgi +from nova.api.openstack import common class Fault(webob.exc.HTTPException): - """An RS API fault response.""" _fault_names = { @@ -57,7 +57,8 @@ class Fault(webob.exc.HTTPException): fault_data[fault_name]['retryAfter'] = retry # 'code' is an attribute on the fault tag itself metadata = {'application/xml': {'attributes': {fault_name: 'code'}}} - serializer = wsgi.Serializer(metadata) + default_xmlns = common.XML_NS_V10 + serializer = wsgi.Serializer(metadata, default_xmlns) content_type = req.best_match_content_type() self.wrapped_exc.body = serializer.serialize(fault_data, content_type) return self.wrapped_exc diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 5550ee532..77baf5947 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime - import webob.exc from nova import compute @@ -22,7 +20,6 @@ from nova import exception from nova import flags from nova import log from nova import utils -from nova import wsgi from nova.api.openstack import common from nova.api.openstack import faults from nova.api.openstack.views import images as images_view -- cgit From 323eb60884cf8736448d997068d32f252d22e7f3 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 30 Mar 2011 12:05:25 -0700 Subject: Key type values in ec2_api off of container format --- nova/api/ec2/cloud.py | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 7ba8dfbea..1e6c4d1db 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -146,7 +146,7 @@ class CloudController(object): floating_ip = db.instance_get_floating_address(ctxt, instance_ref['id']) ec2_id = ec2utils.id_to_ec2_id(instance_ref['id']) - image_ec2_id = self._image_ec2_id(instance_ref['image_id'], 'machine') + image_ec2_id = self._image_ec2_id(instance_ref['image_id'], 'ami') data = { 'user-data': base64.b64decode(instance_ref['user_data']), 'meta-data': { @@ -176,7 +176,7 @@ class CloudController(object): for image_type in ['kernel', 'ramdisk']: if '%s_id' % image_type in instance_ref: ec2_id = self._image_ec2_id(instance_ref['%s_id' % image_type], - image_type) + self._image_type(image_type)) data['meta-data']['%s-id' % image_type] = ec2_id if False: # TODO(vish): store ancestor ids @@ -862,13 +862,27 @@ class CloudController(object): self.compute_api.update(context, instance_id=instance_id, **kwargs) return True - _type_prefix_map = {'machine': 'ami', - 'kernel': 'aki', - 'ramdisk': 'ari'} + @staticmethod + def _image_type(image_type): + """Converts to a three letter image type. - def _image_ec2_id(self, image_id, image_type='machine'): - prefix = self._type_prefix_map[image_type] - template = prefix + '-%08x' + aki, kernel => aki + ari, ramdisk => ari + anything else => ami + + """ + if image_type == 'kernel': + return 'aki' + if image_type == 'ramdisk': + return 'ari' + if image_type not in ['aki', 'ari']: + return 'ami' + return image_type + + @staticmethod + def _image_ec2_id(image_id, image_type='ami'): + """Returns image ec2_id using id and three letter type""" + template = image_type + '-%08x' return ec2utils.id_to_ec2_id(int(image_id), template=template) def _get_image(self, context, ec2_id): @@ -881,7 +895,7 @@ class CloudController(object): def _format_image(self, image): """Convert from format defined by BaseImageService to S3 format.""" i = {} - image_type = image['properties'].get('type') + image_type = self._image_type(image.get('container_format')) ec2_id = self._image_ec2_id(image.get('id'), image_type) name = image.get('name') if name: @@ -890,16 +904,19 @@ class CloudController(object): i['imageId'] = ec2_id kernel_id = image['properties'].get('kernel_id') if kernel_id: - i['kernelId'] = self._image_ec2_id(kernel_id, 'kernel') + i['kernelId'] = self._image_ec2_id(kernel_id, 'aki') ramdisk_id = image['properties'].get('ramdisk_id') if ramdisk_id: - i['ramdiskId'] = self._image_ec2_id(ramdisk_id, 'ramdisk') + i['ramdiskId'] = self._image_ec2_id(ramdisk_id, 'ari') i['imageOwnerId'] = image['properties'].get('owner_id') i['imageLocation'] = image['properties'].get('image_location') i['imageState'] = image['properties'].get('image_state') i['displayName'] = image.get('name') i['description'] = image.get('description') - i['type'] = image_type + display_mapping = {'aki': 'kernel', + 'ari': 'ramdisk', + 'ami': 'machine'} + i['type'] = display_mapping.get(image_type) i['isPublic'] = str(image['properties'].get('is_public', '')) == 'True' i['architecture'] = image['properties'].get('architecture') return i @@ -932,8 +949,9 @@ class CloudController(object): image_location = kwargs['name'] metadata = {'properties': {'image_location': image_location}} image = self.image_service.create(context, metadata) + image_type = self._image_type(image.get('container_format')) image_id = self._image_ec2_id(image['id'], - image['properties']['type']) + image_type) msg = _("Registered image %(image_location)s with" " id %(image_id)s") % locals() LOG.audit(msg, context=context) -- cgit From 1703592992ebdd5bbf19952f79f05022a4cdc849 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 30 Mar 2011 12:34:10 -0700 Subject: remove all references to image_type and change nova-manage upload to set container format more intelligently --- nova/api/openstack/servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index f7696d918..d640e8684 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -562,7 +562,7 @@ class Controller(wsgi.Controller): _("Cannot build from image %(image_id)s, status not active") % locals()) - if image_meta['properties']['disk_format'] != 'ami': + if image_meta.get('container_format') != 'ami': return None, None try: -- cgit From b8173ba9fe80045665064208e66f46cca75fd3ba Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 30 Mar 2011 13:48:07 -0700 Subject: review cleanup --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 1e6c4d1db..3541e49ca 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -881,7 +881,7 @@ class CloudController(object): @staticmethod def _image_ec2_id(image_id, image_type='ami'): - """Returns image ec2_id using id and three letter type""" + """Returns image ec2_id using id and three letter type.""" template = image_type + '-%08x' return ec2utils.id_to_ec2_id(int(image_id), template=template) -- cgit From 7f98416fdba59c4305f88a30a457bbf6c68ef372 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 31 Mar 2011 09:20:47 -0400 Subject: Limit image metadata to the configured metadata quota for a project. --- nova/api/openstack/image_metadata.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index c9d6ac532..76abdaa7e 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -18,6 +18,7 @@ from webob import exc from nova import flags +from nova import quota from nova import utils from nova import wsgi from nova.api.openstack import faults @@ -39,6 +40,15 @@ class Controller(wsgi.Controller): metadata = image.get('properties', {}) return metadata + def _check_quota_limit(self, context, metadata): + if metadata is None: + return + num_metadata = len(metadata) + quota_metadata = quota.allowed_metadata_items(context, num_metadata) + if quota_metadata < num_metadata: + expl = ("Image metadata limit exceeded") + raise exc.HTTPBadRequest(explanation=expl) + def index(self, req, image_id): """Returns the list of metadata for a given instance""" context = req.environ['nova.context'] @@ -61,6 +71,7 @@ class Controller(wsgi.Controller): if 'metadata' in body: for key, value in body['metadata'].iteritems(): metadata[key] = value + self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) return dict(metadata=metadata) @@ -77,6 +88,7 @@ class Controller(wsgi.Controller): img = self.image_service.show(context, image_id) metadata = self._get_metadata(context, image_id, img) metadata[id] = body[id] + self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) -- cgit From 38b4cd9e68d7e1c262b08474b277573440ec3e87 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 4 Apr 2011 16:17:04 -0400 Subject: Refactor so that instances.instance_type is now instances.instance_type_id. Update the Openstack API to return the correct flavor_id. --- nova/api/ec2/cloud.py | 7 +++++-- nova/api/openstack/servers.py | 6 ++++-- nova/api/openstack/views/servers.py | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 7ba8dfbea..cd1195502 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -722,7 +722,10 @@ class CloudController(object): instance['project_id'], instance['host']) i['productCodesSet'] = self._convert_to_set([], 'product_codes') - i['instanceType'] = instance['instance_type'] + if instance['instance_type']: + i['instanceType'] = instance['instance_type'].get('name', None) + else: + i['instanceType'] = None i['launchTime'] = instance['created_at'] i['amiLaunchIndex'] = instance['launch_index'] i['displayName'] = instance['display_name'] @@ -805,7 +808,7 @@ class CloudController(object): ramdisk = self._get_image(context, kwargs['ramdisk_id']) kwargs['ramdisk_id'] = ramdisk['id'] instances = self.compute_api.create(context, - instance_type=instance_types.get_by_type( + instance_type=instance_types.get_instance_type_by_name( kwargs.get('instance_type', None)), image_id=self._get_image(context, kwargs['image_id'])['id'], min_count=int(kwargs.get('min_count', max_count)), diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 4e2ebb2bd..327911f6f 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -160,9 +160,11 @@ class Controller(wsgi.Controller): name = name.strip() try: + inst_type = \ + instance_types.get_instance_type_by_flavor_id(flavor_id) (inst,) = self.compute_api.create( context, - instance_types.get_by_flavor_id(flavor_id), + inst_type, image_id, kernel_id=kernel_id, ramdisk_id=ramdisk_id, @@ -175,7 +177,7 @@ class Controller(wsgi.Controller): except quota.QuotaError as error: self._handle_quota_error(error) - inst['instance_type'] = flavor_id + inst['instance_type'] = inst_type inst['image_id'] = requested_image_id builder = self._get_view_builder(req) diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index 4e7f62eb3..2f400eef6 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -115,7 +115,7 @@ class ViewBuilderV10(ViewBuilder): def _build_flavor(self, response, inst): if 'instance_type' in dict(inst): - response['flavorId'] = inst['instance_type'] + response['flavorId'] = inst['instance_type']['flavorid'] class ViewBuilderV11(ViewBuilder): @@ -134,7 +134,7 @@ class ViewBuilderV11(ViewBuilder): def _build_flavor(self, response, inst): if "instance_type" in dict(inst): - flavor_id = inst["instance_type"] + flavor_id = inst["instance_type"]['flavorid'] flavor_ref = self.flavor_builder.generate_href(flavor_id) response["flavorRef"] = flavor_ref -- cgit From 5c67809e4b9a1546c48316ea52676dfeba8f1a75 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 4 Apr 2011 15:58:41 -0700 Subject: openstack api requires uppercase image format status responses --- nova/api/openstack/views/images.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 16195b050..9dec8a355 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -34,11 +34,11 @@ class ViewBuilder(object): def _format_status(self, image): """Update the status field to standardize format.""" status_mapping = { - 'pending': 'queued', - 'decrypting': 'preparing', - 'untarring': 'saving', - 'available': 'active', - 'killed': 'failed', + 'pending': 'QUEUED', + 'decrypting': 'PREPARING', + 'untarring': 'SAVING', + 'available': 'ACTIVE', + 'killed': 'FAILED', } try: -- cgit From a6c283f00d67a5172d8271d7e48bd58484ac6b96 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 4 Apr 2011 16:00:59 -0700 Subject: openstack api metadata responses must be strings --- nova/api/openstack/views/servers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index d24c025be..baa911590 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -82,7 +82,8 @@ class ViewBuilder(object): # Return the metadata as a dictionary metadata = {} for item in inst.get('metadata', []): - metadata[item['key']] = item['value'] + # metadata values must be strings per API + metadata[item['key']] = str(item['value']) inst_dict['metadata'] = metadata inst_dict['hostId'] = '' -- cgit From 7702affdbf09127e3e7cdfafcb6382673914438e Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Tue, 5 Apr 2011 18:43:11 +0900 Subject: Remove and from AllocateAddress response, and fix bug #751176. --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 425784e8a..3b333437b 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -777,7 +777,7 @@ class CloudController(object): def allocate_address(self, context, **kwargs): LOG.audit(_("Allocate address"), context=context) public_ip = self.network_api.allocate_floating_ip(context) - return {'addressSet': [{'publicIp': public_ip}]} + return {'publicIp': public_ip} def release_address(self, context, public_ip, **kwargs): LOG.audit(_("Release address %s"), public_ip, context=context) -- cgit From d8ab404fd74facd181eaaee7cbc78b39a82afd1c Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 5 Apr 2011 08:26:16 -0400 Subject: Add missing underscore. --- nova/api/openstack/image_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index 76abdaa7e..c733172ae 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -46,7 +46,7 @@ class Controller(wsgi.Controller): num_metadata = len(metadata) quota_metadata = quota.allowed_metadata_items(context, num_metadata) if quota_metadata < num_metadata: - expl = ("Image metadata limit exceeded") + expl = _("Image metadata limit exceeded") raise exc.HTTPBadRequest(explanation=expl) def index(self, req, image_id): -- cgit From e6505fd78cc28a5e91ec6fbf55e2e8de07679c7c Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Tue, 5 Apr 2011 10:56:24 -0400 Subject: Fixed the addresses and metadata collections in xml responses. Added corresponding tests. --- nova/api/openstack/servers.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 6704a68ae..cada92813 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -55,6 +55,13 @@ class Controller(wsgi.Controller): "imageRef"], "link": ["rel", "type", "href"], }, + "dict_collections": { + "metadata": {"item_name": "meta", "item_key": "key"}, + }, + "list_collections": { + "public": {"item_name": "ip", "item_key": "addr"}, + "private": {"item_name": "ip", "item_key": "addr"}, + }, }, } -- cgit From 56eb682a169c20a9a8275b3b9bfd423f0d562cbc Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 5 Apr 2011 15:01:33 -0700 Subject: unite the filtering done by glance client and s3 --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 02a30220a..b4705820e 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -927,7 +927,7 @@ class CloudController(object): 'ari': 'ramdisk', 'ami': 'machine'} i['imageType'] = display_mapping.get(image_type) - i['isPublic'] = str(image['properties'].get('is_public', '')) == 'True' + i['isPublic'] = image.get('is_public') == True i['architecture'] = image['properties'].get('architecture') return i -- cgit From 5f414311fb581ba612cf152f3ec9983d5f39c2e4 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 5 Apr 2011 15:45:01 -0700 Subject: fallback to status if image_state is not set --- nova/api/ec2/cloud.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b4705820e..4e5b8bfc6 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -920,7 +920,11 @@ class CloudController(object): get('image_location'), name) else: i['imageLocation'] = image['properties'].get('image_location') - i['imageState'] = image['properties'].get('image_state') + # NOTE(vish): fallback status if image_state isn't set + state = image.get('status') + if state == 'active': + state = 'available' + i['imageState'] = image['properties'].get('image_state', state) i['displayName'] = name i['description'] = image.get('description') display_mapping = {'aki': 'kernel', -- cgit From 5534113f65cc578dcdf93432981b836bf4e3dfaf Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Wed, 6 Apr 2011 16:12:32 -0400 Subject: Added support for listing addresses of a server in the openstack api. Now you can GET * /servers/1/ips * /servers/1/ips/public * /servers/1/ips/private Supports v1.0 json and xml. Added corresponding tests. --- nova/api/openstack/__init__.py | 6 +++ nova/api/openstack/ips.py | 71 +++++++++++++++++++++++++++++++++++ nova/api/openstack/servers.py | 9 ----- nova/api/openstack/views/addresses.py | 10 ++++- 4 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 nova/api/openstack/ips.py (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 7545eb0c9..5e76a06f7 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -34,6 +34,7 @@ from nova.api.openstack import consoles from nova.api.openstack import flavors from nova.api.openstack import images from nova.api.openstack import image_metadata +from nova.api.openstack import ips from nova.api.openstack import limits from nova.api.openstack import servers from nova.api.openstack import server_metadata @@ -144,6 +145,11 @@ class APIRouterV10(APIRouter): parent_resource=dict(member_name='server', collection_name='servers')) + mapper.resource("ip", "ips", controller=ips.Controller(), + collection=dict(public='GET', private='GET'), + parent_resource=dict(member_name='server', + collection_name='servers')) + class APIRouterV11(APIRouter): """Define routes specific to OpenStack API V1.1.""" diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py new file mode 100644 index 000000000..89b0936d5 --- /dev/null +++ b/nova/api/openstack/ips.py @@ -0,0 +1,71 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import time + +from webob import exc + +import nova +import nova.api.openstack.views.addresses +from nova.api.openstack import faults + + +class Controller(nova.wsgi.Controller): + """The servers addresses API controller for the Openstack API.""" + + _serialization_metadata = { + 'application/xml': { + 'list_collections': { + 'public': {'item_name': 'ip', 'item_key': 'addr'}, + 'private': {'item_name': 'ip', 'item_key': 'addr'}, + }, + }, + } + + def __init__(self): + self.compute_api = nova.compute.API() + self.builder = nova.api.openstack.views.addresses.ViewBuilderV10() + + def index(self, req, server_id): + try: + instance = self.compute_api.get(req.environ['nova.context'], id) + except nova.exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return {'addresses': self.builder.build(instance)} + + def public(self, req, server_id): + try: + instance = self.compute_api.get(req.environ['nova.context'], id) + except nova.exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return {'public': self.builder.build_public_parts(instance)} + + def private(self, req, server_id): + try: + instance = self.compute_api.get(req.environ['nova.context'], id) + except nova.exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return {'private': self.builder.build_private_parts(instance)} + + def show(self, req, server_id, id): + return faults.Fault(exc.HTTPNotImplemented()) + + def create(self, req, server_id): + return faults.Fault(exc.HTTPNotImplemented()) + + def delete(self, req, server_id, id): + return faults.Fault(exc.HTTPNotImplemented()) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index cada92813..67e6eefd8 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -70,15 +70,6 @@ class Controller(wsgi.Controller): self._image_service = utils.import_object(FLAGS.image_service) super(Controller, self).__init__() - def ips(self, req, id): - try: - instance = self.compute_api.get(req.environ['nova.context'], id) - except exception.NotFound: - return faults.Fault(exc.HTTPNotFound()) - - builder = self._get_addresses_view_builder(req) - return builder.build(instance) - def index(self, req): """ Returns a list of server names and ids for a given user """ return self._items(req, is_detail=False) diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index 90c77855b..2810cce39 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -28,10 +28,16 @@ class ViewBuilder(object): class ViewBuilderV10(ViewBuilder): def build(self, inst): - private_ips = utils.get_from_path(inst, 'fixed_ip/address') - public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address') + private_ips = self.build_private_parts(inst) + public_ips = self.build_public_parts(inst) return dict(public=public_ips, private=private_ips) + def build_public_parts(self, inst): + return utils.get_from_path(inst, 'fixed_ip/floating_ips/address') + + def build_private_parts(self, inst): + return utils.get_from_path(inst, 'fixed_ip/address') + class ViewBuilderV11(ViewBuilder): def build(self, inst): -- cgit From e16571671e4baab50521870ec64e4ab954c7d165 Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Wed, 6 Apr 2011 17:50:11 -0400 Subject: Controllers now inherit from nova.api.openstack.common.OpenstackController. --- nova/api/openstack/ips.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py index 89b0936d5..778e9ba1a 100644 --- a/nova/api/openstack/ips.py +++ b/nova/api/openstack/ips.py @@ -21,10 +21,11 @@ from webob import exc import nova import nova.api.openstack.views.addresses +from nova.api.openstack import common from nova.api.openstack import faults -class Controller(nova.wsgi.Controller): +class Controller(common.OpenstackController): """The servers addresses API controller for the Openstack API.""" _serialization_metadata = { -- cgit From dc8b3cb3fa124755ed3e5282b2d11a811f1db2d5 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 6 Apr 2011 15:39:44 -0700 Subject: removed comment on API compliance --- nova/api/openstack/views/servers.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index baa911590..096bf70c6 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -82,7 +82,6 @@ class ViewBuilder(object): # Return the metadata as a dictionary metadata = {} for item in inst.get('metadata', []): - # metadata values must be strings per API metadata[item['key']] = str(item['value']) inst_dict['metadata'] = metadata -- cgit From 404feb59a829e24e026f793a362e54aad1aaa03f Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 7 Apr 2011 13:55:42 -0500 Subject: Renamed computeFault to cloudServersFault --- nova/api/openstack/faults.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index bc97639a0..87118ce19 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -47,7 +47,7 @@ class Fault(webob.exc.HTTPException): """Generate a WSGI response based on the exception passed to ctor.""" # Replace the body with fault details. code = self.wrapped_exc.status_int - fault_name = self._fault_names.get(code, "computeFault") + fault_name = self._fault_names.get(code, "cloudServersFault") fault_data = { fault_name: { 'code': code, -- cgit From c7fd470d7ff0df4b23664b6599e5ae5acdb21511 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 7 Apr 2011 15:52:14 -0400 Subject: Drop extra 'None' arg from dict.get call. --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index e4bcccb2b..c3124b89d 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -731,7 +731,7 @@ class CloudController(object): instance['host']) i['productCodesSet'] = self._convert_to_set([], 'product_codes') if instance['instance_type']: - i['instanceType'] = instance['instance_type'].get('name', None) + i['instanceType'] = instance['instance_type'].get('name') else: i['instanceType'] = None i['launchTime'] = instance['created_at'] -- cgit From 7badb6c0278c8cc51fc3e870fd3810ea3706f494 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 7 Apr 2011 23:39:29 -0400 Subject: Update the describe_image_attribute and modify_image_attribute functions in the ec2 API so they use the top level 'is_public' attribute of image objects. This brings these functions in line with the base image service. Added missing EC2 unit tests on describing and modifying image attributes. --- nova/api/ec2/cloud.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4ed8a9ecf..651ec47f9 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -984,7 +984,7 @@ class CloudController(object): except exception.NotFound: raise exception.NotFound(_('Image %s not found') % image_id) result = {'imageId': image_id, 'launchPermission': []} - if image['properties']['is_public']: + if image['is_public']: result['launchPermission'].append({'group': 'all'}) return result @@ -1009,7 +1009,7 @@ class CloudController(object): internal_id = image['id'] del(image['id']) - image['properties']['is_public'] = (operation_type == 'add') + image['is_public'] = (operation_type == 'add') return self.image_service.update(context, internal_id, image) def update_image(self, context, image_id, **kwargs): -- cgit From 5752838917237e7b86a64117f46c71d1c2a356f3 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 11 Apr 2011 16:18:18 +0200 Subject: Replace instance ref from compute.api.get_all with one from instance_get. This should ensure it gets fully populated with all the relevant attributes. --- nova/api/ec2/cloud.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 651ec47f9..83c894bea 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -142,6 +142,11 @@ class CloudController(object): instance_ref = self.compute_api.get_all(ctxt, fixed_ip=address) if instance_ref is None: return None + + # This ensures that all attributes of the instance + # are populated. + instance_ref = db.instance_get(ctxt, instance_ref['id']) + mpi = self._get_mpi_data(ctxt, instance_ref['project_id']) if instance_ref['key_name']: keys = {'0': {'_name': instance_ref['key_name'], -- cgit From 07c1f30225fb27cbc8e7cfeebc6a73ec67a7f2e5 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 12 Apr 2011 09:41:42 +0200 Subject: pep8 --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 83c894bea..5e81878c4 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -144,7 +144,7 @@ class CloudController(object): return None # This ensures that all attributes of the instance - # are populated. + # are populated. instance_ref = db.instance_get(ctxt, instance_ref['id']) mpi = self._get_mpi_data(ctxt, instance_ref['project_id']) -- cgit