summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorTushar Patil <tushar.vitthal.patil@gmail.com>2011-07-19 15:35:54 -0700
committerTushar Patil <tushar.vitthal.patil@gmail.com>2011-07-19 15:35:54 -0700
commit6cbd1d860d6a3fe96417391c21fb79b1750ecdcf (patch)
tree8ca17d4d4d5bbc15942602bf985c58c3cd1dd95d /nova/api
parent6d410105828c4dbfa11df5dd146b3b2591a24409 (diff)
Added a new extension instead of directly making changes to OS V1.1. API
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/contrib/createserverext.py234
-rw-r--r--nova/api/openstack/create_instance_helper.py51
-rw-r--r--nova/api/openstack/extensions.py9
-rw-r--r--nova/api/openstack/servers.py35
4 files changed, 262 insertions, 67 deletions
diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py
new file mode 100644
index 000000000..e8fe9afad
--- /dev/null
+++ b/nova/api/openstack/contrib/createserverext.py
@@ -0,0 +1,234 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+#
+# 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
+from webob import exc
+
+from nova import exception
+import nova.image
+from nova import network
+from nova import quota
+from nova import rpc
+
+from nova.api.openstack import create_instance_helper as helper
+from nova.api.openstack import extensions
+from nova.api.openstack import faults
+from nova.compute import instance_types
+from nova.api.openstack import servers
+from nova.api.openstack import wsgi
+from nova.auth import manager as auth_manager
+
+
+class CreateInstanceHelperEx(helper.CreateInstanceHelper):
+ def __init__(self, controller):
+ super(CreateInstanceHelperEx, self).__init__(controller)
+
+ def create_instance(self, req, body, create_method):
+ """Creates a new server for the given user as per
+ the network information if it is provided
+ """
+ if not body:
+ raise faults.Fault(exc.HTTPUnprocessableEntity())
+
+ context = req.environ['nova.context']
+
+ password = self.controller._get_server_admin_password(body['server'])
+
+ key_name = None
+ key_data = None
+ key_pairs = auth_manager.AuthManager.get_key_pairs(context)
+ if key_pairs:
+ key_pair = key_pairs[0]
+ key_name = key_pair['name']
+ key_data = key_pair['public_key']
+
+ image_href = self.controller._image_ref_from_req_data(body)
+ try:
+ image_service, image_id = nova.image.get_image_service(image_href)
+ kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image(
+ req, image_id)
+ images = set([str(x['id']) for x in image_service.index(context)])
+ assert str(image_id) in images
+ except Exception, e:
+ msg = _("Cannot find requested image %(image_href)s: %(e)s" %
+ locals())
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ personality = body['server'].get('personality')
+
+ injected_files = []
+ if personality:
+ injected_files = self._get_injected_files(personality)
+
+ requested_networks = body['server'].get('networks')
+
+ if requested_networks is not None:
+ requested_networks = self._get_requested_networks(
+ requested_networks)
+
+ flavor_id = self.controller._flavor_id_from_req_data(body)
+
+ if not 'name' in body['server']:
+ msg = _("Server name is not defined")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ zone_blob = body['server'].get('blob')
+ name = body['server']['name']
+ self._validate_server_name(name)
+ name = name.strip()
+
+ reservation_id = body['server'].get('reservation_id')
+ min_count = body['server'].get('min_count')
+ max_count = body['server'].get('max_count')
+ # min_count and max_count are optional. If they exist, they come
+ # in as strings. We want to default 'min_count' to 1, and default
+ # 'max_count' to be 'min_count'.
+ min_count = int(min_count) if min_count else 1
+ max_count = int(max_count) if max_count else min_count
+ if min_count > max_count:
+ min_count = max_count
+
+ try:
+ inst_type = \
+ instance_types.get_instance_type_by_flavor_id(flavor_id)
+ extra_values = {
+ 'instance_type': inst_type,
+ 'image_ref': image_href,
+ 'password': password}
+
+ return (extra_values,
+ create_method(context,
+ inst_type,
+ image_id,
+ kernel_id=kernel_id,
+ ramdisk_id=ramdisk_id,
+ display_name=name,
+ display_description=name,
+ key_name=key_name,
+ key_data=key_data,
+ metadata=body['server'].get('metadata', {}),
+ injected_files=injected_files,
+ admin_password=password,
+ zone_blob=zone_blob,
+ reservation_id=reservation_id,
+ min_count=min_count,
+ max_count=max_count,
+ requested_networks=requested_networks))
+ except quota.QuotaError as error:
+ self._handle_quota_error(error)
+ except exception.ImageNotFound as error:
+ msg = _("Can not find requested image")
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ except rpc.RemoteError as err:
+ msg = "%(err_type)s: %(err_msg)s" % \
+ {'err_type': err.exc_type, 'err_msg': err.value}
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ # Let the caller deal with unhandled exceptions.
+
+ def _get_requested_networks(self, requested_networks):
+ """
+ Create a list of requested networks from the networks attribute
+ """
+ networks = []
+ for network in requested_networks:
+ try:
+ network_id = network['id']
+ network_id = int(network_id)
+ #fixed IP address is optional
+ #if the fixed IP address is not provided then
+ #it will use one of the available IP address from the network
+ fixed_ip = network.get('fixed_ip', None)
+ if fixed_ip is not None:
+ self._validate_fixed_ip(fixed_ip)
+ # check if the network id is already present in the list,
+ # we don't want duplicate networks to be passed
+ # at the boot time
+ for id, ip in networks:
+ if id == network_id:
+ expl = _("Duplicate networks (%s) are not allowed")\
+ % network_id
+ raise faults.Fault(exc.HTTPBadRequest(
+ explanation=expl))
+
+ networks.append((network_id, fixed_ip))
+ except KeyError as key:
+ expl = _('Bad network format: missing %s') % key
+ raise faults.Fault(exc.HTTPBadRequest(explanation=expl))
+ except ValueError:
+ expl = _("Bad networks format: network id should "
+ "be integer (%s)") % network_id
+ raise faults.Fault(exc.HTTPBadRequest(explanation=expl))
+ except TypeError:
+ expl = _('Bad networks format')
+ raise exc.HTTPBadRequest(explanation=expl)
+
+ return networks
+
+
+class CreateServerExtController(servers.ControllerV11):
+ """This is the controller for the extended version
+ of the create server OS V1.1
+ """
+ def __init__(self):
+ super(CreateServerExtController, self).__init__()
+ self.helper = CreateInstanceHelperEx(self)
+
+
+class Createserverext(extensions.ExtensionDescriptor):
+ """The servers create ext
+
+ Exposes addFixedIp and removeFixedIp actions on servers.
+
+ """
+ def get_name(self):
+ return "Createserverext"
+
+ def get_alias(self):
+ return "os-servers-create-ext"
+
+ def get_description(self):
+ return "Extended support to the Create Server v1.1 API"
+
+ def get_namespace(self):
+ return "http://docs.openstack.org/ext/serverscreateext/api/v1.1"
+
+ def get_updated(self):
+ return "2011-07-19T00:00:00+00:00"
+
+ def get_resources(self):
+ resources = []
+
+ headers_serializer = servers.HeadersSerializer()
+ metadata = servers._get_metadata()
+ body_serializers = {
+ 'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
+ xmlns=wsgi.XMLNS_V11),
+ }
+
+ body_deserializers = {
+ 'application/xml': helper.ServerXMLDeserializer(),
+ }
+
+ serializer = wsgi.ResponseSerializer(body_serializers,
+ headers_serializer)
+ deserializer = wsgi.RequestDeserializer(body_deserializers)
+
+ res = extensions.ResourceExtension('os-servers-create-ext',
+ controller=CreateServerExtController(),
+ deserializer=deserializer,
+ serializer=serializer)
+ resources.append(res)
+
+ return resources
diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py
index 32b1b2f7c..8579c45df 100644
--- a/nova/api/openstack/create_instance_helper.py
+++ b/nova/api/openstack/create_instance_helper.py
@@ -25,7 +25,6 @@ from nova import flags
from nova import log as logging
import nova.image
from nova import quota
-from nova import rpc
from nova import utils
from nova.compute import instance_types
@@ -102,12 +101,6 @@ class CreateInstanceHelper(object):
if personality:
injected_files = self._get_injected_files(personality)
- requested_networks = body['server'].get('networks')
-
- if requested_networks is not None:
- requested_networks = self._get_requested_networks(
- requested_networks)
-
flavor_id = self.controller._flavor_id_from_req_data(body)
if not 'name' in body['server']:
@@ -154,17 +147,12 @@ class CreateInstanceHelper(object):
zone_blob=zone_blob,
reservation_id=reservation_id,
min_count=min_count,
- max_count=max_count,
- requested_networks=requested_networks))
+ max_count=max_count))
except quota.QuotaError as error:
self._handle_quota_error(error)
except exception.ImageNotFound as error:
msg = _("Can not find requested image")
raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
- except rpc.RemoteError as err:
- msg = "%(err_type)s: %(err_msg)s" % \
- {'err_type': err.exc_type, 'err_msg': err.value}
- raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
# Let the caller deal with unhandled exceptions.
@@ -296,42 +284,6 @@ class CreateInstanceHelper(object):
raise exc.HTTPBadRequest(explanation=msg)
return password
- def _get_requested_networks(self, requested_networks):
- """
- Create a list of requested networks from the networks attribute
- """
- networks = []
- for network in requested_networks:
- try:
- network_id = network['id']
- network_id = int(network_id)
- #fixed IP address is optional
- #if the fixed IP address is not provided then
- #it will use one of the available IP address from the network
- fixed_ip = network.get('fixed_ip', None)
- if fixed_ip is not None:
- self._validate_fixed_ip(fixed_ip)
- # check if the network id is already present in the list,
- # we don't want duplicate networks to be passed
- # at the boot time
- for id, ip in networks:
- if id == network_id:
- expl = _("Duplicate networks (%s) are not allowed")\
- % network_id
- raise faults.Fault(exc.HTTPBadRequest(
- explanation=expl))
-
- networks.append((network_id, fixed_ip))
- except KeyError as key:
- expl = _('Bad network format: missing %s') % key
- raise faults.Fault(exc.HTTPBadRequest(explanation=expl))
- except ValueError:
- expl = _("Bad networks format: network id should "
- "be integer (%s)") % network_id
- raise faults.Fault(exc.HTTPBadRequest(explanation=expl))
-
- return networks
-
class ServerXMLDeserializer(wsgi.XMLDeserializer):
"""
@@ -411,7 +363,6 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer):
def _find_first_child_named(self, parent, name):
"""Search a nodes children for the first child with a given name"""
for node in parent.childNodes:
- LOG.debug(node.nodeName)
if node.nodeName == name:
return node
return None
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index da06ecd15..dec66d8a4 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -263,7 +263,9 @@ class ExtensionMiddleware(base_wsgi.Middleware):
LOG.debug(_('Extended resource: %s'),
resource.collection)
mapper.resource(resource.collection, resource.collection,
- controller=wsgi.Resource(resource.controller),
+ controller=wsgi.Resource(resource.controller,
+ resource.deserializer,
+ resource.serializer),
collection=resource.collection_actions,
member=resource.member_actions,
parent_resource=resource.parent)
@@ -456,9 +458,12 @@ class ResourceExtension(object):
"""Add top level resources to the OpenStack API in nova."""
def __init__(self, collection, controller, parent=None,
- collection_actions={}, member_actions={}):
+ collection_actions={}, member_actions={},
+ deserializer=None, serializer=None):
self.collection = collection
self.controller = controller
self.parent = parent
self.collection_actions = collection_actions
self.member_actions = member_actions
+ self.deserializer = deserializer
+ self.serializer = serializer
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 93f8e832c..6a28f7bf1 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -618,21 +618,7 @@ def create_resource(version='1.0'):
'1.1': ControllerV11,
}[version]()
- metadata = {
- "attributes": {
- "server": ["id", "imageId", "name", "flavorId", "hostId",
- "status", "progress", "adminPass", "flavorRef",
- "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"},
- },
- }
+ metadata = _get_metadata()
xmlns = {
'1.0': wsgi.XMLNS_V10,
@@ -654,3 +640,22 @@ def create_resource(version='1.0'):
deserializer = wsgi.RequestDeserializer(body_deserializers)
return wsgi.Resource(controller, deserializer, serializer)
+
+
+def _get_metadata():
+ metadata = {
+ "attributes": {
+ "server": ["id", "imageId", "name", "flavorId", "hostId",
+ "status", "progress", "adminPass", "flavorRef",
+ "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"},
+ },
+ }
+ return metadata