From 2a6f97940f71c056b4bfb0cd9a86f5d676abc4e1 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Mon, 11 Jul 2011 13:34:39 -0700 Subject: add optional parameter networks to the Create server OS API --- nova/api/openstack/create_instance_helper.py | 55 +++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 1066713a3..839aa9fb9 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -32,7 +32,6 @@ from nova.api.openstack import faults from nova.api.openstack import wsgi from nova.auth import manager as auth_manager - LOG = logging.getLogger('nova.api.openstack.create_instance_helper') FLAGS = flags.FLAGS @@ -102,6 +101,15 @@ class CreateInstanceHelper(object): if personality: injected_files = self._get_injected_files(personality) + requested_networks = body['server'].get('networks') + + if requested_networks is not None: + if len(requested_networks) == 0: + msg = _("No networks found") + raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + requested_networks = self._get_requested_networks( + requested_networks) + flavor_id = self.controller._flavor_id_from_req_data(body) if not 'name' in body['server']: @@ -148,12 +156,17 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_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 exception.NovaException as ex: + LOG.error(ex) + msg = _("Failed to create server: %s") % ex + raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) # Let the caller deal with unhandled exceptions. @@ -276,6 +289,44 @@ 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 used one of the available IP address from the network + fixed_ip = network.get('fixed_ip', None) + + # 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 faults.Fault(exc.HTTPBadRequest(explanation=expl)) + + return networks + class ServerXMLDeserializer(wsgi.XMLDeserializer): """ -- cgit From 51834c2141bdbc283b9d165372be08eb6b9409ca Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 12 Jul 2011 10:36:55 -0700 Subject: Allowed empty networks, handled RemoteError properly, implemented xml format for networks and fixed broken unit test cases --- nova/api/openstack/create_instance_helper.py | 29 ++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index ef2a8393f..5e1c8d8d9 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -25,6 +25,7 @@ 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 @@ -104,9 +105,6 @@ class CreateInstanceHelper(object): requested_networks = body['server'].get('networks') if requested_networks is not None: - if len(requested_networks) == 0: - msg = _("No networks found") - raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) requested_networks = self._get_requested_networks( requested_networks) @@ -163,9 +161,9 @@ class CreateInstanceHelper(object): except exception.ImageNotFound as error: msg = _("Can not find requested image") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) - except exception.NovaException as ex: - LOG.error(ex) - msg = _("Failed to create server: %s") % ex + except rpc.RemoteError as err: + LOG.error(err) + msg = _("%s:%s") % (err.exc_type, err.value) raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) # Let the caller deal with unhandled exceptions. @@ -355,6 +353,9 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): personality = self._extract_personality(server_node) if personality is not None: server["personality"] = personality + networks = self._extract_networks(server_node) + if networks is not None: + server["networks"] = networks return server def _extract_metadata(self, server_node): @@ -383,6 +384,22 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): personality.append(item) return personality + def _extract_networks(self, server_node): + """Marshal the networks attribute of a parsed request""" + networks_node = \ + self._find_first_child_named(server_node, "networks") + if networks_node is None: + return None + networks = [] + for network_node in self._find_children_named(networks_node, "network"): + item = {} + if network_node.hasAttribute("id"): + item["id"] = network_node.getAttribute("id") + if network_node.hasAttribute("fixed_ip"): + item["fixed_ip"] = network_node.getAttribute("fixed_ip") + networks.append(item) + return networks + 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: -- cgit From 2be9a4e19449f9cf37f62f3f6e380de3e7ca0d38 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 12 Jul 2011 11:28:06 -0700 Subject: added xml deserialization unit test cases and fixe some pep errors --- nova/api/openstack/create_instance_helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 5e1c8d8d9..86fa8becc 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -391,7 +391,8 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): if networks_node is None: return None networks = [] - for network_node in self._find_children_named(networks_node, "network"): + for network_node in self._find_children_named(networks_node, + "network"): item = {} if network_node.hasAttribute("id"): item["id"] = network_node.getAttribute("id") -- cgit From 6e75e608cc7260317f014e57ba070b152f83d0e7 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 12 Jul 2011 17:46:03 -0700 Subject: added unit test cases and minor changes (localization fix and added fixed_ip validation) --- nova/api/openstack/create_instance_helper.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 86fa8becc..5eef0ae00 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -162,8 +162,7 @@ class CreateInstanceHelper(object): msg = _("Can not find requested image") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) except rpc.RemoteError as err: - LOG.error(err) - msg = _("%s:%s") % (err.exc_type, err.value) + msg = _("%(err.exc_type)s:%(err.value)s") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) # Let the caller deal with unhandled exceptions. @@ -205,6 +204,15 @@ class CreateInstanceHelper(object): msg = _("Server name is an empty string") raise exc.HTTPBadRequest(explanation=msg) + def _validate_fixed_ip(self, value): + if not isinstance(value, basestring): + msg = _("Fixed IP is not a string or unicode") + raise exc.HTTPBadRequest(explanation=msg) + + if value.strip() == '': + msg = _("Fixed IP is an empty string") + raise exc.HTTPBadRequest(explanation=msg) + def _get_kernel_ramdisk_from_image(self, req, image_id): """Fetch an image from the ImageService, then if present, return the associated kernel and ramdisk image IDs. @@ -298,9 +306,10 @@ class CreateInstanceHelper(object): network_id = int(network_id) #fixed IP address is optional #if the fixed IP address is not provided then - #it will used one of the available IP address from the network + #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 @@ -404,6 +413,7 @@ 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 -- cgit From 2ecbdd46d48bafbeb451875ba6e7f67276d83602 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 13 Jul 2011 13:57:50 -0700 Subject: Minor fixes --- nova/api/openstack/create_instance_helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 5eef0ae00..0337dd5bb 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -162,7 +162,8 @@ class CreateInstanceHelper(object): msg = _("Can not find requested image") raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) except rpc.RemoteError as err: - msg = _("%(err.exc_type)s:%(err.value)s") + 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. -- cgit From 0655f97b2cce1e28485ddb4c37a854a65cbbc276 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Thu, 14 Jul 2011 15:53:16 -0700 Subject: added integrated unit testcases and minor fixes --- nova/api/openstack/create_instance_helper.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 0337dd5bb..32b1b2f7c 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -329,9 +329,6 @@ class CreateInstanceHelper(object): 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 faults.Fault(exc.HTTPBadRequest(explanation=expl)) return networks -- cgit From 6cbd1d860d6a3fe96417391c21fb79b1750ecdcf Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 19 Jul 2011 15:35:54 -0700 Subject: Added a new extension instead of directly making changes to OS V1.1. API --- nova/api/openstack/contrib/createserverext.py | 234 ++++++++++++++++++++++++++ nova/api/openstack/create_instance_helper.py | 51 +----- nova/api/openstack/extensions.py | 9 +- nova/api/openstack/servers.py | 35 ++-- 4 files changed, 262 insertions(+), 67 deletions(-) create mode 100644 nova/api/openstack/contrib/createserverext.py (limited to 'nova/api') 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 -- cgit From 6b47f87c9e22fa09cedc3e48b7c8dcf52b5d016a Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 19 Jul 2011 17:35:44 -0700 Subject: Fixed broken unit testcases after adding extension and minor code refactoring --- nova/api/openstack/contrib/createserverext.py | 11 ++++++++++- nova/api/openstack/create_instance_helper.py | 9 --------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py index e8fe9afad..78dba425a 100644 --- a/nova/api/openstack/contrib/createserverext.py +++ b/nova/api/openstack/contrib/createserverext.py @@ -137,6 +137,15 @@ class CreateInstanceHelperEx(helper.CreateInstanceHelper): # Let the caller deal with unhandled exceptions. + def _validate_fixed_ip(self, value): + if not isinstance(value, basestring): + msg = _("Fixed IP is not a string or unicode") + raise exc.HTTPBadRequest(explanation=msg) + + if value.strip() == '': + msg = _("Fixed IP is an empty string") + raise exc.HTTPBadRequest(explanation=msg) + def _get_requested_networks(self, requested_networks): """ Create a list of requested networks from the networks attribute @@ -202,7 +211,7 @@ class Createserverext(extensions.ExtensionDescriptor): return "Extended support to the Create Server v1.1 API" def get_namespace(self): - return "http://docs.openstack.org/ext/serverscreateext/api/v1.1" + return "http://docs.openstack.org/ext/createserverext/api/v1.1" def get_updated(self): return "2011-07-19T00:00:00+00:00" diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 8579c45df..fba0cb8ba 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -193,15 +193,6 @@ class CreateInstanceHelper(object): msg = _("Server name is an empty string") raise exc.HTTPBadRequest(explanation=msg) - def _validate_fixed_ip(self, value): - if not isinstance(value, basestring): - msg = _("Fixed IP is not a string or unicode") - raise exc.HTTPBadRequest(explanation=msg) - - if value.strip() == '': - msg = _("Fixed IP is an empty string") - raise exc.HTTPBadRequest(explanation=msg) - def _get_kernel_ramdisk_from_image(self, req, image_id): """Fetch an image from the ImageService, then if present, return the associated kernel and ramdisk image IDs. -- cgit From 038565bdc735ff7a227a39d2ee21df0e8194929b Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 19 Jul 2011 18:24:44 -0700 Subject: Modified alias ^Cd minor fixes --- nova/api/openstack/contrib/createserverext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py index 78dba425a..2f8b4389f 100644 --- a/nova/api/openstack/contrib/createserverext.py +++ b/nova/api/openstack/contrib/createserverext.py @@ -205,7 +205,7 @@ class Createserverext(extensions.ExtensionDescriptor): return "Createserverext" def get_alias(self): - return "os-servers-create-ext" + return "os-create-server-ext" def get_description(self): return "Extended support to the Create Server v1.1 API" @@ -234,7 +234,7 @@ class Createserverext(extensions.ExtensionDescriptor): headers_serializer) deserializer = wsgi.RequestDeserializer(body_deserializers) - res = extensions.ResourceExtension('os-servers-create-ext', + res = extensions.ResourceExtension('os-create-server-ext', controller=CreateServerExtController(), deserializer=deserializer, serializer=serializer) -- cgit From d963e25906b75a48c75b6e589deb2a53f75d6ee3 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 22 Jul 2011 20:29:37 -0700 Subject: Config-Drive happiness, minus smoketest --- nova/api/openstack/create_instance_helper.py | 7 ++++++- nova/api/openstack/views/servers.py | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 2654e3c40..fe92bae2e 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -96,6 +96,7 @@ class CreateInstanceHelper(object): locals()) raise faults.Fault(exc.HTTPBadRequest(explanation=msg)) + config_drive = body['server'].get('config_drive') personality = body['server'].get('personality') injected_files = [] @@ -130,6 +131,7 @@ class CreateInstanceHelper(object): extra_values = { 'instance_type': inst_type, 'image_ref': image_href, + 'config_drive': config_drive, 'password': password} return (extra_values, @@ -148,7 +150,8 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_count)) + max_count=max_count, + config_drive=config_drive,)) except quota.QuotaError as error: self._handle_quota_error(error) except exception.ImageNotFound as error: @@ -160,6 +163,8 @@ class CreateInstanceHelper(object): def _handle_quota_error(self, error): """ Reraise quota errors as api-specific http exceptions + + """ if error.code == "OnsetFileLimitExceeded": expl = _("Personality file limit exceeded") diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index ab7e8da61..961932a4e 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -164,6 +164,7 @@ class ViewBuilderV11(ViewBuilder): def _build_extra(self, response, inst): self._build_links(response, inst) + self._build_config_drive(response, inst) def _build_links(self, response, inst): href = self.generate_href(inst["id"]) @@ -182,6 +183,9 @@ class ViewBuilderV11(ViewBuilder): response["server"]["links"] = links + def _build_config_drive(self, response, inst): + response['server']['config_drive'] = inst.get('config_drive') + def generate_href(self, server_id): """Create an url that refers to a specific server id.""" return os.path.join(self.base_url, "servers", str(server_id)) -- cgit From 3335a91c3c53513cc35e3f39a59975b33524950b Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Fri, 29 Jul 2011 18:45:51 -0700 Subject: Fixed review comments: Put parsing logic of network information in create_instance_helper module and refactored unit testcases as per the changed code. --- nova/api/openstack/contrib/createserverext.py | 213 +------------------------- nova/api/openstack/create_instance_helper.py | 83 +++++++++- 2 files changed, 84 insertions(+), 212 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py index b5c06920c..79c017b42 100644 --- a/nova/api/openstack/contrib/createserverext.py +++ b/nova/api/openstack/contrib/createserverext.py @@ -13,220 +13,11 @@ # 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.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 exc.HTTPUnprocessableEntity() - - if not 'server' in body: - raise 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 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 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 exc.HTTPBadRequest(explanation=msg) - - # Let the caller deal with unhandled exceptions. - - def _validate_fixed_ip(self, value): - if not isinstance(value, basestring): - msg = _("Fixed IP is not a string or unicode") - raise exc.HTTPBadRequest(explanation=msg) - - if value.strip() == '': - msg = _("Fixed IP is an empty string") - raise exc.HTTPBadRequest(explanation=msg) - - 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 exc.HTTPBadRequest(explanation=expl) - - networks.append((network_id, fixed_ip)) - except KeyError as key: - expl = _('Bad network format: missing %s') % key - raise exc.HTTPBadRequest(explanation=expl) - except ValueError: - expl = _("Bad networks format: network id should " - "be integer (%s)") % network_id - raise 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 ServerXMLDeserializer(helper.ServerXMLDeserializer): - """ - Deserializer to handle xml-formatted server create requests. - - Handles networks element - """ - def _extract_server(self, node): - """Marshal the server attribute of a parsed request""" - server = super(ServerXMLDeserializer, self)._extract_server(node) - server_node = self.find_first_child_named(node, 'server') - networks = self._extract_networks(server_node) - if networks is not None: - server["networks"] = networks - return server - - def _extract_networks(self, server_node): - """Marshal the networks attribute of a parsed request""" - networks_node = \ - self.find_first_child_named(server_node, "networks") - if networks_node is None: - return None - networks = [] - for network_node in self.find_children_named(networks_node, - "network"): - item = {} - if network_node.hasAttribute("id"): - item["id"] = network_node.getAttribute("id") - if network_node.hasAttribute("fixed_ip"): - item["fixed_ip"] = network_node.getAttribute("fixed_ip") - networks.append(item) - return networks class Createserverext(extensions.ExtensionDescriptor): @@ -261,7 +52,7 @@ class Createserverext(extensions.ExtensionDescriptor): } body_deserializers = { - 'application/xml': ServerXMLDeserializer(), + 'application/xml': helper.ServerXMLDeserializer(), } serializer = wsgi.ResponseSerializer(body_serializers, @@ -269,7 +60,7 @@ class Createserverext(extensions.ExtensionDescriptor): deserializer = wsgi.RequestDeserializer(body_deserializers) res = extensions.ResourceExtension('os-create-server-ext', - controller=CreateServerExtController(), + controller=servers.ControllerV11(), deserializer=deserializer, serializer=serializer) resources.append(res) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 04fbeda73..c7cc88313 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -30,6 +30,7 @@ from nova import utils from nova.compute import instance_types from nova.api.openstack import wsgi +from nova.rpc.common import RemoteError LOG = logging.getLogger('nova.api.openstack.create_instance_helper') FLAGS = flags.FLAGS @@ -106,6 +107,12 @@ class CreateInstanceHelper(object): if personality: injected_files = self._get_injected_files(personality) + requested_networks = server_dict.get('networks') + + if requested_networks is not None: + requested_networks = self._get_requested_networks( + requested_networks) + try: flavor_id = self.controller._flavor_id_from_req_data(body) except ValueError as error: @@ -156,7 +163,8 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_count)) + max_count=max_count, + requested_networks=requested_networks)) except quota.QuotaError as error: self._handle_quota_error(error) except exception.ImageNotFound as error: @@ -165,6 +173,10 @@ class CreateInstanceHelper(object): except exception.FlavorNotFound as error: msg = _("Invalid flavorRef provided.") raise exc.HTTPBadRequest(explanation=msg) + except RemoteError as err: + msg = "%(err_type)s: %(err_msg)s" % \ + {'err_type': err.exc_type, 'err_msg': err.value} + raise exc.HTTPBadRequest(explanation=msg) # Let the caller deal with unhandled exceptions. def _handle_quota_error(self, error): @@ -286,6 +298,53 @@ class CreateInstanceHelper(object): raise exc.HTTPBadRequest(explanation=msg) return password + def _validate_fixed_ip(self, value): + if not isinstance(value, basestring): + msg = _("Fixed IP is not a string or unicode") + raise exc.HTTPBadRequest(explanation=msg) + + if value.strip() == '': + msg = _("Fixed IP is an empty string") + raise exc.HTTPBadRequest(explanation=msg) + + 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 exc.HTTPBadRequest(explanation=expl) + + networks.append((network_id, fixed_ip)) + except KeyError as key: + expl = _('Bad network format: missing %s') % key + raise exc.HTTPBadRequest(explanation=expl) + except ValueError: + expl = _("Bad networks format: network id should " + "be integer (%s)") % network_id + raise exc.HTTPBadRequest(explanation=expl) + except TypeError: + expl = _('Bad networks format') + raise exc.HTTPBadRequest(explanation=expl) + + return networks + class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): """ @@ -317,6 +376,10 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): server["personality"] = self._extract_personality(server_node) + networks = self._extract_networks(server_node) + if networks: + server["networks"] = networks + return server def _extract_personality(self, server_node): @@ -331,3 +394,21 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): item["contents"] = self.extract_text(file_node) personality.append(item) return personality + + def _extract_networks(self, server_node): + """Marshal the networks attribute of a parsed request""" + networks_node = \ + self.find_first_child_named(server_node, "networks") + if networks_node is None: + return None + networks = [] + if networks_node is not None: + for network_node in self.find_children_named(networks_node, + "network"): + item = {} + if network_node.hasAttribute("id"): + item["id"] = network_node.getAttribute("id") + if network_node.hasAttribute("fixed_ip"): + item["fixed_ip"] = network_node.getAttribute("fixed_ip") + networks.append(item) + return networks -- cgit From 91eaa647506a2e343e8c689289529eafea0bc9d3 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 12 Aug 2011 14:33:27 -0700 Subject: Fix ugly little violations before someone says anything --- nova/api/openstack/create_instance_helper.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 0ec455167..d776ae92d 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -178,8 +178,6 @@ class CreateInstanceHelper(object): def _handle_quota_error(self, error): """ Reraise quota errors as api-specific http exceptions - - """ if error.code == "OnsetFileLimitExceeded": expl = _("Personality file limit exceeded") -- cgit From 9081e8b62ea01828238ecaebdcf3e627ada3fe9a Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 16 Aug 2011 16:04:18 -0700 Subject: Added uuid for networks and made changes to the Create server API format to accept network as uuid instead of id --- nova/api/openstack/create_instance_helper.py | 39 ++++++++++++---------------- 1 file changed, 16 insertions(+), 23 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index c1abd2eb6..8d5a9d2a3 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -304,15 +304,6 @@ class CreateInstanceHelper(object): raise exc.HTTPBadRequest(explanation=msg) return password - def _validate_fixed_ip(self, value): - if not isinstance(value, basestring): - msg = _("Fixed IP is not a string or unicode") - raise exc.HTTPBadRequest(explanation=msg) - - if value.strip() == '': - msg = _("Fixed IP is an empty string") - raise exc.HTTPBadRequest(explanation=msg) - def _get_requested_networks(self, requested_networks): """ Create a list of requested networks from the networks attribute @@ -320,31 +311,33 @@ class CreateInstanceHelper(object): networks = [] for network in requested_networks: try: - network_id = network['id'] - network_id = int(network_id) + network_uuid = network['uuid'] + + if not utils.is_uuid_like(network_uuid): + msg = _("Bad networks format: network uuid is not in" + " proper format (%s)") % network_uuid + raise exc.HTTPBadRequest(explanation=msg) + #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) + address = network.get('fixed_ip', None) + if address is not None and not utils.is_valid_ipv4(address): + msg = _("Invalid fixed IP address (%s)") % address + raise exc.HTTPBadRequest(explanation=msg) # 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: + if id == network_uuid: expl = _("Duplicate networks (%s) are not allowed")\ - % network_id + % network_uuid raise exc.HTTPBadRequest(explanation=expl) - networks.append((network_id, fixed_ip)) + networks.append((network_uuid, address)) except KeyError as key: expl = _('Bad network format: missing %s') % key raise exc.HTTPBadRequest(explanation=expl) - except ValueError: - expl = _("Bad networks format: network id should " - "be integer (%s)") % network_id - raise exc.HTTPBadRequest(explanation=expl) except TypeError: expl = _('Bad networks format') raise exc.HTTPBadRequest(explanation=expl) @@ -543,8 +536,8 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer): for network_node in self.find_children_named(node, "network"): item = {} - if network_node.hasAttribute("id"): - item["id"] = network_node.getAttribute("id") + if network_node.hasAttribute("uuid"): + item["uuid"] = network_node.getAttribute("uuid") if network_node.hasAttribute("fixed_ip"): item["fixed_ip"] = network_node.getAttribute("fixed_ip") networks.append(item) -- cgit From 90650e5becb541790a8949edebaf0bff0ceb8f5b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 19:31:01 -0700 Subject: make admin context the default, clean up pipelib --- nova/api/auth.py | 18 ++++++++++++++++++ nova/api/ec2/admin.py | 4 +++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/auth.py b/nova/api/auth.py index cd3e3e8a0..050216fd7 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -45,6 +45,24 @@ class InjectContext(wsgi.Middleware): return self.application +class AdminContext(wsgi.Middleware): + """Return an admin context no matter what""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # Build a context, including the auth_token... + remote_address = req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext('admin', + 'admin', + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application + + class KeystoneContext(wsgi.Middleware): """Make a request context from keystone headers""" diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index df7876b9d..dfbbc0a2b 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -283,8 +283,10 @@ class AdminController(object): # NOTE(vish) import delayed because of __init__.py from nova.cloudpipe import pipelib pipe = pipelib.CloudPipe() + proj = manager.AuthManager().get_project(project) + user_id = proj.project_manager_id try: - pipe.launch_vpn_instance(project) + pipe.launch_vpn_instance(project, user_id) except db.NoMoreNetworks: raise exception.ApiError("Unable to claim IP for VPN instance" ", ensure it isn't running, and try " -- cgit From 41819d8d048b889f2e7f5e4ee0ff2873bfdef904 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:22:30 -0700 Subject: fix integration tests --- nova/api/openstack/auth.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index d42abe1f8..f4a50fc46 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -34,6 +34,23 @@ LOG = logging.getLogger('nova.api.openstack') FLAGS = flags.FLAGS +class NoAuthMiddleware(wsgi.Middleware): + """Return a fake token if one isn't specified.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + if 'X-Auth-Token' in req.headers: + return self.application + logging.debug("Got no auth token, returning fake info.") + res = webob.Response() + res.headers['X-Auth-Token'] = 'fake' + res.headers['X-Server-Management-Url'] = req.url + res.headers['X-Storage-Url'] = '' + res.headers['X-CDN-Management-Url'] = '' + res.content_type = 'text/plain' + res.status = '204' + return res + class AuthMiddleware(wsgi.Middleware): """Authorize the openstack API request or return an HTTP Forbidden.""" -- cgit From 6d87608cf835e1c27f3b6b6b31e6b41b0aa90b90 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 17 Aug 2011 20:35:54 -0700 Subject: pep8 --- nova/api/openstack/auth.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/api') diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index f4a50fc46..b37f9aade 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -51,6 +51,7 @@ class NoAuthMiddleware(wsgi.Middleware): res.status = '204' return res + class AuthMiddleware(wsgi.Middleware): """Authorize the openstack API request or return an HTTP Forbidden.""" -- cgit From c4fc9f0737ec9f8d5c950b850fed9930a68164f4 Mon Sep 17 00:00:00 2001 From: Christopher MacGown Date: Fri, 19 Aug 2011 08:44:14 -0700 Subject: Add copyright notices --- nova/api/openstack/create_instance_helper.py | 1 + nova/api/openstack/views/servers.py | 1 + 2 files changed, 2 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index d776ae92d..563ef1c42 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -1,4 +1,5 @@ # Copyright 2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index c7bc03bcb..19acb0899 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010-2011 OpenStack LLC. +# Copyright 2011 Piston Cloud Computing, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may -- cgit From c3ed01d7d53dbade412122743078d60131adbf9f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 22 Aug 2011 14:24:59 -0700 Subject: change NoAuth to actually use a tenant and user --- nova/api/auth.py | 19 +---------------- nova/api/ec2/__init__.py | 21 +++++++++++++++++++ nova/api/openstack/auth.py | 52 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 31 deletions(-) (limited to 'nova/api') diff --git a/nova/api/auth.py b/nova/api/auth.py index 050216fd7..cd0d38b3f 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -45,24 +45,6 @@ class InjectContext(wsgi.Middleware): return self.application -class AdminContext(wsgi.Middleware): - """Return an admin context no matter what""" - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - # Build a context, including the auth_token... - remote_address = req.remote_addr - if FLAGS.use_forwarded_for: - remote_address = req.headers.get('X-Forwarded-For', remote_address) - ctx = context.RequestContext('admin', - 'admin', - is_admin=True, - remote_address=remote_address) - - req.environ['nova.context'] = ctx - return self.application - - class KeystoneContext(wsgi.Middleware): """Make a request context from keystone headers""" @@ -80,6 +62,7 @@ class KeystoneContext(wsgi.Middleware): req.headers.get('X_STORAGE_TOKEN')) # Build a context, including the auth_token... + remote_address = getattr(req, 'remote_address', '127.0.0.1') remote_address = req.remote_addr if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 17969099d..5430f443d 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -183,6 +183,27 @@ class ToToken(wsgi.Middleware): return self.application +class NoAuth(wsgi.Middleware): + """Add user:project as 'nova.context' to WSGI environ.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + if 'AWSAccessKeyId' not in req.params: + raise webob.exc.HTTPBadRequest() + user_id, _sep, project_id = req.params['AWSAccessKeyId'].partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application + + class Authenticate(wsgi.Middleware): """Authenticate an EC2 request and add 'nova.context' to WSGI environ.""" diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 0d9c7562a..f2dc89094 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -33,6 +33,7 @@ from nova.api.openstack import faults LOG = logging.getLogger('nova.api.openstack') FLAGS = flags.FLAGS +flags.DECLARE('use_forwarded_for', 'nova.api.auth') class NoAuthMiddleware(wsgi.Middleware): @@ -40,17 +41,36 @@ class NoAuthMiddleware(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - if 'X-Auth-Token' in req.headers: + if 'X-Auth-Token' not in req.headers: + os_url = req.url + version = common.get_version_from_href(os_url) + user_id = req.headers.get('X-Auth-User', 'admin') + project_id = req.headers.get('X-Auth-Project-Id', 'admin') + if version == '1.1': + os_url += '/' + project_id + res = webob.Response() + res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id) + res.headers['X-Server-Management-Url'] = os_url + res.headers['X-Storage-Url'] = '' + res.headers['X-CDN-Management-Url'] = '' + res.content_type = 'text/plain' + res.status = '204' + return res + else: + token = req.headers['X-Auth-Token'] + user_id, _sep, project_id = token.partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', + remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx return self.application - logging.debug("Got no auth token, returning fake info.") - res = webob.Response() - res.headers['X-Auth-Token'] = 'fake' - res.headers['X-Server-Management-Url'] = req.url - res.headers['X-Storage-Url'] = '' - res.headers['X-CDN-Management-Url'] = '' - res.content_type = 'text/plain' - res.status = '204' - return res class AuthMiddleware(wsgi.Middleware): @@ -103,9 +123,15 @@ class AuthMiddleware(wsgi.Middleware): project_id = projects[0].id is_admin = self.auth.is_admin(user_id) - req.environ['nova.context'] = context.RequestContext(user_id, - project_id, - is_admin) + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=is_admin, + remote_address=remote_address) + req.environ['nova.context'] = ctx + if not is_admin and not self.auth.is_project_member(user_id, project_id): msg = _("%(user_id)s must be an admin or a " -- cgit From 6f3610042452cc1cb6b1e0c204a127c0c48794f0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 22 Aug 2011 19:25:22 -0700 Subject: unindented per review, added a note about auth v2 --- nova/api/openstack/auth.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index f2dc89094..6754fea27 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -49,6 +49,9 @@ class NoAuthMiddleware(wsgi.Middleware): if version == '1.1': os_url += '/' + project_id res = webob.Response() + # NOTE(vish): This is expecting and returning Auth(1.1), whereas + # keystone uses 2.0 auth. We should probably allow + # 2.0 auth here as well. res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id) res.headers['X-Server-Management-Url'] = os_url res.headers['X-Storage-Url'] = '' @@ -56,21 +59,20 @@ class NoAuthMiddleware(wsgi.Middleware): res.content_type = 'text/plain' res.status = '204' return res - else: - token = req.headers['X-Auth-Token'] - user_id, _sep, project_id = token.partition(':') - project_id = project_id or user_id - remote_address = getattr(req, 'remote_address', '127.0.0.1') - if FLAGS.use_forwarded_for: - remote_address = req.headers.get('X-Forwarded-For', - remote_address) - ctx = context.RequestContext(user_id, - project_id, - is_admin=True, - remote_address=remote_address) - - req.environ['nova.context'] = ctx - return self.application + + token = req.headers['X-Auth-Token'] + user_id, _sep, project_id = token.partition(':') + project_id = project_id or user_id + remote_address = getattr(req, 'remote_address', '127.0.0.1') + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application class AuthMiddleware(wsgi.Middleware): -- cgit