[trigger] Refactorisation (voir détails) - On passe aux tests
* Pour une plus grande modularité, event a été refactorisé, ce qui a impliqué de réécrire le fonctionnement des services. * Maintenant, y a plus qu'à tester.
This commit is contained in:
parent
3d98882755
commit
d29343392b
7 changed files with 283 additions and 130 deletions
|
@ -7,16 +7,33 @@
|
|||
# License : GPLv3
|
||||
# Date : 18/05/2014
|
||||
|
||||
"""
|
||||
This service (event) is designed to receive any modification done on LDAP
|
||||
database, and to make a correct diff between former and later object in order
|
||||
to guess which services has to be updated.
|
||||
"""
|
||||
|
||||
import cmb
|
||||
import cPickle
|
||||
import gestion.config.trigger as trigger_config
|
||||
from gestion.trigger.host import record
|
||||
import cranslib.clogger as clogger
|
||||
import pika
|
||||
import importlib
|
||||
import itertools
|
||||
|
||||
# Trigger features
|
||||
import gestion.config.trigger as trigger_config
|
||||
from gestion.trigger.host import record, TriggerFactory
|
||||
from gestion.trigger.services.service import BasicService
|
||||
|
||||
# Clogger
|
||||
import cranslib.clogger as clogger
|
||||
|
||||
# lc_ldap
|
||||
import lc_ldap.attributs
|
||||
|
||||
logger = clogger.CLogger("trigger.event", "info")
|
||||
|
||||
services = [importlib.import_module("gestion.trigger.services.%s" % (config_service,)) for config_service in trigger_config.all_services]
|
||||
|
||||
class Event(cmb.BasicProducer):
|
||||
"""
|
||||
Event tracker
|
||||
|
@ -49,13 +66,16 @@ class Event(cmb.BasicProducer):
|
|||
raise
|
||||
|
||||
def announce(self, body):
|
||||
"""Feature to send message without giving routing_key
|
||||
|
||||
"""
|
||||
self.send_message("trigger.event", body)
|
||||
|
||||
def diff_o_matic(body=()):
|
||||
"""Fait un diff exhaustif des deux dicos"""
|
||||
|
||||
if not body:
|
||||
raise("diff_o_matic received %r as an argument, which is unusable." % (body,))
|
||||
raise ValueError("diff_o_matic received %r as an argument, which is unusable." % (body,))
|
||||
|
||||
before = dict(body[1]) or {}
|
||||
after = dict(body[2]) or {}
|
||||
|
@ -95,7 +115,6 @@ def compare_lists(list1, list2):
|
|||
"""
|
||||
moins, plus = [], []
|
||||
|
||||
llist2 = [a.lower() for a in list2]
|
||||
for elem in [] + list1:
|
||||
try:
|
||||
ind = list2.index(elem.lower())
|
||||
|
@ -109,67 +128,63 @@ def compare_lists(list1, list2):
|
|||
return moins, plus
|
||||
|
||||
@record
|
||||
def event(body=()):
|
||||
"""Trigger event qui transcrit toute modif ldap en truc exploitable par
|
||||
trigger. Warning, bootstrap incoming.
|
||||
|
||||
body est exceptionnellement un tuple. Pour être précis, un 3-tuple.
|
||||
Le premier élément est le dn de l'objet LDAP, il est pas indispensable.
|
||||
Le deuxième est un dico qui recense l'état complet de l'objet modifié avant
|
||||
validation des modifications.
|
||||
Le troisième est un dico qui recense l'état complet de l'objet modifié après
|
||||
modification.
|
||||
|
||||
Si l'objet vient d'être créé, le deuxième élément est None.
|
||||
Si l'objet vient d'être supprimé, le troisième élément vaut None.
|
||||
|
||||
Il faut donc faire un diff, générer la liste des triggers à envoyer, puis
|
||||
les envoyer.
|
||||
class event(BasicService):
|
||||
"""Event service class. It extends BasicService, but should not implement
|
||||
any change trigger, since it's this service which is designed to call
|
||||
change triggers of other services.
|
||||
|
||||
"""
|
||||
|
||||
logger.info("Received message %r…", body)
|
||||
@classmethod
|
||||
def get_changes(cls, body, diff):
|
||||
"""Compute changes from diff"""
|
||||
|
||||
diff = diff_o_matic(body)
|
||||
return [None]
|
||||
|
||||
# À cette étape, on a un dico des attrs ayant subi une modif
|
||||
# a["macAddress"] par exemple, pourrait ressembler à
|
||||
# (["aa:bb:cc:dd:ee:fg"], ["aa:bb:cc:dd:ee:ff"]), la liste de gauche
|
||||
# étant les trucs perdus, celle de droite ceux gagnés. Suivant le type
|
||||
# des attributs, ça peut être un remplacement (mac, ip...), ou juste
|
||||
# des retraits/ajouts (mailAlias...)
|
||||
# Avec ça on peut trigger tout ce qu'on veut.
|
||||
@classmethod
|
||||
def regen(cls, body=()):
|
||||
"""When any event arrives on trigger-civet-event, this method is called
|
||||
and designed to transcript the body (ldap data) in something usable for
|
||||
the services. Afterwards, it sends these transcripts on the good way
|
||||
using routing_key.
|
||||
|
||||
# Si la mac ou l'IP a changé…
|
||||
if diff.has_key(lc_ldap.attributs.ipHostNumber.ldap_name) or diff.has_key(lc_ldap.attributs.macAddress.ldap_name):
|
||||
logger.info("Detected MAC or IP update, calling trigger_mac_ip…")
|
||||
trigger_mac_ip(body, diff)
|
||||
body is a 3-tuple, containing LDAP dn, the former state of the object
|
||||
(a simple dict), and the later state. The data are non-binding-dependant.
|
||||
|
||||
def trigger_mac_ip(body, diff):
|
||||
macs = tuple([body[i].get(lc_ldap.attributs.macAddress.ldap_name, [''])[0] for i in xrange(1, 3)])
|
||||
ips = tuple([body[i].get(lc_ldap.attributs.ipHostNumber.ldap_name, [''])[0] for i in xrange(1, 3)])
|
||||
hostnames = tuple([body[i].get(lc_ldap.attributs.host.ldap_name, [''])[0] for i in xrange(1, 3)])
|
||||
A new object has body[1] to None, a deleted one has body[2] to None.
|
||||
|
||||
# Régénération du DHCP :
|
||||
if not macs[0]:
|
||||
# Création d'une nouvelle machine.
|
||||
dhcp = {'add': [(macs[1], ips[1], hostnames[1])]}
|
||||
fw = {'add': [(macs[1], ips[1])]}
|
||||
elif not macs[1]:
|
||||
# Destruction d'une machine.
|
||||
dhcp = {'delete': [(macs[0], ips[0])]}
|
||||
fw = {'delete': [(macs[0], ips[0])]}
|
||||
else:
|
||||
# Mise à jour.
|
||||
dhcp = {'update': [(macs[0], ips[0], macs[1], ips[1], hostnames[1])]}
|
||||
fw = {'update': [(macs[0], ips[0], macs[1], ips[1])]}
|
||||
logger.info("Sending DHCP trigger with body %r", dhcp)
|
||||
# XXX - Remove # when putting in production, needs further tests
|
||||
#trigger_send("dhcp", dhcp)
|
||||
logger.info("Sending firewall trigger for mac_ip with body %r", fw)
|
||||
# XXX - Remove # when in prod, tested on 15/06/2014, functionnal.
|
||||
trigger_send("firewall", ("mac_ip", fw))
|
||||
logger.info("trigger_mac_ip done.")
|
||||
"""
|
||||
|
||||
logger.info("Received message %r…", body)
|
||||
|
||||
diff = diff_o_matic(body)
|
||||
|
||||
# Now, diff is a dict containing attributes which has been modified.
|
||||
# diff['macAddress'] could look like (['aa:bb:cc:dd:ee:fg'], ['aa:bb:cc:dd:ee:ff']),
|
||||
# where the list on the left is the former value of attributes, and the list on the
|
||||
# right the latter values.
|
||||
|
||||
# -*- Explain -*-
|
||||
#In [11]: import itertools
|
||||
#
|
||||
#In [12]: a = [[(3, 'lol'), ('7', 3)], [(5, 6), None], [None], [('lol', 'lal')]]
|
||||
#
|
||||
#In [13]: a
|
||||
#Out[13]: [[(3, 'lol'), ('7', 3)], [(5, 6), None], [None], [('lol', 'lal')]]
|
||||
#
|
||||
#In [14]: list(set([message for message in itertools.chain(*a)]))
|
||||
#Out[14]: [('7', 3), (5, 6), None, ('lol', 'lal'), (3, 'lol')] # Only one None from a, since [None, x, y, None] is equivalent for itertools to [x, y]
|
||||
#
|
||||
#In [15]: b = list(set([message for message in itertools.chain(*a) if message is not None]))
|
||||
#
|
||||
#In [16]: b
|
||||
#Out[16]: [('7', 3), (5, 6), ('lol', 'lal'), (3, 'lol')]
|
||||
msg_to_send = [message for message in itertools.chain(*[service.get_changes(body, diff) for service in TriggerFactory.get_services()]) if message is not None]
|
||||
|
||||
for msg in msg_to_send:
|
||||
logger.info("Sending %r on the road \\o/", msg)
|
||||
# XXX - uncomment this when in production
|
||||
# trigger_send(*msg)
|
||||
|
||||
def trigger_send(routing_key, body):
|
||||
sender = Event("civet")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue