murano/murano/policy/congress_rules.py

258 lines
8.7 KiB
Python

# Copyright (c) 2014 OpenStack Foundation.
# All Rights Reserved.
#
# 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.
class CongressRulesManager(object):
"""Converts murano model to list of congress rules:
- murano:objects+(env_id, obj_id, type_name)
- murano:properties+(obj_id, prop_name, prop_value)
- murano:relationships+(source, target, name)
- murano:parent_types+(obj_id, parent_name)
- murano:states+(env_id, state)
"""
_rules = []
_env_id = ''
_class_loader = None
def convert(self, model, class_loader=None, tenant_id=None):
self._rules = []
self._class_loader = class_loader
if model is None:
return self._rules
self._env_id = model['?']['id']
# Environment owner is tenant.
self._owner_id = tenant_id
# Arbitrary property for tenant_id.
if tenant_id is not None:
r = PropertyRule(self._env_id, 'tenant_id', tenant_id)
self._rules.append(r)
state_rule = StateRule(self._env_id, 'pending')
self._rules.append(state_rule)
self._walk(model, self._process_item)
# Convert MuranoProperty containing reference to another object
# to MuranoRelationship.
object_ids = [rule.obj_id for rule in self._rules
if isinstance(rule, ObjectRule)]
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:
return
obj = self._to_dict(obj)
func(obj)
if isinstance(obj, list):
for v in obj:
self._walk(v, func)
elif isinstance(obj, dict):
for key, value in obj.iteritems():
self._walk(value, func)
def _process_item(self, obj):
if isinstance(obj, dict) and '?' in obj:
obj2 = self._create_object_rule(obj)
# Owner of components in environment is environment itself.
self._owner_id = self._env_id
self._rules.append(obj2)
self._rules.extend(self._create_propety_rules(obj2.obj_id, obj))
cls = obj['?']['type']
types = self._get_parent_types(cls, self._class_loader)
self._rules.extend(self._create_parent_type_rules(obj['?']['id'],
types))
@staticmethod
def _to_dict(obj):
# If we have MuranoObject class we need to convert to dictionary.
if 'to_dictionary' in dir(obj):
return obj.to_dictionary()
else:
return obj
def _create_object_rule(self, app):
return ObjectRule(app['?']['id'], self._owner_id, app['?']['type'])
def _create_propety_rules(self, obj_id, obj, prefix=""):
rules = []
# Skip when inside properties of other object.
if '?' in obj and prefix != "":
rules.append(RelationshipRule(obj_id, obj['?']['id'],
prefix.split('.')[0]))
return rules
for key, value in obj.iteritems():
if key == '?':
continue
if value is not None:
value = self._to_dict(value)
if isinstance(value, dict):
rules.extend(self._create_propety_rules(
obj_id, value, prefix + key + "."))
elif isinstance(value, list):
for v in value:
v = self._to_dict(v)
if not isinstance(v, dict):
rule = PropertyRule(obj_id, prefix + key, v)
rules.append(rule)
else:
rule = PropertyRule(obj_id, prefix + key, value)
rules.append(rule)
return rules
@staticmethod
def _is_relationship(rule, app_ids):
if not isinstance(rule, PropertyRule):
return False
return rule.prop_value in app_ids
def _create_relationship(self, rule, app_ids):
if self._is_relationship(rule, app_ids):
return RelationshipRule(rule.obj_id, rule.prop_value,
rule.prop_name)
else:
return rule
def _get_parent_types(self, type_name, class_loader):
types = set()
types.add(type_name)
if class_loader is not None:
cls = class_loader.get_class(type_name)
if cls is not None:
for parent in cls.parents:
types.add(parent.name)
types = types.union(
self._get_parent_types(parent.name, class_loader))
return types
@staticmethod
def _create_parent_type_rules(app_id, types):
rules = []
for type_name in types:
rules.append(ParentTypeRule(app_id, type_name))
return rules
class ObjectRule(object):
def __init__(self, obj_id, owner_id, type_name):
self.obj_id = obj_id
self.owner_id = owner_id
self.type_name = type_name
def __str__(self):
return 'murano:objects+("{0}", "{1}", "{2}")'.format(self.obj_id,
self.owner_id,
self.type_name)
class PropertyRule(object):
def __init__(self, obj_id, prop_name, prop_value):
self.obj_id = obj_id
self.prop_name = prop_name
self.prop_value = prop_value
def __str__(self):
return 'murano:properties+("{0}", "{1}", "{2}")'.format(
self.obj_id, self.prop_name, self.prop_value)
class RelationshipRule(object):
def __init__(self, source_id, target_id, rel_name):
self.source_id = source_id
self.target_id = target_id
self.rel_name = rel_name
def __str__(self):
return 'murano:relationships+("{0}", "{1}", "{2}")'.format(
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
self.type_name = type_name
def __str__(self):
return 'murano:parent_types+("{0}", "{1}")'.format(self.obj_id,
self.type_name)
class StateRule(object):
def __init__(self, obj_id, state):
self.obj_id = obj_id
self.state = state
def __str__(self):
return 'murano:states+("{0}", "{1}")'.format(self.obj_id, self.state)