scripts/gestion/trigger/services/event.py
Pierre-Elliott Bécue bfd2f185e2 [trigger/event] Correction d'erreurs
* Il faut faire des copies des dicos et des listes, sinon on les modifie in place
 * routing_key foireuse (trigger.announce au lieu de trigger.event)
 * Exchange erroné (trigger.event au lieu de trigger)
2014-06-14 17:13:03 +02:00

166 lines
5.3 KiB
Python

#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
#
# Trigger library, designed to send events messages.
#
# Author : Pierre-Elliott Bécue <becue@crans.org>
# License : GPLv3
# Date : 18/05/2014
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 lc_ldap.attributs
logger = clogger.CLogger("trigger", "info")
class Event(cmb.BasicProducer):
"""
Event tracker
"""
def __init__(self, app_id):
"""Extended
"""
logger.info("Starting trigger Event program…")
super(Event, self).__init__(trigger_config.master, 'trigger', app_id)
self._connection = self.connect()
self.get_chan()
def send_message(self, routing_key, body):
"""Sends basic message with app_id and body
"""
try:
logger.info("Sending message %s with routing_key %s.", body, routing_key)
body = cPickle.dumps(body)
self._channel.basic_publish(exchange=self._exchange_name,
routing_key=routing_key,
body=body,
properties=pika.BasicProperties(
delivery_mode=2,
app_id=self._app_id,
))
except:
print "Failure in trigger.event"
raise
def announce(self, body):
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,))
before = dict(body[1]) or {}
after = dict(body[2]) or {}
# set(dico) retourne un set de dico.keys()
keys_pool = set(before).union(set(after))
diff = {}
for key in keys_pool:
if before.has_key(key):
if not isinstance(before[key], list):
blist = [before[key]]
else:
blist = list(before[key])
else:
blist = []
if after.has_key(key):
if not isinstance(after[key], list):
alist = [after[key]]
else:
alist = list(after[key])
else:
alist = []
moins, plus = compare_lists(blist, alist)
if moins != [] or plus != []:
diff[key] = (moins, plus)
return diff
def compare_lists(list1, list2):
"""Compare deux listes, retourne deux listes, une
avec les données perdues, et une avec les données
apparues
Insensible à la casse.
"""
moins, plus = [], []
llist2 = [a.lower() for a in list2]
for elem in [] + list1:
try:
ind = list2.index(elem.lower())
except ValueError:
moins.append(elem)
continue
list1.remove(elem)
list2.pop(ind)
plus = plus + 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.
"""
diff = diff_o_matic(body)
# À 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.
# 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):
trigger_mac_ip(body, diff)
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)])
# Régénération du DHCP :
if not macs[0]:
# Création d'une nouvelle machine.
to_send = {'add': (macs[1], ips[1], hostnames[1])}
elif not macs[1]:
# Destruction d'une machine.
to_send = {'delete': (macs[0], ips[0])}
else:
# Mise à jour.
to_send = {'update': (macs[0], ips[0], macs[1], ips[1], hostnames[1])}
trigger_send('dhcp', to_send)
# Régénération du parefeu.
trigger_send('mac_ip', to_send)
def trigger_send(ttype, to_send):
print "Sending trigger %s with %s" % (ttype, to_send)