diff --git a/gestion/config/trigger.py b/gestion/config/trigger.py new file mode 100644 index 00000000..4c54ade0 --- /dev/null +++ b/gestion/config/trigger.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Trigger library config file +# License : GPLv3 + +# Serveur maître +master = "civet.adm.crans.org" + +# Liste des services associés aux hôtes +# useradd : Envoie le mail de bienvenue, et crée le home +# userdel : Détruit le home, déconnecte l'utilisateur sur zamok, détruit les indexes dovecot, désinscrit l'adresse crans des mailing listes associées +services = { + 'dhcp' : ["dhcp"], + 'dyson' : ["autostatus"], + 'isc' : ["dhcp"], + 'komaz' : ["firewall", "secours"], + 'owl' : ["userdel"], + 'redisdead' : ["mailman", "modif_ldap", "solde", "userdel", "secours"], + 'sable' : ["dns"], + 'titanic' : ["secours"], + 'zamok' : ["userdel"], + 'zbee' : ["useradd", "userdel"], + } diff --git a/gestion/trigger/trigger.py b/gestion/trigger/trigger.py new file mode 100755 index 00000000..dbbc7983 --- /dev/null +++ b/gestion/trigger/trigger.py @@ -0,0 +1,112 @@ +#!/bin/bash /usr/scripts/python.sh +# -*- coding: utf-8 -*- +# +# Trigger library, designed to be called on any host, it will +# retreive its appropriate modules based on the hostname. Based +# on the Crans Message Broker stuff, it adds specific methods designed +# to be used as a replacement for generate +# +# Author : Pierre-Elliott Bécue +# License : GPLv3 +# Date : 29/04/2014 + +import argparse +import cranslib.clogger as clogger +import cmb +import cPickle +import socket +import gestion.config.trigger as trigger_config +import gestion.affichage as affichage +import sys + +hostname = socket.gethostname().split(".")[0] + +import importlib +triggerhost = importlib.import_module("gestion.trigger.hosts.%s" % (hostname,)) +logger = clogger.CLogger("trigger", "info") + +class EvenementListener(cmb.AsynchronousConsumer): + """ + Gestionnaire d'événement + """ + + def on_message(self, channel, basic_deliver, properties, body): + """Invoked by pika when a message is delivered from RabbitMQ. The + channel is passed for your convenience. The basic_deliver object that + is passed in carries the exchange, routing key, delivery tag and + a redelivered flag for the message. The properties passed in is an + instance of BasicProperties with the message properties and the body + is the message that was sent. + + :param pika.channel.Channel channel: The channel object + :param pika.Spec.Basic.Deliver: basic_deliver method + :param pika.Spec.BasicProperties: properties + :param str|unicode body: The message body + + """ + about = basic_deliver.routing_key.split(".")[1] + origin = properties.app_id + message_id = properties.message_id + logger.info('Received message # %s from %s: %s', + basic_deliver.delivery_tag, properties.app_id, body) + body = cPickle.loads(body) + try: + triggerhost.trigger(about)(body) + except AttributeError: + logger.warning('No suitable trigger found for message # %s from %s: %s on host %s. Discarding it.', + basic_deliver.delivery_tag, properties.app_id, body, hostname) + self.acknowledge_message(basic_deliver.delivery_tag) + + def run(self): + """Run the example consumer by connecting to RabbitMQ and then + starting the IOLoop to block and allow the SelectConnection to operate. + + """ + logger.info("""Crans Message Broker ++--------------------------------------------+ +| Welcome on CMB | ++--------------------------------------------+""") + self._connection = self.connect() + for service in trigger_config.services[hostname]: + self.add_queue("trigger-%s-%s" % (hostname, service), "trigger.%s" % (service,)) + self._connection.ioloop.start() + +def daemonize(): + listener = EvenementListener(trigger_config.master, "trigger", "topic") + try: + listener.run() + except KeyboardInterrupt: + logger.warning("Caught SIGINT, will now go for shutdown.") + listener.stop() + +if __name__ == '__main__': + # We use a parser to capture all possible arguments designed for one host + parser = argparse.ArgumentParser(description="Initier une régénération de services.", add_help=False) + parser.add_argument('-a', '--all', help="Régénération complète des services sur l'hôte %s." % (hostname,), action="store_true") + parser.add_argument('-d', '--daemon', help="Écouter sur civet en arrière plan.", action="store_true") + parser.add_argument('-h', '--help', help="Affiche ce message et quitte.", action="store_true") + # For each service supposingly managed by host, generate one parser option + for service in trigger_config.services[hostname]: + parser.add_argument('--%s' % (service,), help="Force la régénération du service %s." % (service,), action="store_true") + args = parser.parse_args() + + if args.help: + parser.print_help() + sys.exit(0) + elif args.all: + # Regenerates all services availables, don't crash on nonexistant ones + for service in trigger_config.services[hostname]: + try: + print affichage.style(" (Ré)Génération du service %s" % (service,), "cyan") + triggerhost.trigger(service)(True) + except AttributeError: + print "No suitable trigger handle found for service %s on host %s" % (service, hostname) + elif args.daemon: + # Daemonize the trigger app, in order to listen and execute commands from civet. + daemonize() + else: + # If not all and not daemon, try all services one by one. + for service in trigger_config.services[hostname]: + if getattr(args, service, False) == True: + print affichage.style(" (Ré)Génération du service %s" % (service,), "cyan") + triggerhost.trigger(service)(True)