summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Gundlach <michael.gundlach@rackspace.com>2010-09-29 12:27:56 -0400
committerMichael Gundlach <michael.gundlach@rackspace.com>2010-09-29 12:27:56 -0400
commit8201ef0b16835762fc39956e8b5e4985e94ceb4c (patch)
treeeaa01250a6736951d0a733ec36fac991fe136153
parent29eca7e7992fc5c073d70f7c8ca5e5bc03f62af7 (diff)
parent1ecff5b5766c8ac0854b80bd819f364b12f3f038 (diff)
Update from trunk to handle one-line merge conflict
-rw-r--r--nova/api/rackspace/__init__.py9
-rw-r--r--nova/api/rackspace/auth.py7
-rw-r--r--nova/api/rackspace/backup_schedules.py7
-rw-r--r--nova/api/rackspace/faults.py61
-rw-r--r--nova/api/rackspace/flavors.py3
-rw-r--r--nova/api/rackspace/images.py7
-rw-r--r--nova/api/rackspace/servers.py17
-rw-r--r--nova/tests/api/rackspace/testfaults.py30
8 files changed, 120 insertions, 21 deletions
diff --git a/nova/api/rackspace/__init__.py b/nova/api/rackspace/__init__.py
index 48104f6df..89a4693ad 100644
--- a/nova/api/rackspace/__init__.py
+++ b/nova/api/rackspace/__init__.py
@@ -31,6 +31,7 @@ import webob
from nova import flags
from nova import utils
from nova import wsgi
+from nova.api.rackspace import faults
from nova.api.rackspace import backup_schedules
from nova.api.rackspace import flavors
from nova.api.rackspace import images
@@ -67,7 +68,7 @@ class AuthMiddleware(wsgi.Middleware):
user = self.auth_driver.authorize_token(req.headers["X-Auth-Token"])
if not user:
- return webob.exc.HTTPUnauthorized()
+ return faults.Fault(webob.exc.HTTPUnauthorized())
if not req.environ.has_key('nova.context'):
req.environ['nova.context'] = {}
@@ -112,8 +113,10 @@ class RateLimitingMiddleware(wsgi.Middleware):
delay = self.get_delay(action_name, username)
if delay:
# TODO(gundlach): Get the retry-after format correct.
- raise webob.exc.HTTPRequestEntityTooLarge(headers={
- 'Retry-After': time.time() + delay})
+ exc = webob.exc.HTTPRequestEntityTooLarge(
+ explanation='Too many requests.',
+ headers={'Retry-After': time.time() + delay})
+ raise faults.Fault(exc)
return self.application
def get_delay(self, action_name, username):
diff --git a/nova/api/rackspace/auth.py b/nova/api/rackspace/auth.py
index 8bfb0753e..c45156ebd 100644
--- a/nova/api/rackspace/auth.py
+++ b/nova/api/rackspace/auth.py
@@ -11,6 +11,7 @@ from nova import db
from nova import flags
from nova import manager
from nova import utils
+from nova.api.rackspace import faults
FLAGS = flags.FLAGS
@@ -36,13 +37,13 @@ class BasicApiAuthManager(object):
# honor it
path_info = req.path_info
if len(path_info) > 1:
- return webob.exc.HTTPUnauthorized()
+ return faults.Fault(webob.exc.HTTPUnauthorized())
try:
username, key = req.headers['X-Auth-User'], \
req.headers['X-Auth-Key']
except KeyError:
- return webob.exc.HTTPUnauthorized()
+ return faults.Fault(webob.exc.HTTPUnauthorized())
username, key = req.headers['X-Auth-User'], req.headers['X-Auth-Key']
token, user = self._authorize_user(username, key)
@@ -57,7 +58,7 @@ class BasicApiAuthManager(object):
res.status = '204'
return res
else:
- return webob.exc.HTTPUnauthorized()
+ return faults.Fault(webob.exc.HTTPUnauthorized())
def authorize_token(self, token_hash):
""" retrieves user information from the datastore given a token
diff --git a/nova/api/rackspace/backup_schedules.py b/nova/api/rackspace/backup_schedules.py
index 46da778ee..cb83023bc 100644
--- a/nova/api/rackspace/backup_schedules.py
+++ b/nova/api/rackspace/backup_schedules.py
@@ -20,6 +20,7 @@ from webob import exc
from nova import wsgi
from nova.api.rackspace import _id_translator
+from nova.api.rackspace import faults
import nova.image.service
class Controller(wsgi.Controller):
@@ -27,12 +28,12 @@ class Controller(wsgi.Controller):
pass
def index(self, req, server_id):
- return exc.HTTPNotFound()
+ return faults.Fault(exc.HTTPNotFound())
def create(self, req, server_id):
""" No actual update method required, since the existing API allows
both create and update through a POST """
- return exc.HTTPNotFound()
+ return faults.Fault(exc.HTTPNotFound())
def delete(self, req, server_id):
- return exc.HTTPNotFound()
+ return faults.Fault(exc.HTTPNotFound())
diff --git a/nova/api/rackspace/faults.py b/nova/api/rackspace/faults.py
new file mode 100644
index 000000000..fd6bc3623
--- /dev/null
+++ b/nova/api/rackspace/faults.py
@@ -0,0 +1,61 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack LLC.
+# 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.
+
+
+import webob.dec
+
+from nova import wsgi
+
+
+class Fault(wsgi.Application):
+
+ """An RS API fault response."""
+
+ _fault_names = {
+ 400: "badRequest",
+ 401: "unauthorized",
+ 403: "resizeNotAllowed",
+ 404: "itemNotFound",
+ 405: "badMethod",
+ 409: "inProgress",
+ 413: "overLimit",
+ 415: "badMediaType",
+ 501: "notImplemented",
+ 503: "serviceUnavailable"}
+
+ def __init__(self, exception):
+ """Create a Fault for the given webob.exc.exception."""
+ self.exception = exception
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ """Generate a WSGI response based on self.exception."""
+ # Replace the body with fault details.
+ code = self.exception.status_int
+ fault_name = self._fault_names.get(code, "cloudServersFault")
+ fault_data = {
+ fault_name: {
+ 'code': code,
+ 'message': self.exception.explanation}}
+ if code == 413:
+ retry = self.exception.headers['Retry-After']
+ fault_data[fault_name]['retryAfter'] = retry
+ # 'code' is an attribute on the fault tag itself
+ metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
+ serializer = wsgi.Serializer(req.environ, metadata)
+ self.exception.body = serializer.to_content_type(fault_data)
+ return self.exception
diff --git a/nova/api/rackspace/flavors.py b/nova/api/rackspace/flavors.py
index ba7aa937c..916449854 100644
--- a/nova/api/rackspace/flavors.py
+++ b/nova/api/rackspace/flavors.py
@@ -17,6 +17,7 @@
from webob import exc
+from nova.api.rackspace import faults
from nova.compute import instance_types
from nova import wsgi
import nova.api.rackspace
@@ -50,7 +51,7 @@ class Controller(wsgi.Controller):
item = dict(ram=val['memory_mb'], disk=val['local_gb'],
id=val['flavorid'], name=name)
return dict(flavor=item)
- raise exc.HTTPNotFound()
+ raise faults.Fault(exc.HTTPNotFound())
def _all_ids(self):
"""Return the list of all flavorids."""
diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py
index 7da17e6a7..4a7dd489c 100644
--- a/nova/api/rackspace/images.py
+++ b/nova/api/rackspace/images.py
@@ -21,6 +21,7 @@ from nova import wsgi
from nova.api.rackspace import _id_translator
import nova.api.rackspace
import nova.image.service
+from nova.api.rackspace import faults
class Controller(wsgi.Controller):
@@ -60,14 +61,14 @@ class Controller(wsgi.Controller):
def delete(self, req, id):
# Only public images are supported for now.
- raise exc.HTTPNotFound()
+ raise faults.Fault(exc.HTTPNotFound())
def create(self, req):
# Only public images are supported for now, so a request to
# make a backup of a server cannot be supproted.
- raise exc.HTTPNotFound()
+ raise faults.Fault(exc.HTTPNotFound())
def update(self, req, id):
# Users may not modify public images, and that's all that
# we support for now.
- raise exc.HTTPNotFound()
+ raise faults.Fault(exc.HTTPNotFound())
diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py
index 958fc86a3..08d533563 100644
--- a/nova/api/rackspace/servers.py
+++ b/nova/api/rackspace/servers.py
@@ -24,6 +24,7 @@ from nova import rpc
from nova import utils
from nova import wsgi
from nova.api.rackspace import _id_translator
+from nova.api.rackspace import faults
from nova.compute import power_state
import nova.api.rackspace
import nova.image.service
@@ -126,7 +127,7 @@ class Controller(wsgi.Controller):
if inst:
if inst.user_id == user_id:
return _entity_detail(inst)
- raise exc.HTTPNotFound()
+ raise faults.Fault(exc.HTTPNotFound())
def delete(self, req, id):
""" Destroys a server """
@@ -134,13 +135,13 @@ class Controller(wsgi.Controller):
instance = self.db_driver.instance_get(None, id)
if instance and instance['user_id'] == user_id:
self.db_driver.instance_destroy(None, id)
- return exc.HTTPAccepted()
- return exc.HTTPNotFound()
+ return faults.Fault(exc.HTTPAccepted())
+ return faults.Fault(exc.HTTPNotFound())
def create(self, req):
""" Creates a new server for a given user """
if not req.environ.has_key('inst_dict'):
- return exc.HTTPUnprocessableEntity()
+ return faults.Fault(exc.HTTPUnprocessableEntity())
inst = self._build_server_instance(req)
@@ -153,22 +154,22 @@ class Controller(wsgi.Controller):
def update(self, req, id):
""" Updates the server name or password """
if not req.environ.has_key('inst_dict'):
- return exc.HTTPUnprocessableEntity()
+ return faults.Fault(exc.HTTPUnprocessableEntity())
instance = self.db_driver.instance_get(None, id)
if not instance:
- return exc.HTTPNotFound()
+ return faults.Fault(exc.HTTPNotFound())
attrs = req.environ['nova.context'].get('model_attributes', None)
if attrs:
self.db_driver.instance_update(None, id, _filter_params(attrs))
- return exc.HTTPNoContent()
+ return faults.Fault(exc.HTTPNoContent())
def action(self, req, id):
""" multi-purpose method used to reboot, rebuild, and
resize a server """
if not req.environ.has_key('inst_dict'):
- return exc.HTTPUnprocessableEntity()
+ return faults.Fault(exc.HTTPUnprocessableEntity())
def _build_server_instance(self, req):
"""Build instance data structure and save it to the data store."""
diff --git a/nova/tests/api/rackspace/testfaults.py b/nova/tests/api/rackspace/testfaults.py
new file mode 100644
index 000000000..74caffd30
--- /dev/null
+++ b/nova/tests/api/rackspace/testfaults.py
@@ -0,0 +1,30 @@
+import unittest
+import webob
+import webob.exc
+
+from nova.api.rackspace import faults
+
+class TestFaults(unittest.TestCase):
+
+ def test_fault_parts(self):
+ req = webob.Request.blank('/.xml')
+ f = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram'))
+ resp = req.get_response(f)
+
+ first_two_words = resp.body.strip().split()[:2]
+ self.assertEqual(first_two_words, ['<badRequest', 'code="400">'])
+ body_without_spaces = ''.join(resp.body.split())
+ self.assertTrue('<message>scram</message>' in body_without_spaces)
+
+ def test_retry_header(self):
+ req = webob.Request.blank('/.xml')
+ exc = webob.exc.HTTPRequestEntityTooLarge(explanation='sorry',
+ headers={'Retry-After': 4})
+ f = faults.Fault(exc)
+ resp = req.get_response(f)
+ first_two_words = resp.body.strip().split()[:2]
+ self.assertEqual(first_two_words, ['<overLimit', 'code="413">'])
+ body_sans_spaces = ''.join(resp.body.split())
+ self.assertTrue('<message>sorry</message>' in body_sans_spaces)
+ self.assertTrue('<retryAfter>4</retryAfter>' in body_sans_spaces)
+ self.assertEqual(resp.headers['Retry-After'], 4)