Import initial !

darcs-hash:20040831131446-d1718-0734aa73d3b8481b3b4b861e447e85128e488e8a.gz
This commit is contained in:
bernat 2004-08-31 15:14:46 +02:00
parent c9083dfd86
commit 6626a44f15
20 changed files with 6494 additions and 0 deletions

164
gestion/affich_tools.py Executable file
View file

@ -0,0 +1,164 @@
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" Collection de fonction/classe pour avoir un bel affichage
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
import sys, sre, os, tempfile
def dialog(backtitle,arg) :
""" Affiche la boite de dialogue défine avec les arguments fournis
(cf man dialog)
si tout se déroule bien retourne :
[ 0, [ reponse(s) ] ]
si annulatin retourne :
[ 1, [] ]
si appui sur ESC demande confirmation de l'abandon et exécute sys.exit(0)
si erreur dans les arguments raise RuntimeError
"""
f = tempfile.NamedTemporaryFile()
cmd = u'/usr/bin/dialog --backtitle "%s" --cancel-label "Retour" %s 2>%s' % (backtitle,arg,f.name)
res = os.system(cmd.encode('iso-8859-15','ignore'))
if res == 256 :
# Annuler
f.close()
return [ 1, [] ]
# Lecture du fichier de résultat et effacement
try:
result=f.readlines()
f.close()
except :
result = [ "n'importe quoi", '']
res = 65280
# Traitement
if res==65280 and result:
# Erreur dans les arguments
raise RuntimeError( arg, result[1].strip() )
elif res==65280 :
# Appui sur ESC
arg1 = u'--title "Annulation" --yesno "Quitter ?\nLes dernières modifications seront perdues." 6 48'
print backtitle
cmd = u'/usr/bin/dialog --backtitle "%s" %s' % (backtitle,arg1)
res = os.system(cmd.encode('iso-8859-15','ignore') )
if res==0 : sys.exit(0)
else : return dialog(backtitle,arg)
elif not result : result=['']
return [ 0, result ]
def coul(txt,col):
"""
Retourne la chaine donnée encadrée des séquences qui
vont bien pour obtenir la couleur souhaitée
Les couleur sont celles de codecol
Il est possible de changer la couleur de fond grace aux couleur f_<couleur>
"""
codecol={'rouge' : 31 , 'vert' : 32 , 'jaune' : 33 , 'bleu': 34 , 'violet' : 35 , 'cyan' : 36 , 'gras' : 50}
try :
if col[:2]=='f_' : add=10; col=col[2:]
else : add=0
txt = "\033[1;%sm%s\033[1;0m" % (codecol[col]+add,txt)
finally :
return txt
OK = coul('OK','vert')
WARNING = coul('WARNING','jaune')
ERREUR = coul('ERREUR','rouge')
def cprint(txt,col):
print coul(txt,col)
def tableau(largeurs,data) :
"""
retourne une chaine formatée repésentant un tableau
largeur est la liste des largeurs des colones
data est une liste de tuples :
[ ( données entète), (données ligne1), .... ]
"""
sep_col = u'|'
# Ligne de séparation entète corps
s=u'\n'
for l in largeurs :
s+= sep_col + u'-'*l
s += sep_col + u'\n'
nb_cols = len(largeurs)
# Remplissage tableau
f=u''
for ligne in data :
for i in range(0, nb_cols) :
f+= sep_col
# Centrage
l = len(sre.sub('\x1b\[1;([0-9]|[0-9][0-9])m','',ligne[i])) # Longeur sans les chaines de formatage
if l >= largeurs[i] :
f += ligne[i]
else :
n = largeurs[i] - l
f += u' '*(n/2) + ligne[i] + u' '*(n/2 + n%2)
f+= sep_col + u'\n'
# Final
f = f.replace(u'\n',s,1) # Insertion du séparateur entète - corps
return f[:-1] # Supression du \n final
def prompt(prompt, defaut=''):
""" Pose la question prompt, retourne la réponse """
sys.stdout.write(coul(prompt,'gras'))
if defaut :
sys.stdout.write(" ["+defaut+"]")
sys.stdout.write(" ")
v=sys.stdin.readline().strip()
if not v : v = defaut
return v
class anim :
""" Permet de créer une animation :
truc................./
truc.................-
truc.................\
truc.................|
ou une barre de progression si le nombre total d'itéraitions est founi.
"""
def __init__(self,truc,iter=0) :
""" Affichage de :
truc................."""
self.txt = truc + '.'*(40-len(truc))
self.c = 1
self.iter = iter
sys.stdout.write(self.txt)
sys.stdout.flush()
def reinit(self) :
""" Efface la ligne courrante et
affiche : truc................. """
sys.stdout.write('\r' + self.txt)
if self.iter :
sys.stdout.write(' '*28)
sys.stdout.write('\r' + self.txt)
sys.stdout.flush()
def cycle(self) :
""" Efface la ligne courrante et
affiche : truc..................?
? caratère variant à chaque appel """
sys.stdout.write('\r' + self.txt)
if self.iter!=0 :
sys.stdout.write('[')
av = float(self.c) / float(self.iter)
n = int(20 * av)
sys.stdout.write('='*n)
sys.stdout.write('>')
sys.stdout.write(' '*(20 - n))
sys.stdout.write('] %3i%%' % int(100 * av) )
else :
sys.stdout.write('/-\|'[self.c%4])
sys.stdout.flush()
self.c += 1

508
gestion/annuaires.py Executable file
View file

@ -0,0 +1,508 @@
#!/usr/bin/python
# -*- coding: iso8859-15 -*-
aide={
'g' : "appart du RDC=G901" ,
'i' : "Chambres 209g + 309g + 105g + 403g + 110d + 312 + 007g +007d + locaux clubs sur prise 150.",
'h' : "Chambres 7 et 8 et les locaux clubs sur la prise 046"
}
# Toute chambre ne commencant pas par 3 chiffres sera considéré comme un local club
# En conséquence les locaux club ne devront pas commencer par 3 chiffres.
#Pour le G :
# le signe - indique un cable 10 Mbps
# et XXX = prise vide
#Les chambres G121, G241 et G201 ont chacune 2 cables en 100 Mbps qui
#sont branches sur les switchs.
# Correspondance chbre -> prise
chbre_prises={ 'a' :
{'101d':'101d' , '101g':'101g' , '102':'102' , '103':'103' ,
'104':'104' , '105':'105' , '106':'106' , '107':'107' ,
'108':'108' , '109':'109' , '110d':'110d' , '110g':'110g' ,
'111d':'111d' , '111g':'111g' , '112d':'112d' , '112g':'112g' ,
'113d':'113d' , '113g':'113g' , '114':'114' , '115':'115' ,
'201d':'201d' , '201g':'201g' , '202':'202' , '203':'203' ,
'204':'204' , '205':'205' , '206':'206' , '207':'207' ,
'208':'208' , '209':'209' , '210d':'210d' , '210g':'210g' ,
'211d':'211d' , '211g':'211g' , '212d':'212d' , '212g':'212g' ,
'213d':'213d' , '213g':'213g' , '214':'214' , '215':'215' ,
'301d':'301d' , '301g':'301g' , '302':'302' , '303':'303' ,
'304':'304' , '305':'305' , '306':'306' , '307':'307' ,
'308':'308' , '309':'309' , '310d':'310d' , '310g':'310g' ,
'311d':'311d' , '311g':'311g' , '312d':'312d' , '312g':'312g' ,
'313d':'313d' , '313g':'313g' , '314':'314' , '315':'315' ,
'401d':'401d' , '401g':'401g' , '402':'402' , '403':'403' ,
'404':'404' , '405':'405' , '406':'406' , '407':'407' ,
'408':'408' , '409':'409' , '410d':'410d' , '410g':'410g' ,
'411d':'411d' , '411g':'411g' , '412d':'412d' , '412g':'412g' ,
'413d':'413d' , '413g':'413g' , '414':'414' , '415':'415' ,
'501d':'501d' , '501g':'501g' , '502d':'502d' , '502g':'502g' ,
'503d':'503d' , '503g':'503g' , '504d':'504d' , '504g':'504g' ,
'505d':'505d' , '505g':'505g' , '506d':'506d' , '506g':'506g' ,
'507d':'507d' , '507g':'507g' , '507d':'507d' , '508g':'508g' ,
'601d':'601d' , '601g':'601g' , '602d':'602d' , '602g':'602g' ,
'603d':'603d' , '603g':'603g' , '604d':'604d' , '604g':'604g' ,
'605d':'605d' , '605g':'605g' , '606d':'606d' , '606g':'606g' ,
'607d':'607d' , '607g':'607g' , '607d':'607d' , '608g':'608g' } ,
'b' :
{'cl0':'047' , 'cl1':'045' ,
'105':'001' , '106':'002' , '107':'003' , '108':'004' ,
'120g':'005' , '120d':'006' , '121g':'007' , '121d':'008' ,
'205':'009' , '206':'010' , '207':'011' , '208':'012' ,
'109':'013' , '110':'014' , '111':'015' , '112':'016' ,
'122g':'017' , '122d':'018' , '123':'019' , '124':'020' ,
'209':'021' , '210':'022' , '211':'023' , '212':'024' ,
'cl6':'025' , 'cl5':'026' , 'cl4':'027' , 'cl3':'028' ,
'101g':'029' , '101d':'030' , '102g':'031' , '102d':'032' ,
'113':'033' , '114':'034' , '115':'035' , '116':'036' ,
'103g':'037' , '103d':'038' , '104g':'039' , '104d':'040' ,
'117':'041' , '118':'042' , '119g':'043' , '119d':'044' ,
'220g':'101' , '220d':'102' , '221g':'103' , '221d':'104' ,
'305':'105' , '306':'106' , '307':'107' , '308':'108' ,
'320g':'109' , '320d':'110' , '321g':'111' , '321d':'112' ,
'222g':'113' , '222d':'114' , '223':'115' , '224':'116' ,
'309':'117' , '310':'118' , '311':'119' , '312':'120' ,
'322g':'121' , '322d':'122' , '323':'123' , '324':'124' ,
'201g':'125' , '201d':'126' , '202g':'127' , '202d':'128' ,
'213':'129' , '214':'130' , '215':'131' , '216':'132' ,
'301g':'133' , '301d':'134' , '302g':'135' , '302d':'136' ,
'203g':'137' , '203d':'138' , '204g':'139' , '204d':'140' ,
'217':'141' , '218':'142' , '219g':'143' , '219d':'144' ,
'303g':'145' , '303d':'146' , '304g':'147' , '304d':'148' ,
'405':'201' , '406':'202' , '407':'203' , '408':'204' ,
'420g':'205' , '420d':'206' , '421g':'207' , '421d':'208' ,
'505g':'209' , '505d':'210' , '506g':'211' , '506d':'212' ,
'411':'213' , '412':'214' , '410':'215' , '409':'216' ,
'422g':'217' , '422d':'218' , '423':'219' , '424':'220' ,
'507g':'221' , '507d':'222' , '508g':'223' , '508d':'224' ,
'313':'225' , '314':'226' , '316':'227' , '315':'228' ,
'401g':'229' , '401d':'230' , '402g':'231' , '402d':'232' ,
'413':'233' , '414':'234' , '415':'235' , '416':'236' ,
'317':'237' , '318':'238' , '319g':'239' , '319d':'240' ,
'403g':'241' , '403d':'242' , '404g':'243' , '404d':'244' ,
'417':'245' , '418':'246' , '419g':'247' , '419d':'248' ,
'511g':'301' , '511d':'302' , '512g':'303' , '512d':'304' ,
'603g':'305' , '603d':'306' , '604g':'307' , '604d':'308' ,
'609g':'309' , '609d':'310' , '610g':'311' , '610d':'312' ,
'513g':'313' , '513d':'314' , '514g':'315' , '514d':'316' ,
'605g':'317' , '605d':'318' , '606g':'319' , '606d':'320' ,
'611g':'321' , '611d':'322' , '612g':'323' , '612d':'324' ,
'501g':'325' , '501d':'326' , '502g':'327' , '502d':'328' ,
'607g':'329' , '607d':'330' , '608g':'331' , '608d':'332' ,
'613g':'333' , '613d':'334' , '614g':'335' , '614d':'336' ,
'503g':'337' , '503d':'338' , '504g':'339' , '504d':'340' ,
'509g':'341' , '509d':'342' , '510g':'343' , '510d':'344' ,
'601g':'345' , '601d':'346' , '602g':'347' , '602d':'348' } ,
'c' :
{'312d':'001', '313g':'002' , '313d':'003' , '314':'004' ,
'315':'005' , '401g':'006' , '401d':'007' , '402g':'008' ,
'402d':'009' , '403g':'010' , '406g':'011' , '406d':'012' ,
'407g':'013' , '407d':'014' , '408g':'015' , '408d':'016' ,
'409g':'017' , '409d':'018' , '410g':'019' , '410d':'020' ,
'411g':'021' , '411d':'022' , '414':'023' , '415':'024' ,
'501g':'025' , '501d':'026' , '502':'027' , '503':'028' ,
'601g':'029' , '601d':'030' , '602':'031' , '603':'032' ,
'403d':'033' , '404g':'034' , '404d':'035' , '412g':'036' ,
'412d':'037' , '413g':'038' , '413d':'039' , '405g':'040' ,
'405d':'041' , '311d':'042' , '312g':'043' , '311g':'044' ,
'310d':'045' , '310g':'046' , '309d':'047' , '309g':'048' ,
'103d':'101' , '111d':'102' , '103g':'103' , '111g':'104' ,
'102d':'105' , '110d':'106' , '102g':'107' , '110g':'108' ,
'101d':'109' , '109d':'110' , '101g':'111' , '109g':'112' ,
'205d':'113' , '213d':'114' , '307g':'115' , '205g':'116' ,
'213g':'117' , '306d':'118' , '204d':'119' , '212d':'120' ,
'306g':'121' , '204g':'122' , '212g':'123' , '305d':'124' ,
'203d':'125' , '211d':'126' , '305g':'127' , '203g':'128' ,
'211g':'129' , '304d':'130' , '104g':'131' , '112g':'132' ,
'206g':'133' , '214':'134' , '307d':'135' , '104d':'136' ,
'112d':'137' , '105g':'138' , '113g':'139' , '105d':'140' ,
'113d':'141' , '106g':'142' , '114g':'143' , '106d':'144' ,
'114d':'145' , '107g':'146' , '201g':'147' , '107d':'148' ,
'304g':'201' , '303d':'202' , '303g':'203' , '210d':'204' ,
'210g':'205' , '209d':'206' , '302d':'207' , '209g':'208' ,
'208d':'209' , '302g':'210' , '208g':'211' , '301d':'212' ,
'207d':'213' , '301g':'214' , '207g':'215' , '308d':'216' ,
'206d':'217' , '215':'218' , '308g':'219' , '108d':'220' ,
'202d':'221' , '108g':'222' , '202g':'223' , '201d':'224' } ,
'g' :
{'257':'001' , '180':'002-', '183':'003' , '135':'004' ,
'298':'005' , '174':'006' , 'XXX':'007-', '138':'008' ,
'136':'009' , '127':'010' , '176':'011' , '139':'012-' ,
'132':'013' , '172':'014' , '137':'015' , '186':'016' ,
'125':'017-', '181':'018' , '128':'019' , '178':'020' ,
'175':'021' , '130':'022' , '177':'023' , '185':'024' ,
'182':'025' , '131':'026' , '179':'027' , '299':'028' ,
'259':'029-', '310':'030' , '300':'031' , '251':'032-' ,
'295':'033' , 'XXX':'034-', '306':'035-', '058':'036' ,
'304':'037' , '254':'038' , '261':'039' , '002':'040' ,
'308':'041-', '252':'042' , '255':'043' , '256':'044' ,
'253':'045' , '297':'046' , '260':'047' , '015':'048' ,
'247':'101-', '301':'102-', '249':'103' , '049':'104' ,
'014':'105' , '054':'106' , '061':'107-', '010':'108' ,
'005':'109' , '013':'110' , '006':'111' , '008':'112-' ,
'009':'113-', 'XXX':'114-', '001':'115-', '052':'116-' ,
'012':'117-', '053':'118' , '004':'119' , '066':'120' ,
'076':'121' , '118':'122' , '119':'123' , '072':'124' ,
'065':'125' , '073':'126' , '114':'127' , '112':'128' ,
'123':'129' , '120':'130' , '068':'131' , '113':'132' ,
'XXX':'133' , '055':'134' , '117':'135-', '074':'136' ,
'063':'137' , '077':'138' , '071':'139' , '111':'140' ,
'121':'141' , '901':'142' , '122':'143' , '050':'144' ,
'064':'145' , '067':'146' , '115':'147' , '124':'148' ,
'059':'201' , '258':'202' , '307':'203' , 'XXX':'204' ,
'187':'205' , '246':'206-', '190':'207' , '193':'208' ,
'195':'209' , '309':'210' , '189':'211' , '242':'212' ,
'238':'213' , 'XXX':'214' , '241':'215' , '244':'216-' ,
'198':'217-', '199':'218-', '192':'219' , '239':'220' ,
'245':'221' , '303':'222' , '305':'223' , '191':'224' ,
'XXX':'225' , 'XXX':'226' , 'XXX':'227' , 'XXX':'228' ,
'XXX':'229' , 'XXX':'230' , 'XXX':'231' , 'XXX':'232' ,
'XXX':'233' , 'XXX':'234' , 'XXX':'235' , 'XXX':'236' ,
'XXX':'237' , 'XXX':'238' , 'XXX':'239' , 'XXX':'240' ,
'XXX':'241' , 'XXX':'242' , 'XXX':'243' , 'XXX':'244' ,
'XXX':'245' , 'XXX':'246' , 'XXX':'247' , 'XXX':'248' ,
'288':'401' , '204':'402' , '206':'403' , '207':'404-' ,
'208':'405' , '210':'406' , '212':'407' , '213':'408-' ,
'216':'409' , '217':'410' , '218':'411' , '220':'412' ,
'222':'413' , '223':'414' , '224':'415' , '226':'416-' ,
'231':'417' , '233':'418' , '234':'419' , '235':'420' ,
'236':'421' , '237':'422-', '263':'423' , '264':'424' ,
'265':'425' , '266':'426' , '267':'427' , '269':'428' ,
'270':'429' , '271':'430' , '272':'431' , '273':'432' ,
'274':'433' , '275':'434' , '277':'435' , '279':'436' ,
'281':'437' , '282':'438' , '285':'439' , '286':'440' ,
'287':'441' , '149':'442' , '289':'443' , '290':'444' ,
'291':'445' , '292':'446' , '293':'447' , '221':'448' ,
'140':'501' , '141':'502' , '142':'503' , '143':'504' ,
'XXX':'505' , '147':'506-', '165':'507' , 'XXX':'508' ,
'150':'509' , '151':'510' , '152':'511' , '153':'512' ,
'154':'513' , '155':'514-', '156':'515' , '157':'516-' ,
'158':'517' , '144':'518' , '160':'519' , '161':'520' ,
'162':'521-', '163':'522' , '164':'523' , 'XXX':'524' ,
'166':'525' , '167':'526-', '168':'527-', '169':'528' ,
'280':'529' , '171':'530' , '101':'531' , '173':'532' ,
'294':'533' , '026':'534' , '225':'535' , '031':'536' ,
'146':'537' , '148':'538' , 'XXX':'539' , 'XXX':'540' ,
'XXX':'541' , 'XXX':'541' , 'XXX':'543' , 'XXX':'544' ,
'XXX':'545' , '096':'546-' , '903':'547' , '036':'548' ,
'106':'601-', '229':'602' , 'Med':'603' , '089':'604-' ,
'092':'605' , '016':'606' , '017':'607' , '018':'608' ,
'019':'609' , '200':'610' , '024':'611' , '025':'612' ,
'028':'613' , '030':'614' , '034':'615' , '035':'616-' ,
'037':'617' , '038':'618' , '039':'619' , '045':'620-' ,
'041':'621' , '042':'622' , '043':'623' , '044':'624' ,
'XXX':'625' , '046':'626' , '078':'627-', '079':'628' ,
'080':'629' , '081':'630' , '082':'631' , '083':'632-' ,
'084':'633' , '085':'634-', '086':'635' , '088':'636-' ,
'090':'637' , '091':'638' , '095':'639' , '097':'640-' ,
'103':'641' , '102':'642' , '107':'643-', '109':'644' ,
'227':'645' , '201':'646' , '202':'647' , '203':'648' } ,
'h' :
{'007g':'046' , '007d':'046' , '008g':'046' , '008d':'046' ,
'cl2':'046' , 'cl3':'046' , 'cl4':'046' , 'cl1':'046' ,
'004g':'046' ,
'301':'101' , '302':'103' , '303':'105' , '304':'107' ,
'305':'109' , '306':'111' , '307':'113' , '308':'115' ,
'309g':'117' , '309d':'119' , '310g':'121' , '310d':'123' ,
'311g':'125' , '311d':'127' , '312':'129' , '313':'131' ,
'314':'133' , '315':'135' , '316':'137' , '317':'139' ,
'318':'141' , '319':'143' ,
'401g':'102' , '401d':'104' , '402g':'106' , '402d':'108' ,
'403g':'110' , '403d':'112' , '404g':'114' , '404d':'116' ,
'405g':'118' , '405d':'120' , '406g':'122' , '406d':'124' ,
'407g':'126' , '407d':'128' , '408g':'130' , '408d':'132' ,
'409g':'134' , '409d':'136' , '410g':'138' , '410d':'140' ,
'411g':'142' , '411d':'144' ,
'101g':'001' , '101d':'003' , '102g':'005' , '102d':'007' ,
'103g':'009' , '103d':'011' , '104g':'013' , '104d':'015' ,
'105g':'017' , '105d':'019' , '106g':'021' , '106d':'023' ,
'107g':'025' , '107d':'027' , '108g':'029' , '108d':'031' ,
'109g':'033' , '109d':'035' , '110g':'037' , '110d':'039' ,
'001g':'041' , '001d':'043' , '003g':'045' , '003d':'047' ,
'004d':'048' , '005g':'145' , '005d':'147' , '006g':'146' ,
'006d':'148' ,
'201':'002' , '202':'004' , '203':'006' , '204':'008' ,
'205':'010' , '206':'012' , '207':'014' , '208':'016' ,
'209g':'018' , '209d':'020' , '210g':'022' , '210d':'024' ,
'211g':'026' , '211d':'028' , '212':'030' , '213':'032' ,
'214':'034' , '215':'036' , '216':'038' , '217':'040' ,
'218':'042' , '219':'044' } ,
'i' :
{'110d':'150' , '403g':'150' , '105g':'150' , '209g':'150' ,
'309g':'150' , '312':'150' , 'cl2':'150' , 'cl3':'150' ,
'cl4':'150' , 'cl1':'150' , '007g':'150' , '007d':'150' ,
'009d':'102' , '009g':'101' , '008d':'103' , '005d':'104' ,
'008g':'105' , '005g':'106' , '006d':'107' , '004d':'108' ,
'006g':'109' , '004g':'110' , '001g':'147' , '001d':'148' ,
'002g':'145' , '002d':'146' , '003g':'143' , '003d':'144' ,
'107g':'114' , '103g':'111' , '106d':'112' , '102d':'128' ,
'106g':'129' , '102g':'131' , '105d':'130' , '101d':'132' ,
'101g':'133' , '103d':'134' , '107d':'135' , '104g':'136' ,
'108g':'137' , '104d':'138' , '108d':'139' , '109g':'140' ,
'109d':'141' , '110g':'142' ,
'217':'044' , '216':'041' , '213':'047' , '206':'125' ,
'215':'043' , '208':'046' , '214':'045' , '207':'048' ,
'212':'123' , '205':'124' , '211d':'126' , '204':'127' ,
'211g':'121' , '203':'122' , '210d':'119' , '202':'120' ,
'210g':'117' , '201':'118' , '209d':'115' , '219':'116' ,
'218':'113' ,
'315':'034' , '308':'035' , '314':'036' , '307':'037' ,
'313':'038' , '306':'040' , '305':'039' , '311d':'042' ,
'304':'009' , '311g':'010' , '303':'008' , '302':'006' ,
'310d':'004' , '310g':'002' , '301':'007' , '309d':'005' ,
'319':'003' , '318':'001' , '317':'032' , '316':'033' ,
'405g':'021' , '411g':'022' , '405d':'019' , '411d':'020' ,
'401g':'018' , '406g':'017' , '406d':'015' , '401d':'016' ,
'407g':'013' , '402g':'014' , '402d':'012' , '407d':'011' ,
'410d':'023' , '410g':'024' , '409d':'025' , '404d':'026' ,
'404g':'028' , '409g':'027' , '408d':'029' , '403d':'030' ,
'408g':'031' } ,
'j' :
{'002d':'002d' , '002g':'002g' , '003d':'003d' , '003g':'003g' ,
'004d':'004d' , '004g':'004g' , '005d':'005d' , '005g':'005g' ,
'101d':'101d' , '101g':'101g' , '102d':'102d' , '102g':'102g' ,
'103d':'103d' , '103g':'103g' , '104d':'104d' , '104g':'104g' ,
'105d':'105d' , '105g':'105g' , '106d':'106d' , '106g':'106g' ,
'107d':'107d' , '107g':'107g' , '108d':'108d' , '108g':'108g' ,
'109d':'109d' , '109g':'109g' , '110d':'110d' , '110g':'110g' ,
'111d':'111d' , '111g':'111g' , '112d':'112d' , '112g':'112g' ,
'201':'201' , '202':'202' , '203':'203' , '204':'204' ,
'205':'205' , '206':'206' , '207':'207' , '208':'208' ,
'209':'209' , '210':'210' , '211d':'211d' , '211g':'211g' ,
'212d':'212d' , '212g':'212g' , '213d':'213d' , '213g':'213g' ,
'214':'214' , '215':'215' , '216':'216' , '217':'217' ,
'218':'218' , '219':'219' , '220':'220' , '221':'221' ,
'222':'222' , '223':'223' ,
'301':'301' , '302':'302' , '303':'303' , '304':'304' ,
'305':'305' , '306':'306' , '307':'307' , '308':'308' ,
'309':'309' , '310':'310' , '311d':'311d' , '311g':'311g' ,
'312d':'312d' , '312g':'312g' , '313d':'313d' , '313g':'313g' ,
'314':'314' , '315':'315' , '316':'316' , '317':'317' ,
'318':'318' , '319':'319' , '320':'320' , '321':'321' ,
'322':'322' , '323':'323' ,
'401d':'401d' , '401g':'401g' , '402d':'402d' , '402g':'402g' ,
'403d':'403d' , '403g':'403g' , '404d':'404d' , '404g':'404g' ,
'405d':'405d' , '405g':'405g' , '406d':'406d' , '406g':'406g' ,
'407d':'407d' , '407g':'407g' , '408d':'408d' , '408g':'408g' ,
'409d':'409d' , '409g':'409g' , '410d':'410d' , '410g':'410g' ,
'411d':'411d' , '411g':'411g' , '412d':'412d' , '412g':'412g' ,
'413d':'413d' , '413g':'413g' } ,
'm' :
{'001':'001' , '002':'002' , '003':'003' , '004':'004' ,
'005':'005' , '006':'006' ,
'101':'101' , '102':'102' , '103':'103' , '104':'104' ,
'105':'105' , '106':'106' , '106b':'106b' , '107':'107' ,
'108':'108' , '109':'109' , '110':'110' , '111':'111' ,
'112':'112' , '113':'113' , '114':'114' , '115':'115' ,
'116':'116' , '117':'117' , '118':'118' , '119':'119' ,
'120':'120' , '121':'121' , '122':'122' , '123':'123' ,
'124':'124' , '125':'125' , '126':'126' , '127':'127' ,
'128':'128' , '129':'129' , '130':'130' , '131':'131' ,
'132':'132' , '133':'133' , '134':'134' , '135':'135' ,
'136':'136' , '137':'137' , '138':'138' , '138b':'138b' ,
'139':'101' , '140':'140' , '141':'141' , '142':'142' ,
'143':'101' , '144':'144' , '145':'145' , '146':'146' ,
'147':'101' , '148':'148' , '149':'149' , '150':'150' ,
'151':'101' , '152':'152' , '153':'153' , '154':'154' ,
'155':'101' , '156':'156' , '157':'157' , '158':'158' ,
'159':'101' , '160':'160' , '161':'161' , '162':'162' ,
'163':'101' , '164':'164' , '165':'165' , '166':'166' ,
'167':'101' , '168':'168' ,
'201':'201' , '202':'202' , '203':'203' , '204':'204' ,
'205':'205' , '206':'206' , '206b':'206b' , '207':'207' ,
'208':'208' , '209':'209' , '210':'210' , '211':'211' ,
'212':'212' , '213':'213' , '214':'214' , '215':'215' ,
'216':'216' , '217':'217' , '218':'218' , '219':'219' ,
'220':'220' , '221':'221' , '222':'222' , '223':'223' ,
'224':'224' , '225':'225' , '226':'226' , '227':'227' ,
'228':'228' , '229':'229' , '230':'230' , '231':'231' ,
'232':'232' , '233':'233' , '234':'234' , '235':'235' ,
'236':'236' , '237':'237' , '238':'238' , '238b':'238b' ,
'239':'201' , '240':'240' , '241':'241' , '242':'242' ,
'243':'201' , '244':'244' , '245':'245' , '246':'246' ,
'247':'201' , '248':'248' , '249':'249' , '250':'250' ,
'251':'201' , '252':'252' , '253':'253' , '254':'254' ,
'255':'201' , '256':'256' , '257':'257' , '258':'258' ,
'259':'201' , '260':'260' , '261':'261' , '262':'262' ,
'263':'201' , '264':'264' , '265':'265' , '266':'266' ,
'267':'201' , '268':'268' ,
'301':'301' , '302':'302' , '303':'303' , '304':'304' ,
'305':'305' , '306':'306' , '306b':'306b' , '307':'307' ,
'308':'308' , '309':'309' , '310':'310' , '311':'311' ,
'312':'312' , '313':'313' , '314':'314' , '315':'315' ,
'316':'316' , '317':'317' , '318':'318' , '319':'319' ,
'320':'320' , '321':'321' , '322':'322' , '323':'323' ,
'324':'324' , '325':'325' , '326':'326' , '327':'327' ,
'328':'328' , '329':'329' , '330':'330' , '331':'331' ,
'332':'332' , '333':'333' , '334':'334' , '335':'335' ,
'336':'336' , '337':'337' , '338':'338' , '338b':'338b' ,
'339':'301' , '340':'340' , '341':'341' , '342':'342' ,
'343':'301' , '344':'344' , '345':'345' , '346':'346' ,
'347':'301' , '348':'348' , '349':'349' , '350':'350' ,
'351':'301' , '352':'352' , '353':'353' , '354':'354' ,
'355':'301' , '356':'356' , '357':'357' , '358':'358' ,
'359':'301' , '360':'360' , '361':'361' , '362':'362' ,
'363':'301' , '364':'364' , '365':'365' , '366':'366' ,
'367':'301' , '368':'368' ,
'401':'401' , '402':'402' , '403':'403' , '404':'404' ,
'405':'405' , '406':'406' , '406b':'406b' , '407':'407' ,
'408':'408' , '409':'409' , '410':'410' , '411':'411' ,
'412':'412' , '413':'413' , '414':'414' , '415':'415' ,
'416':'416' , '417':'417' , '418':'418' , '419':'419' ,
'420':'420' , '421':'421' , '422':'422' , '423':'423' ,
'424':'424' , '425':'425' , '426':'426' , '427':'427' ,
'428':'428' , '429':'429' , '430':'430' , '431':'431' ,
'432':'432' , '433':'433' , '434':'434' , '435':'435' ,
'436':'436' , '437':'437' , '438':'438' , '438b':'438b' ,
'439':'401' , '440':'440' , '441':'441' , '442':'442' ,
'443':'401' , '444':'444' , '445':'445' , '446':'446' ,
'447':'401' , '448':'448' , '449':'449' , '450':'450' ,
'451':'401' , '452':'452' , '453':'453' , '454':'454' ,
'455':'401' , '456':'456' , '457':'457' , '458':'458' ,
'459':'401' , '460':'460' , '461':'461' , '462':'462' ,
'463':'401' , '464':'464' , '465':'465' , '466':'466' ,
'467':'401' , '468':'468' ,
'501':'501' , '502':'502' , '502b':'502b' , '503':'503' ,
'504':'504' , '505':'505' , '506':'506' , '507':'507' ,
'508':'508' , '509':'509' , '510':'510' , '511':'511' ,
'512':'512' , '513':'513' , '514':'514' , '515':'515' ,
'516':'516' , '517':'517' , '518':'518' , '519':'519' ,
'520':'520' , '521':'521' , '522':'522' , '523':'523' ,
'524':'524' , '525':'525' , '526':'526' , '527':'527' ,
'528':'528' } ,
'p' :
{'101':'101' , '102':'102' , '103':'103' , '104':'104' ,
'105':'105' , '106':'106' , '111':'111' , '112':'112' ,
'113':'113' , '114':'114' , '115':'115' , '116':'116' ,
'117':'117' , '118':'118' , '119':'119' , '120':'120' ,
'121':'121' , '122':'122' , '123':'123' , '124':'124' ,
'125':'125' , '126':'126' , '127':'127' , '128':'128' ,
'129':'129' , '130':'130' , '131':'131' ,
'201':'201' , '202':'202' , '203':'203' , '204':'204' ,
'205':'205' , '206':'206' , '211':'211' , '212':'212' ,
'213':'213' , '214':'214' , '215':'215' , '216':'216' ,
'217':'217' , '218':'218' , '219':'219' , '220':'220' ,
'221':'221' , '222':'222' , '223':'223' , '224':'224' ,
'225':'225' , '226':'226' , '227':'227' , '228':'228' ,
'229':'229' , '230':'230' , '231':'231' ,
'301':'301' , '302':'302' , '303':'303' , '304':'304' ,
'305':'305' , '306':'306' , '311':'311' , '312':'312' ,
'313':'313' , '314':'314' , '315':'315' , '316':'316' ,
'317':'317' , '318':'318' , '319':'319' , '320':'320' ,
'321':'321' , '322':'322' , '323':'323' , '324':'324' ,
'325':'325' , '326':'326' , '327':'327' , '328':'328' ,
'329':'329' , '330':'330' , '331':'331' ,
'401':'401' , '402':'402' , '403':'403' , '404':'404' ,
'405':'405' , '406':'406' , '411':'411' , '412':'412' ,
'413':'413' , '414':'414' , '415':'415' , '416':'416' ,
'417':'417' , '418':'418' , '419':'419' , '420':'420' ,
'421':'421' , '422':'422' , '423':'423' , '424':'424' ,
'425':'425' , '426':'426' , '427':'427' , '428':'428' ,
'429':'429' , '430':'430' , '431':'431' }
}
# Prises d'uplink, de machines du crans ou de bornes wifi
uplink_prises={ 'i' :
{ 49 : 'uplink->backbone' , 50 : 'uplink->bati1',
149 : 'uplink->bati' } ,
'h' :
{ 49 : 'uplink->backbone' , 50 : 'uplink->bath1',
149 : 'uplink->bath' , 150 : 'pegase' } ,
'g' :
{ 49 : 'uplink->backbone', 50 : 'uplink->batg1' ,
149 : 'uplink->batg' , 150 : 'uplink->batg2' ,
249 : 'uplink->batg1' , 250 : 'uplink->batg4' ,
449 : 'uplink->batg2' , 450 : 'uplink->batg5' ,
549 : 'uplink->batg4' , 550 : 'uplink->batg6' ,
649 : 'uplink->batg5' , 547 : 'wifi_lodur' } ,
'b' :
{ 49 : 'uplink->backbone', 50 : 'uplink->batb1',
149 : 'uplink->batb', 150 : 'uplink->batb2',
249 : 'uplink->batb1', 250 : 'uplink->batb3',
349 : 'uplink->batb2' },
'c' :
{ 49 : 'uplink->backbone', 50 : 'uplink->batc1',
149 : 'uplink->batc' , 150 : 'uplink->batc2',
225 : 'uplink->batc1', 226 : 'locaux_clubs' }
}
# Dictionnaire inverse
def reverse(bat) :
""" Retourne un dictionnaire : { prise : [ chambre(s) ] } """
reverse={}
for chbre, prise in chbre_prises[bat].items() :
if reverse.has_key(prise) :
reverse[prise] += [ chbre ]
else :
reverse[prise] = [ chbre ]
return reverse
# Locaux clubs : lecture dans chbre_prises et ajout des locaux dans les bats non
# manageables
def locaux_clubs() :
""" Retourne le dicctionaire des locaux club : { bat :[ locaux ] } """
# Corespondance chbre -> nom du local club
locaux_clubs = { 'Bcl0' : 'Kfet' ,
'Bcl1' : 'Krobot',
'Gcl0' : 'Med' ,
'Pcl0' : 'Bds' }
# Ajout des locaux d'étage A, B et C
for b in 'ABC' :
for i in range(2,7) :
locaux_clubs['%scl%i' % ( b, i)] = '%i@%s' % (i, b)
# Ajout de ceux des H, I et J
for b in 'HIJ' :
for i in range(1,5) :
locaux_clubs['%scl%i' % ( b, i)] = '%i@%s' % (i, b)
# Supression du 2@B et 4@J
locaux_clubs.pop('Bcl2')
locaux_clubs.pop('Jcl4')
return locaux_clubs

41
gestion/chgpass.py Executable file
View file

@ -0,0 +1,41 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
import getpass, commands, os
import ldap_secret
def chgpass(dn) :
print "Le mot de passe tapé ne sera pas écris à l'écran."
print "Taper Ctrl-C pour abandonner"
try :
while 1 :
mdp = getpass.getpass('Nouveau mot de passe :')
mdp1 = getpass.getpass('Retaper mot de passe :')
if mdp != mdp1 :
print 'Les deux mots de passe entrés sont différents, réesayer'
continue
# Test du mdp
test = commands.getoutput('echo "%s" | /usr/sbin/crack_testlib' % mdp)
if test.split(':')[1] == ' ok' :
break
else :
print test.split(':')[1]
# Changement mdp
if os.system('/usr/bin/ldappasswd -x -D "%s" -w %s "%s" -s %s > /dev/null' % (ldap_secret.auth_dn, ldap_secret.password, dn, mdp) ):
print 'Erreur lors du changement de mot de passe'
else :
print 'Changement effectué avec succès'
except KeyboardInterrupt :
pass
if __name__ == '__main__' :
print 'TODO : interface de sélection'
chgpass('aid=10,dc=crans,dc=org')

101
gestion/config.py Normal file
View file

@ -0,0 +1,101 @@
# -*- python -*-
# -*- coding: iso-8859-15 -*-
############################################
## Définition du comportement des scripts ##
############################################
#Précablage possible ?
precab=1
#Bloquage si carte d'étudiant manquante pour l'année en cours
carte_et=1
##Création de comptes
# Gid des comptes créés
gid=100
# Shell
login_shell='/bin/zsh'
# Longueur maximale d'un login
maxlen_login=15
# Année scolaire en cours
from time import localtime
dat = localtime()
if dat[1]<9 : ann_scol = dat[0]-1
else : ann_scol = dat[0]
## Quelques fichiers
# Log des destructions de machines ou d'adhérents
delete_log = '/tmp/delete.log'
#############################
## Paramètres des machines ##
#############################
## >>>>>>>>>>>>>>> La modification des paramètres suivants doit se
## >> ATTENTION >> faire avec précaution, il faut mettre la base à
## >>>>>>>>>>>>>>> jour en parralèle de ces modifs.
# Sous réseaux alloués à chaque type de machine ou bâtiment
NETs = { 'wifi' : [ '138.231.149.0/24', '138.231.150.0/23' ],
'b' : [ '138.231.137.0/24' ],
'm' : [ '138.231.138.0/24' ],
'c' : [ '138.231.139.0/24' ],
'p' : [ '138.231.140.0/25' ],
'a' : [ '138.231.140.128/25'],
'g' : [ '138.231.141.0/24' ],
'i' : [ '138.231.142.0/25' ],
'j' : [ '138.231.142.128/25'],
'h' : [ '138.231.143.0/24' ],
'all' : [ '138.231.136.0/21', '138.231.148.0/22' ] }
# Domaines dans lesquels les machines sont placées suivant leur type
domains = { 'wifi' : 'wifi.crans.org' ,
'fixe' : 'crans.org' ,
'borne': 'wifi.crans.org' }
#######################
## Mail de bienvenue ##
#######################
#From est batx@crans.org ou respbat si adhérent extérieur
txt_mail_bienvenue = """From: Crans <%(From)s>
To: %(To)s
Subject: Bienvenue au Cr@ns !
Si tu lis ce mail, c'est que ton inscription à l'association est effective !
Rappel : Le site web de l'association est http://www.crans.org.
Par ailleurs, toutes les informations concernant l'association sont
disponibles sur le WIKI à l'adresse http://wiki.crans.org
Notamment, il est important de prendre le temps de lire la page :
http://wiki.crans.org/moin.cgi/CransPratique
Elle regroupe toutes les informations nécessaires à l'utilisation des
ressources de l'association.
Sans lire attentivement ce document, l'accès au Web peut ne pas fonctionner.
-----
L'accés aux news et au wiki sont limités à un usage interne au CRANS.
Pour y avoir accés depuis l'extérieur il faut utiliser un mot de passe:
- Pour les news :
* Utilisateur : Vivelapa
* Mot de passe : ranoia!
- Pour le wiki :
* Utilisateur : ViveLe
* Mot de passe : Wiki!
-----
Sur ce, bienvenue au Cr@ns !
--
Les membres actifs."""
## Ports ouvers par défaut pour tous les adhérents
# A Priori stocké dans la base ldap quand Fred se sera bougé le cul
port_default = { 'tcp_input' : ['22','1024:'],
'tcp_output': [':79','81:134','136','140:444','446:'],
'udp_input' : [''],
'udp_output': [':136','140:'] }

140
gestion/gen_confs/__init__.py Executable file
View file

@ -0,0 +1,140 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" Package pour la génération des fichiers de conf
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
import sys, os, signal
sys.path.append('/usr/scripts/gestion')
import time, commands
from affich_tools import *
from lock import *
import config
from tempfile import NamedTemporaryFile
# Notes snmp
oid = '.1.3.6.1.4.1.2021.255'
try :
if sys.argv[1] == '-s' and sys.argv[2] == oid :
service = sys.argv[4].strip().strip('"')
except :
None
# Fin notes snmp
class gen_config :
""" Base pour toutes les classes de génération de fichiers de conf """
base = None
ann_scol = config.ann_scol
debug = 0
_locked = 0
__restore={} # pour restorer la config d'origine en cas d'erreur de génération
def lock(self) :
""" Lock le service courant """
if not self._locked :
make_lock(str(self.__class__),'')
self._locked = 1
def unlock(self) :
""" Supression du lock """
if self._locked : remove_lock(str(self.__class__))
def __del__(self) :
# Au cas où...
self.unlock()
def _restore(self) :
""" Affichage d'une erreur et du traceback si debug
Puis restauration des fichers """
print ERREUR
if self.debug :
import traceback
traceback.print_exc()
# Restauration
for nom, f in self.__restore.items() :
os.system('cp -f %s %s' % ( f.name, nom ) )
def _open_conf(self,nom,comment=None) :
""" Créé un fichier
si comment est fourni, insère une entète qui utilisera le caractère
de commentaire fourni
copie l'ancien fichier dans un fichier temporaire pour permettre
la restauration en cas d'échec de la configuration
Retourne le descripteur du fichier """
f = NamedTemporaryFile()
os.system('cp %s %s 2> /dev/null' % ( nom, f.name ) )
self.__restore[nom] = f
fd = open(nom, 'w')
if comment :
e = """***********************************************************
Ce fichier est généré par les scripts de %s
Les données proviennent de la base LDAP et de la conf
présente au début du script.
Génération : %s
Fichier : %s
NE PAS EDITER
***********************************************************""" % \
(__name__, nom, time.strftime('%A %d %B %Y %H:%M') )
e = comment + e.replace('\n', '\n%s' % comment) + '\n'
fd.write(e)
return fd
def gen_conf(self) :
""" Génération des fichiers de conf, retourne 1 si erreur """
self.lock()
self.anim = anim('\tgénération fichiers')
try :
warn = self._gen()
if warn :
self.anim.reinit()
print WARNING
if self.debug : sys.stderr.write(warn.encode("ISO-8859-15"))
else :
self.anim.reinit()
print OK
self.unlock()
except :
self.anim.reinit()
self._restore()
self.unlock()
return 1
def restart(self) :
""" Redémarrage du service concerné """
self.lock()
self.anim = anim('\trestart')
status, output = commands.getstatusoutput(self.restart_cmd)
if status :
self.anim.reinit()
print ERREUR
if self.debug :
sys.stderr.write(output+'\n')
self.unlock()
return 1
else :
print OK
self.unlock()
def reconfigure(self) :
""" Génère les fichiers puis redémarre le service
si la génération c'est bien passée """
cprint('Reconfiguration %s :' % self.__str__() , 'gras')
if not self.gen_conf() :
return self.restart()
else : return 1

186
gestion/gen_confs/bind.py Executable file
View file

@ -0,0 +1,186 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" Génération de la configuration pour bind9
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
# TODO :
# verif si SOA zamok.crans.org bon partout
# verif si liste NS identiques partout ok
# traitement warnings
import time, sre
from gen_confs import gen_config
class dns(gen_config) :
"""
Génération des fichiers de configuration de bind9 :
* fichier DNS_CONF qui contient les définitions de zone conformément
à zone_template. Ce fichier doit être inclus à partir de la config statique
de bind
* les fichiers de zones, ce sont eux qui contiennent les données du
dns, ils ont appellés par le fichier DNS_CONF et sont générés dans DNS_DIR
Leur entète est générée à partir de zone_entete.
Les fichiers générés placent bind comme autoritaire sur les noms de
zones_direct et les adresses de zones_reverse. Les données proviennent de
la base LDAP
"""
######################################PARTIE DE CONFIGURATION
### Fichiers à écrire
# Répertoire d'écriture des fichiers de zone
DNS_DIR='/etc/bind/generated/' # Avec un / à la fin
# Fichier de définition des zones
DNS_CONF=DNS_DIR + 'zones_crans'
### Sur quelles zones on a autorité ?
# Résolution directe
zones_direct = [ 'crans.org' , 'crans.ens-cachan.fr', 'wifi.crans.org' ]
# Résolution inverse (regexp pour définir les débuts des IP correspondantes)
zones_reverse = sre.compile('^138\.231\.1(3[6-9]|4[0-9]|5[01])') # 138.231.136.0 à 138.231.151.255
### Liste DNS
# Le premier est doit être le maitre
DNSs = [ 'zamok.crans.org' , 'sila.crans.org' , 'freebox.crans.org' ]
### Serveurs de mail
# format : [ priorité serveur , .... ]
MXs = ['10 zamok.crans.org', '20 freebox.crans.org' ]
### Entète des fichiers de zone
zone_entete="""
$ORIGIN %(zone)s.
$TTL 86400
@\tIN\tSOA zamok.crans.org. root.crans.org. (
%(serial)i ; numero de serie
21600 ; refresh (s)
3600 ; retry (s)
1209600 ; expire (s)
86400 ; TTL (s)
)
"""
# Syntaxe utilisée dans le fichier DNS_CONF pour définir une zone
zone_template="""
zone "%(NOM_zone)s" {
type master;
file "%(FICHIER_zone)s";
};
"""
### Verbosité
# Si =1 ralera (chaine warnings) si machines hors zone trouvée
# Si =0 ralera seulement contre les machines ne pouvant être classées
verbose = 1
restart_cmd = '/etc/init.d/bind9 reload'
######################################FIN PARTIE DE CONFIGURATION
def __str__(self) :
return "DNS"
def _gen(self) :
### Génération du numéro de série
# Le + 1000.... s'explique pas l'idée précédente et peu pratique d'avoir
# le numéro de série du type AAAAMMJJNN (année, mois, jour, incrément par jour)
serial = time.time() + 1000000000
### DNS
DNS='; DNS de la zone par ordre de priorité\n'
for d in self.DNSs :
DNS += '@\tIN\tNS %s.\n' % d
DNS += '\n'
### Serveurs de mail
MX='; Serveurs de mails\n'
for m in self.MXs :
MX += '%(zone)s.\t' # Sera remplacé par le nom de zone plus tard
MX += 'IN\tMX\t%s.\n' % m
MX += '\n'
### Tri des machines
direct = {} # format : { zone : [ lignes correspondantes] }
reverse = {}
warnings = ''
self.anim.iter=len(self.machines)
for machine in self.machines :
self.anim.cycle()
# Calculs préliminaires
try :
nom , zone = machine.nom().split('.',1)
zone = zone.encode('iso-8859-1')
except :
warnings += u'Machine ignorée (mid=%s) : format nom incorrect (%s)\n' % ( machine.id().encode('iso-8859-1'), machine.nom().encode('iso-8859-1') )
continue
# Le direct
if zone in self.zones_direct :
ligne = "%s\tIN\tA\t%s\n" % ( nom, machine.ip() )
try : direct[zone] += ligne
except : direct[zone] = ligne
elif self.verbose :
warnings += u'Résolution directe ignorée (mid=%s) : zone non autoritaire (%s)\n' % ( machine.id().encode('iso-8859-1'), zone.encode('iso-8859-1') )
# Le direct avec alias
for alias in machine.alias() :
# Bon format ?
alias_l = alias.split('.')
ok = 0
for i in range(len(alias_l)) :
zone_essai = '.'.join(alias_l[i:])
if zone_essai in self.zones_direct :
# On est autoritaire sur cette zone
# On place donc l'alias dans le fichier de cette zone
zone = zone_essai
nom = '.'.join(alias_l[:i])
ok = 1
break
if not ok :
warnings += u'Alias ignoré (mid=%s) : %s\n' % ( machine.id().encode('iso-8859-1'), alias.encode('iso-8859-1') )
continue
zone = zone.encode('iso-8859-1')
ligne = "%s\tIN\tCNAME\t%s.\n" % ( nom, machine.nom() )
try : direct[zone] += ligne
except : direct[zone] = ligne
# Le reverse
if self.zones_reverse.match(machine.ip()) :
base_ip = machine.ip().split('.')
base_ip.reverse()
zone = "%s.%s.%s.in-addr.arpa" % tuple(base_ip[1:])
zone = zone.encode('iso-8859-1')
ligne = '%s\tIN\tPTR\t%s.\n' % (base_ip[0],machine.nom())
try : reverse[zone] += ligne
except : reverse[zone] = ligne
elif self.verbose :
warnings += u'Résolution inverse ignorée (mid=%s) : ip sur zone non autoritaire (%s)\n' % ( machine.id().encode('iso-8859-1'), machine.ip().encode('iso-8859-1') )
### Ajouts pour les fichiers de résolution directs
for zone in direct.keys() :
# MXs
direct[zone] = MX % { 'zone' : zone } + direct[zone]
### Ecriture des fichiers de zone et préparation du fichier de définition
f = ''
for zone, lignes in direct.items() + reverse.items() :
file = self.DNS_DIR + 'db.' + zone
fd = self._open_conf(file,';')
fd.write(self.zone_entete % \
{ 'zone' : zone, 'serveur_autoritaire' : self.DNSs[0] , 'serial' : serial } )
fd.write('\n')
fd.write(DNS)
fd.write(lignes)
fd.close()
f += self.zone_template % { 'NOM_zone' : zone, 'FICHIER_zone' : file }
### Ecriture fichier de définition
fd = self._open_conf(self.DNS_CONF,'//')
fd.write(f)
fd.close()
return warnings

View file

@ -0,0 +1,42 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
from firewall import bl_upload_fw
from squid import bl_upload_squid
class bl_upload :
""" Classe d'interface avec les classes spécifiques des
opération de configuration pour upload """
debug = 0
description = u'Bloquage de toute communiquation vers l\'extérieur.'
def __str__(self) :
return "blackliste upload"
def __init__(self) :
self.fw = bl_upload_fw()
self.squid = bl_upload_squid()
def __set(self) :
""" Attribution des proprietes des differentes classes """
self.fw.base = self.base
self.fw.debug = self.debug
self.squid.base = self.base
self.squid.debug = self.debug
def reconfigure(self) :
self.__set()
self.fw.reconfigure()
self.squid.reconfigure()
def restart(self) :
self.__set()
self.fw.restart()
self.squid.restart()
def gen_conf(self) :
self.__set()
self.fw.gen_conf()
self.squid.gen_conf()

115
gestion/gen_confs/dhcpd.py Executable file
View file

@ -0,0 +1,115 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" Génération de la configuration pour le dhcp
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
from iptools import AddrInNet, param
from gen_confs import gen_config
class dhcp(gen_config) :
""" Génération du fichier de configuration pour dhcpd (DHCPD_CONF)
Le fichier comporte une partie par réseau servi, chaque réseau
servi doit être une clef du dictionnaire reseaux, la valeur correspondante
est une chaine décrivant les options spécifiques à ce réseau.
Les options communes sont celles de base_dhcp.
Chaque machines possède ensuite une entrée de la forme de host_template
"""
######################################PARTIE DE CONFIGURATION
# Fichier à écire
DHCPD_CONF='/etc/dhcpd.conf'
# Réseaux servis avec leurs options spécifiques
reseaux = { '138.231.136.0/21' :
"""option routers 138.231.136.4;
option domain-name-servers 138.231.136.6, 138.231.136.10, 138.231.136.9;
option domain-name "crans.org";
option option-119 "crans.org wifi.crans.org";""",
'138.231.148.0/22' :
"""option routers 138.231.148.1;
option domain-name-servers 138.231.148.1;
option domain-name "wifi.crans.org";
option option-119 "wifi.crans.org crans.org";"""
}
# Options communes à toutes les réseaux servis
base_dhcp="""
subnet %(network)s netmask %(netmask)s {
default-lease-time 86400;
option subnet-mask %(netmask)s;
option broadcast-address %(broadcast)s;
%(OPTIONS_RESEAU)s
option time-servers 138.231.136.6;
option ntp-servers 138.231.136.6;
option smtp-server 138.231.136.6;
option netbios-name-servers 138.231.136.6;
option netbios-dd-server 138.231.136.6;
option netbios-node-type 8;
option ip-forwarding off;
option option-252 "http://www.crans.org/proxy.pac" ;
deny unknown-clients;
not authoritative;
%(HOSTs)s
}
"""
host_template="""
host %(nom)s {
hardware ethernet %(mac)s;
fixed-address %(ip)s;
option host-name "%(nom)s";
}
"""
### Verbosité
# Si =1 ralera (chaine warnings) si machines hors zone trouvée
# Si =0 ralera seulement si réseau vide
verbose = 1
restart_cmd = '/etc/init.d/dhcp restart'
######################################FIN PARTIE DE CONFIGURATION
def __str__(self) :
return 'dhcp'
def _gen(self) :
warnings =''
### Construction de la partie du fichier contenant les machines
hosts={}
self.anim.iter=len(self.machines)
for machine in self.machines :
self.anim.cycle()
t = 0
for net in self.reseaux.keys() :
if AddrInNet(machine.ip(),net) :
d = { 'nom' : machine.nom().split('.')[0] , 'mac' : machine.mac() , 'ip' : machine.ip() }
try : hosts[net] += self.host_template % d
except : hosts[net] = self.host_template % d
t = 1
if not t and self.verbose :
warnings += u'Machine ignorée (mid=%s) : ip en dehors des réseaux servis (%s)\n' % ( machine.id(), machine.ip() )
### Ecriture du fichier
fd = self._open_conf(self.DHCPD_CONF,'#')
for net, options in self.reseaux.items() :
if not hosts.has_key(net) :
warnings += u'Réseau %s ignoré : aucune machine à servir\n' % net
continue
d = param(net)
d['OPTIONS_RESEAU'] = options
d['HOSTs'] = hosts[net]
fd.write(self.base_dhcp % d)
fd.close()
return warnings

80
gestion/gen_confs/droits.py Executable file
View file

@ -0,0 +1,80 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
import sys, signal
from gen_confs import gen_config, anim, cprint, OK, ERREUR
sys.path.append('/usr/scripts/gestion')
from ldap_crans import crans_ldap, crans, ann_scol, preattr, ldap
class droits(crans_ldap,gen_config) :
####### Les groupes
base_group_dn = 'ou=Group,dc=crans,dc=org'
# Quels droits donnent l'appartenacne à quel groupe ?
groupes = { 'adm' : [ u'Nounou' ] ,
'respbats' : [ u'Câbleur' , u'Déconnecteur', u'Nounou' ] ,
'moderateurs' : [ u'Modérateur' ] ,
'disconnect' : [ u'Déconnecteur' ] ,
'webcvs' : [ u'CVSWeb'] }
####### Les ML
# Le + devant un nom de ML indique une synchronisqtion
# ML <-> fonction partielle : il n'y a pas d'effacement
# des abonnés si le droit est retiré
mailing_listes = { 'roots' : [ u'Nounou', u'Apprenti' ],
'+nounou' : [ u'Nounou', u'Apprenti' ],
'respbats' : [ u'Câbleur', u'Nounou' ],
'moderateurs' : [ u'Modérateur' ],
'disconnect' : [ u'Déconnecteur' ] }
def restart(s) :
# Rien à faire
pass
def __str__(self):
return "droits"
def build_group(self) :
""" Reconstruit les groupes dans la base LDAP """
self.anim.iter = len( self.groupes.keys() )
for group, fonctions in self.groupes.items() :
self.anim.cycle()
# Qui doit être dans ce groupe ?
res = []
for f in fonctions :
res += self.search('droits=%s' % f)['adherent']
# Récupération de la constitution du groupe actuel
dn = 'cn=%s,%s' % (group, self.base_group_dn)
data = self.conn.search_s(dn ,0,'objectClass=posixGroup')[0][1]
init_data = data.copy()
# Supression de tout les membres
data['memberUid'] = []
# Ajout des bonnes personnes
for adher in res :
uid = preattr(adher.compte())[1]
if uid and uid not in data['memberUid'] :
data['memberUid'].append(uid)
# Sauvegarde
modlist = ldap.modlist.modifyModlist(init_data,data)
self.conn.modify_s(dn,modlist)
def gen_conf(self) :
self.anim = anim('\tconfiguration groupes')
try :
self.build_group()
self.anim.reinit()
print OK
except :
self.anim.reinit()
print ERREUR
if self.debug :
import traceback
traceback.print_exc()
self.anim = anim('\tconfiguration ML Crans')
print 'TODO'

60
gestion/gen_confs/firewall.py Executable file
View file

@ -0,0 +1,60 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" Génération de la configuration pour le firewall
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
from gen_confs import gen_config
from time import localtime
class firewall(gen_config) :
""" Génère le fichier de paires MAC-IP """
# Fichier
MACIP = '/CRANS/generated/ether/pairesMAC-IP.txt'
restart_cmd = '/etc/init.d/firewall macip'
def __str__(self) :
return "firewall"
def _gen(self) :
macip= self._open_conf(self.MACIP)
self.anim.iter=len(self.machines)
for machine in self.machines :
self.anim.cycle()
macip.write( "%s %s\n" % ( machine.mac(), machine.ip() ) )
macip.close()
class bl_upload_fw(gen_config) :
""" Génère le fichier de blackliste d'upload pour le firewall"""
# Fichier
BL_UPLOAD = '/tmp/bl_upload_fw'
restart_cmd = '/etc/init.d/firewall blacklist'
def __str__(self) :
return "blackliste upload firewall"
def _gen(self) :
upload = self._open_conf( self.BL_UPLOAD, '#' )
if localtime()[1] == 9:
# On est en septembre, on autorise ceux qui ont payé l'an dernier et cette année
base = self.base.search('(paiement=%d|paiement=%d)' % (int(self.ann_scol),
int(self.ann_scol) - 1))
else:
base = self.base.search('paiement=%s' % self.ann_scol)
for adh in ( [ self.crans ] + base['adherent'] + base['club'] ):
for machine in adh.machines() :
self.anim.cycle()
bl = machine.blacklist_actif()
if 'bl_upload' in bl and not 'bloq' in bl :
upload.write( '%s:smtp,smtps,pop3,pop3s,imap,imaps,http\n' % machine.nom() )
upload.close()

161
gestion/gen_confs/generate.py Executable file
View file

@ -0,0 +1,161 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
import sys, signal, os, commands, getopt
sys.path.append('/usr/scripts/gestion')
from ldap_crans import crans_ldap, crans, ann_scol
from lock import *
from affich_tools import anim, cprint, OK, ERREUR, WARNING
from time import localtime
import config
signal.signal(signal.SIGINT,signal.SIG_IGN) # Pas de Ctrl-C
db = crans_ldap()
make_lock('auto_generate')
##### Options fournies ?
try :
if len(sys.argv) > 1 :
options, arg = getopt.getopt(sys.argv[1:], '', ['quiet', 'home=', 'ML-ENS=', 'droits', 'switch=' , 'dhcp', 'dns', 'firewall' ])
else :
options, arg = ( [],'')
except getopt.error, msg :
sys.stderr.write('%s\n' % msg)
sys.exit(255)
debug = 1 # défaut
to_do = {}
for opt, val in options :
if opt == '--quiet' :
debug = 0
elif len(opt)>2 and opt[:2]=='--' :
to_do[opt[2:]] = [ val ]
##### Lecture de la base LDAP si besion ce qu'il y a a faire et préparation
if not to_do :
if debug : print 'Lecture services à redémarrer dans la base LDAP'
to_do = db.services_to_restart()
auto = 1
else :
auto = 0
if debug : print 'Services à redémarrer imposés (non lecture de la base LDAP)'
inst = []
if 'home' in to_do.keys() :
if auto : db.services_to_restart('-home')
cprint('Création home','gras')
for args in to_do['home'] :
anim('\t' + args)
try :
home, uid, login = args.split(',')
os.mkdir(home, 0755)
os.chown(home, int(uid) ,config.gid)
os.mkdir(home + '/Mail', 0700)
os.chown(home + '/Mail', int(uid) ,config.gid)
status, output = commands.getstatusoutput('/usr/sbin/edquota -p pauget %s' % login )
if status :
print WARNING
if debug :
sys.stderr.write(output+'\n')
else :
print OK
except :
print ERREUR
if debug :
import traceback
traceback.print_exc()
if 'ML-ENS' in to_do.keys() :
db.services_to_restart('-ML-ENS')
cprint('Inscription ML-ENS','gras')
for mail in to_do['ML-ENS'] :
anim('\t'+mail)
status, output = commands.getstatusoutput("echo '%s' | /usr/sbin/add_members -r - com-ens >/dev/null 2>&1" % mail)
if status :
# Il y a eu une erreur
print ERREUR
if debug :
sys.stderr.write(output+'\n')
else :
print OK
if 'droits' in to_do.keys() :
db.services_to_restart('-droits')
from gen_confs.droits import droits
a = droits()
a.debug = debug
a.reconfigure()
if 'switch' in to_do.keys() :
if auto : db.services_to_restart('-switch')
from gen_confs.switchs import switch
a = switch(to_do['switch'])
a.debug = debug
a.reconfigure()
# Les services suivants ont besoin de la liste des machines
# On va donc la lire une seule fois pour leur passer ensuite
if 'firewall' in to_do.keys() :
# Quand sila et komaz liront la base LDAP
# db.services_to_restart('firewall-komaz')
# db.services_to_restart('firewall-sila')
db.services_to_restart('-firewall')
from gen_confs.firewall import firewall
inst.append(firewall())
if 'dns' in to_do.keys() :
db.services_to_restart('-dns')
from gen_confs.bind import dns
inst.append(dns())
if 'dhcp' in to_do.keys() :
from gen_confs.dhcpd import dhcp
db.services_to_restart('-dhcp')
inst.append(dhcp())
##### On fait ce qu'il reste à faire
if inst :
##### Récolte des données
cprint('Lecture base LDAP','gras')
# Machines de l'assoce
machines = crans().machines()
# Machines des adhérents et clubs de l'année en cours
if localtime()[1] == 9:
# On est en septembre, on autorise ceux qui ont payé l'an dernier et cette année
base = db.search('(paiement=%d|paiement=%d)' % (int(ann_scol),
int(ann_scol) - 1))
else:
base = db.search('paiement=%s' % ann_scol)
base = base['adherent'] + base['club']
a = anim('\ttri machines',len(base))
for adh in base :
a.cycle()
# Adhérent ayant payé l'année en cours
if 'bloq' in adh.blacklist_actif() :
# Adhérent ignoré
continue
machines += adh.machines()
a.reinit()
print OK
#### Reconfiguration des services
for i in inst :
i.debug = debug
i.machines = machines
try :
i.reconfigure()
except :
sys.stderr.write('Erreur dans le service %s\n' % i)
if debug :
print 'Non traité ici mais signalé dans la base LDAP : \n\t', db.services_to_restart()
signal.signal(signal.SIGINT,signal.SIG_DFL) # Comportement normal de Ctrl-C
remove_lock('auto_generate')

14
gestion/gen_confs/home.py Normal file
View file

@ -0,0 +1,14 @@
home
home = args[0]
os.mkdir(home, 0755)
os.mkdir(home + '/Mail', 0700)
os.lchown(home, int(args[1]) ,config.gid)
os.system('/usr/sbin/edquota -p pauget %s' % arg[3] )
ML-ENS
if os.system("echo '%s' | /usr/sbin/add_members -r - com-ens" % mail) :
# Il y a eu une erreur
arg = u'--title "Inscription Mailing liste de communiquation ENS" '
arg+= u'--msgbox "Une erreur s\'est produite lors de l\'ajout à la mailing liste.\n\n\n" 0 0'
dialog(arg)

90
gestion/gen_confs/squid.py Executable file
View file

@ -0,0 +1,90 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" Génération de la configuration pour squid """
from gen_confs import gen_config
from time import localtime
class bl_carte_etudiant(gen_config) :
""" Génère le fichier de blackliste pour carte d'étudiant pour squid"""
BL_CARTE = '/tmp/bl_carte_et'
restart_cmd = '/etc/init.d/squid reload'
def __str__(self) :
return "blackliste cartes d'étudiant"
def _gen(self) :
fd=self._open_conf(self.BL_CARTE)
if localtime()[1] == 9:
# On est en septembre, on autorise ceux qui ont payé l'an dernier et cette année
base = self.base.search('carteEtudiant!=%s&(paiement=%d|paiement=%d)' % (self.ann_scol,
int(self.ann_scol),
int(self.ann_scol) - 1))
else:
base = self.base.search('paiement=%s' % self.ann_scol)
for adh in ( [ self.crans ] + base['adherent'] + base['club'] ):
for machine in adh.machines() :
self.anim.cycle()
if 'bloq' in machine.blacklist_actif() : continue
if machine.proprietaire().idn != 'aid' : continue
fd.write(machine.nom() + '\n')
fd.close()
class bl_upload_squid(gen_config) :
""" Génère le fichier de blackliste d'upload pour squid"""
# Fichier
BL_UPLOAD = '/tmp/bl_upload_squid'
restart_cmd = '/etc/init.d/squid reload'
def __str__(self) :
return "blackliste upload squid"
def _gen(self) :
upload = self._open_conf( self.BL_UPLOAD )
if localtime()[1] == 9:
# On est en septembre, on autorise ceux qui ont payé l'an dernier et cette année
base = self.base.search('(paiement=%d|paiement=%d)' % (int(self.ann_scol),
int(self.ann_scol) - 1))
else:
base = self.base.search('paiement=%s' % self.ann_scol)
for adh in ( [ self.crans ] + base['adherent'] + base['club'] ):
for machine in adh.machines() :
self.anim.cycle()
bl = machine.blacklist_actif()
if 'bl_upload' in bl and not 'bloq' in bl :
upload.write( machine.nom() + '\n' )
upload.close()
class bl_virus(gen_config) :
""" Génère le fichier de blackliste virus pour squid"""
# Fichiers
BL_VIRUS = '/tmp/bl_virus'
def __str__(self) :
return "blackliste virus"
restart_cmd = '/etc/init.d/squid reload'
description = u'Bloquage accès http vers l\'extérieur, page expliquative'
def _gen(self) :
virus = self._open_conf( self.BL_VIRUS )
if localtime()[1] == 9:
# On est en septembre, on autorise ceux qui ont payé l'an dernier et cette année
base = self.base.search('(paiement=%d|paiement=%d)' % (int(self.ann_scol),
int(self.ann_scol) - 1))
else:
base = self.base.search('paiement=%s' % self.ann_scol)
for machine in base['machine'] :
self.anim.cycle()
bl = machine.blacklist_actif()
if 'bl_virus' in bl and not 'bloq' in bl :
virus.write( machine.nom() + '\n')
virus.close()

372
gestion/gen_confs/switchs.py Executable file
View file

@ -0,0 +1,372 @@
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" met à jour les propriétés des prises des switchs du bat :
mac autorisée(s), état (activé ou non) et nom de la prise
argument : nom du switch
procédure de configuration initiale :
* mot de passe admin (password manager user-name <username>)
* upgrade firmware (copy tftp flash 138.231.136.7 <file>)
* reboot (boot)
* génération clef ssh (crypto key generate ssh)
* copie fichier de conf (copy tftp startup-config 138.231.136.7 <file>)
* faire le stacking et le snmpv3 à la main
pour les reconfiguration juste copier le fichier de conf
"""
import string, sys, os, commands
sys.path.append('/CRANS/code')
#sys.path.append('/home/fred/Crans/zamok/CRANS/code')
from hptools import hp
sys.path.append('/usr/scripts/gestion')
from ldap_crans import crans_ldap, ann_scol
from annuaires import chbre_prises #, uplink_prises, reverse
from gen_confs import gen_config, OK, ERREUR, anim
from time import localtime
# from qqch import switchs # Liste des switchs
bat_switchs = [ 'b', 'c', 'h', 'i', 'g' ]
class switch(gen_config) :
def __init__(self,chbres):
""" Chbre doit être une liste de chambres """
self.db = crans_ldap()
# On enlève la chambre "CRA"
self.chbres = [ch for ch in chbres if ch != "CRA"]
def __str__(self) :
return 'switchs'
def restart(self) :
# Rien à faire ici
pass
def gen_conf(self) :
self.chbres.sort()
for chbre in self.chbres :
bat = chbre[0].lower()
a = self.db.search('chbre=%s' % chbre)['adherent']
action = ''
for adh in a :
if ((ann_scol in adh.paiement() or
((ann_scol-1) in adh.paiement and localtime()[1]==9)) and
'bloq' not in adh.blacklist_actif()) :
# Il faut activer la prise
anim('\tactivation chbre %s' % chbre)
action = 'enable'
break
if action == '' :
# Il faut désactiver la prise
anim('\tdésactivation chbre %s' % chbre)
action = 'disable'
try :
if bat in bat_switchs :
# Action sur le switch
prise = chbre_prises[bat][chbre[1:].lower()]
sw=hp(bat,int(prise[0]))
r = sw.set_prise(int(prise[1:4]),'disable')
sw.close()
if not r :
raise RuntimeError('Erreur de communiquation')
else :
# Mail au bat
To = "bat%s@crans.org" % bat
From = To
conn=smtplib.SMTP('localhost')
txt_mail = "From: Crans scripts <%(From)s>\n"
txt_mail+= "To: %(To)s\n"
txt_mail+= "Subject: Bienvenue au Cr@ns !\n\n"
txt_mail+= "Chambre %s à débrancher." % chbre
conn.sendmail(From, To , txt_mail % { 'From' : From, 'To' : To })
conn.quit()
print OK
except :
print ERREUR
if self.debug :
import traceback
traceback.print_exc()
### BROUILLON POUR PLUS TARD
class switch2(gen_config) :
# Répertoire ou écire les fichiers de conf
CONF_REP='/tmp/' # avec un / derrière
config = """; J4899A Configuration Editor; Created on release #H.07.32
hostname "%(switch)s"
;-------------------------------------------------------- Snmp
snmp-server contact "root@crans.org"
snmp-server location "Batiment %(bat)s"
;A faire à la main
;snmpv3 enable
;snmpv3 restricted-access
;snmpv3 user "initial"
;snmpv3 user "crans"
;snmpv3 group ManagerPriv user "crans" sec-model ver3
;snmp-server community "public" Operator
;-------------------------------------------------------- Réglage heure/date
time timezone 60
time daylight-time-rule Western-Europe
sntp server 138.231.136.6
timesync sntp
sntp unicast
;-------------------------------------------------------- Misc
console inactivity-timer 30
;-------------------------------------------------------- Logs
logging 138.231.136.7
;-------------------------------------------------------- Logs
%(INTERFACES_CONF)s
;-------------------------------------------------------- IP du switch
ip default-gateway 138.231.136.4
vlan 1
name "DEFAULT_VLAN"
untagged 1-%(nb_prises)d
ip address %(ip)s 255.255.248.0
ip igmp
no ip igmp querier
exit
;-------------------------------------------------------- Accès d'adminsitration
no web-management
aaa authentication ssh login public-key
ip ssh
ip ssh version 2
ip authorized-managers 138.231.136.0 255.255.255.0
ip authorized-managers 138.231.137.216
ip authorized-managers 138.231.137.215
;STACKING_CONF
;-------------------------------------------------------- Spanning-tree
spanning-tree
; Config des uplinks
no spanning-tree %(uplinks)s edge-port
; Config des prises adhérent
spanning-tree %(non_uplinks)s point-to-point-mac auto
spanning-tree %(non_uplinks)s priority 15
no cdp run
;-------------------------------------------------------- Avec mot de passe ;)
password manager
"""
interface_template = """interface %(prise)i\n%(etat)s
name %(nom)s
flow-control%(speed)s
no lacp
exit
"""
filtre_mac_template = "port-security %(prise)i learn-mode static address-limit 3 mac-address%(macs)s\n"
nom = 'switchs'
def __str__(self) :
return self.nom
def __init__(self,qqch='') :
"""
Si qqch='' : reconfigure tous les switchs
Si qqch=<nom d'un switch> (batX-N) : reconfigure que ce switch
Si qqch=<chambre> (XNNN) : regénère le fichier de conf du switch correspondant
et reconfigure uniquement la prise """
# Traitement argument
if not qqch :
self.__params = {}
elif qqch in switchs :
self.__params = { 'bat' : qqch[3].lower() ,
'sw_num' : qqch[5] ,
'switch' : qqch.lower() }
self.nom = 'switch %(switch)s' % self.__params
else :
# Ca doit être une chambre
bat = qqch[0].lower()
if bat in mail_bats :
# Pas de switch à reconfigurer
self.__params = { 'chbre' : qqch.capitalize() ,
'bat' : bat }
else :
try :
prise = chbre_prises[bat][qqch[1:]]
self.__params = { 'nom_prise' : qqch.capitalize() ,
'sw_prise' : int(prise[1:]) ,
'bat' : bat ,
'sw_num': int(prise[0]),
'switch': 'bat%s-%s' % (bat, prise[0]) }
self.nom = 'prise %s%s (chbre %s)' % (bat.upper(), prise,qqch)
except :
# Prise inconnue
raise RuntimeError("Impossible d'associer une prise à la chambre.")
def gen_conf(self) :
""" Génération configuration :
* soit d'un switch si présent dans self.__params
* soit de tous les switchs si aucun présent dans self.__params
* soit d'aucun switch si chbre dans bat non manageable, dans ce cas
envoi un mail quand il faut aller débrancher et retourne 2
En cas d'erreur retourne 1
"""
if self.__params.has_key('chbre') :
# Pas de switch manageable, il suffit de mailer
anim('\tmail à bat%(bat)s' % self.__params)
print 'TODO'
return 2
self.lock()
if self.__params.has_key('switch') :
# Regénération de la conf d'un switch
self.__gen_switch(self.__params)
else :
# Regénération de la conf de tous les switchs
for switch in switchs :
params = { 'bat' : switch[3].lower() ,
'sw_num' : int(switch[5]) ,
'switch' : switch.lower() }
self.__gen_switch(params)
self.unlock()
def restart(self) :
"""
Si l'instance à été initialisée avec une chbre reconfigure cette chambre
Sinon reconfigure le switch founi, si aucun fourni, les reconfigure tous
"""
if self.__params.has_key('chbre') :
# Rien à faire, le mail doit être envoyé
return
self.lock()
if self.__params.has_key('sw_prise') :
# Utiliser la classe hptools et/ou faire du snmp
anim('\treconfiguration prise' )
elif self.__params.has_key('switch') :
# Restart d'un seul switch
self.__restart_switch(self.__params['switch'])
else :
# Restart tous les switchs
for switch in switchs :
self.__restart_switch(switch)
self.unlock()
def __gen_switch(self,params) :
""" params est un dictionnaire avec les keys bat, sw_num et switch """
aff = anim('\tgénération de %(switch)s.conf' % params)
try :
bat = params['bat']
# Nombre de prises
nb_prises = """
METTRE CA DANS LA CLASSE S'OCCUPANT DU SNMP
try :
nb = int(sys.argv[2])
except :
a=os.popen("snmpget -c public %s system.sysDescr.0 2>/dev/null" % switch)
try :
version = string.strip(string.split(string.split(a.readlines()[0],',')[0],'=')[1])
except :
version = ''
a.close()
if version == 'HP J4900A ProCurve Switch 2626' :
nb=26
elif version == 'HP J4899A ProCurve Switch 2650' :
nb=50
else :
print "Erreur : impossible de déterminer le nombre de ports"
sys.exit(1)
"""
nb_prises = 50
params['nb_prises'] = nb_prises
# Récupération IP
params['ip'] = commands.getoutput("host %s" % switch).split()[-1]
params['INTERFACES_CONF'] = ''
params['MAC_FILTER'] = ''
# Dictionnaire prise -> chambre
prise_chbres = reverse(bat)
# Configuration des prises
mac_filter=[]
aff.iter = nb_prises+1
for prise in range(1,nb_prises+1):
aff.cycle()
prise_params = { 'prise' : prise , 'speed' : '', 'etat' : '' }
annu_prise = '%i%02i' % (params['sw_num'], prise)
if uplink_prises[bat].has_key(int(annu_prise)) :
### Prise d'uplink
prise_params['nom'] = uplink_prises[bat][int(annu_prise)]
try : params['uplinks'] += ',%i' % prise
except : params['uplinks'] = str(prise)
else :
### Prise adhérent
try : params['non_uplinks'] += ',%i' % prise
except : params['non_uplinks'] = str(prise)
if prise_chbres.has_key(annu_prise) :
chbres = prise_chbres[annu_prise]
elif prise_chbres.has_key(annu_prise+'-') :
# Prise en 10
prise_params['speed'] = ' speed-duplex auto-10\n'
chbres = prise_chbres[annu_prise+'-']
else :
# Prise non référencée dans l'annuaire
prise_params['nom'] = "Pas_dans_l'annuaire"
prise_params['etat']=' disable\n'
chbres = []
if chbres :
prise_params['nom'] = 'Chambre'
if len(chbres) > 1 :
prise_params['nom'] += 's'
for chbre in chbres :
prise_params['nom'] += '_' + chbre
# Etat
macs = ''
machines = self.base.search('chbre=%s%s' % (bat.upper(), chbre) )['machine']
for m in machines :
if 'bloq' in m.blacklist_actif() : continue
macs += ' ' + m.mac()
if not macs :
prise_params['etat']=' disable\n'
else :
params['MAC_FILTER'] += self.filtre_mac_template % vars()
params['INTERFACES_CONF'] += self.interface_template % prise_params
# Petites verif
if not params.has_key('uplinks') or not params.has_key('non_uplinks') :
raise RuntimeError('Switch sans uplink ou sans prise adhérent.')
fd = self._open_conf(self.CONF_REP + 'switch' + '.conf')
fd.write(self.config % params)
fd.close()
aff.reinit()
print OK
except :
aff.reinit()
self._restore()
return 1
def __restart_switch(self,switch) :
anim('\trestart %s' % switch)
try :
print 'TODO'
except :
print ERREUR
if self.debug :
import traceback
traceback.print_exc()
return 1

1540
gestion/gest_crans.py Executable file

File diff suppressed because it is too large Load diff

93
gestion/iptools.py Executable file
View file

@ -0,0 +1,93 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
"""
Manipulation d'IPv4
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
def QuadToDec(ip) :
"""
Retourne la représentation décimale d'une ip
ip est de la forme xxx.xxx.xxx.xxx
"""
test = ip.split('.')
if len(test)!=4 : raise ValueError('IP Invalide')
ip_dec = 0
for z in range(0,4) :
n = int(test[z])
if n<0 or n>255 : raise ValueError('IP Invalide')
ip_dec += n * ( 256**(3-z) )
return ip_dec
def DecToQuad(ip_dec) :
"""
Retourne la représentation habituelle d'une ip (xxx.xxx.xxx.xxx)
ip_dec est l'IP en base 10
"""
try :
return "%d.%d.%d.%d" % ( \
ip_dec/(256**3) ,
(ip_dec%(256**3)) / (256**2) ,
( (ip_dec%(256**3)) % (256**2) ) / 256 ,
( (ip_dec%(256**3)) % (256**2) ) % 256 )
except :
raise ValueError('IP Invalide')
def param(net) :
"""
net est un résau fourni sous la forme xxx.xxx.xxx.xxx/yy
si donnée valide retourne un dictionnaire :
{ 'network' : xxx.xxx.xxx.xxx ,
'netmask' : yyy.yyy.yyy.yyy ,
'broadcast' : zzz.zzz.zzz.zzz }
sinon retourne {}
"""
reseau = {}
ip, mask = net.split('/')
try :
mask = int(mask)
dec_ip = QuadToDec(ip)
if dec_ip == -1 : raise
except :
return {}
# Calcul du netmask
dec_netmask=0
non_dec_netmask=0 # On calcule aussi le complémentaire
for i in range(0,32) :
if i < mask :
dec_netmask += 2**(31-i)
else :
non_dec_netmask += 2**(31-i)
reseau['netmask'] = DecToQuad(dec_netmask)
# Calcul du network
reseau['network'] = DecToQuad( dec_ip & dec_netmask )
# Calcul du broadcast
reseau['broadcast'] = DecToQuad( dec_ip | non_dec_netmask )
return reseau
def AddrInNet(ip,net) :
"""
ip est de la forme xxx.xxx.xxx.xxx
net est de la forme xxx.xxx.xxx.xxx/yy
net peut être une liste de chaînes ci-dessus
Retourne True si l'ip est dans un des réseaux.
Note : retourne Fasle si l'IP est une adresse de réseau ou broadcast
"""
if type(net)==str : net = [ net ]
r = False
for ne in net :
n = param(ne)
if ip == n['broadcast'] or ip ==n['network'] :
return False
r = r or QuadToDec(n['netmask']) & QuadToDec(ip) == QuadToDec(n['network'])
return r

1909
gestion/ldap_crans.py Executable file

File diff suppressed because it is too large Load diff

50
gestion/lock.py Executable file
View file

@ -0,0 +1,50 @@
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" Gestion de lock
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
import os,string,time,sys,pwd, affich_tools
def make_lock(lock_name, lock_comment='') :
""" Création d'un lock """
lock_file = '/var/lock/' + lock_name
if os.path.isfile(lock_file) :
### Lock existant
# Lecture du lock
fd = open(lock_file, "r")
pid= fd.readline().strip()
fd.close()
# Informations sur le processus lockant
if os.system( "ps %s > /dev/null 2>&1" % pid ) :
# Le script lockant ne tourne plus
os.remove(lock_file)
else :
# Il faut attendre
a = affich_tools.anim('\tattente du lock')
for i in range(8) :
time.sleep(1)
a.cycle()
sys.stdout.write('\r')
return make_lock(lock_name, lock_comment)
### Prise du lock
lock_fd=open(lock_file, "w")
lock_fd.write("%s\n%s\n%s" % (os.getpid(), pwd.getpwuid(os.getuid())[0], lock_comment) )
lock_fd.close()
def remove_lock( lock_name ) :
""" Destruction du lock """
lock_file = '/var/lock/' + lock_name
try :
fd = open(lock_file, "r")
if fd.readline().strip()=="%s" % os.getpid():
os.remove(lock_file)
fd.close()
except :
None

69
gestion/secours.py Executable file
View file

@ -0,0 +1,69 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
# Fichiers à modifier et chaine indiquant un commentaire dans ceux-ci
fichiers = { '/etc/bind/named.conf.options' : '//' ,
'/etc/postfix/main.cf' : '#' }
import sys, sre
def edit(file,comment,secours) :
""" Edite le fichier fourni en commentant (mode normal)
ou décommentant (mode secours) les lignes se terminant avec #POUR SECOURS """
signal = '#POUR SECOURS'
l = len(signal)
fd = open(file)
line = fd.readline()
new = ''
while line :
l = line.rstrip()
if sre.match('.*'+signal+'$',l) :
# Ligne pour secours
if not sre.match('^' + comment,l) and not secours:
# On est actuellement configuré en secours
# Il faut passer en normal
new += comment + line
elif sre.match('^' + comment,l) and secours :
# On est actuellement configuré en normal
# Il faut passer en secours
new += line.replace(comment,'',1)
else :
# Rien à faire, on est bien configuré
new += line
else :
# Ligne normale
new += line
line = fd.readline()
fd.close()
# Ecriture de la nouvelle version
fd = open(file,'w')
fd.write(new)
fd.close()
def usage() :
print 'Usage : %s 0 pour reconfigurer les services locaux en normal' % sys.argv[0]
print ' %s 1 pour reconfigurer les services locaux en secours' % sys.argv[0]
print 'Fichiers modifiés par le changement de mode : \n\t', '\n\t'.join(fichiers.keys())
if __name__ == '__main__' :
if len(sys.argv) != 2 :
usage()
else :
mode = sys.argv[1]
if mode not in '01' :
usage()
else :
mode = int(mode)
for f, c in fichiers.items() :
try :
print 'Edition de %s' % f
edit(f,c,mode)
except :
import traceback
traceback.print_exc()

759
gestion/whos.py Executable file
View file

@ -0,0 +1,759 @@
#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
# Copyright (C) Frédéric Pauget
# Licence : GPLv2
"""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
Les options de recherches sont :
* limitations sur la recherche :
-a ou --adherent : limitation de la recherche aux adhérents
-m ou --machine : limitation de la recherche aux machines
-c ou --club : limitation de la recherche aux clubs
-b ou --bornes : limitation de la recherche aux bornes wifi
* 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 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)
"""
from ldap_crans import is_actif , crans_ldap, isadm, ann_scol
from affich_tools import *
limit_aff_details = 1
limit_aff_historique = 4
aff_ipsec = 0
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' :
print adhers_brief(qqch)
elif t == 'mid' :
if mtech : print list_machines(qqch)
else : print machines_brief(qqch)
elif t == 'cid' :
print clubs_brief(qqch)
else :
i = 0
for c in qqch :
t = c.idn
if i : print coul(u'='*80,'cyan')
i = 1
if t == 'aid' : print adher_details(c)
elif t == 'mid' :
print machine_details(c)
elif t == 'cid' : print club_details(c)
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 = [ ( u'aid' , u'Prénom Nom' , u'Chbre', u'P', u'C', u'Machines' ) ]
for a in adhers :
## Etat administratif
ok = u'\x1b[1;32mo\x1b[1;0m'
nok = u'\x1b[1;31mn\x1b[1;0m'
# Paiement
if ann_scol in a.paiement() : paid = ok
else : paid = nok
# Précablage
if ann_scol+1 in a.paiement() : paid = coul(paid,'f_vert')
# Carte d'étudiant
if ann_scol in a.carteEtudiant() : carte = ok
else : carte = nok
machines = ''
# Récupération des machines
for machine in a.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)
# Données
data.append((a.id() , a.Nom(), a.chbre(),paid,carte,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\n" + \
u"C : carte d'étudiant année en cours\n" + \
tableau([5, 30 , 5, 1, 1,30], data)
def machines_brief(machines) :
"""
Formatage sous forme d'un tableau des propriétés de la liste de machine :
* mid
* type (fixe ou wifi, born)
* nom
* adresse IP
* adresse MAC
* si blacklistée
"""
data = [ ( u'mid' , u'Type', u'Nom de machine', u'Propriétaire', u'Chbre', u'Limitation' ) ]
for m in machines :
t, bl = __bases_machines(m)
# Propriétaire
a = m.proprietaire()
p = a.Nom()
# A jour administrativement
if ann_scol not in a.paiement() or ann_scol not in a.carteEtudiant() :
p = coul(p,'rouge')
# Données
data.append((m.id() , t, m.nom().split('.')[0], p, a.chbre(), bl))
return u"Le propriétaire en rouge signale un problème administratif\n" + \
tableau([5, 4, 18, 30, 5, 10], data)
def clubs_brief(clubs) :
"""
Formatage sous forme de tableau des infos sur la liste de clubs fournie :
* cid
* nom
* local
* machines
"""
data = [ ( u'cid' , u'Nom ', u'Local',u'P', u'Responsable', u'Machines' ) ]
for c in clubs :
## Etat administratif
ok = u'\x1b[1;32m\xa4\x1b[1;0m'
nok = u'\x1b[1;31m\xa4\x1b[1;0m'
# Paiement
if ann_scol in c.paiement() : paid = ok
else : paid = nok
# Précablage
if ann_scol+1 in c.paiement() : paid = coul(paid,'f_vert')
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
resp = c.responsable().Nom()
# 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([5, 15 , 6, 1, 21, 24], data)
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 = [ ( u'mid' , u'Type', u'Nom de machine', u'Adresse IP', u'Adresse MAC', u'Limitation' ) ]
for m in machines :
t, bl = __bases_machines(m)
# Données
data.append((m.id() , t, m.nom().split('.')[0], m.ip(), m.mac(), bl))
return tableau([5, 4, 18, 17, 19, 10], data)
def list_bornes(bornes) :
"""
Formatage sous forme d'un tableau des propriétés de la liste de bornes wifi :
* mid
* nom
* adresse IP
* adresse MAC
* puissance
* canal
* lieu (la première remarque en fait)
"""
p = u'**'
if isadm : p = u'P'
data = [ ( u'mid' , u'Nom', u'Adresse IP', u'Adresse MAC', u'C' , p, u'Lieu') ]
for b in bornes :
t, bl = __bases_machines(b)
if t != 'born' : continue
if isadm :
p = b.puissance()
# Données
data.append((b.id() , b.nom().split('.')[0], b.ip(), b.mac(), b.canal(), p, b.info()[0] ))
if isadm :
t = u"C=canal, P=puissance\n"
else :
t = u"C=canal\n"
return t + tableau([5, 14, 17, 19, 2, 2, 13], data)
def adher_details(adher) :
"""
Affichage du détail des propriétés d'un adhérent
"""
f=''
# Aid
f+= coul(u'aid=%s ' % adher.id() ,'bleu')
# Nom, prenom
f += coul(u'Nom : ','gras') + "%s\n" % adher.Nom()
# Mail
if adher.mail().find('@')!=-1 :
f += coul(u'Adresse mail : ','gras') + "%s" % adher.mail()
else :
f += coul(u'Login : ','gras') + "%s\t" % adher.mail()
alias = ', '.join([adher.cannonical_alias()] + adher.alias())
if alias :
if alias[0]==',' :
# Cannonical étéait vide
alias = alias[2:]
f += coul(u'Alias : ','gras') + alias
f+= u'\n'
# Etat administratif
f += coul(u'Etat administratif : ','gras')
jour=1
if ann_scol not in adher.carteEtudiant() :
f += coul(u"manque carte d'étudiant",'violet')
jour = 0
if ann_scol not in adher.paiement() :
if not jour : f += ' et '
f += coul(u"cotisation %s/%d non réglée"% (ann_scol, ann_scol+1 ),'violet')
jour = 0
if jour :
f += coul(u"à jour",'vert')
f += '\n'
# Telephone
tel = adher.tel()
try :
tel = u'%s %s %s %s %s' % ( tel[:2], tel[2:4], tel[4:6], tel[6:8], tel[8:] )
except :
None
f += coul(u'Numéro de téléphone : ','gras') + "%s\n" % tel.ljust(12)
# Adresse
chbre = adher.chbre()
if chbre == 'EXT' :
# Adhérent extérieur
f += coul(u'Adresse : ','gras')
addr = adher.adresse()
f += addr[0] + u'\n'
if addr[1] != ' ' : f += u' ' + addr[1] + u'\n'
f+= u' ' + addr[2] + u' ' + addr[3]
else :
# Chambre + prise (d'après annuaire)
f += coul(u'Chambre : ','gras') + u"%s " % chbre
prise = adher.prise()
if prise :
f += u'(prise %s)' % prise
f += '\n'
# Etudes
if adher.etudes(1).isdigit() :
f += coul(u'Etudes : ','gras')+ "%s %s%s\n" % \
( adher.etudes(0), adher.etudes(1), adher.etudes(2) )
else :
f += coul(u'Etudes : ','gras')+ "%s %s %s\n" % \
( adher.etudes(0), adher.etudes(1), adher.etudes(2) )
# Role dans l'assoce
d = adher.droits()
if d :
f += coul(u"Droits sur les serveurs : ",'gras') + ', '.join(d)
f += u'\n'
# Paiement
if adher.paiement() :
if len(adher.paiement()) == 1 :
f += coul(u'Cotisation payée pour l\'année scolaire :','gras')
else :
f += coul(u'Cotisation payée pour les années scolaires :','gras')
g = u''
for an in adher.paiement() : g += u" %i-%i" % ( an, an+1 )
if len(g) > 35 : f += '\n\t'
f += g
f += u'\n'
# Cartes d'étudiant fournie
if adher.carteEtudiant() :
if len(adher.carteEtudiant()) == 1 :
f += coul(u"Carte d'étudiant fournie pour l'année scolaire :",'gras')
else :
f += coul(u"Carte d'étudiant fournie pour les années scolaires :",'gras')
g = u''
for an in adher.carteEtudiant() : g += u" %i-%i" % ( an, an+1 )
if len(g) > 25 : f += '\n\t'
f += g
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'
return f
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 machine.ipsec() : a='Machine wifi'
elif machine.canal() : a='Borne wifi'
else : a='Machine fixe'
f+= coul(a+' : ' ,'gras')
f+= "%s\n" % machine.nom()
# Alias ?
alias = machine.alias()
if machine.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()
# Propriétaire
a = machine.proprietaire()
f+= coul(u'Propriétaire : ','gras')
f += "%s" % a.Nom()
if a.chbre() : f += " (%s)" % a.chbre()
f+= '\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 isadm and machine.puissance() :
f += coul(u'Puissance : ','gras') + machine.puissance()
f += coul(u'\tCanal : ', 'gras') + machine.canal()
f += '\n'
if aff_ipsec and machine.ipsec() :
f += coul(u'Clef IPsec : ','gras') + machine.ipsec()
f += '\n'
# Ports spéciaux
if machine.portTCPin() :
f += coul(u'Ports TCP ouvert ext->machine : ','gras') + machine.portTCPin() + '\n'
if machine.portTCPout() :
f += coul(u'Ports TCP ouvert machine->ext : ','gras') + machine.portTCPout() + '\n'
if machine.portTCPin() :
f += coul(u'Ports UDP ouvert ext->machine : ','gras') + machine.portUDPin() + '\n'
if machine.portUDPout() :
f += coul(u'Ports UDP ouvert machine->ext : ','gras') + machine.portUDPout() + '\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 adhérent
"""
f=''
# Cid
f+= coul(u'cid=%s ' % club.id() ,'bleu')
# Nom
f += coul(u'Nom : ','gras') + "%s\n" % club.Nom()
# Etat administratif
f += coul(u'Etat administratif : ','gras')
jour=1
if ann_scol not in club.paiement() :
if not jour : f += ' et '
f += coul(u"charte %s/%d non signée"% (ann_scol, ann_scol+1 ),'violet')
jour = 0
if jour :
f += coul(u"à jour",'vert')
f += '\n'
# Chambre + prise
f += coul(u'Local : ','gras') + "%s " % club.local()
prise = club.prise()
if prise :
f += '(prise %s)' % prise
f += '\n'
# Paiement
if club.paiement() :
f += coul(u'Charte signée pour les années scolaires :','gras')
g = ''
for an in club.paiement() : g += " %i-%i" % ( an, an+1 )
if len(g) > 35 : f += '\n\t'
f += g
f += '\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'
return f
###########################################
# Fonctions annexes de formatage de données
def _blacklist(clas) :
""" Formatage blackliste de la classe fournie """
f = u''
for event in clas.blacklist() :
if is_actif(event) :
# Colorisation si sanction en cours
c = 'rouge'
else :
c = 'blanc'
f += u"%s\n\t " % coul(u'du %s au %s : %s, %s' % tuple(event.split(',')) ,c)
f = f[:-6] # supression des espaces superflus
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 ''
f=''
h = clas.historique()
h.reverse()
if h :
f += coul(u'Historique : ','gras')
for i in range(0,limit_aff_historique) :
try :
a = h[i] # Produit une erreur si i trop grand
if i !=0 : f += ' '
f += '%s\n' % a
except :
break
try :
if h[i+1] : f += ' [...]\n'
except :
None
return f
def __bases_machines(m) :
""" Retourne [ type de la machines, blacklist ] """
#Type
if m.ipsec() : t='wifi'
elif m.canal() : t='born'
else : t='fixe'
# 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
##############################################################################
## Partie dévolue au système de recherche
def __usage_brief(err='') :
""" Message d'erreur """
if err : cprint(err,'gras')
print "Pour obtenir de l'aide sur l'utilisation de ce programme utilisez l'option -h"
sys.exit(2)
def __usage() :
""" Comment ca marche ? """
list = ['']
for c in base.auto_search_champs.values() :
for champ in c :
coul_champ = coul(champ,'bleu')
if list[-1] == '' :
list[-1] = coul_champ
l = len(champ)
else :
l += len(champ) + 2
if l < 80 :
list[-1] += ', ' + coul_champ
else :
list.append(coul_champ)
l = len(champ)
for c in base.non_auto_search_champs.values() :
for champ in c :
l += len(champ) + 2
if l < 80 :
list[-1] += ', ' + champ
else :
list.append(champ)
l = len(champ)
print __doc__ % { 'prog' : sys.argv[0].split('/')[-1] ,
'champs_rech' : '\n'.join(list) ,
'limit_aff_details' : limit_aff_details ,
'limit_aff_historique' : limit_aff_historique }
sys.exit(0)
def __recherche() :
"""
Recherche et affichage des résultats à partir des options founies (sys.argv)
"""
global aff_ipsec, limit_aff_details, limit_aff_historique, debug
# Récupération des options
if len(sys.argv) == 1 :
# Pas d'option fournie
__usage_brief()
try :
options, arg = getopt.getopt(sys.argv[1:], 'hamctbil:L:', [ 'debug', 'help', 'adherent', 'machine', 'club' , 'tech', 'bornes', 'limit=', 'limit-historique=', 'ipsec' ])
except getopt.error, msg :
__usage_brief(msg)
# Traitement des options
only_adh=0
only_mac=0
only_club=0
only_bornes=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('Valeur du paramètre %s incorecte (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('Valeur du paramètre %s incorecte (doit être un entier positif)' % opt)
elif opt in [ '-a', '--adherent' ] :
only_adh = 1
print "Recherche limitée aux adhérents."
elif opt in [ '-m', '--machine' ] :
only_mac = 1
print "Recherche limitée aux machines."
elif opt in [ '-c', '--club' ] :
only_club = 1
print "Recherche limitée aux clubs."
elif opt in [ '-b', '--bornes' ] :
only_bornes = 1
print "Recherche limitée aux bornes wifi."
# On va tenter de limiter un peu la recherche
if arg[0] == '*' :
# Recherche initiale sans critètre
arg = [ 'canal=*' ]
elif arg[0].find('=')!=-1 :
# Recherche avec critères
arg += [ 'canal=*' ]
elif opt in [ '-t', '--tech' ] :
# Format affichage des machines
mtech = 1
elif opt in [ '-i', '--ipsec' ] :
# Affichage des clefs ipsec
aff_ipsec = 1
if only_adh + only_mac + only_club + only_bornes > 1 :
__usage_brief('Options utilisées incompatibles')
if not arg :
# Pas de chaine de recherche fournie
__usage_brief('Chaine de recherche incorrecte.')
try :
res = base.search(' '.join(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']:
print "Aucun résultat trouvé."
sys.exit(3)
# L'affichage souhaité a été précisé ?
elif only_bornes :
if not res['machine'] :
print 'Aucun résultat à afficher'
sys.exit(4)
else :
if len(res['machine']) > limit_aff_details :
print list_bornes(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) :
print 'Aucun résultat à afficher'
sys.exit(4)
aff(to_aff)
elif res['club'] :
print '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) :
print 'Aucun résultat à afficher'
sys.exit(4)
aff(to_aff)
elif res['adherent'] :
print 'Aucun résultat à afficher'
sys.exit(4)
# Non : on affiche tout.
else :
if res['adherent'] :
cprint("Résultats trouvés parmi les adhérents :",'cyan')
aff(res['adherent'])
if res['machine'] :
cprint("Résultats trouvés parmi les machines :",'cyan')
aff(res['machine'],mtech)
if res['club']:
cprint("Résultats trouvés parmi les clubs :",'cyan')
aff(res['club'])
if __name__ == '__main__' :
global debug
debug = 0
import sys, getopt
base = crans_ldap()
try :
__recherche()
except KeyboardInterrupt :
print "Recherche interrompue par l'utilisateur."
sys.exit(255)
except SystemExit, c :
# Fin
sys.exit(c)
except :
print """Une erreur fatale c'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."""
if debug :
print '-'*40
print 'Détails techniques :'
import traceback
traceback.print_exc()
sys.exit(1)