scripts/gestion/whos.py
2015-08-08 03:42:12 +02:00

1491 lines
50 KiB
Python
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
# Copyright (C) Frédéric Pauget
# Licence : GPLv2
u"""Ce script permet de recherche et d'afficher le détail d'une machine ou
d'un adhérent.
Usage: %(prog)s [options] <chaine de recherche>
La chaine de recherche peut être :
* soit un terme unique, dans ce cas la recherche sera effectuée sur les
champs en bleu ci-dessous.
* soit du type "champ1=valeur1&champ2!=valeur2 ...", les résultats seront
alors limités aux entrées correspondantes à tous les critères.
Les champs de recherche possibles sont :
%(champs_rech)s
Recherche sur prise possible (utiliser uniquement ce champ dans ce cas).
Les options de recherches sont :
* limitations sur l'affichage :
-a ou --adherent : limitation de l'affichage aux adhérents
-m ou --machine : limitation de l'affichage aux machines
-c ou --club : limitation de l'affichage aux clubs
-b ou --bornes : limitation de l'affichage aux bornes wifi
--crans : recherche uniquement les machines du crans
* options d'affichage :
-t ou --tech : affichages des infos techniques des machines
à la place des infos administratives dans les résumés.
-i ou --ipsec : montre la clef wifi (anciennement ipsec) des machines wifi
-l <num> ou --limit=<num> : limite du nombre de résultats pour utiliser
le mode d'affichage condensé au lieu du mode détaillé (défaut %(limit_aff_details)i)
-L <num> ou --limit-historique=<num> : limitation du nombre de lignes
d'historique affichées (défaut %(limit_aff_historique)i)
-d <num> ou --limit-blacklist=<num> : limitation du nombre de lignes
d'historique des déconnexions affichées (défaut %(limit_aff_blacklist)i)
-s ou --ssh : affiche les fingerprint ssh des machines
--servers : limite les résultats aux servers
--adm : limite les résultats aux machines adm
-6 ou --ipv6 : affiche les ipv6 dans l'affichage synthétique
"""
import commands
import getopt
import subprocess
import sys
from time import strftime, localtime, time
from affich_tools import *
from ldap_crans import is_actif, crans_ldap, AssociationCrans, hostname
from ldap_crans import MachineCrans, MachineWifi, BorneWifi
from ldap_crans import Adherent
from hptools import sw_chbre, ConversationError
import gestion.annuaires_pg as annuaires_pg
import gestion.config as config
import ridtools
import user_tests
base = None
limit_aff_details = 1
limit_aff_machines = 15
limit_aff_historique = 4
limit_aff_blacklist = 4
aff_ipsec = 0
aff_ssh = 0
aff_ipv6 = 0
################################################################################
### Fonctions utiles
def indicatif (num):
"""
Sépare l'indicatif de nationalité du reste d'un numéro de téléphone
"""
if num[0:2] != '00':
return ('', num)
if num[2] == '1':
return ('1 '+num[3:6], num[6:])
if num[2] in ['0','7']:
return (num[2], num[3:])
# indicatifs nationaux à 2 chiffres
ids = [20, 27, 36, 86, 98]
ids.extend(range(30,35))
ids.extend(range(39,42))
ids.extend(range(43,50))
ids.extend(range(51,59))
ids.extend(range(60,67))
ids.extend(range(81,85))
ids.extend(range(90,96))
if int(num[2:4]) in ids:
return (num[2:4], num[4:])
return (num[2:5], num[5:])
def format_tel (num):
"""
Formate un numéro de téléphone
* Remplace un éventuel "00" au début par un "+"
* Insère des espaces pour que ce soit lisible
"""
res = ''
(indic, reste) = indicatif(num)
if indic != '':
res += '+' + indic + ' '
l = len(reste)
if l%2 == 1:
res += reste[0] + ' '
for i in range(l%2,l,2):
res += reste[i:i+2] + ' '
return res
################################################################################
def aff(qqch,mtech=0) :
""" Affichage de qqch.
qqch peut être une liste d'instances des classes adhérent ou machine
(un seul type dans la liste) dans ce cas :
* si la longueur de la liste est inférieure à limit_aff_details
affiche les propriétés détaillées de chaque élément.
* sinon résume dans un tabeau des principales propriétés
si qqch est une instance seul la traité comme une liste à une élément
Si mtech = 1 affiches les infomations techniques des machines plutot
qu'administratives dans le tableau des propriétés
"""
if type(qqch) != list :
qqch = [ qqch ]
if len(qqch) > limit_aff_details :
t = qqch[0].idn
if t == 'aid':
cprint(adhers_brief(qqch))
elif t == 'mid':
if mtech:
cprint(list_machines(qqch))
else:
cprint(machines_brief(qqch))
elif t == 'cid':
cprint(clubs_brief(qqch))
elif t == 'fid':
cprint(factures_brief(qqch))
else :
i = 0
for c in qqch :
t = c.idn
if i: cprint(u'='*80, 'cyan')
i = 1
if t == 'aid':
cprint(adher_details(c).strip())
elif t == 'mid':
cprint(machine_details(c).strip())
elif t == 'cid':
cprint(club_details(c).strip())
elif t == 'fid':
cprint(facture_details(c).strip())
# affiche le nombre de résultats
if len(qqch) > 1:
cprint(u"Total: %d" % len(qqch))
def adhers_brief(adhers) :
"""
Formatage sous forme de tableau des infos sur la liste d'adhérent fournie :
* aid
* prénom nom
* chambre
* machines
"""
data = []
# Copie locale triée par (nom, prenom)
adhers = adhers[:]
adhers.sort(lambda x, y: cmp((x.nom(), x.prenom()), (y.nom(), y.prenom())))
for a in adhers:
## État administratif
ok = u'\x1b[1;32mo\x1b[1;0m'
ook = u'\x1b[1;32mO\x1b[1;0m'
nok = u'\x1b[1;31mn\x1b[1;0m'
# Paiement
if a.adhesion() > time():
if 'p' in a.controle(): paid = ook
else: paid = ok
else: paid = nok
machines = ''
# Récupération des machines
if len(adhers) <= limit_aff_machines:
for machine in a.machines() :
nom = machine.nom().split('.')[0]
if machine.blacklist_actif() : k = 'rouge'
elif isinstance(machine, MachineWifi): k = 'cyan'
else : k= ''
if machines : machines += ', ' + coul(nom,k)
else : machines = coul(nom,k)
else:
machines = None
# Données
if len(adhers) <= limit_aff_machines:
data.append([a.id(), a.Nom(), a.chbre(), paid, machines])
else:
data.append([a.id(), a.Nom(), a.chbre(), paid])
if len(adhers) <= limit_aff_machines:
return u"Machines en rouge = machines avec limitation de services\n" + \
u"P : paiement année en cours, le fond vert indique le précâblage (G bleu = inscription gratuite)\n" + \
u"C : carte d'étudiant année en cours\n" + \
tableau(data,
titre = [u'aid', u'Prénom Nom', u'Chbre', u'P', u'Machines'],
largeur = [5, 30, 5, 1, '*'],
alignement = ['d', 'c', 'g', 'c', 'c'])
else:
return u"Machines en rouge = machines avec limitation de services\n" + \
u"P : paiement année en cours, le fond vert indique le précâblage (G bleu = inscription gratuite)\n" + \
u"C : carte d'étudiant année en cours\n" + \
tableau(data,
titre = [u'aid', u'Prénom Nom', u'Chbre', u'P'],
largeur = [5, '*', 5, 1],
alignement = ['d', 'c', 'g', 'c'])
def machines_brief(machines) :
"""
Formatage sous forme d'un tableau des propriétés de la liste de machine :
* mid
* rid
* type (serveur, adm, fil, wifi, adm, borne)
* nom
* adresse IP
* adresse MAC
* si blacklistée
"""
data = []
# Copie locale triée par nom
machines = machines[:]
machines.sort(lambda x, y: cmp(x.nom(), y.nom()))
for m in machines :
t, bl = __bases_machines(m)
# Propriétaire
a = m.proprietaire()
p = a.Nom()
# A jour administrativement
if not a.paiement_ok():
p = coul(p,'rouge')
# Données
data.append([m.id(), m.rid() , t, m.nom().split('.')[0], p, a.chbre(), bl])
return u"Le propriétaire en rouge signale un problème administratif, en bleu une inscription gratuite\n" + \
tableau(data,
titre = [u'mid', u'rid', u'Type', u'Nom de machine', u'Propriétaire', u'Chbre', u'Limitation'],
largeur = [5, 5, 13, 18, '*', 5, 10],
alignement = ['d', 'd', 'c', 'c', 'c', 'g', 'c'])
def factures_brief(factures) :
"""
Formatage sous forme d'un tableau des propriétés de la liste de machine :
* fid
* priorio
* articles
* modePaiement
* recuPaiement
* controle
* total
"""
data = []
# Copie locale triée par nom
factures = [] + factures
factures.sort(lambda x, y: cmp(int(x.id()), int(y.id())))
for facture in factures:
# Propriétaire
a = facture.proprietaire()
p = a.Nom()
# A jour administrativement
if not a.paiement_ok():
p = coul(p,'rouge')
# Contrôle
controle = facture.controle()
if controle == "TRUE":
controle = coul(u"Validée", "vert")
elif controle == "FALSE":
controle = coul(u"Invalide", "rouge")
else:
controle = u"N/A"
# Données
data.append([
facture.id(),
p,
', '.join(article['code'] for article in facture.articles()),
facture.modePaiement(),
coul(facture.recuPaiement(), "vert") if facture.recuPaiement() else coul("NON", "rouge"),
controle,
unicode(facture.total()) + u" €",
])
return u"Le propriétaire en rouge signale un problème administratif.\n" + \
tableau(data,
titre = [u'fid', u'Propriétaire', u'Articles', u'Mode de paiement', u'Payé', u"Contrôle", u"Total"],
largeur = [5, 18, '*', 8, 19, 8, 8],
alignement = ['d', 'g', 'g', 'c', 'c', 'g', 'd']
)
def clubs_brief(clubs) :
"""
Formatage sous forme de tableau des infos sur la liste de clubs fournie :
* cid
* nom
* local
* machines
"""
data = []
# Copie locale triée par Nom
clubs = clubs[:]
clubs.sort(lambda x, y: cmp(x.Nom(), y.Nom()))
for c in clubs :
## État administratif
ok = u'\x1b[1;32mo\x1b[1;0m'
ook = u'\x1b[1;32mO\x1b[1;0m'
nok = u'\x1b[1;31mn\x1b[1;0m'
# Paiement
if c.paiement_ok():
# TODO
#if 'p' in c.controle(): paid = ook
#else: paid = ok
paid = ok
else : paid = nok
# Précablage
# TODO
machines = ''
# Récupération des machines
for machine in c.machines() :
nom = machine.nom().split('.')[0]
if machine.blacklist_actif() : k = 'rouge'
else : k= ''
if machines : machines += ', ' + coul(nom,k)
else : machines = coul(nom,k)
# Responsable
try:
resp = c.responsable().Nom()
except ValueError, e:
resp = e
# Données
data.append([c.id() , c.Nom(), c.local(), paid, resp, machines])
return u"Machines en rouge = machines avec limitation de services\n" + \
u"P : signature charte année en cours, le fond vert indique le précâblage\n" + \
tableau(data,
titre = [u'cid', u'Nom ', u'Local', u'P', u'Responsable', u'Machines'],
largeur = [5, '*', 6, 1, 21, 15],
alignement = ['d', 'c', 'g', 'c', 'c', 'c'])
def list_machines(machines) :
"""
Formatage sous forme d'un tableau des propriétés de la liste de machine :
* mid
* type (fixe ou wifi)
* nom
* adresse IP
* adresse MAC
* si blacklistée
"""
data = []
# Copie locale triée par nom
machines = machines[:]
machines.sort(lambda x, y: cmp(x.nom(), y.nom()))
for m in machines :
t, bl = __bases_machines(m)
# Données
if not aff_ipv6:
data.append([m.id(), m.rid(), t, m.nom().split('.')[0], m.ip(), m.mac(), bl])
larg = 15
else:
larg = 22
data.append([m.id(), m.rid(), t, m.nom().split('.')[0], m.ipv6(), m.mac(), bl])
return tableau(data,
titre = [u'mid', u'rid', u'Type', u'Nom de machine', u'Adresse IP', u'Adresse MAC', u'Limitation'],
largeur = [5, 5, 9, '*', larg, 17, 10],
alignement = ['d', 'd', 'c', 'c', 'c', 'c', 'c'])
def list_factures(factures) :
"""
Formatage sous forme d'un tableau des propriétés de la liste de facture :
* fid
* mode de paiement
* payé
* codes article
* total
"""
data = []
# Copie locale triée par fid
factures = factures[:]
factures.sort(lambda x, y: cmp(x.id(), y.id()))
for f in factures :
controle = f.controle()
if controle == "TRUE":
controle = coul(u"Validée", "vert")
elif controle == "FALSE":
controle = coul(u"Invalide", "rouge")
else:
controle = u"N/A"
data.append([
f.id(),
f.modePaiement(),
coul("OK", "vert") if f.recuPaiement() else coul("NON", "rouge"),
controle,
', '.join(a['code'] for a in f.articles()),
u"%s" % sum([float(a['pu'])*int(a['nombre']) for a in f.articles()])
])
return tableau(data,
titre = [u'fid', u'Mode de paiement', u'Payé', u"Contrôle", u'Articles', u"Total"],
largeur = [5, 16, 6, 10, '*', 8],
alignement = ['d', 'g', 'c', 'c', 'g', 'd'])
def list_spec(machines) :
"""
Formatage sous forme d'un tableau des propriétés de la liste de machines spéciales :
* mid
* rid
* nom
* adresse IPv4/IPv6
* adresse MAC
Pour les bornes :
* État
* puissance
* canal
* lieu (la première remarque en fait)
Pour le reste :
* ??
"""
data = []
# Copie locale triée par nom
machines = machines[:]
machines.sort(lambda x, y: cmp(x.nom(), y.nom()))
for b in machines:
t, bl = __bases_machines(b)
# Données
if t == 'borne':
try :
l = [x for x in b.info() if not x[0]=='<'][0]
if len(l) > 11 :
l = l[0:11]
except :
l = u'????'
if '-' in b.puissance() :
puiss = coul(b.puissance(),'rouge')
else :
puiss = b.puissance()
if aff_ipv6:
data.append([b.id(), b.rid(), b.nom().split('.')[0], b.ipv6(), b.mac(), b.canal(), puiss, b.prise(), l])
else:
data.append([b.id(), b.rid(), b.nom().split('.')[0], b.ip(), b.mac(), b.canal(), puiss, b.prise(), l])
titre = [u'mid', u'rid', u'Nom', u'Adresse IP', u'Adresse MAC', u'Can', u'P', u'Pris', u'Lieu']
largeur = [5, 5, 16, 22, 17, 1, 5, 3, 4, '*']
alignement = ['d', 'd', 'c', 'c', 'c', 'c', 'c', 'c', 'g', 'c']
else:
if aff_ipv6:
data.append([b.id(), b.rid(), t, b.nom().split('.')[0], b.ipv6(), b.mac()])
else:
data.append([b.id(), b.rid(), t, b.nom().split('.')[0], b.ip(), b.mac()])
titre = [u'mid', u'rid', u'Type', u'Nom', u'Adresse IP', u'Adresse MAC']
largeur = [5, 5, 10, 20, '*', 17]
alignement = ['d', 'd', 'd', 'c', 'c', 'c']
output = tableau(data, titre=titre, largeur=largeur, alignement=alignement)
return output
def adher_details(adher) :
"""
Affichage du détail des propriétés d'un adhérent
"""
f=u''
# Aid
f+= coul(u'aid=%s ' % adher.id() ,'bleu')
# Nom, prenom
f += coul(u'Nom : ','gras') + "%s\n" % adher.Nom()
# Mail
GL = RMH = u''
if adher.mail() == '' or adher.compte() == '':
f += coul(u'Adresse mail : ','gras')
if adher.mail() == '':
f += coul('INCONNUE','rouge')
elif adher.mail_invalide():
f += coul(adher.mail(),'rouge')
else:
f += adher.mail()
f += "\n"
else :
f += coul(u'Login : ','gras')
if adher.mail_invalide():
f += coul(adher.compte(),'rouge')
else:
f += adher.compte()
if not adher.active():
f += " " + coul(u"(Compte désactivé)", "rouge")
f += "\t"
# controurneGreylisting
if not adher.contourneGreylist():
GL = u' (%s)'%coul(u'GreyList','gris')
if adher.rewriteMailHeaders():
RMH = u' (%s)'%coul(u'réécriture en-têtes mail','gris')
alias = u', '.join([adher.canonical_alias()] + adher.alias())
if alias:
if alias[0] == u',':
# Canonical étéait vide
alias = alias[2:]
f += coul(u'Alias : ','gras') + alias
f += GL
f += RMH
f += u'\n'
if adher.email_exterieur():
f += coul(u'Mail extérieur : ', 'gras')
f += adher.email_exterieur()
f += u'\n'
# Réservé aux comptes Crans.
if len(adher.gpgFingerprint()) > 0:
f += u"\n".join([coul(u'Fingerprint GPG : ', 'gras') + u"%s" % (i) for i in adher.gpgFingerprint()])+"\n"
try:
forward = file(os.path.join(adher.home(), ".forward")).readlines()
if len(forward) > 1:
forward = forward[0].strip() + u" ...\n"
elif len(forward) == 1:
forward = forward[0].strip() + u"\n"
if forward:
f += coul(u'Redirection : ', 'gras') + forward
except IOError, e:
# Pas de .forward, ou .forward privé... on laisse tomber
pass
f += coul(u'Dernière connexion : ', 'gras')
timestamp = adher.derniereConnexion()
if timestamp == 0:
f += coul(u'Jamais', 'rouge')
else:
f += coul(strftime('%d/%m/%Y %H:%M', localtime(timestamp)),
time()-timestamp > 32*24*3600 and 'rouge' or '')
f += u"\n"
# État administratif
f += coul(u"Date d'inscription : ", "gras")
f += strftime('%d/%m/%Y %H:%M:%S', localtime(adher.dateInscription()))
f += coul(u'\nÉtat administratif : ','gras')
jour = True
if adher.adhesion() <= time():
if not jour: f += ' et '
f += coul(u"non adhérent actuellement",'violet')
jour = False
if not adher.paiement_ok():
if not jour: f += ' et '
f += coul(u"pas d'accès Internet ", "violet")
if jour:
f += coul(u"à jour",'vert')
f += "\n"
# Telephone
tel = adher.tel()
if tel != 'inconnu' :
f += coul(u'Numéro de téléphone : ','gras') + "%s\n" % format_tel(tel).ljust(12)
# Adresse
chbre = adher.chbre()
if chbre == 'EXT' :
# Adhérent extérieur
addr = adher.adresse()
if addr[0] :
f += coul(u'Adresse : ','gras')
f += addr[0] + u'\n'
if addr[1] != ' ' : f += u' ' + addr[1] + u'\n'
f+= u' ' + addr[2] + u' ' + addr[3] + '\n'
elif chbre == '????' :
f += coul(u'Chambre invalide\n','violet')
else :
# Chambre + prise (d'après annuaire)
etat, vlans, cablage = prise_etat(adher.chbre())
f += coul(u'Chambre : ','gras') + u"%s " % chbre
f += u'(%s)' % etat
f += u'\n'
# VLAN
if vlans :
f += coul(u'VLAN : ','gras') + u'%s' % vlans
f += u'\n'
# Études
if adher.etudes(1).isdigit() :
f += coul(u'Études : ','gras')+ "%s %s%s\n" % \
( adher.etudes(0), adher.etudes(1), adher.etudes(2) )
elif adher.etudes(0) :
f += coul(u'Études : ','gras')+ "%s %s %s\n" % \
( adher.etudes(0), adher.etudes(1), adher.etudes(2) )
# Solde
solde = adher.solde()
if solde :
f += coul(u'Solde : ','gras')
if solde < 0 :
f+= coul(str(solde).replace('.',','),'rouge')
else :
f += str(solde).replace('.',',')
f += u" Euros\n"
# Role dans l'assoce
d = adher.droits()
if d :
f += coul(u"Droits sur les serveurs : ",'gras') + ', '.join(d)
if adher.droitsGeles():
f += coul(u" (droits gelés car pas cotisé cette année)",'bleu')
f += u'\n'
# Adhésion
if adher.adhesion() > time():
f += coul(u"Adhésion jusqu'au %s" % strftime("%d/%m/%Y %H:%M:%S", localtime(adher.adhesion())), "vert")
f += u"\n"
elif config.periode_transitoire and (config.debut_periode_transitoire <= min(adher.adhesion(), adher.connexion()) <= config.fin_periode_transitoire):
f += coul(u"Fin d'adhésion, mais en sursis jusqu'au %s" % (strftime("%d/%m/%Y %H:%M:%S", localtime(config.fin_periode_transitoire)),), "rouge")
f += u"\n"
# Paiement
if adher.connexion() > time():
f += coul(u"Connexion jusqu'au %s" % strftime("%d/%m/%Y %H:%M:%S", localtime(min(adher.connexion(), adher.adhesion()))), "vert")
if adher.adhesion() < adher.connexion():
f += coul(u"(Théoriquement %s, sous réserve de réadhésion)" % (strftime("%d/%m/%Y %H:%M:%S", localtime(adher.connexion())),), "rouge")
f += u'\n'
f += _blacklist(adher)
f += _info(adher)
f += _hist(adher)
# Formatage des machines aussi
f += coul(u'Machine(s) : ','gras')
m = adher.machines()
if m :
f += u'\n' + list_machines(m)
else :
f += u'aucune'
f += u"\n"
f += coul(u'Facture(s) : ','gras')
m = adher.factures()
if m :
f += u'\n' + list_factures(m)
else :
f += u'aucune'
return f
def facture_details(facture) :
"""
Affichage du détail des propriétés d'une facture
"""
controle = facture.controle()
if controle == "TRUE":
controle = coul(u"Validée", "vert")
elif controle == "FALSE":
controle = coul(u"Invalide", "rouge")
else:
controle = u"N/A"
f=u''
# Fid
f+= coul(u'fid=%s ' % facture.id() ,'bleu')
# Proprio
f += coul(u'Proprio : ','gras') + "%s\n" % facture.proprietaire().Nom()
# Articles
f += coul(u"Articles :", "gras") + u"\n"
f += list_articles(facture)
f += u"\n"
f += coul(u"Total : ", "gras") + u"%s €\n" % facture.total()
# Mode de paiement
f += coul(u"Mode de paiement : ", "gras")
f += facture.modePaiement()
f += u" "
f += coul(u"Paiement reçu : ", "gras")
f += coul(facture.recuPaiement(), "vert") if facture.recuPaiement() else coul(u"Non", "rouge")
f += u"\n"
f += coul(u"Contrôle : ", "gras")
f += controle
return f
def list_articles(facture):
"""Liste en détail des articles d'une facture
"""
data = [[article['code'], article['designation'], article['nombre'], article['pu']] for article in facture.articles()]
return tableau(data,
titre = [u'Code', u'Désignation', u'Nombre', u"Prix unitaire"],
largeur = [10, '*', 10, 13],
alignement = ['c', 'g', 'c', 'c'])
clients_ipsec = None
def ipsec_ok(machine) :
"""Indique si une machine est correctement authentifiée"""
return False
def machine_details(machine) :
"""
Formatage du détail des propriétés d'une machine
"""
f = ''
f+= coul(u'mid=%s ' % machine.id(),'bleu')
# Type de machine
if isinstance(machine, MachineWifi): a = 'Machine wifi'
elif isinstance(machine, BorneWifi): a = 'Borne wifi'
else: a = 'Machine fixe'
f += coul(a + ' : ', 'gras')
f+= "%s " % machine.nom()
if machine.rid() != '':
f+= coul(u'rid=%s '% machine.rid(), 'bleu')
f+= "\n"
# Alias ?
alias = machine.alias()
if alias :
f += coul(u'Alias : ' ,'gras') + ', '.join(alias)
f+= '\n'
f+= coul(u'IP : ','gras') + "%s\t\t" %machine.ip()
f+= coul(u'MAC : ','gras') + "%s\n" %machine.mac()
if machine.ipv6() != None:
f+= coul(u'IPv6 : ','gras') + "%s\n" %machine.ipv6()
if len(machine.sshFingerprint()) > 0 and aff_ssh:
f += u"\n".join([coul(u'Fingerprint SSH : ', 'gras') + u"%s" % (i) for i in machine.sshFingerprint()])+"\n"
# Propriétaire
f+= coul(u'Propriétaire : ','gras')
try :
f += machine.proprio + coul(' (adhérent détruit)', 'jaune')
a = AssociationCrans()
except :
a = machine.proprietaire()
f += "%s" % a.Nom()
if a.chbre() in ['EXT', '????']:
f += ' (%s = %s)' % (a.idn, a.id())
elif a.chbre() != 'CRA':
f += " (%s)" % a.chbre()
else :
f += coul(u'\t\tPrise : ','gras') + machine.prise()
f+= '\n'
if isinstance(machine, MachineCrans):
n = machine.nombrePrises()
if n >= 0:
f += coul(u'Nombre de prises : ', 'gras')
f += "%d\n" % n
# Adhérent blacklisté ?
bl = a.blacklist_actif()
if bl :
f += coul(u'Restrictions sur adhérent : ','gras')
f += coul(u', '.join(bl),'rouge')
f += '\n'
# Borne wifi
if isinstance(machine, BorneWifi):
f += coul(u'Hotspot : ', 'gras')
f += machine.hotspot() and 'oui' or 'non'
position = machine.position()
if position:
f += coul(u'\t\t\tCoordonnées : ', 'gras')
f += u'%s°N, %s°E\n' % position
else:
f += '\n'
if (machine.prise() != 'N/A'):
try:
f += coul(u'VLANs : ', 'gras')
from hptools import sw_prise
f += ', '.join(sw_prise(machine.prise()).vlans())
f += '\n'
except:
pass
f += coul(u'Puissance : ','gras') + u"%4.d" % int(machine.puissance())
f += coul(u'\tCanaux : ', 'gras') + machine.canal()
f += coul(u'\tÉtat : ', 'gras')
if borne_etat(machine.nom()):
f += coul(u'borne active', 'vert')
f += '\n'
canal_clients = borne_clients_canal(machine.nom())
clients = canal_clients["mac-rssi"]
canal = canal_clients["canal"]
f += coul(u'\t Canal courant : ', 'gras') + u"%d" % canal
f += "\n"
# S'il y a des clients, on les liste
if clients and base:
f += coul(u'Clients : \n','gras')
for (client, rssi) in clients:
# On va chercher le nom correspondant à l'adresse MAC
res = base.search("mac=%s" % client)['machine']
authentification=""
if not res:
client_nom = '????'
client_chbre = '????'
else:
client_nom = ", ".join(["%s [%s]" % (x.nom().split(".")[0],
x.proprietaire().Nom()) for x in res])
# On va regarder si le client est authentifié
auth_ok = ipsec_ok(x)
if auth_ok != None:
if auth_ok:
authentification = "; IPSec: %s" % OK
else:
authentification = "; IPSec: %s" % ERREUR
client_chbre = " ".join(["%s " % (x.proprietaire().chbre()) for x in res])
# On va choisir la bonne couleur pour le RSSI
if rssi > -88:
coul_rssi = 'vert'
elif rssi > -93:
coul_rssi = 'jaune'
else:
coul_rssi = 'rouge'
f += u' %s (%s Chbre : %s) (RSSI: %s%s)\n' % (client, client_nom, client_chbre,
coul("%d" % rssi, coul_rssi),
authentification)
else:
f += coul(u'borne éteinte','rouge')
f += '\n'
if machine.nvram():
f += coul(u'NVRAM : ', 'gras')
f += ', '.join(machine.nvram()) + '\n'
if aff_ipsec and isinstance(machine, MachineWifi):
f += coul(u'Clef WiFi : ','gras') + machine.ipsec()
f += '\n'
# Ports spéciaux
if machine.portTCPin():
f += coul(u'Ports TCP ouvert ext->machine : ','gras') + ' '.join(machine.portTCPin()) + '\n'
if machine.portTCPout():
f += coul(u'Ports TCP ouvert machine->ext : ','gras') + ' '.join(machine.portTCPout()) + '\n'
if machine.portUDPin():
f += coul(u'Ports UDP ouvert ext->machine : ','gras') + ' '.join(machine.portUDPin()) + '\n'
if machine.portUDPout():
f += coul(u'Ports UDP ouvert machine->ext : ','gras') + ' '.join(machine.portUDPout()) + '\n'
# Exemption d'upload
if machine.exempt() :
f += coul(u'Upload exempté vers : ','gras') + ', '.join(machine.exempt()) + '\n'
f += _blacklist(machine)
f += _info(machine)
f += _hist(machine)
return f
def club_details(club) :
"""
Affichage du détail des propriétés d'un club
"""
f=''
# Cid
f+= coul(u'cid=%s ' % club.id() ,'bleu')
# Nom
f += coul(u'Nom : ','gras') + "%s\n" % club.Nom()
# responsale
f += coul(u'Responsable : ','gras') + "%s\n" % club.responsable().Nom()
# Imprimeurs
if len(club.imprimeurs()) > 0:
f += (coul(u'Imprimeurs : ', 'gras') + "%s\n" % ', '.join(map(lambda x:
club.search("aid=%s" % x)['adherent'][0].Nom(), club.imprimeurs())))
# État administratif
f += coul(u'État administratif : ','gras')
jour = True
if club.adhesion() < time():
jour = False
f += coul(u"Non adhérent." ,'violet')
if jour:
f += coul(u"à jour",'vert')
f += '\n'
# Chambre + prise
etat, vlans, cablage = prise_etat(club.chbre())
f += coul(u'Local : ','gras') + u"%s " % club.local()
f += u'(%s)' % etat
f += u'\n'
# VLAN
if vlans :
f += coul(u'VLAN : ','gras') + u'%s' % vlans
f += u'\n'
# Adhésion
if club.adhesion() > time():
f += coul(u"Adhésion jusque %s (connexion incluse)." % (strftime("%d/%m/%Y %H:%M:%S", localtime(club.adhesion())),), "vert")
f += '\n'
login = club.compte()
if login :
f += coul(u'Login : ','gras') + login
alias = club.alias()
if alias :
f += coul(u'\tAlias : ','gras') + ', '.join(alias)
f+= u'\n'
# Solde
solde = club.solde()
if solde:
f += coul(u'Solde : ', 'gras')
if solde < 0:
f+= coul(str(solde).replace('.', ','), 'rouge')
else:
f += str(solde).replace('.', ',')
f += u" Euros\n"
f += _blacklist(club)
f += _info(club)
f += _hist(club)
# Formatage des machines aussi
f += coul(u'Machine(s) : ','gras')
m = club.machines()
if m :
f += '\n' + list_machines(m)
else :
f += 'aucune'
f += u"\n"
f += coul(u'Facture(s) : ','gras')
m = club.factures()
if m :
f += u'\n' + list_factures(m)
else :
f += u'aucune'
return f
###########################################
# Fonctions annexes de formatage de données
def _blacklist(clas):
""" Formatage blackliste de la classe fournie """
if limit_aff_blacklist == 0: return ''
f = u''
bl = clas.blacklist()
bl.reverse()
nb = 0
for event in bl:
nb += 1
if nb > limit_aff_blacklist: break
if is_actif(event):
# Colorisation si sanction en cours
c = 'rouge'
else :
c = 'blanc'
event = event.split('$')
dates = strftime('%d/%m/%Y %H:%M', localtime(int(event[0])))
if event[1] == '-':
dates = u'à partir du %s' % dates
else:
dates = u'du %s au ' % dates
dates += strftime('%d/%m/%Y %H:%M', localtime(int(event[1])))
f += u"%s\n\t " % coul(u'%s : %s [%s]' % (dates, event[2], event[3]), c)
f = f[:-6] # suppression des espaces superflus
if len(bl) > limit_aff_blacklist:
f += ' [...]\n'
if f:
return coul(u'Blackliste : ', 'gras') + f
else:
return ''
def _info(clas) :
""" Formatage des remarques de la classe fournie """
f= u''
c = clas.info()
if c :
f += coul(u'Remarque :\n ' ,'gras')
f += u'\n '.join(c)
f += u'\n'
return f
def _hist(clas):
""" Formatage de l'historique de la classe fournie """
if limit_aff_historique == 0:
return u''
f = u''
h = clas.historique()
if h:
f += coul(u'Historique : ', 'gras')
notfirst = False
lim = - (limit_aff_historique + 1)
for a in h[:lim:-1]: # les `limit_aff_historique` (au plus) derniers éléments, en partant du dernier
if notfirst:
f += u' '
else:
notfirst = True
try:
# TODO: clarifier ça, a priori `a` c'est déjà un unicode
# on force l'encodage ici sinon il est fait au moment de l'impression a
# l'ecran et il empeche l'affichage de toutes les infos
f += u'%s\n' % a
except Exception, e:
if debug:
f += coul(u'*** non affichable [%s] ***\n' % str(e), 'rouge')
else:
f += coul(u'*** non affichable ***\n', 'rouge')
if len(h) > limit_aff_historique:
f += u' [...]\n'
return f
def __bases_machines(m) :
""" Retourne [ type de la machines, blacklist ] """
#Type
t = 'inconnu'
if m.rid():
t, _ = ridtools.find_rid_plage(int(m.rid()))
# Déconnectée ?
b = m.blacklist_actif()
if not b :
bl = '-'
elif len(b) == 1 :
bl = coul(b[0],'rouge')
else :
bl = coul(u'cf détails','rouge')
return t , bl
def borne_etat(borne) :
"""Renvoie vrai si la borne est up, faux sinon"""
# On utilise fping, en cas d'erreur, on considere
# que la borne est down (alors qu'elle peut simplement
# ne pas exister)
try:
retour = os.system("fping6 -q -c 1 %s > /dev/null 2> /dev/null" % borne)
if (retour == 0):
return True
else:
retour = os.system("fping -q -c 1 %s > /dev/null 2> /dev/null" % borne)
return retour == 0
except:
return False
def borne_clients_canal(borne) :
"""Renvoie la liste des adresses MAC associées à la borne ainsi que le canal.
Chaque adresse MAC est en fait contenue dans un couple comprenant
l'adresse MAC et le RSSI du client associé.
On en profite pour renvoyer également le canal en cours de la
borne. On fait cela dans la même fonction car cela évite de faire
deux connexions ssh (ce qui est fort coûteux).
Au final, on renvoie un dictionnaire
- mac-rssi: une liste de couples (MAC, RSSI)
- canal: le canal en cours
"""
macs = []
canal = -1
return {"canal": canal, "mac-rssi": macs} # Suite à réimplémenter
# Uniquement possible pour les admins et si on a la clef
for clef in ["/etc/wifi/ssh/wifi","/usr/scripts/gestion/clef-wifi"]:
if os.path.isfile(clef) and user_tests.isadm():
try:
wl = subprocess.Popen("ssh -o StrictHostKeyChecking=no -i %s root@%s 'cat /tmp/auth-mac.dump ; echo -n \"CANAL \" ; /usr/sbin/nvram get wl0_channel'" % (clef, borne), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
wl.stdin.close()
for line in wl.stdout.readlines():
# Chaque ligne est de la forme
# 00:11:22:33:44:55 -20
line = line.strip().split()
if line[0] == "CANAL":
canal = int(line[-1])
else:
macs.append((line[0].strip(), int(line[1].strip())))
except:
pass
break # Pas la peine d'essayer une autre clef
return {"canal": canal, "mac-rssi": macs}
def prise_etat(chbre) :
"""
Retoune un triplet contenant l'état de la prise associée à la [chbre],
les VLANs activés ainsi que l'état du brassage.
"""
f = u''
vlans = u''
cablage = u''
try:
# On met aussi l'état
prise = sw_chbre(chbre)
vlans += ', '.join(prise.vlans())
f += u'prise %s' % prise.prise_brute
rows, cols = get_screen_size()
if prise.is_up() :
f += u', ' + coul(u'machine branchée','vert')
reste_cols = cols - 45
f+=', '
macs = prise.show_prise_mac()
if len(macs) == 0:
if reste_cols < 20 :
# Faut aller à la ligne
f += u'\n '
f += coul(u'aucune MAC détectée', 'jaune')
else:
if len(macs) == 1 and reste_cols > 25 :
# Une seule mac et on a assez de place
f += u"MAC: %s" % macs[0]
else :
# On va à la ligne
# Combien d'adresses MAC peut-on mettre par ligne ?
# Une adresse MAC =~ 20 caracteres
cols -= 17 # On met 15espaces devant chaque ligne
parligne = int(cols/20)
count = 0
while len(macs) > 0:
if count % parligne == 0:
f += u'\n MACs: '
else:
f += u', '
f += u"%s" % macs.pop()
count += 1
elif not prise.is_enable():
if prise.is_fake():
f += u', ' + coul(u"Chambre virtuelle", "violet")
else:
f += u', ' + coul(u'prise désactivée','rouge')
else :
f += u', activée, lien non détecté'
try:
extra_info = annuaires_pg.chbre_commentaire(chbre[0],chbre[1:]).decode('utf-8')
if extra_info:
f += u'\n' + ' '*10 + extra_info
except ValueError:
pass
except ConversationError, r:
# Switch non manageable ou down
f += ', '
f+= {'Unknown host (No such file or directory)' :
u'switch non manageable',
'Failure in sendto (Operation not permitted)' :
u'infos prise non dispo : filtre vers adm par fw',
'sh: line 1: command not found' :
u'infos prise non dispo depuis cette machine'
}.get(r.args[0],r.args[0])
except ValueError, r :
f = r
except :
f = u'infos prise non dispo : erreur interne'
return f, vlans, cablage
##############################################################################
## Partie dévolue au système de recherche
def __usage_brief(err='') :
""" Message d'erreur """
if err : cprint(err,'gras')
cprint(u"Pour obtenir de l'aide sur l'utilisation de ce programme utilisez l'option -h")
sys.exit(2)
def __usage() :
""" Comment ca marche ? """
liste = []
accu = ""
longueur = 0
# Champs automatiques
champs = []
for c in base.auto_search_champs.values():
for champ in c:
if champ not in champs:
champs.append(champ)
for champ in champs:
coul_champ = coul(champ, "bleu")
if accu == "":
accu = coul_champ
longueur = len(champ)
elif longueur + 2 + len(champ) < 80:
longueur += 2 + len(champ)
accu += ", " + coul_champ
else:
liste.append(accu)
accu = coul_champ
longueur = len(champ)
# Champs manuels
champs = []
for c in base.non_auto_search_champs.values():
for champ in c:
if champ not in champs:
champs.append(champ)
for champ in champs:
if longueur + 2 + len(champ) < 80:
longueur += 2 + len(champ)
accu += ", " + champ
else:
liste.append(accu)
accu = champ
longueur = len(champ)
# Dernière ligne
liste.append(accu)
cprint(__doc__ % { 'prog': sys.argv[0].split('/')[-1].split('.')[0],
'champs_rech': '\n'.join(liste),
'limit_aff_details': limit_aff_details,
'limit_aff_historique': limit_aff_historique,
'limit_aff_blacklist': limit_aff_blacklist })
sys.exit(0)
def __recherche() :
"""
Recherche et affichage des résultats à partir des options founies (sys.argv)
"""
global aff_ipsec, aff_ssh, limit_aff_details, limit_aff_historique, limit_aff_blacklist, debug, aff_ipv6
# Récupération des options
if len(sys.argv) == 1 :
# Pas d'option fournie
__usage_brief()
try :
options, arg = getopt.getopt(sys.argv[1:], 'hamcs6tbil:L:d:', [ 'debug', 'help', 'adherent', 'machine', 'club' , 'tech', 'bornes', 'limit=', 'limit-historique=', 'limit-blacklist=', 'ipsec', 'crans', 'ssh', 'ipv6', 'servers', 'adm' ])
except getopt.error, msg :
__usage_brief(unicode(msg))
# Traitement des options
only_adh = 0
only_mac = 0
only_club = 0
only_bornes = 0
only_crans = 0
only_servers = 0
only_adm = 0
mtech = 0
for opt, val in options :
if opt == '-h' or opt=='--help' :
__usage()
elif opt =='--debug' :
# Mode debug
debug = 1
elif opt == '-l' or opt =='--limit':
# Passage mode condensé, mode détaillé
try : limit_aff_details = int(val)
except :
__usage_brief(u'Valeur du paramètre %s incorrect (doit être un entier positif)' % opt)
elif opt == '-L' or opt =='--limit-historique':
# Limitation du nombre de lignes d'historique
try : limit_aff_historique = int(val)
except :
__usage_brief(u'Valeur du paramètre %s incorrect (doit être un entier positif)' % opt)
elif opt == '-d' or opt =='--limit-blacklist':
# Limitation du nombre de lignes d'historique
try : limit_aff_blacklist = int(val)
except :
__usage_brief(u'Valeur du paramètre %s incorrect (doit être un entier positif)' % opt)
elif opt in [ '-a', '--adherent' ] :
only_adh = 1
cprint(u"Affichage limité aux adhérents.")
elif opt in [ '-m', '--machine' ] :
only_mac = 1
cprint(u"Affichage limité aux machines.")
elif opt in [ '-c', '--club' ] :
only_club = 1
cprint(u"Affichage limité aux clubs.")
elif opt == '--crans' :
only_crans = 1
mtech = 1
cprint(u"Affichage limité aux machines du crans.")
elif opt in [ '-b', '--bornes' ] :
only_bornes = 1
cprint(u"Affichage limité aux bornes wifi.")
# On va tenter de limiter un peu la recherche
if not arg :
# Recherche initiale sans critère
arg = [ 'canal=*&host=*.crans.org']
elif arg[0].find('=')!=-1 :
# Recherche avec critères
arg += [ '&canal=*' ]
elif opt in [ '--servers' ]:
only_servers = 1
cprint(u'Affichage limité aux servers crans')
if not arg:
arg = [ 'ip=138.231.136.*' ]
elif arg[0].find('=') != -1:
arg += [ '&ip=138.231.136.*' ]
elif opt in [ '--adm' ]:
cprint(u'Affichage limité aux machines adm')
only_adm = 1
if not arg:
arg = [ 'host=*.adm.crans.org' ]
elif arg[0].find('=') != -1:
arg += [ '&host=*.adm.crans.org' ]
elif opt in [ '-t', '--tech' ] :
# Format affichage des machines
mtech = 1
elif opt in [ '-i', '--ipsec' ] :
# Affichage des clefs ipsec
aff_ipsec = 1
elif opt in [ '-s', '--ssh' ]:
aff_ssh = 1
elif opt in [ '-6', '--ipv6' ]:
aff_ipv6 = 1
if only_adh + only_mac + only_club + only_bornes > 1 :
__usage_brief(u'Options utilisées incompatibles')
arg = ' '.join(arg)
# Cas particulier de recherche sur prise
if arg.count('=') == 1 and arg.split('=')[0] == 'prise' :
prise = arg.split('=')[1]
# Récupération de la chambre
try:
from annuaires_pg import reverse
chbre = reverse(prise[0].lower(), prise[1:])
except KeyError:
chbre=None
if chbre:
if len(chbre) != 1 :
cprint(u"Prise correspondante à plusieurs chambres %s" % ' '.join(chbre))
return
# On fait la recherche sur la chambre
chbre= prise[0] + chbre[0]
#cprint(u"Recherche sur chambre %s" % chbre)
arg = u'chbre=%s' % chbre
# sinon on ne fait rien et on recherche sur le champ prise
try:
if only_crans :
res = { 'machine' : AssociationCrans().machines() , 'adherent' : [] , 'club' : [] }
else :
if not arg :
# Pas de chaine de recherche fournie
__usage_brief(u'Chaine de recherche incorrecte.')
res = base.search(arg)
except ValueError, c :
__usage_brief(c.args[0])
# Traitement du résultat
if not res['adherent'] and not res['machine'] and not res['club'] and not res['facture']:
# Pas de résultat dans la base
# Recherche sur chambre ?
if arg.count('=') == 1 and arg.split('=')[0] == 'chbre' :
# Affichage des infos de la chambre
chbre = arg.split('=')[1]
cprint(u"Chambre %s inoccupée ou invalide (%s)" % (chbre,prise_etat(chbre)[0]))
else :
cprint(u"Aucun résultat trouvé.")
sys.exit(3)
# L'affichage souhaité a été précisé ?
elif only_bornes or only_servers or only_adm:
if not res['machine'] :
cprint(u'Aucun résultat à afficher')
sys.exit(4)
else :
if len(res['machine']) > limit_aff_details :
cprint(list_spec(res['machine']))
else :
aff(res['machine'])
elif only_adh :
if res['adherent'] : aff(res['adherent'])
elif res['machine'] :
to_aff=[]
traite =[]
for m in res['machine'] :
a = m.proprietaire()
if a.idn != 'aid' or a.id() in traite : continue
traite.append(a.id())
to_aff.append(m.proprietaire())
if not(to_aff) :
cprint(u'Aucun résultat à afficher')
sys.exit(4)
aff(to_aff)
elif res['club'] :
cprint(u'Aucun résultat à afficher')
sys.exit(4)
elif only_mac :
if res['machine'] : aff(res['machine'],mtech)
else :
to_aff = []
for a in res['adherent'] + res['club'] :
to_aff += a.machines()
aff(to_aff)
elif only_club :
if res['club'] : aff(res['club'])
elif res['machine'] :
to_aff=[]
traite =[]
for m in res['machine'] :
a = m.proprietaire()
if a.idn != 'cid' or a.id() in traite : continue
if a.id() in traite : continue
traite.append(a.id())
to_aff.append(m.proprietaire())
if not(to_aff) :
cprint(u'Aucun résultat à afficher')
sys.exit(4)
aff(to_aff)
elif res['adherent'] :
cprint(u'Aucun résultat à afficher')
sys.exit(4)
elif only_crans :
cprint(u"Résultats trouvés parmi les machines :", 'cyan')
aff(res['machine'],mtech)
# Non : on affiche tout.
else :
if res['adherent'] :
cprint(u"Résultats trouvés parmi les adhérents :", 'cyan')
aff(res['adherent'])
if res['machine'] :
cprint(u"Résultats trouvés parmi les machines :", 'cyan')
aff(res['machine'],mtech)
if res['club']:
cprint(u"Résultats trouvés parmi les clubs :", 'cyan')
aff(res['club'])
if res['facture']:
cprint(u"Résultats trouvés parmi les factures :", 'cyan')
aff(res['facture'])
if __name__ == '__main__' :
global debug
debug = 0
base = crans_ldap()
try :
__recherche()
except KeyboardInterrupt :
cprint(u"Recherche interrompue par l'utilisateur.")
sys.exit(255)
except SystemExit, c :
# Fin
sys.exit(c)
except :
cprint(u"""Une erreur fatale s'est produite durant l'exécution.
Pour l'amélioration de ce programme merci de prévenir nounou en spécifiant la
marche à suivre pour reproduire cette erreur.""")
raise
if debug :
cprint('-'*40)
cprint(u'Détails techniques :')
import traceback
# On veut le traceback sur la sortie standard
sys.stderr = sys.stdout
traceback.print_exc()
sys.exit(1)