summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorSandy Walsh <sandy.walsh@rackspace.com>2011-09-15 16:52:58 -0700
committerSandy Walsh <sandy.walsh@rackspace.com>2011-09-15 16:52:58 -0700
commit06c4eb3d8570e4f07a6c3012d2dc2ab907c460d8 (patch)
treefc61a8f43a941586b973d46e458dcffcf0432b33 /nova/api
parent2fd55ddfdf065a8792fdd0e3f3e97b5e56e4a4a3 (diff)
parent8a91c953b286c9de4a715cd01ab82c571db4e3c9 (diff)
downloadnova-06c4eb3d8570e4f07a6c3012d2dc2ab907c460d8.tar.gz
nova-06c4eb3d8570e4f07a6c3012d2dc2ab907c460d8.tar.xz
nova-06c4eb3d8570e4f07a6c3012d2dc2ab907c460d8.zip
trunk merge
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/ec2/cloud.py12
-rw-r--r--nova/api/openstack/common.py24
-rw-r--r--nova/api/openstack/contrib/flavorextradata.py46
-rw-r--r--nova/api/openstack/contrib/volumes.py3
-rw-r--r--nova/api/openstack/flavors.py4
-rw-r--r--nova/api/openstack/schemas/v1.1/flavor.rng4
-rw-r--r--nova/api/openstack/versions.py4
-rw-r--r--nova/api/openstack/views/flavors.py3
-rw-r--r--nova/api/openstack/views/images.py10
-rw-r--r--nova/api/openstack/wsgi.py45
10 files changed, 122 insertions, 33 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 0efb90d6e..fb1afa43a 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -326,11 +326,6 @@ class CloudController(object):
instance_ref = db.instance_get(ctxt, instance_ref[0]['id'])
mpi = self._get_mpi_data(ctxt, instance_ref['project_id'])
- if instance_ref['key_name']:
- keys = {'0': {'_name': instance_ref['key_name'],
- 'openssh-key': instance_ref['key_data']}}
- else:
- keys = ''
hostname = instance_ref['hostname']
host = instance_ref['host']
availability_zone = self._get_availability_zone_by_host(ctxt, host)
@@ -358,11 +353,16 @@ class CloudController(object):
'placement': {'availability-zone': availability_zone},
'public-hostname': hostname,
'public-ipv4': floating_ip or '',
- 'public-keys': keys,
'reservation-id': instance_ref['reservation_id'],
'security-groups': security_groups,
'mpi': mpi}}
+ # public-keys should be in meta-data only if user specified one
+ if instance_ref['key_name']:
+ data['meta-data']['public-keys'] = {
+ '0': {'_name': instance_ref['key_name'],
+ 'openssh-key': instance_ref['key_data']}}
+
for image_type in ['kernel', 'ramdisk']:
if instance_ref.get('%s_id' % image_type):
ec2_id = self.image_ec2_id(instance_ref['%s_id' % image_type],
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index a836a584c..ca7848678 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -187,30 +187,16 @@ def limited_by_marker(items, request, max_limit=FLAGS.osapi_max_limit):
def get_id_from_href(href):
- """Return the id portion of a url as an int.
+ """Return the id or uuid portion of a url.
Given: 'http://www.foo.com/bar/123?q=4'
- Returns: 123
+ Returns: '123'
- In order to support local hrefs, the href argument can be just an id:
- Given: '123'
- Returns: 123
+ Given: 'http://www.foo.com/bar/abc123?q=4'
+ Returns: 'abc123'
"""
- LOG.debug(_("Attempting to treat %(href)s as an integer ID.") % locals())
-
- try:
- return int(href)
- except ValueError:
- pass
-
- LOG.debug(_("Attempting to treat %(href)s as a URL.") % locals())
-
- try:
- return int(urlparse.urlsplit(href).path.split('/')[-1])
- except ValueError as error:
- LOG.debug(_("Failed to parse ID from %(href)s: %(error)s") % locals())
- raise
+ return urlparse.urlsplit("%s" % href).path.split('/')[-1]
def remove_version_from_href(href):
diff --git a/nova/api/openstack/contrib/flavorextradata.py b/nova/api/openstack/contrib/flavorextradata.py
new file mode 100644
index 000000000..d0554c7b4
--- /dev/null
+++ b/nova/api/openstack/contrib/flavorextradata.py
@@ -0,0 +1,46 @@
+# Copyright 2011 Canonical Ltd.
+# 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.
+
+"""
+The Flavor extra data extension
+Openstack API version 1.1 lists "name", "ram", "disk", "vcpus" as flavor
+attributes. This extension adds to that list:
+ rxtx_cap
+ rxtx_quota
+ swap
+"""
+
+from nova.api.openstack import extensions
+
+
+class Flavorextradata(extensions.ExtensionDescriptor):
+ """The Flavor extra data extension for the OpenStack API."""
+
+ def get_name(self):
+ return "FlavorExtraData"
+
+ def get_alias(self):
+ return "os-flavor-extra-data"
+
+ def get_description(self):
+ return "Provide additional data for flavors"
+
+ def get_namespace(self):
+ return "http://docs.openstack.org/ext/flavor_extra_data/api/v1.1"
+
+ def get_updated(self):
+ return "2011-09-14T00:00:00+00:00"
+
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py
index d62225e58..9d4254f1f 100644
--- a/nova/api/openstack/contrib/volumes.py
+++ b/nova/api/openstack/contrib/volumes.py
@@ -372,8 +372,7 @@ class BootFromVolumeController(servers.ControllerV11):
for key in ['instance_type', 'image_ref']:
inst[key] = extra_values[key]
- builder = self._get_view_builder(req)
- server = builder.build(inst, is_detail=True)
+ server = self._build_view(req, inst, is_detail=True)
server['server']['adminPass'] = extra_values['password']
return server
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index 805aad772..8a310c900 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -92,6 +92,10 @@ class FlavorXMLSerializer(wsgi.XMLDictSerializer):
if detailed:
flavor_elem.set('ram', str(flavor_dict['ram']))
flavor_elem.set('disk', str(flavor_dict['disk']))
+
+ for attr in ("vcpus", "swap", "rxtx_quota", "rxtx_cap"):
+ flavor_elem.set(attr, str(flavor_dict.get(attr, "")))
+
for link in flavor_dict.get('links', []):
elem = etree.SubElement(flavor_elem,
'{%s}link' % xmlutil.XMLNS_ATOM)
diff --git a/nova/api/openstack/schemas/v1.1/flavor.rng b/nova/api/openstack/schemas/v1.1/flavor.rng
index a00e4e9ee..6d3adc8dc 100644
--- a/nova/api/openstack/schemas/v1.1/flavor.rng
+++ b/nova/api/openstack/schemas/v1.1/flavor.rng
@@ -4,6 +4,10 @@
<attribute name="id"> <text/> </attribute>
<attribute name="ram"> <text/> </attribute>
<attribute name="disk"> <text/> </attribute>
+ <attribute name="rxtx_cap"> <text/> </attribute>
+ <attribute name="rxtx_quota"> <text/> </attribute>
+ <attribute name="swap"> <text/> </attribute>
+ <attribute name="vcpus"> <text/> </attribute>
<zeroOrMore>
<externalRef href="../atom-link.rng"/>
</zeroOrMore>
diff --git a/nova/api/openstack/versions.py b/nova/api/openstack/versions.py
index 31dd9dc11..75a1d0ba4 100644
--- a/nova/api/openstack/versions.py
+++ b/nova/api/openstack/versions.py
@@ -107,7 +107,9 @@ class Versions(wsgi.Resource):
headers_serializer=headers_serializer)
supported_content_types = ('application/json',
+ 'application/vnd.openstack.compute+json',
'application/xml',
+ 'application/vnd.openstack.compute+xml',
'application/atom+xml')
deserializer = VersionsRequestDeserializer(
supported_content_types=supported_content_types)
@@ -308,7 +310,9 @@ def create_resource(version='1.0'):
serializer = wsgi.ResponseSerializer(body_serializers)
supported_content_types = ('application/json',
+ 'application/vnd.openstack.compute+json',
'application/xml',
+ 'application/vnd.openstack.compute+xml',
'application/atom+xml')
deserializer = wsgi.RequestDeserializer(
supported_content_types=supported_content_types)
diff --git a/nova/api/openstack/views/flavors.py b/nova/api/openstack/views/flavors.py
index aea34b424..def969a6c 100644
--- a/nova/api/openstack/views/flavors.py
+++ b/nova/api/openstack/views/flavors.py
@@ -50,6 +50,9 @@ class ViewBuilder(object):
"disk": flavor_obj["local_gb"],
}
+ for key in ("vcpus", "swap", "rxtx_quota", "rxtx_cap"):
+ detail[key] = flavor_obj.get(key, "")
+
detail.update(simple)
return detail
diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py
index 8983b2957..86e8d7f3a 100644
--- a/nova/api/openstack/views/images.py
+++ b/nova/api/openstack/views/images.py
@@ -71,6 +71,7 @@ class ViewBuilder(object):
}
self._build_server(image, image_obj)
+ self._build_image_id(image, image_obj)
if detail:
image.update({
@@ -96,6 +97,12 @@ class ViewBuilderV10(ViewBuilder):
except (KeyError, ValueError):
pass
+ def _build_image_id(self, image, image_obj):
+ try:
+ image['id'] = int(image_obj['id'])
+ except ValueError:
+ pass
+
class ViewBuilderV11(ViewBuilder):
"""OpenStack API v1.1 Image Builder"""
@@ -119,6 +126,9 @@ class ViewBuilderV11(ViewBuilder):
except KeyError:
return
+ def _build_image_id(self, image, image_obj):
+ image['id'] = "%s" % image_obj['id']
+
def generate_href(self, image_id):
"""Return an href string pointing to this object."""
return os.path.join(self.base_url, self.project_id,
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index bdcadcb99..180f328b9 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -1,3 +1,19 @@
+# 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 json
from lxml import etree
@@ -19,6 +35,21 @@ XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
LOG = logging.getLogger('nova.api.openstack.wsgi')
+# The vendor content types should serialize identically to the non-vendor
+# content types. So to avoid littering the code with both options, we
+# map the vendor to the other when looking up the type
+_CONTENT_TYPE_MAP = {
+ 'application/vnd.openstack.compute+json': 'application/json',
+ 'application/vnd.openstack.compute+xml': 'application/xml',
+}
+
+_SUPPORTED_CONTENT_TYPES = (
+ 'application/json',
+ 'application/vnd.openstack.compute+json',
+ 'application/xml',
+ 'application/vnd.openstack.compute+xml',
+)
+
class Request(webob.Request):
"""Add some Openstack API-specific logic to the base webob.Request."""
@@ -30,7 +61,7 @@ class Request(webob.Request):
"""
supported_content_types = supported_content_types or \
- ('application/json', 'application/xml')
+ _SUPPORTED_CONTENT_TYPES
parts = self.path.rsplit('.', 1)
if len(parts) > 1:
@@ -52,7 +83,7 @@ class Request(webob.Request):
if not "Content-Type" in self.headers:
return None
- allowed_types = ("application/xml", "application/json")
+ allowed_types = _SUPPORTED_CONTENT_TYPES
content_type = self.content_type
if content_type not in allowed_types:
@@ -192,7 +223,7 @@ class RequestDeserializer(object):
supported_content_types=None):
self.supported_content_types = supported_content_types or \
- ('application/json', 'application/xml')
+ _SUPPORTED_CONTENT_TYPES
self.body_deserializers = {
'application/xml': XMLDeserializer(),
@@ -250,7 +281,8 @@ class RequestDeserializer(object):
def get_body_deserializer(self, content_type):
try:
- return self.body_deserializers[content_type]
+ ctype = _CONTENT_TYPE_MAP.get(content_type, content_type)
+ return self.body_deserializers[ctype]
except (KeyError, TypeError):
raise exception.InvalidContentType(content_type=content_type)
@@ -316,7 +348,7 @@ class XMLDictSerializer(DictSerializer):
def to_xml_string(self, node, has_atom=False):
self._add_xmlns(node, has_atom)
- return node.toprettyxml(indent=' ', encoding='UTF-8')
+ return node.toxml('UTF-8')
#NOTE (ameade): the has_atom should be removed after all of the
# xml serializers and view builders have been updated to the current
@@ -444,7 +476,8 @@ class ResponseSerializer(object):
def get_body_serializer(self, content_type):
try:
- return self.body_serializers[content_type]
+ ctype = _CONTENT_TYPE_MAP.get(content_type, content_type)
+ return self.body_serializers[ctype]
except (KeyError, TypeError):
raise exception.InvalidContentType(content_type=content_type)