summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorBrian Lamar <brian.lamar@rackspace.com>2011-08-23 10:59:17 -0400
committerBrian Lamar <brian.lamar@rackspace.com>2011-08-23 10:59:17 -0400
commit30133c4f40a4891bbe4d5d8930af00b26026230d (patch)
tree60fff6150d902e53763380dd784f1021b611ec94 /nova/api
parent2fbaac5e07b558d7829253915523f073b07e24d4 (diff)
parent787913ddedce4f3395b4e3d5073a0eea259ed9f7 (diff)
Merged trunk.
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/auth.py1
-rw-r--r--nova/api/ec2/__init__.py21
-rw-r--r--nova/api/ec2/admin.py4
-rw-r--r--nova/api/openstack/auth.py52
-rw-r--r--nova/api/openstack/contrib/createserverext.py66
-rw-r--r--nova/api/openstack/create_instance_helper.py79
-rw-r--r--nova/api/openstack/views/servers.py5
7 files changed, 222 insertions, 6 deletions
diff --git a/nova/api/auth.py b/nova/api/auth.py
index cd3e3e8a0..cd0d38b3f 100644
--- a/nova/api/auth.py
+++ b/nova/api/auth.py
@@ -62,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/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 "
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index b6ff1126b..6754fea27 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -33,6 +33,46 @@ 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):
+ """Return a fake token if one isn't specified."""
+
+ @webob.dec.wsgify(RequestClass=wsgi.Request)
+ def __call__(self, req):
+ 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()
+ # 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'] = ''
+ res.headers['X-CDN-Management-Url'] = ''
+ res.content_type = 'text/plain'
+ res.status = '204'
+ return res
+
+ 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):
@@ -85,9 +125,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 "
diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py
new file mode 100644
index 000000000..ba72fdb0b
--- /dev/null
+++ b/nova/api/openstack/contrib/createserverext.py
@@ -0,0 +1,66 @@
+# 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 nova.api.openstack import create_instance_helper as helper
+from nova.api.openstack import extensions
+from nova.api.openstack import servers
+from nova.api.openstack import wsgi
+
+
+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-create-server-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/createserverext/api/v1.1"
+
+ def get_updated(self):
+ return "2011-07-19T00:00:00+00:00"
+
+ def get_resources(self):
+ resources = []
+
+ headers_serializer = servers.HeadersSerializer()
+ body_serializers = {
+ 'application/xml': servers.ServerXMLSerializer(),
+ }
+
+ body_deserializers = {
+ 'application/xml': helper.ServerXMLDeserializerV11(),
+ }
+
+ serializer = wsgi.ResponseSerializer(body_serializers,
+ headers_serializer)
+ deserializer = wsgi.RequestDeserializer(body_deserializers)
+
+ res = extensions.ResourceExtension('os-create-server-ext',
+ controller=servers.ControllerV11(),
+ 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 339f260b9..483ff4985 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
@@ -29,7 +30,7 @@ from nova import utils
from nova.compute import instance_types
from nova.api.openstack import common
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,7 @@ class CreateInstanceHelper(object):
raise exc.HTTPBadRequest(explanation=msg)
personality = server_dict.get('personality')
+ config_drive = server_dict.get('config_drive')
injected_files = []
if personality:
@@ -120,6 +122,11 @@ class CreateInstanceHelper(object):
sg_names = list(set(sg_names))
+ 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:
@@ -154,6 +161,7 @@ class CreateInstanceHelper(object):
extra_values = {
'instance_type': inst_type,
'image_ref': image_href,
+ 'config_drive': config_drive,
'password': password}
return (extra_values,
@@ -175,9 +183,11 @@ class CreateInstanceHelper(object):
reservation_id=reservation_id,
min_count=min_count,
max_count=max_count,
+ requested_networks=requested_networks,
security_group=sg_names,
user_data=user_data,
- availability_zone=availability_zone))
+ availability_zone=availability_zone,
+ config_drive=config_drive,))
except quota.QuotaError as error:
self._handle_quota_error(error)
except exception.ImageNotFound as error:
@@ -188,6 +198,10 @@ class CreateInstanceHelper(object):
raise exc.HTTPBadRequest(explanation=msg)
except exception.SecurityGroupNotFound as error:
raise exc.HTTPBadRequest(explanation=unicode(error))
+ 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):
@@ -316,6 +330,46 @@ 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_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
+ 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_uuid:
+ expl = _("Duplicate networks (%s) are not allowed")\
+ % network_uuid
+ raise exc.HTTPBadRequest(explanation=expl)
+
+ networks.append((network_uuid, address))
+ except KeyError as key:
+ expl = _('Bad network format: missing %s') % key
+ raise exc.HTTPBadRequest(explanation=expl)
+ except TypeError:
+ expl = _('Bad networks format')
+ raise exc.HTTPBadRequest(explanation=expl)
+
+ return networks
+
class ServerXMLDeserializer(wsgi.XMLDeserializer):
"""
@@ -480,6 +534,10 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer):
if personality is not None:
server["personality"] = personality
+ networks = self._extract_networks(server_node)
+ if networks is not None:
+ server["networks"] = networks
+
security_groups = self._extract_security_groups(server_node)
if security_groups is not None:
server["security_groups"] = security_groups
@@ -501,6 +559,23 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer):
else:
return None
+ def _extract_networks(self, server_node):
+ """Marshal the networks attribute of a parsed request"""
+ node = self.find_first_child_named(server_node, "networks")
+ if node is not None:
+ networks = []
+ for network_node in self.find_children_named(node,
+ "network"):
+ item = {}
+ 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)
+ return networks
+ else:
+ return None
+
def _extract_security_groups(self, server_node):
"""Marshal the security_groups attribute of a parsed request"""
node = self.find_first_child_named(server_node, "security_groups")
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index dfbcda9db..8f8d04ca0 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
@@ -190,6 +191,7 @@ class ViewBuilderV11(ViewBuilder):
def _build_extra(self, response, inst):
self._build_links(response, inst)
response['uuid'] = inst['uuid']
+ self._build_config_drive(response, inst)
def _build_links(self, response, inst):
href = self.generate_href(inst["id"])
@@ -208,6 +210,9 @@ class ViewBuilderV11(ViewBuilder):
response["links"] = links
+ def _build_config_drive(self, response, inst):
+ response['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, self.project_id,