From 6451f626284580458dbf5968e9490fc7c88b2349 Mon Sep 17 00:00:00 2001 From: Dmitry Shulyak Date: Thu, 30 Jul 2015 18:04:36 +0300 Subject: [PATCH] Add several tokens to control events between resources --- docs/events.md | 26 +++++----- solar/solar/events/__init__.py | 0 solar/solar/events/control.py | 75 +++++++++++++++++++++++++++ solar/solar/interfaces/db/redis_db.py | 2 +- solar/solar/test/test_events.py | 22 +++++--- 5 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 solar/solar/events/__init__.py create mode 100644 solar/solar/events/control.py diff --git a/docs/events.md b/docs/events.md index 7eec7e20..e93a35fb 100644 --- a/docs/events.md +++ b/docs/events.md @@ -24,11 +24,11 @@ Propagating events when there is no data changed Control for specifying events: - on . . +on . . -# on mariadb.run changed keystone.run -# on keystone.run changed keystone_config.run -# on keystone_config.run changed haproxy.reload +on mariadb.run changed keystone.run +on keystone.run changed keystone_config.run +on keystone_config.run changed haproxy.reload +---------------------+ | mariadb.run | @@ -46,13 +46,13 @@ Control for specifying events: | haproxy.reload | +---------------------+ - - : - +. - - . -When connection between several resources created - events connections should +When data connection between several resources created - events connections should be created as well, resource a connect resource b: -on a changed b reload -on a removed b run +on a.run changed b.reload +on a.remove changed b.run ------------------------------------------------- Resolving cycles on a data plane @@ -73,9 +73,9 @@ rmq.2.cluster_join, rmq.2.cluster_join Also cluster operation should happen only when rmq.cluster is changed. -on rmq.cluster changed rmq.1 cluster_create -on rmq.1.cluster_create changed rmq.2 cluster_join -on rmq.1.cluster_create changed rmq.3 cluster_join +on rmq.cluster changed rmq.1.cluster_create +on rmq.1.cluster_create changed rmq.2.cluster_join +on rmq.1.cluster_create changed rmq.3.cluster_join +----------------------+ | rmq.1.run | @@ -102,7 +102,7 @@ on rmq.1.cluster_create changed rmq.3 cluster_join | rmq.3.run | | +----------------------+ | | | - | cahnged | + | changed | v | +----------------------+ | | rmq.3.cluster_join | <+ @@ -113,7 +113,7 @@ on rmq.1.cluster_create changed rmq.3 cluster_join --------------------------------------------------- Resolve cycles on a execution level -Lets say we have 5 objects, which forms 2 pathes +We have 5 objects, which forms 2 pathes - keystone-config -> keystone-service -> haproxy-sevice - glance-config -> glance-service -> haproxy-service diff --git a/solar/solar/events/__init__.py b/solar/solar/events/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/solar/solar/events/control.py b/solar/solar/events/control.py new file mode 100644 index 00000000..35853ef4 --- /dev/null +++ b/solar/solar/events/control.py @@ -0,0 +1,75 @@ +""" +Available controls: + +*depends_on* implements relationship that will guarantee that depended action +on resource will be executed after parent, if parent will be executed. It means +that this control contributes only to ordering, and wont trigger any action +if dependent resource isnt changed. + + depends_on: + - parent:run -> ok -> dependent:run + +*react_on* - relationship which will guarantee that action on dependent resource +will be executed if parent action is going to be executed. This control will +trigger action even if no changes noticed on dependent resource. + + react_on: + - parent:update -> ok -> dependent:update +""" + +import re + +from solar.core.log import log + + +EVENT = re.compile(r'\s+->\s+') + + +class Event(object): + + def __init__(self, event): + self.parent, self.dependent, self.state = EVENT.split(event) + self.parent_node, self.parent_action = self.parent.split(':') + self.dep_node, self.dep_action = self.dependent.split(':') + + def __repr__(self): + return '{}({} -> {} -> {})'.format( + self.__class__.__name__, + self.parent, self.dependent, self.state) + + +class Dependency(Event): + + def add(self, changed_resources, changes_graph): + if self.parent in changes_graph: + changes_graph.add_edge( + self.parent, self.dependent, state=self.state) + + +class React(Event): + + def add(self, changed_resources, changes_graph): + changes_graph.add_edge(self.parent, self.dependent, state=self.state) + changed_resources.append(self.dep_node) + + +def build_edges(changed_resources, changes_graph, events): + """ + :param changed_resources: list of resource names that were changed + :param changes_graph: nx.DiGraph object with actions to be executed + :param events: + """ + stack = changed_resources[:] + while stack: + node = stack.pop() + events_objects = [] + + if node in events: + log.debug('Events %s for resource %s', events[node], node) + + for e in events[node].get('react_on', ()): + React(e).add(stack, changes_graph) + for e in events[node].get('depends_on', ()): + Dependency(e).add(stack, changes_graph) + + return changes_graph \ No newline at end of file diff --git a/solar/solar/interfaces/db/redis_db.py b/solar/solar/interfaces/db/redis_db.py index eaea121c..0d0dc803 100644 --- a/solar/solar/interfaces/db/redis_db.py +++ b/solar/solar/interfaces/db/redis_db.py @@ -10,7 +10,7 @@ from solar import errors class RedisDB(object): COLLECTIONS = Enum( 'Collections', - 'connection resource state_data state_log' + 'connection resource state_data state_log events' ) DB = { 'host': 'localhost', diff --git a/solar/solar/test/test_events.py b/solar/solar/test/test_events.py index 04a225df..7d6ebb4d 100644 --- a/solar/solar/test/test_events.py +++ b/solar/solar/test/test_events.py @@ -3,6 +3,7 @@ import networkx as nx from pytest import fixture + @fixture def simple(): dg = nx.DiGraph() @@ -10,24 +11,29 @@ def simple(): dg.add_edge('keystone_config.run', 'haproxy.reload', event='changed') return dg + def test_simple(simple): - nx.write_dot(simple, 'simple.dot') + pass + @fixture def rmq(): """Example of a case when we have cycles on a data plane.""" dg = nx.DiGraph() - dg.add_edge('rmq.1.run', 'rmq.1.cluster_create', event='changed') - dg.add_edge('rmq.1.cluster_create', 'rmq.2.cluster_join', event='changed') - dg.add_edge('rmq.1.cluster_create', 'rmq.3.cluster_join', event='changed') - dg.add_edge('rmq.2.run', 'rmq.2.cluster_join', event='changed') - dg.add_edge('rmq.3.run', 'rmq.3.cluster_join', event='changed') + dg.add_edge('rmq_cluster.run', 'rmq_cluster.1.create', event='changed') + dg.add_edge('rmq_cluster.run', 'rmq_cluster.2.join', event='changed') + dg.add_edge('rmq_cluster.run', 'rmq_cluster.3.join', event='changed') + dg.add_edge('rmq.1.run', 'rmq_cluster.1.create', event='changed') + dg.add_edge('rmq.2.run', 'rmq_cluster.2.join', event='changed') + dg.add_edge('rmq.3.run', 'rmq_cluster.3.join', event='changed') + dg.add_edge('rmq_cluster.1.create', 'rmq_cluster.2.join', event='changed') + dg.add_edge('rmq_cluster.1.create', 'rmq_cluster.3.join', event='changed') return dg def test_rmq(rmq): - nx.write_dot(rmq, 'rmq.dot') + pass @fixture @@ -43,4 +49,4 @@ def haproxy(): def test_haproxy(haproxy): - nx.write_dot(haproxy, 'haproxy.dot') + pass