#!/usr/bin/env python # -*- 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 # # Licence: MIT # import optparse import os import shutil import socket import subprocess import sys import tempfile from hosts_plugins import hosts_plugins # Plugins munin classiques à ignorer IGNORE_PLUGINS = ( 'apt_all', 'if_', 'if_err_', 'multips', 'nfs4_client', 'port_', 'sensors_', 'squid_icp', 'tomcat_', 'vlan_', ) # 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 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("apt")) 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)