Merged from trunk and fixed review comments
This commit is contained in:
commit
94b3055ade
|
@ -20,7 +20,8 @@ use = egg:Paste#urlmap
|
|||
|
||||
[pipeline:ec2cloud]
|
||||
pipeline = logrequest authenticate cloudrequest authorizer ec2executor
|
||||
#pipeline = logrequest ec2lockout authenticate cloudrequest authorizer ec2executor
|
||||
# NOTE(vish): use the following pipeline for keystone
|
||||
# pipeline = logrequest totoken authtoken keystonecontext cloudrequest authorizer ec2executor
|
||||
|
||||
[pipeline:ec2admin]
|
||||
pipeline = logrequest authenticate adminrequest authorizer ec2executor
|
||||
|
@ -37,6 +38,9 @@ paste.filter_factory = nova.api.ec2:RequestLogging.factory
|
|||
[filter:ec2lockout]
|
||||
paste.filter_factory = nova.api.ec2:Lockout.factory
|
||||
|
||||
[filter:totoken]
|
||||
paste.filter_factory = nova.api.ec2:ToToken.factory
|
||||
|
||||
[filter:authenticate]
|
||||
paste.filter_factory = nova.api.ec2:Authenticate.factory
|
||||
|
||||
|
@ -72,9 +76,13 @@ use = egg:Paste#urlmap
|
|||
|
||||
[pipeline:openstackapi10]
|
||||
pipeline = faultwrap auth ratelimit osapiapp10
|
||||
# NOTE(vish): use the following pipeline for keystone
|
||||
#pipeline = faultwrap authtoken keystonecontext ratelimit osapiapp10
|
||||
|
||||
[pipeline:openstackapi11]
|
||||
pipeline = faultwrap auth ratelimit extensions osapiapp11
|
||||
# NOTE(vish): use the following pipeline for keystone
|
||||
# pipeline = faultwrap authtoken keystonecontext ratelimit extensions osapiapp11
|
||||
|
||||
[filter:faultwrap]
|
||||
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
|
||||
|
@ -99,3 +107,22 @@ pipeline = faultwrap osversionapp
|
|||
|
||||
[app:osversionapp]
|
||||
paste.app_factory = nova.api.openstack.versions:Versions.factory
|
||||
|
||||
##########
|
||||
# Shared #
|
||||
##########
|
||||
|
||||
[filter:keystonecontext]
|
||||
paste.filter_factory = nova.api.auth:KeystoneContext.factory
|
||||
|
||||
[filter:authtoken]
|
||||
paste.filter_factory = keystone.middleware.auth_token:filter_factory
|
||||
service_protocol = http
|
||||
service_host = 127.0.0.1
|
||||
service_port = 808
|
||||
auth_host = 127.0.0.1
|
||||
auth_port = 5001
|
||||
auth_protocol = http
|
||||
auth_uri = http://127.0.0.1:5000/
|
||||
admin_token = 999888777666
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 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.
|
||||
"""
|
||||
Common Auth Middleware.
|
||||
|
||||
"""
|
||||
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from nova import context
|
||||
from nova import flags
|
||||
from nova import wsgi
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_boolean('use_forwarded_for', False,
|
||||
'Treat X-Forwarded-For as the canonical remote address. '
|
||||
'Only enable this if you have a sanitizing proxy.')
|
||||
|
||||
|
||||
class InjectContext(wsgi.Middleware):
|
||||
"""Add a 'nova.context' to WSGI environ."""
|
||||
|
||||
def __init__(self, context, *args, **kwargs):
|
||||
self.context = context
|
||||
super(InjectContext, self).__init__(*args, **kwargs)
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
req.environ['nova.context'] = self.context
|
||||
return self.application
|
||||
|
||||
|
||||
class KeystoneContext(wsgi.Middleware):
|
||||
"""Make a request context from keystone headers"""
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
try:
|
||||
user_id = req.headers['X_USER']
|
||||
except KeyError:
|
||||
return webob.exc.HTTPUnauthorized()
|
||||
# get the roles
|
||||
roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')]
|
||||
project_id = req.headers['X_TENANT']
|
||||
# Get the auth token
|
||||
auth_token = req.headers.get('X_AUTH_TOKEN',
|
||||
req.headers.get('X_STORAGE_TOKEN'))
|
||||
|
||||
# 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(user_id,
|
||||
project_id,
|
||||
roles=roles,
|
||||
auth_token=auth_token,
|
||||
remote_address=remote_address)
|
||||
|
||||
req.environ['nova.context'] = ctx
|
||||
return self.application
|
|
@ -20,6 +20,7 @@ Starting point for routing EC2 requests.
|
|||
|
||||
"""
|
||||
|
||||
import httplib2
|
||||
import webob
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
@ -37,15 +38,16 @@ from nova.auth import manager
|
|||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger("nova.api")
|
||||
flags.DEFINE_boolean('use_forwarded_for', False,
|
||||
'Treat X-Forwarded-For as the canonical remote address. '
|
||||
'Only enable this if you have a sanitizing proxy.')
|
||||
flags.DEFINE_integer('lockout_attempts', 5,
|
||||
'Number of failed auths before lockout.')
|
||||
flags.DEFINE_integer('lockout_minutes', 15,
|
||||
'Number of minutes to lockout if triggered.')
|
||||
flags.DEFINE_integer('lockout_window', 15,
|
||||
'Number of minutes for lockout window.')
|
||||
flags.DEFINE_string('keystone_ec2_url',
|
||||
'http://localhost:5000/v2.0/ec2tokens',
|
||||
'URL to get token from ec2 request.')
|
||||
flags.DECLARE('use_forwarded_for', 'nova.api.auth')
|
||||
|
||||
|
||||
class RequestLogging(wsgi.Middleware):
|
||||
|
@ -138,6 +140,49 @@ class Lockout(wsgi.Middleware):
|
|||
return res
|
||||
|
||||
|
||||
class ToToken(wsgi.Middleware):
|
||||
"""Authenticate an EC2 request with keystone and convert to token."""
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
# Read request signature and access id.
|
||||
try:
|
||||
signature = req.params['Signature']
|
||||
access = req.params['AWSAccessKeyId']
|
||||
except KeyError:
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
# Make a copy of args for authentication and signature verification.
|
||||
auth_params = dict(req.params)
|
||||
# Not part of authentication args
|
||||
auth_params.pop('Signature')
|
||||
|
||||
# Authenticate the request.
|
||||
client = httplib2.Http()
|
||||
creds = {'ec2Credentials': {'access': access,
|
||||
'signature': signature,
|
||||
'host': req.host,
|
||||
'verb': req.method,
|
||||
'path': req.path,
|
||||
'params': auth_params,
|
||||
}}
|
||||
headers = {'Content-Type': 'application/json'},
|
||||
resp, content = client.request(FLAGS.keystone_ec2_url,
|
||||
'POST',
|
||||
headers=headers,
|
||||
body=utils.dumps(creds))
|
||||
# NOTE(vish): We could save a call to keystone by
|
||||
# having keystone return token, tenant,
|
||||
# user, and roles from this call.
|
||||
result = utils.loads(content)
|
||||
# TODO(vish): check for errors
|
||||
token_id = result['auth']['token']['id']
|
||||
|
||||
# Authenticated!
|
||||
req.headers['X-Auth-Token'] = token_id
|
||||
return self.application
|
||||
|
||||
|
||||
class Authenticate(wsgi.Middleware):
|
||||
"""Authenticate an EC2 request and add 'nova.context' to WSGI environ."""
|
||||
|
||||
|
@ -147,7 +192,7 @@ class Authenticate(wsgi.Middleware):
|
|||
try:
|
||||
signature = req.params['Signature']
|
||||
access = req.params['AWSAccessKeyId']
|
||||
except KeyError, e:
|
||||
except KeyError:
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
# Make a copy of args for authentication and signature verification.
|
||||
|
|
|
@ -30,6 +30,7 @@ from nova.api.ec2 import cloud
|
|||
|
||||
LOG = logging.getLogger('nova.api.ec2.metadata')
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DECLARE('use_forwarded_for', 'nova.api.auth')
|
||||
|
||||
|
||||
class MetadataRequestHandler(wsgi.Application):
|
||||
|
|
|
@ -45,10 +45,8 @@ class Createserverext(extensions.ExtensionDescriptor):
|
|||
resources = []
|
||||
|
||||
headers_serializer = servers.HeadersSerializer()
|
||||
metadata = servers._get_metadata()
|
||||
body_serializers = {
|
||||
'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
|
||||
xmlns=wsgi.XMLNS_V11),
|
||||
'application/xml': servers.ServerXMLSerializer(),
|
||||
}
|
||||
|
||||
body_deserializers = {
|
||||
|
|
|
@ -936,7 +936,21 @@ def create_resource(version='1.0'):
|
|||
'1.1': ControllerV11,
|
||||
}[version]()
|
||||
|
||||
metadata = _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"},
|
||||
},
|
||||
}
|
||||
|
||||
xmlns = {
|
||||
'1.0': wsgi.XMLNS_V10,
|
||||
|
@ -969,25 +983,6 @@ def create_resource(version='1.0'):
|
|||
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
|
||||
|
||||
|
||||
def remove_invalid_options(context, search_options, allowed_search_options):
|
||||
"""Remove search options that are not valid for non-admin API/context"""
|
||||
if FLAGS.allow_admin_api and context.is_admin:
|
||||
|
|
|
@ -422,12 +422,11 @@ class API(base.Base):
|
|||
LOG.debug(_("Casting to scheduler for %(pid)s/%(uid)s's"
|
||||
" (all-at-once)") % locals())
|
||||
|
||||
filter_class = 'nova.scheduler.host_filter.InstanceTypeFilter'
|
||||
request_spec = {
|
||||
'image': image,
|
||||
'instance_properties': base_options,
|
||||
'instance_type': instance_type,
|
||||
'filter': filter_class,
|
||||
'filter': None,
|
||||
'blob': zone_blob,
|
||||
'num_instances': num_instances,
|
||||
}
|
||||
|
|
|
@ -62,12 +62,13 @@ class AbstractScheduler(driver.Scheduler):
|
|||
host = build_plan_item['hostname']
|
||||
base_options = request_spec['instance_properties']
|
||||
image = request_spec['image']
|
||||
instance_type = request_spec.get('instance_type')
|
||||
|
||||
# TODO(sandy): I guess someone needs to add block_device_mapping
|
||||
# support at some point? Also, OS API has no concept of security
|
||||
# groups.
|
||||
instance = compute_api.API().create_db_entry_for_new_instance(context,
|
||||
image, base_options, None, [])
|
||||
instance_type, image, base_options, None, [])
|
||||
|
||||
instance_id = instance['id']
|
||||
kwargs['instance_id'] = instance_id
|
||||
|
|
|
@ -58,8 +58,6 @@ def choose_host_filter(filter_name=None):
|
|||
if not filter_name:
|
||||
filter_name = FLAGS.default_host_filter
|
||||
for filter_class in _get_filters():
|
||||
host_match = "%s.%s" % (filter_class.__module__, filter_class.__name__)
|
||||
if (host_match.startswith("nova.scheduler.filters") and
|
||||
(host_match.split(".")[-1] == filter_name)):
|
||||
if filter_class.__name__ == filter_name:
|
||||
return filter_class()
|
||||
raise exception.SchedulerHostFilterNotFound(filter_name=filter_name)
|
||||
|
|
|
@ -32,6 +32,7 @@ from nova import utils
|
|||
from nova import wsgi
|
||||
import nova.api.openstack.auth
|
||||
from nova.api import openstack
|
||||
from nova.api import auth as api_auth
|
||||
from nova.api.openstack import auth
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import versions
|
||||
|
@ -83,9 +84,9 @@ def wsgi_app(inner_app10=None, inner_app11=None, fake_auth=True,
|
|||
ctxt = fake_auth_context
|
||||
else:
|
||||
ctxt = context.RequestContext('fake', 'fake')
|
||||
api10 = openstack.FaultWrapper(wsgi.InjectContext(ctxt,
|
||||
api10 = openstack.FaultWrapper(api_auth.InjectContext(ctxt,
|
||||
limits.RateLimitingMiddleware(inner_app10)))
|
||||
api11 = openstack.FaultWrapper(wsgi.InjectContext(ctxt,
|
||||
api11 = openstack.FaultWrapper(api_auth.InjectContext(ctxt,
|
||||
limits.RateLimitingMiddleware(
|
||||
extensions.ExtensionMiddleware(inner_app11))))
|
||||
else:
|
||||
|
|
|
@ -32,6 +32,7 @@ from nova import context
|
|||
from nova import exception
|
||||
from nova import test
|
||||
from nova import wsgi
|
||||
from nova.api import auth
|
||||
from nova.api import ec2
|
||||
from nova.api.ec2 import apirequest
|
||||
from nova.api.ec2 import cloud
|
||||
|
@ -199,7 +200,7 @@ class ApiEc2TestCase(test.TestCase):
|
|||
# NOTE(vish): skipping the Authorizer
|
||||
roles = ['sysadmin', 'netadmin']
|
||||
ctxt = context.RequestContext('fake', 'fake', roles=roles)
|
||||
self.app = wsgi.InjectContext(ctxt,
|
||||
self.app = auth.InjectContext(ctxt,
|
||||
ec2.Requestify(ec2.Authorizer(ec2.Executor()),
|
||||
'nova.api.ec2.cloud.CloudController'))
|
||||
|
||||
|
|
12
nova/wsgi.py
12
nova/wsgi.py
|
@ -271,18 +271,6 @@ class Middleware(Application):
|
|||
return self.process_response(response)
|
||||
|
||||
|
||||
class InjectContext(Middleware):
|
||||
"""Add a 'nova.context' to WSGI environ."""
|
||||
def __init__(self, context, *args, **kwargs):
|
||||
self.context = context
|
||||
super(InjectContext, self).__init__(*args, **kwargs)
|
||||
|
||||
@webob.dec.wsgify(RequestClass=Request)
|
||||
def __call__(self, req):
|
||||
req.environ['nova.context'] = self.context
|
||||
return self.application
|
||||
|
||||
|
||||
class Debug(Middleware):
|
||||
"""Helper class for debugging a WSGI application.
|
||||
|
||||
|
|
Loading…
Reference in New Issue