Skip to content

Commit

Permalink
Merge 0b92038 into 8ab4be5
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarolopez committed Apr 8, 2015
2 parents 8ab4be5 + 0b92038 commit 40ccdfb
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 41 deletions.
27 changes: 22 additions & 5 deletions ooi/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.

import copy

from ooi import utils

import webob.exc
Expand All @@ -25,14 +27,29 @@ def __init__(self, app, openstack_version):
self.openstack_version = openstack_version

def _get_req(self, req, path=None, content_type=None, body=None):
req.script_name = self.openstack_version
"""Return a new Request object to interact with OpenStack.
This method will create a new request starting with the same WSGI
environment as the original request, prepared to interact with
OpenStack. Namely, it will override the script name to match the
OpenStack version. It will also override the path, content_type and
body of the request, if any of those keyword arguments are passed.
:param req: the original request
:param path: new path for the request
:param content_type: new content type for the request
:param body: new body for the request
:returns: a Request object
"""
new_req = webob.Request(copy.copy(req.environ))
new_req.script_name = self.openstack_version
if path is not None:
req.path_info = path
new_req.path_info = path
if content_type is not None:
req.content_type = content_type
new_req.content_type = content_type
if body is not None:
req.body = utils.utf8(body)
return req
new_req.body = utils.utf8(body)
return new_req

@staticmethod
def get_from_response(response, element, default):
Expand Down
2 changes: 1 addition & 1 deletion ooi/occi/core/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class Entity(object):
"occi.core.title"])

kind = kind.Kind(helpers.build_scheme('core'), 'entity',
'entity', attributes, '/entity/')
'entity', attributes, 'entity/')

def __init__(self, title, mixins, id=None):
helpers.check_type(mixins, mixin.Mixin)
Expand Down
2 changes: 1 addition & 1 deletion ooi/occi/core/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Link(entity.Entity):
"occi.core.target"])

kind = kind.Kind(helpers.build_scheme("core"), 'link', 'link',
attributes, '/link/')
attributes, 'link/')

def __init__(self, title, mixins, source, target):
super(Link, self).__init__(title, mixins)
Expand Down
2 changes: 1 addition & 1 deletion ooi/occi/core/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Resource(entity.Entity):
attributes = attribute.AttributeCollection(["occi.core.summary"])

kind = kind.Kind(helpers.build_scheme('core'), 'resource',
'resource', attributes, '/resource/',
'resource', attributes, 'resource/',
related=[entity.Entity.kind])

def __init__(self, title, mixins, id=None, summary=None):
Expand Down
2 changes: 1 addition & 1 deletion ooi/occi/infrastructure/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class ComputeResource(resource.Resource):
"occi.compute.state"])
actions = (start, stop, restart, suspend)
kind = kind.Kind(helpers.build_scheme('infrastructure'), 'compute',
'compute resource', attributes, '/compute/',
'compute resource', attributes, 'compute/',
actions=actions,
related=[resource.Resource.kind])

Expand Down
21 changes: 12 additions & 9 deletions ooi/occi/rendering/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ class KindRenderer(CategoryRenderer):


class ActionRenderer(CategoryRenderer):
def render(self, instance=None, env={}):
# We have an instance id, render it as a link
if instance is not None:
def render(self, ass_obj=None, env={}):
# We have an associated object, render it as a link to that object
if ass_obj is not None:
url = env.get("application_url", "")
url = utils.join_url(url, [instance, self.obj.location])
term = ass_obj.kind.term + "/"
url = utils.join_url(url, [term, ass_obj.id, self.obj.location])
d = {"location": url,
"rel": self.obj.type_id}
link = "<%(location)s>; rel=%(rel)s" % d
Expand All @@ -80,7 +81,7 @@ def render(self, env={}):
for what in [self.obj.kinds, self.obj.mixins, self.obj.actions,
self.obj.resources, self.obj.links]:
for el in what:
url = app_url + el.location
url = utils.join_url(app_url, el.location)
ret.append(('X-OCCI-Location', '%s' % url))
return ret

Expand All @@ -100,16 +101,18 @@ def render(self, env={}):
class ResourceRenderer(HeaderRenderer):
def render(self, env={}):
ret = []
ret.extend(KindRenderer(self.obj.kind).render())
ret.extend(KindRenderer(self.obj.kind).render(env=env))
for m in self.obj.mixins:
ret.extend(MixinRenderer(m).render())
ret.extend(MixinRenderer(m).render(env=env))
for a in self.obj.attributes:
# FIXME(aloga): I dont like this test here
if self.obj.attributes[a].value is None:
continue
ret.extend(AttributeRenderer(self.obj.attributes[a]).render())
r = AttributeRenderer(self.obj.attributes[a])
ret.extend(r.render(env=env))
for a in self.obj.actions:
ret.extend(ActionRenderer(a).render(instance=self.obj.id))
r = ActionRenderer(a)
ret.extend(r.render(ass_obj=self.obj, env=env))
for l in self.obj.links:
pass
# FIXME(aloga): we need to fix this
Expand Down
2 changes: 2 additions & 0 deletions ooi/tests/fakes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import ooi.wsgi


application_url = "https://foo.example.org:8774/ooiv1"

tenants = {
"foo": {"id": uuid.uuid4().hex,
"name": "foo"},
Expand Down
39 changes: 25 additions & 14 deletions ooi/tests/middleware/test_compute_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

from ooi.tests import fakes
from ooi.tests.middleware import test_middleware
from ooi import utils


def build_occi_server(server):
Expand Down Expand Up @@ -58,14 +59,22 @@ def build_occi_server(server):
'occi.core.id="%s"' % server_id,
]
links = []
links.append('<%s?action=restart>; rel=http://schemas.ogf.org/occi/'
'infrastructure/compute/action#restart' % server_id)
links.append('<%s?action=start>; rel=http://schemas.ogf.org/occi/'
'infrastructure/compute/action#start' % server_id)
links.append('<%s?action=stop>; rel=http://schemas.ogf.org/occi/'
'infrastructure/compute/action#stop' % server_id)
links.append('<%s?action=suspend>; rel=http://schemas.ogf.org/occi/'
'infrastructure/compute/action#suspend' % server_id)
links.append('<%s/compute/%s?action=restart>; '
'rel=http://schemas.ogf.org/occi/'
'infrastructure/compute/action#restart' %
(fakes.application_url, server_id))
links.append('<%s/compute/%s?action=start>; '
'rel=http://schemas.ogf.org/occi/'
'infrastructure/compute/action#start' %
(fakes.application_url, server_id))
links.append('<%s/compute/%s?action=stop>; '
'rel=http://schemas.ogf.org/occi/'
'infrastructure/compute/action#stop' %
(fakes.application_url, server_id))
links.append('<%s/compute/%s?action=suspend>; '
'rel=http://schemas.ogf.org/occi/'
'infrastructure/compute/action#suspend' %
(fakes.application_url, server_id))

result = []
for c in cats:
Expand All @@ -92,8 +101,6 @@ def test_list_vms_empty(self):

resp = req.get_response(app)

self.assertEqual("/%s/servers" % tenant["id"], req.path_info)

expected_result = ""
self.assertContentType(resp)
self.assertExpectedResult(expected_result, resp)
Expand All @@ -107,12 +114,14 @@ def test_list_vms_one_vm(self):

resp = req.get_response(app)

self.assertEqual("/%s/servers" % tenant["id"], req.path_info)

self.assertEqual(200, resp.status_code)
expected = []
for s in fakes.servers[tenant["id"]]:
expected.append(("X-OCCI-Location", "/compute/%s" % s["id"]))
expected.append(
("X-OCCI-Location", utils.join_url(self.application_url + "/",
"compute/%s" % s["id"]))
)
self.assertContentType(resp)
self.assertExpectedResult(expected, resp)

def test_show_vm(self):
Expand Down Expand Up @@ -158,7 +167,9 @@ def test_create_vm(self):
headers=headers)
resp = req.get_response(app)

expected = [("X-OCCI-Location", "/compute/%s" % "foo")]
expected = [("X-OCCI-Location",
utils.join_url(self.application_url + "/",
"compute/%s" % "foo"))]
self.assertEqual(200, resp.status_code)
self.assertExpectedResult(expected, resp)
self.assertContentType(resp)
Expand Down
7 changes: 4 additions & 3 deletions ooi/tests/middleware/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def setUp(self):
super(TestMiddleware, self).setUp()

self.accept = None
self.application_url = fakes.application_url

def get_app(self, resp=None):
return wsgi.OCCIMiddleware(fakes.FakeApp())
Expand All @@ -47,9 +48,7 @@ def assertExpectedResult(self, expected, result):
expected = ["%s: %s" % e for e in expected]
# NOTE(aloga): the order of the result does not matter
results = result.text.splitlines()
results.sort()
expected.sort()
self.assertEqual(expected, results)
self.assertItemsEqual(expected, results)

def _build_req(self, path, tenant_id, **kwargs):
if self.accept is not None:
Expand All @@ -59,6 +58,8 @@ def _build_req(self, path, tenant_id, **kwargs):
m.user.project_id = tenant_id
environ = {"keystone.token_auth": m}

kwargs["base_url"] = self.application_url

return webob.Request.blank(path, environ=environ, **kwargs)

def test_404(self):
Expand Down
87 changes: 87 additions & 0 deletions ooi/tests/test_base_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-

# Copyright 2015 Spanish National Research Council
#
# 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 json

import webob
import webob.exc

import ooi.api.base
from ooi.tests import base
from ooi import utils


class TestController(base.TestCase):
def test_controller(self):
class Foo(object):
pass
controller = ooi.api.base.Controller(Foo(), "version")
self.assertIsInstance(controller.app, Foo)
self.assertEqual("version", controller.openstack_version)

def test_new_request(self):
controller = ooi.api.base.Controller(None, "version")
req = webob.Request.blank("foo")
new_req = controller._get_req(req)
self.assertEqual("version", new_req.script_name)
self.assertEqual("foo", new_req.path_info)
self.assertIsNot(req, new_req)

def test_new_request_with_path(self):
controller = ooi.api.base.Controller(None, "version")
req = webob.Request.blank("foo")
new_req = controller._get_req(req, path="bar")
self.assertEqual("bar", new_req.path_info)

def test_new_request_with_body(self):
controller = ooi.api.base.Controller(None, "version")
req = webob.Request.blank("foo")
new_req = controller._get_req(req, body="bar")
self.assertEqual(utils.utf8("bar"), new_req.body)

def test_new_request_with_content_type(self):
controller = ooi.api.base.Controller(None, "version")
req = webob.Request.blank("foo")
new_req = controller._get_req(req, content_type="foo/bar")
self.assertEqual("foo/bar", new_req.content_type)

def test_get_from_response(self):
d = {"element": {"foo": "bar"}}
body = json.dumps(d)
response = webob.Response(status=200, body=body)
result = ooi.api.base.Controller.get_from_response(response,
"element",
{})
self.assertEqual(d["element"], result)

def test_get_from_response_with_default(self):
d = {"element": {"foo": "bar"}}
body = json.dumps({})
response = webob.Response(status=200, body=body)
result = ooi.api.base.Controller.get_from_response(response,
"element",
d["element"])
self.assertEqual(d["element"], result)

def test_get_from_response_with_exception(self):
d = {"unauthorized": {"message": "unauthorized"}}
body = json.dumps(d)
response = webob.Response(status=403, body=body)
self.assertRaises(webob.exc.HTTPForbidden,
ooi.api.base.Controller.get_from_response,
response,
"foo",
{})
3 changes: 3 additions & 0 deletions ooi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,8 @@ def join_url(base, parts):
parts = [parts]

for p in parts:
if p.startswith("/"):
# We won't get an absolute url
p = p[1:]
url = urlparse.urljoin(url, p)
return url
6 changes: 4 additions & 2 deletions ooi/wsgi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ def serialize(self, request, content_type, default_serializers=None):
else:
_mtype, _serializer = self.get_serializer(content_type,
default_serializers)
serializer = _serializer()
env = {"application_url": request.application_url + "/"}
serializer = _serializer(env)

response = webob.Response()
response.status_int = self.code
Expand Down Expand Up @@ -379,7 +380,8 @@ def __call__(self, req):
mtype = serializers.get_media_map().get(content_type,
"text")
serializer = serializers.get_default_serializers()[mtype]
serialized_exc = serializer().serialize(self.wrapped_exc)
env = {}
serialized_exc = serializer(env).serialize(self.wrapped_exc)
self.wrapped_exc.body = serialized_exc[1]
self.wrapped_exc.content_type = content_type

Expand Down
13 changes: 9 additions & 4 deletions ooi/wsgi/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@
])


class TextSerializer(object):
class BaseSerializer(object):
def __init__(self, env):
self.env = env


class TextSerializer(BaseSerializer):
def serialize(self, data):
if not isinstance(data, list):
data = [data]
Expand All @@ -36,11 +41,11 @@ def serialize(self, data):
for d in data:
renderers.append(text_rendering.get_renderer(d))

ret = "\n".join([r.render() for r in renderers])
ret = "\n".join([r.render(env=self.env) for r in renderers])
return None, utils.utf8(ret)


class HeaderSerializer(object):
class HeaderSerializer(BaseSerializer):
def serialize(self, data):
if not isinstance(data, list):
data = [data]
Expand All @@ -51,7 +56,7 @@ def serialize(self, data):

# Header renderers will return a list, so we must flatten the results
# before returning them
headers = [i for r in renderers for i in r.render()]
headers = [i for r in renderers for i in r.render(env=self.env)]
return headers, ""


Expand Down

0 comments on commit 40ccdfb

Please sign in to comment.