#!/bin/bash /usr/scripts/python.sh # -*- encoding: utf-8 -*- import socket import sys import re import psycopg2 import psycopg2.extras import gestion.affichage as affichage import lc_ldap.shortcuts import argparse import time import gestion.config as config import subprocess ldap = lc_ldap.shortcuts.lc_ldap_readonly() encoding = "UTF-8" def get_stats(args): output = pretty_header(args) + "\n" output += u"Période considérée : du %s au %s.\n" % (args.begin, args.end) output += u"Attention, l'upload total et le download total sont calculés sur les %s plus grosses entrées." % (args.limit) if not args.fichier: print output.encode(encoding) output = u"" else: output += u"\n" pgsql = psycopg2.connect(database='filtrage', user='crans') pgsql.set_session(autocommit=True) curseur = pgsql.cursor(cursor_factory=psycopg2.extras.DictCursor) ip_requete = """ SELECT SUM(upload) as tot_upload, SUM(download) as tot_download, mac, ip_crans, ip_ext, port_crans, port_ext FROM ( ( SELECT sum(bytes) as upload, '0' as download, mac_src as mac, ip_src as ip_crans, ip_dst as ip_ext, port_src as port_crans, port_dst as port_ext FROM upload LEFT JOIN machines ON machines.mac_addr = upload.mac_src WHERE ip_src=%(pg_value)s AND NOT ip_src <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(plage_ens)s AND NOT ip_dst <<= inet%(appt)s AND NOT ip_dst <<= inet%(plage_ipv6)s AND stamp_inserted >= %(begin)s AND stamp_updated <= %(end)s GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext ) UNION ( SELECT '0' as upload, sum(bytes) as download, mac_dst as mac, ip_dst as ip_crans, ip_src as ip_ext, port_dst as port_crans, port_src as port_ext FROM upload LEFT JOIN machines ON machines.mac_addr = upload.mac_dst WHERE ip_dst=%(pg_value)s AND NOT ip_src <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(ipv6_local)s AND NOT ip_src <<= inet%(plage_ens)s AND NOT ip_src <<= inet%(appt)s AND NOT ip_src <<= inet%(plage_ipv6)s AND stamp_inserted >= %(begin)s AND stamp_updated <= %(end)s GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext ) ) AS famille GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext """ mac_requete = """ SELECT SUM(upload) as tot_upload, SUM(download) as tot_download, mac, ip_crans, ip_ext, port_crans, port_ext FROM ( ( SELECT sum(bytes) as upload, '0' as download, mac_src as mac, ip_src as ip_crans, ip_dst as ip_ext, port_src as port_crans, port_dst as port_ext FROM upload LEFT JOIN machines ON machines.mac_addr = upload.mac_src WHERE machines.mac_addr=%(pg_value)s AND NOT ip_src <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(plage_ens)s AND NOT ip_dst <<= inet%(appt)s AND NOT ip_dst <<= inet%(plage_ipv6)s AND stamp_inserted >= %(begin)s AND stamp_updated <= %(end)s GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext ) UNION ( SELECT '0' as upload, sum(bytes) as download, mac_dst as mac, ip_dst as ip_crans, ip_src as ip_ext, port_dst as port_crans, port_src as port_ext FROM upload LEFT JOIN machines ON machines.mac_addr = upload.mac_dst WHERE machines.mac_addr=%(pg_value)s AND NOT ip_src <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(ipv6_local)s AND NOT ip_src <<= inet%(plage_ens)s AND NOT ip_src <<= inet%(appt)s AND NOT ip_src <<= inet%(plage_ipv6)s AND stamp_inserted >= %(begin)s AND stamp_updated <= %(end)s GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext ) ) AS famille GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext """ adh_requete = """ SELECT SUM(upload) as tot_upload, SUM(download) as tot_download, mac, ip_crans, ip_ext, port_crans, port_ext FROM ( ( SELECT sum(bytes) as upload, '0' as download, mac_src as mac, ip_src as ip_crans, ip_dst as ip_ext, port_src as port_crans, port_dst as port_ext FROM upload LEFT JOIN machines ON machines.mac_addr = upload.mac_src WHERE machines.type=%(pg_filter)s AND machines.id=%(pg_value)s AND NOT ip_src <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(plage_ens)s AND NOT ip_dst <<= inet%(appt)s AND NOT ip_dst <<= inet%(plage_ipv6)s AND stamp_inserted >= %(begin)s AND stamp_updated <= %(end)s GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext ) UNION ( SELECT '0' as upload, sum(bytes) as download, mac_dst as mac, ip_dst as ip_crans, ip_src as ip_ext, port_dst as port_crans, port_src as port_ext FROM upload LEFT JOIN machines ON machines.mac_addr = upload.mac_dst WHERE machines.type=%(pg_filter)s AND machines.id=%(pg_value)s AND NOT ip_src <<= inet%(ipv6_local)s AND NOT ip_dst <<= inet%(ipv6_local)s AND NOT ip_src <<= inet%(plage_ens)s AND NOT ip_src <<= inet%(appt)s AND NOT ip_src <<= inet%(plage_ipv6)s AND stamp_inserted >= %(begin)s AND stamp_updated <= %(end)s GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext ) ) AS famille GROUP BY mac, ip_crans, ip_ext, port_crans, port_ext """ if args.download: mac_requete += "ORDER BY tot_download DESC" ip_requete += "ORDER BY tot_download DESC" adh_requete += "ORDER BY tot_download DESC" down_color = "rouge" up_color = "vert" else: mac_requete += "ORDER BY tot_upload DESC" ip_requete += "ORDER BY tot_upload DESC" adh_requete += "ORDER BY tot_upload DESC" down_color = "vert" up_color = "rouge" mac_requete += " LIMIT %s" % (args.limit) ip_requete += " LIMIT %s" % (args.limit) adh_requete += " LIMIT %s" % (args.limit) fill_in_dict = { "begin" : args.begin, "end" : args.end, "plage_ens" : config.plage_ens, "ipv6_local" : 'fe80::/8', "plage_ipv6" : config.prefix['subnet'][0], "appt" : config.NETs['personnel-ens'][0], } if args.aid: pg_filter = "adherent" pg_value = int(args.aid) fill_in_dict.update({'pg_filter' : pg_filter, 'pg_value' : pg_value}) curseur.execute(adh_requete, fill_in_dict) elif args.cid: pg_filter = "adherent" pg_value = int(args.cid) fill_in_dict.update({'pg_filter' : pg_filter, 'pg_value' : pg_value}) curseur.execute(adh_requete, fill_in_dict) elif args.ip: print "Attention, les statistiques par IP sont une mauvaise idée depuis que les extensions de vie privée IPv6 sont actives." pg_value = args.ip fill_in_dict.update({'pg_value' : pg_value}) curseur.execute(ip_requete, fill_in_dict) elif args.mac: pg_value = args.mac fill_in_dict.update({'pg_value' : pg_value}) curseur.execute(mac_requete, fill_in_dict) else: pg_value = ldap.search(u"(host=%s)" % (args.data))[0]['macAddress'][0].value if pg_value == '': print "MAC de la machine %s fixée à %s, problème. :o" % (args.data, pg_value) fill_in_dict.update({'pg_value' : pg_value}) curseur.execute(mac_requete, fill_in_dict) stats = curseur.fetchall() peuplade_ip = {} def convert_ip(ip): host = peuplade_ip.get(ip, []) if host: return host else: peuplade_ip[ip] = socket.getfqdn(ip) return peuplade_ip[ip] if args.dns: convert = lambda ip:convert_ip(ip) else: convert = lambda ip:ip if not args.fichier or args.couleur: styles = (None, None, None, None, None, down_color, up_color) else: styles = None entete = [u"Mac", u"Ip_crans", u"Ip_ext", u"Port_crans", u"Port_ext", unicode("Download (Mio)"), unicode("Upload (Mio)")] longueur = [17, '*', '*', 10, 8, 14, 14] format = ('s', 's', 's', 's', 's', 'o', 'o') data = [[unicode(ligne['mac']), unicode(convert(ligne['ip_crans'])), unicode(convert(ligne['ip_ext'])), unicode(ligne['port_crans']), unicode(ligne['port_ext']), unicode(ligne["tot_download"]), unicode(ligne["tot_upload"])] for ligne in stats] final_data = affichage.tableau(data, titre=entete, largeur=longueur, styles=styles, format=format) output += u"Upload total : %s Mio, download total : %s Mio\n" % (sum([int(ligne["tot_upload"]) for ligne in stats])/1024/1024, sum([int(ligne["tot_download"]) for ligne in stats])/1024/1024) output += final_data if not args.fichier: print output.encode(encoding) else: with open(args.fichier, 'w') as fichier: fichier.write(output.encode(encoding)) def pretty_header(args): if args.aid: adh = ldap.search(u"(aid=%s)" % (args.aid))[0] prenom = unicode(adh['prenom'][0]) nom = unicode(adh['nom'][0]) good_sentence = u"Statistiques d'upload de %s %s" % (prenom, nom) elif args.cid: club = ldap.search(u"(cid=%s)" % (args.cid))[0] prenom = "club" nom = unicode(club['nom'][0]) good_sentence = u"Statistiques d'upload du %s %s" % (prenom, nom) elif args.ip: machine = ldap.search(u"(|(ipHostNumber=%(val)s)(ip6HostNumber=%(val)s))" % {'val' : args.ip})[0] nom = unicode(machine['host'][0]) good_sentence = u"Statistiques d'upload de la machine %s" % (nom) elif args.mac: machine = ldap.search(u"(macAddress=%s)" % (args.mac))[0] nom = unicode(machine['host'][0]) good_sentence = u"Statistiques d'upload de la machine %s" % (nom) else: machine = ldap.search(u"(host=%s)" % (args.data))[0] nom = unicode(machine['host'][0]) good_sentence = u"Statistiques d'upload de la machine %s" % (nom) if not args.fichier or args.couleur: return affichage.style(good_sentence, 'gras') return good_sentence def self_call(args): args = ["/usr/scripts/surveillance/analyse2.py"] + args subprocess.Popen(args, stdout=subprocess.PIPE) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Script d'analyse d'échange de données entre un truc et un autre.", add_help=False) meg = parser.add_mutually_exclusive_group() meg.add_argument("-a", "--aid", help="Rechercher tout l'upload d'un adhérent. Il ne faut pas renseigner data si on utilise cette option.", action="store", type=int) parser.add_argument("-b", "--begin", help="Date de début, dans un format \"AAAA/MM/JJ HH:MM:SS\"", type=str, action="store") meg.add_argument("-c", "--cid", help="Rechercher tout l'upload d'un club. Il ne faut pas renseigner data si on utilise cette option.", action="store", type=int) parser.add_argument("-C", "--couleur", help="Force la présence des couleurs et styles, y compris avec -f.", action="store_true") parser.add_argument("-d", "--download", help="Trier par download décroissant", action="store_true") parser.add_argument("-e", "--end", help="Date de fin, dans un format \"AAAA/MM/JJTHH:MM:SS\"", type=str, action="store") parser.add_argument("-f", "--fichier", help="Sauvegarder le résultat de la recherche dans le fichier FILE. Désactive les fonctions de style (couleur...)", type=str, action="store") parser.add_argument("-h", "--help", help="Affiche cette aide et quitte.", action="store_true") meg.add_argument("-i", "--ip", help="Filtrer sur l'ip fournie. Format IPv4 ou IPv6. Il ne faut pas renseigner data si on utilise cette option.", action="store", type=str) parser.add_argument("-l", "--limit", help="Limiter le nombre de résultats.", action="store", type=int) meg.add_argument("-m", "--mac", help="Filtrer sur la mac fournie. Format \"aa:bb:cc:dd:ee:ff\". Il ne faut pas renseigner data si on utilise cette option.", action="store", type=str) parser.add_argument("-w", "--dns", help="Forcer la résolution dns des IP lorsque c'est possible. Attention, c'est gourmand en temps.", action="store_true") meg.add_argument("data", help="La donnée suivant laquelle rechercher. Si pas d'autre option, doit être renseigné à un nom de machine.", type=str, nargs='?') args = parser.parse_args() now = time.time() if args.help: parser.print_help() sys.exit(0) if not (args.data or args.aid or args.cid or args.ip or args.mac): print "Il faut fournir un nom de machine, ou autre." parser.print_help() sys.exit(7) if not args.begin: args.begin = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(now - 86400)) if not args.end: args.end = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(now)) if not args.download: args.download = False if not args.limit: args.limit = 1000 get_stats(args)