analyse.py 12.1 KB
Newer Older
1
#! /usr/bin/env python
2
# -*- encoding: utf-8 -*-
3

4
import socket
chove's avatar
chove committed
5
import sys, re
6
import psycopg2
7
sys.path.append('/usr/scripts/gestion/')
8
from affich_tools import tableau
9 10 11 12

def stats(ip_crans=[], ip_ext=[],
        show=['ip_crans', 'ip_ext', 'port_crans', 'port_ext'],
        upload_mini=0, show_limit=10, begin_time=24, end_time=0,
13
        show_download=False,resolve_dns=True):
14
    """
15
    Retourne une chaine de caratères formatée avec le tableau de statistiques
16
    d'upload de l'ip fourni
17 18

    ip_crans     : ips des machines Cr@ns
19
                     chaine de caratère si il a qu'une machine
20
                     liste si il y a plusieurs machines
21 22
    ip_ext       : ips des machines extérieures
                     chaine de caratère si il a qu'une machine
chove's avatar
chove committed
23
                     liste si il y a plusieurs machines
24
    show         : liste des champs à afficher parmi :
chove's avatar
chove committed
25
                     ip_crans, ip_ext, port_crans, port_ext
26 27 28
    upload_mini  : n'affiche que les lignes avec plus d'upload que la valeur donnée
    show_limit   : nombre max de lignes à afficher
    begin_time   : date de départ d'analyse (en heure avant maintenant)
29
    end_time     : date de fin d'analyse (en heure avant maintenant)
30
    show_download: trie par rapport au download plutôt que l'upload
31
    """
32

chove's avatar
chove committed
33 34 35 36 37
    if type(ip_crans) == str:
        ip_crans = [ip_crans]

    if type(ip_ext) == str:
        ip_ext = [ip_ext]
38

39 40 41 42
    # variables pour la requete et l'affichage
    ##########################################

    select = []
43 44 45 46
    largeur = []
    titre = []
    format = []
    alignement = []
47

chove's avatar
chove committed
48 49
    if 'ip_crans' in show and len(ip_crans)!=1:
        select.append('ip_crans')
chove's avatar
chove committed
50
        largeur.append(13)
chove's avatar
chove committed
51 52 53
        titre.append('machine crans')
        format.append('s')
        alignement.append('c')
54

chove's avatar
chove committed
55
    if 'ip_ext' in show :
56
        select.append('ip_ext')
57
        largeur.append('*')
58 59 60
        titre.append('machine ext')
        format.append('s')
        alignement.append('c')
61

chove's avatar
chove committed
62
    if 'port_ext' in show :
63
        select.append('port_ext')
64 65 66 67
        largeur.append(10)
        titre.append('port ext')
        format.append('s')
        alignement.append('d')
68

chove's avatar
chove committed
69
    if 'port_crans' in show :
70
        select.append('port_crans')
71 72 73 74
        largeur.append(10)
        titre.append('port crans')
        format.append('s')
        alignement.append('d')
75

76 77 78 79
    select += ['sum(download) as download','sum(upload) as upload']
    largeur += [10,10]
    format += ['o','o']
    alignement += ['d','d']
80 81 82 83 84 85 86 87

    if show_download:
        sort_by = 'download'
        titre += ['*download*','upload']
    else:
        sort_by = 'upload'
        titre += ['download','*upload*']

88 89
    # requete dans la base
    ######################
chove's avatar
chove committed
90 91 92 93 94
    ip_crans = ' OR '.join([ "ip_crans='%s'"%x for x in ip_crans ])
    if not ip_crans: ip_crans='true'
    ip_ext = ' OR '.join([ "ip_ext='%s'"%x for x in ip_ext ])
    if not ip_ext: ip_ext='true'

95 96
    requete = """
        SELECT * FROM (
97 98 99 100 101 102 103
            (
                SELECT %(select)s FROM upload WHERE (%(ip_crans)s) AND (%(ip_ext)s)
                    AND (date > timestamp 'now' - interval '%(begin_time)d hours')
                    AND (date < timestamp 'now' - interval '%(end_time)d hours')
                    GROUP BY %(show)s
            )
        ) AS resultat_intemediaire
104 105 106 107 108 109 110 111 112 113 114 115 116
        WHERE %(sort_by)s >= '%(sort_mini)d'
        ORDER BY %(sort_by)s DESC
        LIMIT %(show_limit)d;""" % {
            'select': ','.join(select),
            'ip_crans': ip_crans,
            'ip_ext': ip_ext,
            'begin_time': begin_time,
            'end_time': end_time,
            'show': ','.join(show),
            'sort_by': sort_by,
            'sort_mini': upload_mini*1024*1024,
            'show_limit': show_limit }

117
    pgsql = psycopg2.connect(database="filtrage", user="crans")
118 119 120 121
    curseur = pgsql.cursor()
    curseur.execute(requete)
    results = curseur.fetchall()

122
    # on transforme tout en chaine
123 124
    results = [ [ str(x).decode("utf-8") for x in line ] for line in results ]
    headers = u""
125 126 127 128
    try:
        upload = 0
        download = 0
        for line in results:
129 130 131 132
            upload += int(line[4])
            download += int(line[3])
        headers += u"    upload: %sMo\n" % (upload/1024/1024)
        headers += u"    download: %sMo\n" % (download/1024/1024)
133 134
    except IndexError:
        pass
135

chove's avatar
chove committed
136
    # on modifie les ip en noms de machine et les ports en noms
137
    def nom_de_machine(ip):
138
        if not resolve_dns: return ip
139
        try:
140
            return socket.gethostbyaddr(ip)[0]
141
        except:
142
            return ip
143

chove's avatar
chove committed
144
    port_to_service = {}
145 146 147 148
    for service,port in [re.split('[ \t]+',
            x.strip().replace('/tcp','').replace('/udp',''))[:2]
            for x in open('/etc/services').readlines()
            if x[0] not in ['\n','#']]:
149
        port_to_service[port] = service
150

151
    for champ in select:
chove's avatar
chove committed
152
        if champ == 'ip_ext':
153
            col = select.index(champ)
154 155
            results = [x[:col] + [nom_de_machine(x[col])] + x[col+1:]
                       for x in results]
156

chove's avatar
chove committed
157 158
        elif champ == 'ip_crans':
            col = select.index(champ)
159 160 161
            results = [x[:col] + [nom_de_machine(x[col]).split('.')[0]] + x[col+1:]
                       for x in results]

chove's avatar
chove committed
162 163
        elif 'port' in champ:
            col = select.index(champ)
164 165
            results = [x[:col] + [port_to_service.get(x[col],x[col])] + x[col+1:]
                       for x in results]
chove's avatar
chove committed
166

167
    return headers + tableau(results, titre=titre, largeur=largeur,
168
            alignement=alignement, format=format)
169

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

def stats_fork(stub='', ip_crans=[],
        upload_mini=0, show_limit=10, begin_time=24, end_time=0,
        show_download=False,resolve_dns=False):
        if '/' in stub:
            return
        import subprocess
        args=[]
        if show_download:
            args.append('--show-download')
        args.extend(['--show-limit','%s' % show_limit])
        args.extend(['--begin-time', '%s' % begin_time])
        args.extend(['--end-time', '%s' % end_time])
        if not resolve_dns:
            args.append('-n')
        for ip in ip_crans:
            subprocess.Popen(
             ["/usr/scripts/surveillance/analyse.py",
              "--file","/usr/scripts/var/analyse/%s%s.txt" % (ip,stub),
              "--ip-crans", "%s" % ip
             ] + args)

192 193 194
if __name__ == '__main__' :

    help = """Statistiques d'upload d'une machine du crans
195
usage: analyse.py [option]...
196
Option fait partie des options suivantes :
197
  -d ou --show-download
198
      trier par download plutôt que par upload
199 200 201
  --ip-crans [ip]
      ip de la machine crans
  --ip-ext [ip]
202
      ip de la machine extérieure
203
  --show [champ]
204
      champs à afficher (parmi ip_crans, ip_ext, port_crans, port_ext)
205
  --upload-mini [n Mo]
206
      upload mini des lignes à afficher
207
  --show-limit [n]
208
      nombre maximum de lignes à afficher
209
  --begin-time [n heures]
210
      heure de départ de l'analyse (en heures depuis maintenant)
211 212
  --end-time [n heures]
      heure de fin de l'analyse (en heures depuis maintenant)
213
  -n
214
      Ne pas résoudre les adresses ip
215 216

Exemple :
217 218
    sudo /usr/scripts/surveillance/analyse.py bilou.crans.org"""

219 220
    headers=""

221 222 223 224 225
    # import des modules
    import getopt

    # aide
    ######
chove's avatar
chove committed
226
    if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv)==1:
227 228
        print help
        sys.exit(0)
229

230 231 232
    # parsage des arguments
    #######################
    try :
233
        opts, args = getopt.getopt(sys.argv[1:], 'dn', ['show-download',
234
                'ip-crans=', 'ip-ext=', 'show=', 'upload-mini=',
235
                'begin-time=', 'end-time=', 'show-limit=', 'file='])
236
    except getopt.GetoptError,message :
chove's avatar
chove committed
237
        print help
238
        sys.exit(4)
239 240 241 242 243 244 245 246

    # affichage des stats de download
    #################################
    show_download = False
    for key,value in opts:
        if key == '-d' or key == '--show-download':
            show_download = True
    if show_download:
247
        headers += u"Statistiques de download\n"
248
    else:
249
        headers += u"Statistiques d'upload\n"
250

251 252
    # recherche de la machine crans
    ###############################
chove's avatar
chove committed
253 254 255 256 257 258 259 260
    ip_crans = []
    ip_crans_nom = []    
    for key,value in opts :
        if key == '--ip-crans' :
            try :
                ip_crans.append(socket.gethostbyaddr(value)[2][0])
                ip_crans_nom.append(socket.gethostbyaddr(value)[0])
            except socket.gaierror :
261
                print "Hôte %s inconnu" % value
chove's avatar
chove committed
262
                sys.exit(5)
263
            except socket.herror:
264
                print "Hôte %s inconnu" % value
265
                ip_crans.append(value)
chove's avatar
chove committed
266
    if len(ip_crans_nom)==1:
267
        headers += u'    depuis la machine %s\n' % ip_crans_nom[0]
chove's avatar
chove committed
268
    elif ip_crans_nom:
269
        headers += u'    depuis les machines %s\n' % ', '.join(ip_crans_nom)
270

chove's avatar
chove committed
271 272 273 274 275 276
    # recherche de la machine ext
    #############################
    ip_ext = []
    ip_ext_nom = []    
    for key,value in opts :
        if key == '--ip-ext' :
277
            # recherche de l'ip de la machine extérieur
278
            try:
chove's avatar
chove committed
279
                ip_ext.append(socket.gethostbyaddr(value)[2][0])
280 281
            except socket.herror:
                ip_ext.append(value)
chove's avatar
chove committed
282
            except socket.gaierror :
283
                print "Hôte %s inconnu" % value
chove's avatar
chove committed
284
                sys.exit(5)
285

chove's avatar
chove committed
286 287 288
            # recherche du nom d'hote
            try :
                ip_ext_nom.append(socket.gethostbyaddr(value)[0])
289 290
            except socket.herror:
                ip_ext_nom.append(ip_ext[-1])
chove's avatar
chove committed
291 292 293
            except socket.gaierror :
                ip_ext_nom.append(ip_ext[-1])
    if len(ip_ext_nom)==1:
294
        headers += u'    vers la machine extérieure %s\n' % ip_ext_nom[0]
chove's avatar
chove committed
295
    elif ip_ext_nom:
296
        headers += u'    vers les machines extérieures %s\n' % ', '.join(ip_ext_nom)
chove's avatar
chove committed
297 298 299

    # limite d'affichage
    ####################
300 301 302
    show = [x[1] for x in opts
            if x[0] == '--show'
            and x[1] in ['ip_crans', 'ip_ext', 'port_crans', 'port_ext']]
chove's avatar
chove committed
303
    if not show :
304
        show = ['ip_crans', 'ip_ext', 'port_crans', 'port_ext']
chove's avatar
chove committed
305
    else :
306
        headers += u'    affichage de %s\n' % ', '.join(show)
307

308
    # upload mini à afficher
chove's avatar
chove committed
309 310 311 312 313 314 315
    ########################
    upload_mini = 0
    for key,value in opts :
        if key == '--upload-mini' :
            try :
                upload_mini = int(value)
            except :
316
                print 'L\'upload mini doit être un entier (en MO)'
chove's avatar
chove committed
317 318 319
                sys.exit(4)
            break
    if upload_mini:
320
        headers += u'    pour les traffics supérieurs à %d Mo\n' % upload_mini
chove's avatar
chove committed
321

322 323 324 325
    # nombre limite d'enregristrements
    ##################################
    limit = 10
    for key,value in opts :
chove's avatar
chove committed
326
        if key == '--show-limit' :
327 328
            try :
                limit = int(value)
329
                headers += u'    affichage des %d premiers résultats\n' % limit
330 331 332 333 334
            except :
                print 'Le nombre limite n\'est pas un entier'
                sys.exit(3)
            break

335
    # Résolution dns
336 337 338
    ################
    resolve_dns = '-n' not in [key for (key,value) in opts]

339
    # début de l'analyse
chove's avatar
chove committed
340 341
    ####################
    begin_time = 24
342
    for key,value in opts :
chove's avatar
chove committed
343
        if key == '--begin-time' :
344
            try :
chove's avatar
chove committed
345
                begin_time = int(value)
346
            except :
347
                print 'Le nombre d\'heures doit être un entier'
348 349
                sys.exit(4)
            break
350

chove's avatar
chove committed
351 352 353
    # fin de l'analyse
    ##################
    end_time = 0
354
    for key,value in opts :
chove's avatar
chove committed
355
        if key == '--end-time' :
356
            try :
chove's avatar
chove committed
357
                end_time = int(value)
358
            except :
359
                print 'Le nombre d\'heures doit être un entier'
360 361
                sys.exit(4)
            break
chove's avatar
chove committed
362
    if begin_time != 24 or end_time:
363
        headers += u'    entre il y a %d heure(s) et il y a %d heure(s)\n' % (begin_time,end_time)
364 365 366

    # fichier de sortie
    ###################
367
    file = None
368 369 370 371
    for key,value in opts :
        if key == '--file' :
            file = value

372

373
    # affichage du résultat
374
    #######################
375
    out = stats(ip_crans, ip_ext, show, upload_mini, limit, begin_time,
376
            end_time, show_download,resolve_dns)
377 378 379
    out = headers + out
    if not file:
        print out.encode("utf-8")
380 381
    else:
        with open(file, 'w') as f:
382
            f.write(out.encode('utf-8'))
383