Update link handling in parser
Change the link structure returned by the parser to better follow the OCCI 1.2 standard. The parser returns a dictionary where keys are the link target kind type and values are lists with dictionaries for each of the links of the links defined for that kind type. Those dictionaries contain the target location (occi.core.target), the link id if defined, and a dictionary with any extra attributes defined. Change-Id: I24205044a6e2b670ceb98f3a0e9224637bf9fcf4
This commit is contained in:
parent
a74fdd5dd5
commit
db9dae4a26
|
@ -97,22 +97,23 @@ class Controller(ooi.api.base.Controller):
|
|||
|
||||
def _build_block_mapping(self, req, obj):
|
||||
mappings = []
|
||||
for l in obj.get("links", {}).values():
|
||||
if l["rel"] == storage.StorageResource.kind.type_id:
|
||||
_, vol_id = ooi.api.helpers.get_id_with_kind(
|
||||
req,
|
||||
l.get("occi.core.target"),
|
||||
storage.StorageResource.kind)
|
||||
mapping = {
|
||||
"source_type": "volume",
|
||||
"uuid": vol_id,
|
||||
"delete_on_termination": False,
|
||||
}
|
||||
try:
|
||||
mapping['device_name'] = l['occi.storagelink.deviceid']
|
||||
except KeyError:
|
||||
pass
|
||||
mappings.append(mapping)
|
||||
links = obj.get("links", {})
|
||||
for l in links.get(storage.StorageResource.kind.type_id, []):
|
||||
_, vol_id = ooi.api.helpers.get_id_with_kind(
|
||||
req,
|
||||
l.get("target"),
|
||||
storage.StorageResource.kind)
|
||||
mapping = {
|
||||
"source_type": "volume",
|
||||
"uuid": vol_id,
|
||||
"delete_on_termination": False,
|
||||
}
|
||||
try:
|
||||
device_name = l['attributes']['occi.storagelink.deviceid']
|
||||
mapping['device_name'] = device_name
|
||||
except KeyError:
|
||||
pass
|
||||
mappings.append(mapping)
|
||||
# this needs to be there if we have a mapping
|
||||
if mappings:
|
||||
image = obj["schemes"][templates.OpenStackOSTemplate.scheme][0]
|
||||
|
@ -127,14 +128,14 @@ class Controller(ooi.api.base.Controller):
|
|||
|
||||
def _get_network_from_req(self, req, obj):
|
||||
networks = []
|
||||
for l in obj.get("links", {}).values():
|
||||
if l["rel"] == network.NetworkResource.kind.type_id:
|
||||
_, net_id = ooi.api.helpers.get_id_with_kind(
|
||||
req,
|
||||
l.get("occi.core.target"),
|
||||
network.NetworkResource.kind)
|
||||
net = {'uuid': net_id}
|
||||
networks.append(net)
|
||||
links = obj.get("links", {})
|
||||
for l in links.get(network.NetworkResource.kind.type_id, []):
|
||||
_, net_id = ooi.api.helpers.get_id_with_kind(
|
||||
req,
|
||||
l.get("target"),
|
||||
network.NetworkResource.kind)
|
||||
net = {'uuid': net_id}
|
||||
networks.append(net)
|
||||
return networks
|
||||
|
||||
def create(self, req, body):
|
||||
|
|
|
@ -68,18 +68,14 @@ class Validator(object):
|
|||
return unmatched
|
||||
|
||||
def _validate_optional_links(self, expected, links):
|
||||
for uri, l in links.items():
|
||||
try:
|
||||
rel = l['rel']
|
||||
except KeyError:
|
||||
raise exception.OCCIMissingType(type_id=uri)
|
||||
for ex in expected:
|
||||
if rel == ex.type_id:
|
||||
break
|
||||
expected_types = [e.type_id for e in expected]
|
||||
for l in links.keys():
|
||||
if l in expected_types:
|
||||
break
|
||||
else:
|
||||
expected_types = ', '.join([e.type_id for e in expected])
|
||||
raise exception.OCCISchemaMismatch(expected=expected_types,
|
||||
found=l['rel'])
|
||||
raise exception.OCCISchemaMismatch(
|
||||
expected=', '.join(expected_types),
|
||||
found=l)
|
||||
|
||||
def validate_attributes(self, required):
|
||||
"""Validate required attributes
|
||||
|
|
|
@ -389,7 +389,7 @@ class TestComputeController(base.TestController):
|
|||
self.assertEqual([], ret)
|
||||
|
||||
def test_build_block_mapping_invalid_rel(self):
|
||||
obj = {'links': {'foo': {'rel': 'bar'}}}
|
||||
obj = {"links": {"foo": [{"target": "bar"}]}}
|
||||
ret = self.controller._build_block_mapping(None, obj)
|
||||
self.assertEqual([], ret)
|
||||
|
||||
|
@ -398,12 +398,13 @@ class TestComputeController(base.TestController):
|
|||
vol_id = uuid.uuid4().hex
|
||||
image_id = uuid.uuid4().hex
|
||||
obj = {
|
||||
'links': {
|
||||
'l1': {
|
||||
'rel': ('http://schemas.ogf.org/occi/infrastructure#'
|
||||
'storage'),
|
||||
'occi.core.target': vol_id,
|
||||
}
|
||||
"links": {
|
||||
"http://schemas.ogf.org/occi/infrastructure#storage": [
|
||||
{
|
||||
"id": "l1",
|
||||
"target": vol_id,
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemes": {
|
||||
templates.OpenStackOSTemplate.scheme: [image_id],
|
||||
|
@ -434,13 +435,16 @@ class TestComputeController(base.TestController):
|
|||
vol_id = uuid.uuid4().hex
|
||||
image_id = uuid.uuid4().hex
|
||||
obj = {
|
||||
'links': {
|
||||
'l1': {
|
||||
'rel': ('http://schemas.ogf.org/occi/infrastructure#'
|
||||
'storage'),
|
||||
'occi.core.target': vol_id,
|
||||
'occi.storagelink.deviceid': 'baz'
|
||||
}
|
||||
"links": {
|
||||
"http://schemas.ogf.org/occi/infrastructure#storage": [
|
||||
{
|
||||
"id": "l1",
|
||||
"target": vol_id,
|
||||
"attributes": {
|
||||
"occi.storagelink.deviceid": "baz"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemes": {
|
||||
templates.OpenStackOSTemplate.scheme: [image_id],
|
||||
|
@ -505,12 +509,14 @@ class TestComputeController(base.TestController):
|
|||
def test_get_network_from_req(self, m_get_id):
|
||||
net_id = uuid.uuid4().hex
|
||||
obj = {
|
||||
'links': {
|
||||
'l1': {
|
||||
'rel': '%s%s' % (occi_network.NetworkResource.kind.scheme,
|
||||
occi_network.NetworkResource.kind.term),
|
||||
'occi.core.target': net_id,
|
||||
}
|
||||
"links": {
|
||||
"%s%s" % (occi_network.NetworkResource.kind.scheme,
|
||||
occi_network.NetworkResource.kind.term): [
|
||||
{
|
||||
"id": "l1",
|
||||
"target": net_id,
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
m_get_id.return_value = (None, net_id)
|
||||
|
@ -529,17 +535,18 @@ class TestComputeController(base.TestController):
|
|||
net_id_1 = uuid.uuid4().hex
|
||||
net_id_2 = uuid.uuid4().hex
|
||||
obj = {
|
||||
'links': {
|
||||
'l1': {
|
||||
'rel': '%s%s' % (occi_network.NetworkResource.kind.scheme,
|
||||
occi_network.NetworkResource.kind.term),
|
||||
'occi.core.target': net_id_1,
|
||||
},
|
||||
'l2': {
|
||||
'rel': '%s%s' % (occi_network.NetworkResource.kind.scheme,
|
||||
occi_network.NetworkResource.kind.term),
|
||||
'occi.core.target': net_id_2,
|
||||
}
|
||||
"links": {
|
||||
"%s%s" % (occi_network.NetworkResource.kind.scheme,
|
||||
occi_network.NetworkResource.kind.term): [
|
||||
{
|
||||
"id": "l1",
|
||||
"target": net_id_1,
|
||||
},
|
||||
{
|
||||
"id": "l2",
|
||||
"target": net_id_2,
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
m_get_id.side_effect = [(None, net_id_1),
|
||||
|
|
|
@ -73,16 +73,27 @@ class BaseParserTest(object):
|
|||
self.assertEqual(expected_attrs, res["attributes"])
|
||||
|
||||
def test_link(self):
|
||||
attrs = {"foo": 1234, "bazonk": "foo=123"}
|
||||
h, b = self.get_test_link(
|
||||
{"term": "foo", "scheme": "http://example.com/scheme#"},
|
||||
{
|
||||
"id": "bar",
|
||||
"attributes": {"foo": 1234, "bazonk": "foo=123"}
|
||||
"id": "link_id",
|
||||
"target": "/bar",
|
||||
"kind": "http://example.com/scheme#link",
|
||||
"attributes": attrs,
|
||||
}
|
||||
)
|
||||
parser = self._get_parser(h, b)
|
||||
res = parser.parse()
|
||||
expected_links = {"bar": {"foo": 1234, "bazonk": "foo=123"}}
|
||||
expected_links = {
|
||||
"http://example.com/scheme#link": [
|
||||
{
|
||||
"id": "link_id",
|
||||
"target": "/bar",
|
||||
"attributes": attrs,
|
||||
}
|
||||
]
|
||||
}
|
||||
self.assertEqual(expected_links, res["links"])
|
||||
|
||||
|
||||
|
@ -124,7 +135,8 @@ class TestHeaderParser(BaseParserTest, base.TestCase):
|
|||
|
||||
def get_test_link(self, kind, link):
|
||||
h, b = self.get_test_kind(kind)
|
||||
l = ["<%(id)s>" % link]
|
||||
l = [('<%(id)s>; "rel"="%(kind)s"; '
|
||||
'occi.core.target="%(target)s"') % link]
|
||||
for n, v in link["attributes"].items():
|
||||
l.append('"%s"=%s' % (n, self._get_attribute_value(v)))
|
||||
h["Link"] = "; ".join(l)
|
||||
|
@ -207,8 +219,8 @@ class TestJsonParser(BaseParserTest, base.TestCase):
|
|||
attrs = []
|
||||
for n, v in link["attributes"].items():
|
||||
attrs.append('"%s": %s' % (n, self._get_attribute_value(v)))
|
||||
target = '"location": "%s"' % link["id"]
|
||||
l = ('"links": [{"attributes": { %s }, "target": { %s } }]'
|
||||
% (",".join(attrs), target))
|
||||
target = '"location": "%(target)s", "kind": "%(kind)s"' % link
|
||||
l = ('"links": [{"attributes": { %s }, "target": { %s }, "id": "%s" }]'
|
||||
% (",".join(attrs), target, link["id"]))
|
||||
body.append(l)
|
||||
return {}, "{ %s }" % ",".join(body)
|
||||
|
|
|
@ -188,7 +188,7 @@ class TestValidator(base.TestCase):
|
|||
def test_optional_links(self):
|
||||
mixins = collections.Counter()
|
||||
schemes = collections.defaultdict(list)
|
||||
links = {"foo": {"rel": "http://example.com/scheme#foo"}}
|
||||
links = {"http://example.com/scheme#foo": [{"location": "foo"}]}
|
||||
pobj = {
|
||||
"kind": "compute",
|
||||
"category": "foo type",
|
||||
|
@ -205,7 +205,7 @@ class TestValidator(base.TestCase):
|
|||
def test_optional_links_invalid(self):
|
||||
mixins = collections.Counter()
|
||||
schemes = collections.defaultdict(list)
|
||||
links = {"foo": {"rel": "http://example.com/scheme#foo"}}
|
||||
links = {"http://example.com/scheme#foo": [{"location": "foo"}]}
|
||||
pobj = {
|
||||
"kind": "compute",
|
||||
"category": "foo type",
|
||||
|
|
|
@ -148,7 +148,7 @@ class TextParser(BaseParser):
|
|||
return attrs
|
||||
|
||||
def parse_links(self, headers):
|
||||
links = {}
|
||||
links = collections.defaultdict(list)
|
||||
try:
|
||||
header_links = headers["Link"]
|
||||
except KeyError:
|
||||
|
@ -158,15 +158,31 @@ class TextParser(BaseParser):
|
|||
# remove the "<" and ">"
|
||||
if ll[0][1] != "<" and ll[0][-1] != ">":
|
||||
raise exception.OCCIInvalidSchema("Unable to parse link")
|
||||
link_dest = ll[0][1:-1]
|
||||
d = {}
|
||||
link_id = ll[0][1:-1]
|
||||
target_location = None
|
||||
target_kind = None
|
||||
attrs = {}
|
||||
try:
|
||||
for attr in ll[1:]:
|
||||
n, v = attr.split("=", 1)
|
||||
d[n.strip().strip('"')] = self.parse_attribute_value(v)
|
||||
n = n.strip().strip('"')
|
||||
v = self.parse_attribute_value(v)
|
||||
if n == "rel":
|
||||
target_kind = v
|
||||
continue
|
||||
elif n == "occi.core.target":
|
||||
target_location = v
|
||||
continue
|
||||
attrs[n] = v
|
||||
except ValueError:
|
||||
raise exception.OCCIInvalidSchema("Unable to parse link")
|
||||
links[link_dest] = d
|
||||
if not (target_kind and target_location):
|
||||
raise exception.OCCIInvalidSchema("Unable to parse link")
|
||||
links[target_kind].append({
|
||||
"target": target_location,
|
||||
"attributes": attrs,
|
||||
"id": link_id,
|
||||
})
|
||||
return links
|
||||
|
||||
def _convert_to_headers(self):
|
||||
|
@ -224,11 +240,16 @@ class JsonParser(BaseParser):
|
|||
return {}
|
||||
|
||||
def parse_links(self, obj):
|
||||
links = {}
|
||||
links = collections.defaultdict(list)
|
||||
for l in obj.get("links", []):
|
||||
attrs = copy.copy(l.get("attributes", {}))
|
||||
try:
|
||||
links[l["target"]["location"]] = attrs
|
||||
d = {
|
||||
"target": l["target"]["location"],
|
||||
"attributes": copy.copy(l.get("attributes", {})),
|
||||
}
|
||||
if "id" in l:
|
||||
d["id"] = l["id"]
|
||||
links[l["target"]["kind"]].append(d)
|
||||
except KeyError:
|
||||
raise exception.OCCIInvalidSchema("Unable to parse link")
|
||||
return links
|
||||
|
|
Loading…
Reference in New Issue