Auteur : PEB <becue@crans.org>
Date : 09/03/2015
Licence : GPLv3
What the fuck is happening?
===========================
Trigger est une sorte de librairie de remplacement de generate et des services
dans la base LDAP, qui fonctionnent avec bien trop de délai.
Trigger est le fruit d'une longue et intelligente (quelle modestie) réflexion,
et donc nous allons ici décrire son fonctionnement.
Mise à jour LDAP : the fuck is happening?
=========================================
Le binding envoit un tuple contenant en première entrée un hash, en deuxième entrée
un dico contenant les attributs avant modif par le binding, en troisième entrée un
dico contenant les attributs après modif, en quatrième entrée des données additionnelles
(inchangées durant tout le processing).
Documentation succincte de trigger
==================================
Tous les fichiers sont renseignés depuis /usr/scripts.
* gestion/trigger/trigger.py est un fichier python qui importe un consumer de
la librairie cmb. Il marche de manière asynchrone, c'est-à-dire qu'il attend et
traîte les messages un par un. Dans gestion/config/trigger.py, il y a la liste
des services que chaque hôte gère. Ainsi, gestion/trigger/trigger.py sait, en
fonction de l'hôte sur lequel il se trouve, comment il doit se comporter, et ce
qu'il doit importer. Par exemple, sur l'hôte dhcp, le seul service présent est
dhcp, et donc trigger va aller chercher gestion/trigger/service/dhcp.py, et
travailler avec.
* gestion/trigger/trigger.py importe des services, qui sont dans le dossier
services, et eux importent une méthode depuis gestion/trigger/host.py, qui leur
permet d'enregistrer des triggers. Cette méthode permet d'aller puiser dans une
factory portant le nom TriggerFactory les références vers les services utiles.
Cela permet ensuite de les régénérer à la volée.
* Le dossier gestion/trigger/services contient la liste des services existants
pour trigger. Le fonctionnement des services sera détaillé ci-après.
Fonctionnement des services
===========================
Un service est un fichier dans le dossier gestion/trigger/services. Il contient
une fonction décorée avec record_service. C'est une fonction qui sera appelée quand
trigger recevra une demande sur un serveur fournissant ledit service.
Pour que civet sache si un service doit être régénéré, et donc qu'il lui envoie
un message, il faut définir un parser. Ces parsers sont contenus dans
gestion/trigger/parsers/, et portent le nom du service associé. Ils contiennent
au moins une fonction décorée avec record_parser (dont les arguments sont des
attributs ldap à surveiller). Quand civet reçoit des modifs des bindings, il regarde
pour chaque attribut ayant changé s'ils sont surveillés par des parsers, et le cas
échéant demande la régénération des services associés.
Ajouter un nouveau service
==========================
Pour ajouter un service, il faut créer un fichier adapté dans trigger/services/,
et un dans trigger/parsers/. Il faut écrire des fonctions adaptées (le nom est libre),
par exemple, pour un parser :
{{{
@record_parser(lc_ldap.attributs.macAddress.ldap_name, lc_ldap.attributs.ipHostNumber.ldap_name)
def send_mac_ip(body, diff):
}}}
body est le message reçu par civet sans transformation. diff est le diff calculé
à la volée. Le nom de la fonction n'est pas important. Le décorateur prend les
noms d'attributs à surveiller en paramètre. La fonction doit retourner un tuple
dont le premier élément est le nom du service à régénérer (par exemple, "dhcp"),
et le second les choses que le service devra lire et gérer pour se régénérer.
Pour un service, voici un exemple :
{{{
@record_service
def dhcp(body=None):
}}}
body contient le "body" construit dans un parseur. La fonction est décorée, et
son nom est stocké dans la TriggerFactory. Comme souligné précédemment, le nom
de la fonction est important, au même titre que le nom des fichiers dans
trigger/parsers et triggers/services.
Il faut ensuite référencer le service dans config/trigger.py pour les serveurs
où il est important, et relancer trigger sur ces machines. Lors des tests, il ne
faut pas hésiter à passer trigger en debug dans le fichier config/trigger.py.
Parmi les choses importantes, l'idéal est d'avoir des dépendances les plus
paresseuses possibles d'un point de vue évaluation. Ainsi, civet qui ne fait
qu'importer le fichier et utiliser les fonctions d'analyse listées dans
changes_trigger peut éviter de jouer avec ce qui ne le concerne pas.
Enfin, si vous avez des questions, posez-les avant, pas après.
Pour chaque service, une "file d'attente" rabbitmq est créée ayant le nom
trigger-nomdel'hôte-nomduservice, et une clef de routage du type
trigger.nomduservice est mise en place pour que les messages envoyés vers
trigger.nomduservice soient dispatchés sur l'ensemble des queues
trigger-*-nomduservice.
Un service spécial
==================
Le service event est celui qui utilise les parseurs pour savoir quels services
doivent être régénérés. Quand il reçoit le body, il fait un calcul des différences
entre body[1] et body[2] (les deux dicos), et fournit ces différences aux parseurs,
qui lui rendent des messages à envoyer.
L'intérêt est d'assurer une indépendance maximale entre binding ldap et la
librairie trigger : le binding doit juste envoyer avec clef de routage
trigger.event les modifs qu'il fait, et c'est la librairie elle-même qui gère
les envois en son sein.
Cela permet aussi d'avoir des définitions de services précises d'un point de vue
spécification, et une portabilité plus que correcte de trigger d'un binding vers
un autre. (les seules données ldap qui l'intéressent sont les noms des
attributs, définis dans le schéma de la base ldap, il faut donc que le binding
fournisse ses données avec les mêmes noms)