#!/usr/bin/env python # -*- coding: utf-8 -*- # # uname_bornes.py # --------------- # Copyright : (c) 2009, Jeremie Dimino # 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 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""" (stdin, stdout, stderr) = os.popen3("ssh -T -x -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=no %(host)s %(cmd)s" % locals()) stdin.close() stderr.close() return stdout def bornes_canal(): names = bornes() outputs = {} for name in names: outputs[name] = ssh_exec(name+".wifi", 'iwlist wl0 channel') 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(): curoutput = output.read().strip() if 'Current Channel' in curoutput: results[name] = curoutput.split(':')[-1].strip() else: results[name] = None output.close() return results def bornes_clients(): names = bornes() outputs = {} for name in names: outputs[name] = ssh_exec(name+".wifi", 'brctl showmacs br-crans') 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(): lines = output.readlines() if lines: macs = map(lambda x: x.split(), lines) results[name] = str(len([x for x in macs if x[0] == '2' and x[2] != 'yes'])) 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 -l 0 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) else: raise NotImplementedError else: main()