# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""
Starting point for routing EC2 requests
"""

import logging
import routes
import webob.dec
import webob.exc

from nova.api.ec2 import admin
from nova.api.ec2 import cloud
from nova import exception
from nova.auth import manager


_log = logging.getLogger("api")
_log.setLevel(logging.DEBUG)


class API(wsgi.Middleware):
    """Routing for all EC2 API requests."""

    def __init__(self):
        self.application = Authenticate(Router())

class Authenticate(wsgi.Middleware):
    """Authenticates an EC2 request."""

    @webob.dec.wsgify
    def __call__(self, req):
        #TODO(gundlach): where do arguments come from?
        args = self.request.arguments

        # Read request signature.
        try:
            signature = args.pop('Signature')[0]
        except:
            raise webob.exc.HTTPBadRequest()

        # Make a copy of args for authentication and signature verification.
        auth_params = {}
        for key, value in args.items():
            auth_params[key] = value[0]

        # Get requested action and remove authentication args for final request.
        try:
            action = args.pop('Action')[0]
            access = args.pop('AWSAccessKeyId')[0]
            args.pop('SignatureMethod')
            args.pop('SignatureVersion')
            args.pop('Version')
            args.pop('Timestamp')
        except:
            raise webob.exc.HTTPBadRequest()

        # Authenticate the request.
        try:
            (user, project) = manager.AuthManager().authenticate(
                access,
                signature,
                auth_params,
                req.method,
                req.host,
                req.path
            )

        except exception.Error, ex:
            logging.debug("Authentication Failure: %s" % ex)
            raise webob.exc.HTTPForbidden()

        _log.debug('action: %s' % action)

        for key, value in args.items():
            _log.debug('arg: %s\t\tval: %s' % (key, value))

        # Authenticated!
        req.environ['ec2.action'] = action
        req.environ['ec2.context'] = APIRequestContext(user, project)
        return self.application


class Router(wsgi.Application):
    """
    Finds controller for a request, executes environ['ec2.action'] upon it, and
    returns an XML response.  If the action fails, returns a 400.
    """
    def __init__(self):
        self.map = routes.Mapper()
        self.map.connect("/{controller_name}/")
        self.controllers = dict(Cloud=cloud.CloudController(),
                                Admin=admin.AdminController())

    @webob.dec.wsgify
    def __call__(self, req):
        # Obtain the appropriate controller for this request.
        match = self.map.match(req.path)
        if not match:
            raise webob.exc.HTTPNotFound()
        controller_name = match['controller_name']

        try:
            controller = self.controllers[controller_name]
        except KeyError:
            self._error('unhandled', 'no controller named %s' % controller_name)
            return

        api_request = APIRequest(controller, req.environ['ec2.action'])
        context = req.environ['ec2.context']
        try:
            return api_request.send(context, **args)
        except exception.ApiError as ex:
            self._error(req, type(ex).__name__ + "." + ex.code, ex.message)
        # TODO(vish): do something more useful with unknown exceptions
        except Exception as ex:
            self._error(type(ex).__name__, str(ex))

    def _error(self, req, code, message):
        req.status = 400
        req.headers['Content-Type'] = 'text/xml'
        req.response = ('<?xml version="1.0"?>\n'
                     '<Response><Errors><Error><Code>%s</Code>'
                     '<Message>%s</Message></Error></Errors>'
                     '<RequestID>?</RequestID></Response>') % (code, message))


@@ -848,16 +848,29 @@
  routes%0A
+import webob%0A
 import w
@@ -1369,92 +1369,31 @@
     
-#TODO(gundlach): where do arguments come from?%0A        args = self.request.arguments
+args = dict(req.params)
 %0A%0A  
@@ -2732,51 +2732,8 @@
 ed!%0A
-        req.environ%5B'ec2.action'%5D = action%0A
     
@@ -2798,16 +2798,105 @@
 roject)%0A
+        req.environ%5B'ec2.action'%5D = action%0A        req.environ%5B'ec2.action_args'%5D = args%0A
         
@@ -3721,32 +3721,39 @@
 or:%0A            
+return 
 self._error('unh
@@ -3804,35 +3804,16 @@
 er_name)
-%0A            return
 %0A%0A      
@@ -3983,20 +3983,46 @@
 text, **
+req.environ%5B'ec2.action_
 args
+'%5D
 )%0A      
@@ -4060,32 +4060,39 @@
 ex:%0A            
+return 
 self._error(req,
@@ -4250,24 +4250,31 @@
             
+return 
 self._error(
@@ -4354,17 +4354,50 @@
       re
-q
+sp = webob.Response()%0A        resp
 .status 
@@ -4412,17 +4412,18 @@
       re
-q
+sp
 .headers
@@ -4466,18 +4466,15 @@
   re
-q.response
+sp.body
  = (
@@ -4700,13 +4700,32 @@
 , message))%0A
+        return resp
 %0A

Return error Responses properly, and don't muck with req.params -- make a copy instead