#!/bin/bash /usr/scripts/python.sh # -*- coding: utf-8 -*- '''Ce script permet de savoir s'il y a du monde dans un local donné, à des fins de perms, et cie, filtre par membres actifs.''' import sys from socket import gethostname import collections import os import xml.dom.minidom from gestion.ldap_crans import crans_ldap, MachineWifi from gestion.hptools import hpswitch, ConversationError from gestion.affich_tools import coul, cprint from gestion.whos import aff import gestion.affichage as affichage # Constantes pour munin. # L'ordre est important : il détermine comment sont empilées les valeurs # dans le graphe (mode STACK). Les premières valeurs ont donc intérêts # à avoir le moins de variations (empilées les premières) STATE_DESCR = collections.OrderedDict([ ('unknown_macs', ('machines inconnues de la base', 0xff0000)), ('crans', ('machines du crans', 0x0000ff)), ('ma', ('machines de membres actifs', 0x00ff00)), ('adh', ('autres machines appartenant aux autres adhérents', 0xe5ff00)), ]) CLUB_CRANS = 35 CLUB_BDE = 1 def pretty_name(item): v = "" if hasattr(item, 'nom'): v = item.nom() if hasattr(item, 'prenom'): v = item.prenom() + " " + v v = v.replace('.wifi.crans.org', ' (WiFi)') v = v.replace('.crans.org', '') return v def show_liste_by_prop(liste): by_owner = dict() for machine in liste: # ldap_crans or lc_ldap owner = (getattr(machine, 'proprietaire', None) or \ getattr(machine, 'proprio', None))() if owner.dn not in by_owner: by_owner[owner.dn] = [pretty_name(owner), []] by_owner[owner.dn][1].append(pretty_name(machine)) for items in by_owner.itervalues(): items[1] = ", ".join(items[1]) print affichage.tableau(by_owner.values(), largeur=[None, '*'], alignement=['g', 'g']).rstrip() def show_liste(liste): print ", ".join(pretty_name(m) for m in liste) def _mucode(u): """Sad but true: munin ne fait pas d'utf-8 …""" return u.encode('iso-8859-15', errors='ignore') class WhosThere(object): """: Nom du local, tel qu'il apparaît sur munin, et cie""" name = u"Unamed Local" """: Liste de macs et hostsname qui doivent être ignorées""" expected = [] _ignore_inactive = True _ignore_wifi_only = False def set_ignore_inactive(self, ignore, wifi_only=None): """Définit si l'ajout d'une mac a effectivement lieu pour les mac à ajouter si elles n'appartiennent pas à un membre actif""" self._ignore_inactive = ignore if wifi_only is not None: self._ignore_wifi_only = wifi_only def populate_from_mac(self, mac): """Rempli à partir de la mac""" fm = self.db.search("mac=%s" % mac) res = self._res if mac in self.expected: return if fm['machine']: m = fm['machine'][0] if m.nom() in self.expected: return proprio = m.proprietaire() if fm['machineCrans'] or fm["borneWifi"] or (proprio.idn == 'cid' and int(proprio.id()) == CLUB_CRANS): key = 'crans' elif hasattr(proprio, 'droits') and proprio.droits(): key = 'ma' elif proprio.idn == "cid" and int(proprio.id()) == CLUB_BDE: key = 'bde' else: if self._ignore_inactive: if fm['machineWifi'] or self._ignore_wifi_only: return key = 'adh' res[key].append(m) else: res['unknown_macs'].append(mac) def populate_from_switch(self, host, port): """Rempli les macs à partir de la prise d'un switch""" sw = hpswitch(host) try: macs = sw.show_prise_mac(port) except ConversationError: cprint("Impossible de communiquer avec le switch !", 'rouge') for mac in macs: self.populate_from_mac(mac) def populate_from_ap(self, host): """Rempli les macs à partir de la prise d'un switch""" path = os.path.join('/usr/scripts/var/wifi_xml/alone/', host) + '.wifi.crans.org.xml' with open(path, 'r') as f: doc = xml.dom.minidom.parse(f) for mac in doc.getElementsByTagName('mac'): self.populate_from_mac(mac.firstChild.nodeValue) def do_scan(self): """Fonction à surcharger pour remplir la liste de personnes présentes. La fonction pourra faire appel à populate_from_*""" pass # à surcharger _res = None def query(self): if self._res: return self._res self._res = { 'ma': [], 'crans': [], 'adh': [], 'bde': [], 'unknown_macs': [], 'ttyfound': 0, } self.db = crans_ldap() self.do_scan() return self._res def summary(self): u"""Réalise un joli aperçu de l'état donné en paramètre.""" current = self.query() if current['ma']: cprint('---=== Machines des membres actifs ===---', 'bleu') show_liste_by_prop(current['ma']) cprint("---=== Il y a du monde ===---", 'vert') else: cprint("---=== Il semble n'y avoir personne ... ===---", 'rouge') for mac in current['unknown_macs']: cprint("Machine inconnue: %s" % mac, 'rouge') if current['crans']: cprint("---=== Machines Cr@ns ===---", 'bleu') show_liste(current['crans']) if current['bde']: cprint("---=== Machines du BDE ===---", 'bleu') show_liste(current['bde']) if current['adh']: cprint("---=== Machines d'adhérents ===---", 'bleu') show_liste_by_prop(current['adh']) def munin_config(self): """Donne la configuration du graphe munin""" munin_title = u"Fréquentation du local %s" % self.name print """graph_title %s graph_vlabel N graph_category environnement""" % _mucode(munin_title) for (name, (descr, color)) in STATE_DESCR.iteritems(): print """%(name)s.label %(descr)s %(name)s.draw AREASTACK %(name)s.colour %(color)06X""" % {'name': name, 'descr': _mucode(descr), 'color': color} # Dans le doute, n'affichons pas les adhérents print "adh.graph no" def munin_values(self): res = self.query() for name in STATE_DESCR.iterkeys(): print """%(name)s.value %(value)d\n""" % \ {'name': name, 'value': len(res[name]) } class WhoKfet(WhosThere): name = u"Kfet" def do_scan(self): self.populate_from_switch('backbone.adm.crans.org', 21) class Who2B(WhosThere): name = u"2B" expected = ['00:07:cb:b1:99:4e'] # Freebox def do_scan(self): # Tous les gens au 2B sont supposés actifs (local technique quoi) # mais on cache quand-même les personnes connectées en WiFi self.set_ignore_inactive(True, wifi_only=True) self.populate_from_switch('backbone.adm.crans.org', 33) class WhoDAlembert(WhosThere): name = u"D'Alembert (PR)" expected = ['danae.wifi.crans.org'] def do_scan(self): self.populate_from_ap('danae') class Who4J(WhosThere): name = u"4J" def do_scan(self): self.set_ignore_inactive(True, wifi_only=True) self.populate_from_switch('batj-3.adm.crans.org', 7) if __name__ == '__main__': where = { 'dalembert': WhoDAlembert, '2b': Who2B, 'kfet': WhoKfet, '4j': Who4J, } for what in sys.argv[1:]: try: name = where[what.lower()] except KeyError: print "Usage: whosthere.py \n Locaux : %s" % (", ".join(where.keys())) sys.exit(1) name().summary()