Computes transitive relationships in murano model

Since rule recursion is not supported by Congress now,
we have to deal with relationships traversal in different way.
There will be murano:connected(source_id, target_id) table
in congress murano policy which contains directly and
indirectly connected objects in environment.

Closes-Bug: #1434530

Change-Id: If2bab9d91a8dea5b08b231ce95f15af3ccd7f85f
This commit is contained in:
Filip Blaha 2015-03-20 13:36:22 +01:00
parent 62c1f10e7b
commit 2a80451151
2 changed files with 60 additions and 0 deletions

View File

@ -56,8 +56,41 @@ class CongressRulesManager(object):
self._rules = [self._create_relationship(rule, object_ids)
for rule in self._rules]
relations = [(rel.source_id, rel.target_id)
for rel in self._rules
if isinstance(rel, RelationshipRule)]
closure = self.transitive_closure(relations)
for rel in closure:
self._rules.append(ConnectedRule(rel[0], rel[1]))
return self._rules
@staticmethod
def transitive_closure(relations):
"""Computes transitive closure on a directed graph.
In other words computes reachability within the graph.
E.g. {(1, 2), (2, 3)} -> {(1, 2), (2, 3), (1, 3)}
(1, 3) was added because there is path from 1 to 3 in the graph.
:param relations: list of relations/edges in form of tuples
:return: transitive closure including original relations
"""
closure = set(relations)
while True:
# Attempts to discover new transitive relations
# by joining 2 subsequent relations/edges within the graph.
new_relations = {(x, w) for x, y in closure
for q, w in closure if q == y}
# Creates union with already discovered relations.
closure_until_now = closure | new_relations
# If no new relations were discovered in last cycle
# the computation is finished.
if closure_until_now == closure:
return closure
closure = closure_until_now
def _walk(self, obj, func):
if obj is None:
@ -195,6 +228,16 @@ class RelationshipRule(object):
self.source_id, self.target_id, self.rel_name)
class ConnectedRule(object):
def __init__(self, source_id, target_id):
self.source_id = source_id
self.target_id = target_id
def __str__(self):
return 'murano:connected+("{0}", "{1}")'.format(
self.source_id, self.target_id)
class ParentTypeRule(object):
def __init__(self, obj_id, type_name):
self.obj_id = obj_id

View File

@ -71,6 +71,12 @@ class TestCongressRules(unittest.TestCase):
return rules_str
def test_transitive_closure(self):
closure = congress.CongressRulesManager.transitive_closure(
[(1, 2), (2, 3), (3, 4)])
self.assertTrue((1, 4) in closure)
self.assertTrue((2, 4) in closure)
def test_empty_model(self):
congress_rules = congress.CongressRulesManager()
rules = congress_rules.convert(None)
@ -101,6 +107,17 @@ class TestCongressRules(unittest.TestCase):
'murano:relationships+("0aafd67e-72e9-4ae0-bb62-fe724f77df2a", '
'"ed8df2b0-ddd2-4009-b3c9-2e7a368f3cb8", "instance")' in rules_str)
def test_convert_model_transitive_relationships(self):
rules_str = self._create_rules_str('model_with_relations.yaml')
self.assertTrue(
'murano:connected+("50fa68ff-cd9a-4845-b573-2c80879d158d", '
'"8ce94f23-f16a-40a1-9d9d-a877266c315d")' in rules_str)
self.assertTrue(
'murano:connected+("8ce94f23-f16a-40a1-9d9d-a877266c315d", '
'"fc6b8c41-166f-4fc9-a640-d82009e0a03d")' in rules_str)
def test_convert_model_complex(self):
self._create_and_check_rules_str('model_complex')