176 lines
5.9 KiB
Python
176 lines
5.9 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.event", "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.
|
|
|
|
"""
|
|
|
|
logger.info("Received message %r…", body)
|
|
|
|
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):
|
|
logger.info("Detected MAC or IP update, calling trigger_mac_ip…")
|
|
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.
|
|
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.")
|
|
|
|
def trigger_send(routing_key, body):
|
|
sender = Event("civet")
|
|
sender.send_message("trigger.%s" % (routing_key,), body)
|