#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
#
# Liste les liens à créer pour les plugins munin.
#
# Utilise la liste de plugins classiques renvoyée par
# munin-node-configure, filtrée, et les plugins customisés Cr@ns
# configurés dans hosts_plugins.py
#
# Copyright © 2009 Nicolas Dandrimont <Nicolas.Dandrimont@crans.org>
#
# Licence: MIT
#

import optparse
import os
import shutil
import socket
import subprocess
import sys
import tempfile

from hosts_plugins import hosts_plugins, general_plugins

from gestion.config.services import services


# Plugins munin classiques à ignorer
IGNORE_PLUGINS = (
    'apt_all',
    'if_',
    'if_err_',
    'multips',
    'nfs4_client',
    'nfsd',
    'nfsd4',
    'exim_mailstats',
    'port_',
    'sensors_',
    'squid_icp',
    'tomcat_',
    'vlan_',
    )

if socket.gethostname() in services.get('iscsi', []):
    IGNORE_PLUGINS += (
        'diskstat',
        'diskstats',
        'diskstat_',
    )

# Chemin d'accès aux plugins munin
MUNIN_PATH = "/usr/share/munin/plugins"

# Hacks crades
QUIRKS = []

if sys.version_info < (2, 5):
    def any(genexpr):
        """Retourne True si quelque chose est vrai dans genexpr"""
        for truc in genexpr:
            if truc:
                return True
        return False

def register_quirk(f):
    QUIRKS.append(f)

def get_munin_plugins():
    """Liste les plugins munin créés par le système

    Retourne un dictionnaire dont la clé est le lien dans
    /etc/munin/plugins, et la valeur le fichier à lier.
    """

    # Dictionnaire de sortie
    output = {}

    # Crée un répertoire vide pour faire croire à Munin qu'on veut
    # créer tous les liens...
    tempdir = tempfile.mkdtemp()

    # Demande à Munin quels plugins il lui semble bon nous faire
    # utiliser. --families=auto,manual permet de récupérer plus de
    # plugins (qu'il faut ensuite filtrer)
    munin = subprocess.Popen(["/usr/sbin/munin-node-configure",
                              "--families=manual",
                              "--families=auto",
                              "--servicedir", tempdir,
                              "--shell"],
                             stdout = subprocess.PIPE,
                             stderr = subprocess.PIPE
                             )
    munin_stdout, munin_stderr = munin.communicate()

#    if munin.returncode != 0:
#        raise RuntimeError, "munin-node-configure... Stderr:\n%s" % munin_stderr

    munin_output_lines = munin_stdout.splitlines()

    for line in munin_output_lines:
        if line.startswith("ln -s"):
            dst, src = line.split()[-2:]
            if dst[0] == "'":
                dst = dst[1:-1]
                src = src[1:-1]
            src = os.path.basename(src)
            # filtrage
            if not os.path.basename(dst) in IGNORE_PLUGINS:
                output[src] = dst

    os.rmdir(tempdir)

    # Exécute les hacks sales sur la sortie
    for quirk in QUIRKS:
        quirk(output)

    return output

def get_all_plugins():
    """Liste les liens de plugins munin à créer

    Retourne un dictionnaire dont la clé est le lien dans
    /etc/munin/plugins, et la valeur le fichier à lier.
    """

    hostname = socket.gethostname()
    result = get_munin_plugins()

    custom_plugins = hosts_plugins.get(hostname, {})
    for (group, hosts) in services.iteritems():
        if hostname in hosts:
            custom_plugins.update(hosts_plugins.get(group, {}))
    custom_plugins.update(general_plugins)

    for plugin, dest_file in custom_plugins.iteritems():
        if dest_file.startswith('/'):
            result[plugin] = dest_file
        else:
            result[plugin] = os.path.join('/usr/scripts/munin', dest_file)

    return result

def list_plugins(plugins, directory):
    """Liste les plugins comme s'ils allaient être liés dans
    directory"""

    for name, path in sorted(plugins.items()):
        print os.path.join(directory, name), '->', path

def link_plugins(plugins, directory):
    """Lie les plugins dans le répertoire directory"""

    for name, path in plugins.iteritems():
        try:
            os.symlink(path, os.path.join(directory, name))
            if options.verbose:
                print "%s installé avec succès!" % name
        except OSError:
            print "Echec à l'installation de %s (déjà installé?)" % name

# Hack propre
def add_plugin(plugin):
    """Crée une fonction qui ajoute le plugin à la liste"""
    # Les clôtures en Python, ça fait rêver...
    def f(plugins, plugin=plugin):
        plugins[plugin] = os.path.join(MUNIN_PATH, plugin)
    return f

register_quirk(add_plugin("uptime")) # Coucou MoSaN
register_quirk(add_plugin("netstat"))
register_quirk(add_plugin("nfs_client"))

# Hacks sales
@register_quirk
def add_ifaces(plugins):
    """Ajoute les interfaces à la liste des plugins si munin n'a pas
    trouvé d'interface (ou si elles ont été filtrées)"""

    if not any(plugin.startswith('if_') for plugin in plugins):
        # Liste les interfaces configurées sur le système
        interfaces_file = None
        interfaces = []
        try:
            interfaces_file = file('/etc/network/interfaces', 'r')
            for line in interfaces_file:
                line = line.split()
                # Recherche d'une ligne type "iface eth0 inet static"
                if line and line[0] == 'iface' and line[-1] == 'static':
                    interface_name = line[1]
                    # on dégage les interfaces sur vlan spéciaux et
                    # aliasées (leur nom contient ':' ou '.')
                    if not set('.:') & set(interface_name):
                        interfaces.append(interface_name)
        finally:
            if interfaces_file is not None:
                interfaces_file.close()

        for interface in interfaces:
            plugins['if_%s' % interface] = os.path.join(MUNIN_PATH,'if_')
            plugins['if_err_%s' % interface] = os.path.join(MUNIN_PATH, 'if_err_')

@register_quirk
def use_correct_postfix_mailstats(plugins):
    """Utilise un postfix_mailstats non buggué sur etch"""
    if "postfix_mailstats" in plugins:
        try:
            debver_file = open('/etc/debian_version')
        except IOError:
            return
        else:
            debver = debver_file.read()
            debver_file.close()
            if debver.startswith('4'):
                plugins["postfix_mailstats"] = "/usr/scripts/munin/postfix_mailstats"

if __name__ == "__main__":
    parser = optparse.OptionParser()
    parser.add_option('-d', '--directory',
                      help = 'Write plugin symlinks to DIR',
                      metavar = 'DIR',
                      default = '/etc/munin/plugins'
                      )
    parser.add_option('-f', '--force',
                      action = 'store_true',
                      help = 'Remove destination directory'
                      )
    parser.add_option('-p', '--pretend',
                      action = 'store_true',
                      help = 'Do not actually make links'
                      )
    parser.add_option('-v', '--verbose',
                      action = 'store_true',
                      help = 'Increase verbosity'
                      )
    (options, args) = parser.parse_args()

    plugins = get_all_plugins()

    if options.pretend:
        list_plugins(plugins, options.directory)
    else:
        if options.force:
            shutil.rmtree(options.directory)
            os.mkdir(options.directory)
        link_plugins(plugins, options.directory)

