Skip to content

Commit

Permalink
Merge 9f1311b into a55edb3
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarolopez committed Apr 24, 2015
2 parents a55edb3 + 9f1311b commit ea99f1b
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 91 deletions.
19 changes: 9 additions & 10 deletions ooi/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

from ooi import utils

from oslo_log import log as logging
import webob.exc

LOG = logging.getLogger(__name__)


class Controller(object):
def __init__(self, app, openstack_version):
Expand Down Expand Up @@ -74,15 +77,6 @@ def get_from_response(response, element, default):
else:
raise exception_from_response(response)

@staticmethod
def validate(schema):
def accepts(f):
def _validate(obj, req, body, *args, **kwargs):
parsed_obj = req.validate(schema)
return f(obj, parsed_obj, req, body, *args, **kwargs)
return _validate
return accepts


def exception_from_response(response):
"""Convert an OpenStack V2 Fault into a webob exception.
Expand Down Expand Up @@ -111,7 +105,12 @@ def exception_from_response(response):
503: webob.exc.HTTPServiceUnavailable,
}
code = response.status_int
message = response.json_body.popitem()[1].get("message")
try:
message = response.json_body.popitem()[1].get("message")
except Exception:
LOG.exception("Unknown error happenened processing response %s"
% response)
return webob.exc.HTTPInternalServerError

exc = exceptions.get(code, webob.exc.HTTPInternalServerError)
return exc(explanation=message)
30 changes: 18 additions & 12 deletions ooi/api/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from ooi.occi.infrastructure import compute
from ooi.occi.infrastructure import storage
from ooi.occi.infrastructure import storage_link
from ooi.occi import validator as occi_validator
from ooi.openstack import contextualization
from ooi.openstack import helpers
from ooi.openstack import templates
Expand Down Expand Up @@ -66,19 +67,24 @@ def index(self, req):

return collection.Collection(resources=occi_compute_resources)

@ooi.api.base.Controller.validate(
{"kind": compute.ComputeResource.kind,
"mixins": [
templates.OpenStackOSTemplate,
templates.OpenStackResourceTemplate,
],
"optional_mixins": [
contextualization.user_data,
contextualization.public_key,
]
})
def create(self, obj, req, body):
def create(self, req, body):
tenant_id = req.environ["keystone.token_auth"].user.project_id
parser = req.get_parser()(req.headers, req.body)
scheme = {
"category": compute.ComputeResource.kind,
"mixins": [
templates.OpenStackOSTemplate,
templates.OpenStackResourceTemplate,
],
"optional_mixins": [
contextualization.user_data,
contextualization.public_key,
]
}
obj = parser.parse()
validator = occi_validator.Validator(obj)
validator.validate(scheme)

name = obj.get("occi.core.title", "OCCI VM")
image = obj["schemes"][templates.OpenStackOSTemplate.scheme][0]
flavor = obj["schemes"][templates.OpenStackResourceTemplate.scheme][0]
Expand Down
80 changes: 80 additions & 0 deletions ooi/occi/validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# -*- 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 copy

from ooi import exception
from ooi.occi import helpers


class Validator(object):
def __init__(self, obj):
self.parsed_obj = obj

def _validate_category(self, category):
try:
if category.type_id != self.parsed_obj["category"]:
raise exception.OCCISchemaMismatch(
expected=category.type_id,
found=self.parsed_obj["category"]
)
except KeyError:
raise exception.OCCIMissingType(
type_id=category.type_id)

def _compare_schemes(self, expected_type, actual):
actual_scheme, actual_term = helpers.decompose_type(actual)
if expected_type.scheme != actual_scheme:
return False
try:
if expected_type.term != actual_term:
return False
except AttributeError:
# ignore the fact the type does not have a term
pass
return True

def _validate_mandatory_mixins(self, mixins, unmatched):
for m in mixins:
for um in unmatched:
if self._compare_schemes(m, um):
unmatched[um] -= 1
break
else:
raise exception.OCCIMissingType(type_id=m.scheme)
return unmatched

def _validate_optional_mixins(self, mixins, unmatched):
for m in mixins:
for um in unmatched:
if self._compare_schemes(m, um):
unmatched[um] -= 1
break
return unmatched

def validate(self, schema):
if "category" in schema:
self._validate_category(schema["category"])
unmatched = copy.copy(self.parsed_obj["mixins"])
unmatched = self._validate_mandatory_mixins(
schema.get("mixins", []), unmatched)
unmatched = self._validate_optional_mixins(
schema.get("optional_mixins", []), unmatched)
unexpected = [m for m in unmatched if unmatched[m]]
if unexpected:
raise exception.OCCISchemaMismatch(expected="",
found=unexpected)
return True
8 changes: 4 additions & 4 deletions ooi/tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_kind(self):
'class="kind"')
parser = parsers.TextParser({}, body)
res = parser.parse()
self.assertEqual("http://example.com/scheme#foo", res["kind"])
self.assertEqual("http://example.com/scheme#foo", res["category"])
self.assertItemsEqual(["foo"],
res["schemes"]["http://example.com/scheme#"])
self.assertEqual({}, res["mixins"])
Expand Down Expand Up @@ -91,7 +91,7 @@ def test_kind(self):
}
parser = parsers.HeaderParser(headers, None)
res = parser.parse()
self.assertEqual("http://example.com/scheme#foo", res["kind"])
self.assertEqual("http://example.com/scheme#foo", res["category"])
self.assertItemsEqual(["foo"],
res["schemes"]["http://example.com/scheme#"])
self.assertEqual({}, res["mixins"])
Expand Down Expand Up @@ -142,9 +142,9 @@ def test_attributes(self):
'Category': ('foo; '
'scheme="http://example.com/scheme#"; '
'class="kind"'),
'X-OCCI-Attribute': ('foo="bar", baz=1234'),
'X-OCCI-Attribute': 'foo="bar", baz=1234, bazonk="foo=123"',
}
parser = parsers.HeaderParser(headers, None)
res = parser.parse()
expected_attrs = {"foo": "bar", "baz": "1234"}
expected_attrs = {"foo": "bar", "baz": "1234", "bazonk": "foo=123"}
self.assertEqual(expected_attrs, res["attributes"])
8 changes: 1 addition & 7 deletions ooi/wsgi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,9 @@ def get_best_match_content_type(self, default_match=None):
return content_type

def get_parser(self):
mtype = parsers.get_media_map().get(self.get_content_type,
"header")
mtype = parsers.get_media_map().get(self.get_content_type(), "header")
return parsers.get_default_parsers()[mtype]

def validate(self, schema):
parser = self.get_parser()(self.headers, self.body)
parser.validate(schema)
return parser.parsed_obj


class OCCIMiddleware(object):

Expand Down
66 changes: 8 additions & 58 deletions ooi/wsgi/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@
# under the License.

import collections
import copy
import shlex

from ooi import exception
from ooi.occi import helpers


_MEDIA_TYPE_MAP = collections.OrderedDict([
Expand All @@ -36,60 +34,6 @@ def __init__(self, headers, body):
def parse(self):
raise NotImplemented

def _validate_kind(self, kind):
try:
if kind.type_id != self.parsed_obj["kind"]:
raise exception.OCCISchemaMismatch(
expected=kind.type_id, found=self.parsed_obj["kind"])
except KeyError:
raise exception.OCCIMissingType(
type_id=kind.type_id)

def _compare_schemes(self, expected_type, actual):
actual_scheme, actual_term = helpers.decompose_type(actual)
if expected_type.scheme != actual_scheme:
return False
try:
if expected_type.term != actual_term:
return False
except AttributeError:
# ignore the fact the type does not have a term
pass
return True

def _validate_mandatory_mixins(self, mixins, unmatched):
for m in mixins:
for um in unmatched:
if self._compare_schemes(m, um):
unmatched[um] -= 1
break
else:
raise exception.OCCIMissingType(type_id=m.scheme)
return unmatched

def _validate_optional_mixins(self, mixins, unmatched):
for m in mixins:
for um in unmatched:
if self._compare_schemes(m, um):
unmatched[um] -= 1
break
return unmatched

def validate(self, schema):
self.parsed_obj = self.parse()
if "kind" in schema:
self._validate_kind(schema["kind"])
unmatched = copy.copy(self.parsed_obj["mixins"])
unmatched = self._validate_mandatory_mixins(
schema.get("mixins", []), unmatched)
unmatched = self._validate_optional_mixins(
schema.get("optional_mixins", []), unmatched)
unexpected = [m for m in unmatched if unmatched[m]]
if unexpected:
raise exception.OCCISchemaMismatch(expected="",
found=unexpected)
return True


def _lexize(s, separator, ignore_whitespace=False):
lex = shlex.shlex(instream=s, posix=True)
Expand All @@ -108,7 +52,7 @@ def _lexise_header(s):

class TextParser(BaseParser):
def parse_categories(self, headers):
kind = None
kind = action = None
mixins = collections.Counter()
schemes = collections.defaultdict(list)
try:
Expand All @@ -125,11 +69,17 @@ def parse_categories(self, headers):
if kind is not None:
raise exception.OCCIInvalidSchema("Duplicated Kind")
kind = ctg_type
elif ctg_class == "action":
if action is not None:
raise exception.OCCIInvalidSchema("Duplicated action")
action = ctg_type
elif ctg_class == "mixin":
mixins[ctg_type] += 1
schemes[d["scheme"]].append(d["term"])
if action and kind:
raise exception.OCCIInvalidSchema("Action and kind together?")
return {
"kind": kind,
"category": kind or action,
"mixins": mixins,
"schemes": schemes,
}
Expand Down

0 comments on commit ea99f1b

Please sign in to comment.