summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorWilliam Wolf <throughnothing@gmail.com>2011-06-06 14:55:04 -0400
committerWilliam Wolf <throughnothing@gmail.com>2011-06-06 14:55:04 -0400
commit51f4b673d9521f43b2396eeb7d65cd43d1f100b6 (patch)
tree0c46761ba4e237d35b546b2eb66f5c78800c4b04 /nova
parent78611b6a002095747ea41e26029cdea5aeb753f7 (diff)
parent4d50e840c7a9dd30cda0670564ad0135027f2ba5 (diff)
merge from trunk, resolved conflicts
Diffstat (limited to 'nova')
-rw-r--r--nova/api/openstack/images.py13
-rw-r--r--nova/db/sqlalchemy/api.py28
-rw-r--r--nova/tests/api/openstack/fakes.py3
-rw-r--r--nova/tests/api/openstack/test_images.py74
4 files changed, 104 insertions, 14 deletions
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index 32ac9d2f1..5ffd8e96a 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -99,7 +99,7 @@ class Controller(object):
raise webob.exc.HTTPBadRequest()
try:
- server_id = body["image"]["serverId"]
+ server_id = self._server_id_from_req_data(body)
image_name = body["image"]["name"]
except KeyError:
raise webob.exc.HTTPBadRequest()
@@ -111,6 +111,9 @@ class Controller(object):
"""Indicates that you must use a Controller subclass."""
raise NotImplementedError
+ def _server_id_from_req_data(self, data):
+ raise NotImplementedError()
+
class ControllerV10(Controller):
"""Version 1.0 specific controller logic."""
@@ -146,6 +149,9 @@ class ControllerV10(Controller):
builder = self.get_builder(req).build
return dict(images=[builder(image, detail=True) for image in images])
+ def _server_id_from_req_data(self, data):
+ return data['image']['serverId']
+
class ControllerV11(Controller):
"""Version 1.1 specific controller logic."""
@@ -183,6 +189,9 @@ class ControllerV11(Controller):
builder = self.get_builder(req).build
return dict(images=[builder(image, detail=True) for image in images])
+ def _server_id_from_req_data(self, data):
+ return data['image']['serverRef']
+
def create_resource(version='1.0'):
controller = {
@@ -198,7 +207,7 @@ def create_resource(version='1.0'):
metadata = {
"attributes": {
"image": ["id", "name", "updated", "created", "status",
- "serverId", "progress"],
+ "serverId", "progress", "serverRef"],
"link": ["rel", "type", "href"],
},
}
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 56739e9db..6dbf53a6c 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -60,9 +60,7 @@ def is_user_context(context):
def authorize_project_context(context, project_id):
- """Ensures that the request context has permission to access the
- given project.
- """
+ """Ensures a request has permission to access the given project."""
if is_user_context(context):
if not context.project:
raise exception.NotAuthorized()
@@ -71,9 +69,7 @@ def authorize_project_context(context, project_id):
def authorize_user_context(context, user_id):
- """Ensures that the request context has permission to access the
- given user.
- """
+ """Ensures a request has permission to access the given user."""
if is_user_context(context):
if not context.user:
raise exception.NotAuthorized()
@@ -89,9 +85,12 @@ def can_read_deleted(context):
def require_admin_context(f):
- """Decorator used to indicate that the method requires an
- administrator context.
+ """Decorator to require admin request context.
+
+ The first argument to the wrapped function must be the context.
+
"""
+
def wrapper(*args, **kwargs):
if not is_admin_context(args[0]):
raise exception.AdminRequired()
@@ -100,12 +99,19 @@ def require_admin_context(f):
def require_context(f):
- """Decorator used to indicate that the method requires either
- an administrator or normal user context.
+ """Decorator to require *any* user or admin context.
+
+ This does no authorization for user or project access matching, see
+ :py:func:`authorize_project_context` and
+ :py:func:`authorize_user_context`.
+
+ The first argument to the wrapped function must be the context.
+
"""
+
def wrapper(*args, **kwargs):
if not is_admin_context(args[0]) and not is_user_context(args[0]):
- raise exception.AdminRequired()
+ raise exception.NotAuthorized()
return f(*args, **kwargs)
return wrapper
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 1066654e0..7d632aaeb 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -142,7 +142,8 @@ def stub_out_networking(stubs):
def stub_out_compute_api_snapshot(stubs):
def snapshot(self, context, instance_id, name):
- return 123
+ return dict(id='123', status='ACTIVE',
+ properties=dict(instance_id='123'))
stubs.Set(nova.compute.API, 'snapshot', snapshot)
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 097bf2aa5..be777df9b 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -369,6 +369,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
fakes.stub_out_key_pair_funcs(self.stubs)
self.fixtures = self._make_image_fixtures()
fakes.stub_out_glance(self.stubs, initial_fixtures=self.fixtures)
+ fakes.stub_out_compute_api_snapshot(self.stubs)
def tearDown(self):
"""Run after each test."""
@@ -1001,6 +1002,79 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
+ def test_create_image(self):
+
+ body = dict(image=dict(serverId='123', name='Backup 1'))
+ req = webob.Request.blank('/v1.0/images')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+ response = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, response.status_int)
+
+ def test_create_image_no_server_id(self):
+
+ body = dict(image=dict(name='Backup 1'))
+ req = webob.Request.blank('/v1.0/images')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+ response = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, response.status_int)
+
+ def test_create_image_v1_1(self):
+
+ body = dict(image=dict(serverRef='123', name='Backup 1'))
+ req = webob.Request.blank('/v1.1/images')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+ response = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, response.status_int)
+
+ def test_create_image_v1_1_xml_serialization(self):
+
+ body = dict(image=dict(serverRef='123', name='Backup 1'))
+ req = webob.Request.blank('/v1.1/images')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+ req.headers["accept"] = "application/xml"
+ response = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, response.status_int)
+ resp_xml = minidom.parseString(response.body.replace(" ", ""))
+ expected_href = "http://localhost/v1.1/images/123"
+ expected_image = minidom.parseString("""
+ <image
+ created="None"
+ id="123"
+ name="None"
+ serverRef="http://localhost/v1.1/servers/123"
+ status="ACTIVE"
+ updated="None"
+ xmlns="http://docs.openstack.org/compute/api/v1.1">
+ <links>
+ <link href="%(expected_href)s" rel="self"/>
+ <link href="%(expected_href)s" rel="bookmark"
+ type="application/json" />
+ <link href="%(expected_href)s" rel="bookmark"
+ type="application/xml" />
+ </links>
+ </image>
+ """.replace(" ", "") % (locals()))
+
+ self.assertEqual(expected_image.toxml(), resp_xml.toxml())
+
+ def test_create_image_v1_1_no_server_ref(self):
+
+ body = dict(image=dict(name='Backup 1'))
+ req = webob.Request.blank('/v1.1/images')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+ response = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, response.status_int)
+
@classmethod
def _make_image_fixtures(cls):
image_id = 123