179 lines
6.6 KiB
Python
179 lines
6.6 KiB
Python
#!/bin/bash /usr/scripts/python.sh
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Basic trigger host, will be imported from any other
|
|
# Contains a TriggerFactory, which records the host functions
|
|
# decorated with @record.
|
|
# Contains a trigger which calls good functions from factory.
|
|
#
|
|
# License : New BSD License
|
|
# Date : 10/03/2015
|
|
# Copyright : Pierre-Elliott Bécue <becue@crans.org>
|
|
#
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
# * Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
# * Neither the name of the <organization> nor the
|
|
# names of its contributors may be used to endorse or promote products
|
|
# derived from this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
"""This module provides host functions for trigger, such as the TriggerFactory which
|
|
stores parsers and services metadata."""
|
|
|
|
import collections
|
|
import functools
|
|
|
|
import gestion.config.trigger as trigger_config
|
|
from gestion.trigger.producer import EventProducer
|
|
|
|
# Clogger
|
|
import cranslib.clogger as clogger
|
|
|
|
LOGGER = clogger.CLogger("trigger", "host.py/ack", trigger_config.log_level, trigger_config.debug)
|
|
|
|
PRODUCER = EventProducer("trigger.civet")
|
|
|
|
# *
|
|
# * Factory
|
|
# *
|
|
|
|
class TriggerFactory(object):
|
|
"""The TriggerFactory is designed to store services and parsers.
|
|
|
|
* services are functions decorated with record_service, and, possibly,
|
|
chaining. Such functions are designed to do some operations on
|
|
servers in order to take into account LDAP modifications (or,
|
|
potentially, whatever else).
|
|
|
|
* parsers are functions decorated with record parsers, which are
|
|
triggered by some keywords (eg, ldap attributes names whose values
|
|
have been modified). They return stuff to do, and a service to
|
|
call.
|
|
|
|
"""
|
|
|
|
_services = {}
|
|
_parsers = collections.defaultdict(list)
|
|
|
|
@classmethod
|
|
def register_service(cls, key, value):
|
|
"""Stores the appropriate service in the factory"""
|
|
cls._services[key] = value
|
|
|
|
@classmethod
|
|
def get_service(cls, key):
|
|
"""Retrieves the appropriate service"""
|
|
return cls._services.get(key, None)
|
|
|
|
@classmethod
|
|
def get_services(cls):
|
|
"""Retrieves the list of all services"""
|
|
return cls._services.values()
|
|
|
|
@classmethod
|
|
def register_parser(cls, keys, parser):
|
|
"""Stores the attributes to watch and the function"""
|
|
for key in keys:
|
|
if not isinstance(key, str) and not isinstance(key, unicode):
|
|
key = getattr(key, 'ldap_name', None)
|
|
if key is not None:
|
|
cls._parsers[key].append(parser)
|
|
else:
|
|
LOGGER.debug("Problem when recording parser %r on keys %r.", parser.func_name, keys)
|
|
|
|
@classmethod
|
|
def get_parser(cls, keyword):
|
|
"""Restitutes the parser using keywords"""
|
|
return cls._parsers[keyword]
|
|
|
|
# *
|
|
# * Factory manipulation
|
|
# *
|
|
|
|
def record_service(ack=True):
|
|
"""Records a service in the TriggerFactory.
|
|
|
|
It uses its name as a key for reference, the value being
|
|
the function itself.
|
|
|
|
"""
|
|
def enhance_func(func):
|
|
"""Creates an enhanced function which tests if ack is True and
|
|
creates an ack if it's the case."""
|
|
@functools.wraps(func)
|
|
def enhanced_func(*args, **kwargs):
|
|
"""Dummy"""
|
|
# The first arg is ob_id, execpt if kwargs.
|
|
if args:
|
|
__ob_id = args[0]
|
|
else:
|
|
__ob_id = kwargs['ob_id']
|
|
|
|
# The function does not return.
|
|
func(*args, **kwargs)
|
|
|
|
LOGGER.debug("[%r] Ran %r on (%r, %r)", __ob_id, func.func_name, args, kwargs,)
|
|
|
|
if ack:
|
|
# We send directly with routing key trigger.ack on the way.
|
|
# Thus, ack service does not need any parser.
|
|
routing_key = "ack"
|
|
body = (__ob_id, func.func_name)
|
|
LOGGER.debug("[%r] Ack %r.", __ob_id, body)
|
|
PRODUCER.send_message("trigger.%s" % (routing_key,), body)
|
|
TriggerFactory.register_service(func.func_name, enhanced_func)
|
|
return enhanced_func
|
|
return enhance_func
|
|
|
|
def trigger_service(what):
|
|
"""Calls the appropriate service"""
|
|
return TriggerFactory.get_service(what)
|
|
|
|
def record_parser(*watched):
|
|
"""Stores the function in TriggerFactory, using args as
|
|
keys for the dict"""
|
|
|
|
def find_parser(func):
|
|
"""Adds the chaining_pos at the end of the return of functions."""
|
|
@functools.wraps(func)
|
|
def enhanced_func(*args, **kwargs):
|
|
"""dummy"""
|
|
__ob_id = args[0]
|
|
ret = func(*args, **kwargs)
|
|
LOGGER.debug("[%r] In record_parser.find_parser, ran %r(%r, %r). Got %r.", __ob_id, func.func_name, args, kwargs, ret)
|
|
if ret is not None:
|
|
ret = [elem for elem in ret] + [getattr(func, "chaining_pos", 0)]
|
|
LOGGER.debug("[%r] In record_parser.find_parser, for %r got chaining_pos %r", __ob_id, func.func_name, ret[-1])
|
|
return ret
|
|
TriggerFactory.register_parser(watched, enhanced_func)
|
|
return enhanced_func
|
|
|
|
return find_parser
|
|
|
|
def chaining(pos):
|
|
"""Allows chaining of operations, by adding a position marker
|
|
on the function."""
|
|
|
|
def add_pos(func):
|
|
"""Adds the chaining_pos variable to func"""
|
|
LOGGER.debug("%r chaining pos : %r", func.func_name, pos)
|
|
setattr(func, "chaining_pos", pos)
|
|
return func
|
|
|
|
return add_pos
|