#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# uname_bornes.py
# ---------------
# Copyright : (c) 2009, Jeremie Dimino <jeremie@dimino.org>
# Licence   : BSD3

"""Script qui fait un uname sur toute les bornes à la fois.

La sortie est la sortie de `uname -v` sur chaque borne."""

import sys, os, commands, re, time
from subprocess import Popen, PIPE
from optparse import OptionParser

LOG = "/var/log/wifi/wifi.log"
DELTA = 60*60

def bornes():
    """Renvoie la liste des bornes

    Moyen rapide, mais peut-être un peu crade pour récupérer la liste des bornes:
    on regarde les machines présentes dans 138.231.148.1/24"""
    names = []
    for line in commands.getoutput("host -l wifi.crans.org sable.crans.org").splitlines()[5:]:
        # Chaque ligne est de la forme:
        # "truc.wifi.crans.org has address 138.231.148.42"
        fields = line.split()
        name = fields[0].split(".")[0]
        ip = fields[3]
        network = ip.rsplit(".", 1)[0]
        # On ne prend que les machines du réseau 138.231.148.0/24, à part
        # ragnarok:
        if network == "138.231.148" and ip != "138.231.148.1":
            names.append(name)
    return names

def ssh_exec(host, cmd):
    """Execute une commande en ssh sur une machine et renvoie le résultat"""
    p = Popen("ssh -T -x -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no -l root -i /usr/scripts/gestion/clef-wifi %(host)s %(cmd)s" % locals(), shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    p.stdin.close()
    p.stderr.close()
    return p.stdout

def bornes_canal():
    names = bornes()
    outputs = {}
    for name in names:
        outputs[name] = ssh_exec(name+".wifi", '/usr/sbin/iwlist wlan0 channel')

    while True:
        try: os.wait()
        except OSError: break

    # On lit tout les résultats:
    results = {}
    for name, output in outputs.iteritems():
        results[name] = None
        for l in output.readlines():
            m = re.match('\s*Current(.*)\\(Channel ([0-9]*)\\)',l)
            if m:
                results[name] = m.group(2)
                break
        output.close()
    return results


def bornes_clients():
    names = bornes()
    outputs = {}
    for name in names:
        outputs[name] = ssh_exec(name+".wifi", '/usr/sbin/iw dev wlan0 station dump')

#    count = 0
#    total = len(names)
    while True:
        try: os.wait()
        except OSError: break

    # On lit tout les résultats:
    results = {}
    for name, output in outputs.iteritems():
        results[name] = str(len([1 for line in output.readlines() if line.startswith('Station')]))
        output.close()
    return results

def bornes_uptime():
    names = bornes()
    outputs = {}
    for name in names:
        outputs[name] = ssh_exec(name+".wifi", 'cat /proc/uptime')

    count = 0
    total = len(names)
    while count < total:
        try: os.wait()
        except OSError: break

    # On lit tout les résultats:
    results = {}
    for name, output in outputs.iteritems():
        uptime = output.read().strip()
        if uptime:
            results[name] = str(float(uptime.split()[1]) / (24*3600))
        else:
            results[name] = '0'
        output.close()
    return results

def munin(config, cmd=None, process=(lambda x: x), results=None, buckets=None, stack=None):
    """plugin munin"""

    if 'autoconf' in sys.argv:
        print 'yes'
        sys.exit(0)

    names = bornes()

    if 'config' in sys.argv:
        print config
        print "graph_category wifi"
        if buckets:
            for i, (val, lbl) in enumerate(buckets.items()):
                print "%s.label %s" % (lbl, val)
                if stack:
                    if i == 0: print "%s.draw %s" % (lbl, stack)
                    else: print "%s.draw STACK" % lbl
        else:
            for i, borne in enumerate(names):
                print "%s.label %s" % (borne, borne)
                if stack:
                    if i == 0: print "%s.draw %s" % (borne, stack)
                    else: print "%s.draw STACK" % borne
        sys.exit(0)

    if not results:
        res = results = {}
        for name in names: res[name] = ssh_exec(name+".wifi", cmd)
        for name in names: results[name] = res[name].read()

    if buckets:
        bins = {}
        for (lbl, val) in buckets.iteritems(): bins[val] = 0
    for name, res in results.iteritems():
        try:
            value = process(res).split('\n', 1)[0].strip()
            if buckets: bins[buckets[value]] += 1
            else: print '%s.value %s' % (name, value)
        except: pass

    if buckets:
        for name, res in bins.iteritems():
            print '%s.value %s' % (name, res)


def main():
    parser = OptionParser(usage=usage)

    parser.add_option('-v', '--uname', help=u"renvoie le uname -v",
                      action='store_const', const='uname -v', dest='cmd', default='uname -v')
    parser.add_option('-u', '--uptime', help=u"renvoie l'uptime",
                      action='store_const', const='uptime', dest='cmd')
    parser.add_option('-w', '--mac-wifi',
                      help=u"renvoie l'addresse MAC de l'access point",
                      action='store_const', dest='cmd',
                      const="iwconfig wl0 | sed -n 's/^.*00:/00:/p'")
    parser.add_option('-c', '--custom', help=u'exécute une commande custom',
                      action='store', dest='cmd')

    (options, args) = parser.parse_args()

    names = bornes()
    outputs = {}
    for name in names:
        outputs[name] = ssh_exec(name+".wifi", commands.mkarg(options.cmd))

    # On attend que tous les fils qu'on a lancé terminent, avec une petite
    # animation si la sortie est un terminal:
    count = 0
    total = len(names)
    show_anim = sys.stderr.isatty()
    message = "Réception des résultats: %d/%d"
    if show_anim: sys.stderr.write(message % (count, total))
    while count < total:
        try: os.wait()
        except OSError: break
        count += 1
    if show_anim: sys.stderr.write("\r" + message % (count, total))
    if show_anim: sys.stderr.write("\n")

    # On lit tout les résultats:
    results = {}
    for name, output in outputs.iteritems():
        results[name] = output.read().strip()
        output.close()

    results = results.items()
    results.sort(key=lambda x: x[0])

    for name, result in results:
        if not result:
            print "%-15s failure" % (name + ":")

    for name, result in results:
        if result:
            print "%-15s %s" % (name + ":", result)


if __name__ == "__main__":
    usage = u"""usage:       %prog [OPTION]
Récupère la liste des bornes et récupère le résultat d'une commande sur
toutes les bornes"""
    prog = os.path.basename(sys.argv[0])
    if len(prog.split('_', 1)) > 1:
        plugin = prog.split('_', 1)[0]
        if plugin == 'uptime':
            config = """graph_title Uptime Bornes
graph_args --base 1000 --logarithmic -l 0.5 -u 500 -r
graph_vlabel uptime in days"""
            def process(uptime):
                if uptime: return str(float (uptime.split()[1]) / (24 * 3600))
                else: return None
            munin(config, cmd = 'cat /proc/uptime', process=process)
        elif plugin == 'clients':
            config = """graph_title Clients connectés
graph_args --base 1000 -l 0
graph_vlabel Clients connectés"""
            munin(config, results=bornes_clients(), stack="AREA")
        elif plugin == 'canal':
            config = """graph_title Canaux utilisés
graph_args --base 1000 -l 0"""
            buckets = {'1': 'un', '2': 'deux', '3': 'trois', '4': 'quatre',
                       '5': 'cinq', '6': 'six', '7': 'sept', '8': 'huit',
                       '9': 'neuf', '10': 'dix', '11': 'onze', '12': 'douze',
                       '13': 'treize', '14': 'quatorze', '0': 'echec' }
            munin(config, results=bornes_canal(), buckets= buckets, stack="AREA")
        else: raise NotImplementedError

    else:
        main()
