[debian/*.py] remove trailing whitespaces

Ignore-this: f62e84c258c847013d307d12acdf489e

darcs-hash:20090309212356-0445d-d8338d06f968f316fcd085446023123be87040f7.gz
This commit is contained in:
Stephane Glondu 2009-03-09 22:23:56 +01:00
parent cc31727b60
commit eae0d21f83
18 changed files with 352 additions and 352 deletions

View file

@ -1,5 +1,5 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# Serveur SSL qui renvoie la MAC correspondant à une IP
# Ce serveur est utilisé sur Nectaris par les bornes wifi pour faire
@ -57,15 +57,15 @@ class ARPProtocol(basic.LineReceiver):
return self.transport.write(str(m) + "\r\n")
else:
return self.transport.write("unknown\r\n")
class ARPFactory(protocol.ServerFactory):
"""Backend du serveur. Effectue la résolution IP/MAC."""
protocol = ARPProtocol
def __init__(self, network='0.0.0.0/0'):
self.ldap = crans_ldap()
self.network = network
def getMac(self, IP):
return defer.succeed(self._getMac(IP))

View file

@ -5,7 +5,7 @@
Script de changement de mots de passe LDAP
Utilisation :
* cas 1 : sans arguements par un utlisateur lambda :
* cas 1 : sans arguements par un utlisateur lambda :
changement de son propre mdp
* cas 2 : avec argument par un utilisateur ayant accès
total à la base LDAP (respbats) mais PAS ROOT :
@ -28,7 +28,7 @@ try :
except :
ldap_password = ''
ldap_auth_dn = ''
uri = 'ldap://ldap.adm.crans.org'
syslog.openlog('chgpass',syslog.LOG_PID,syslog.LOG_AUTH)
@ -45,14 +45,14 @@ Il ne doit pas être basé sur un mot du dictionnaire.""", 'jaune')
print "Il est conseillé d'utiliser une combinaison de minuscules, majuscules,\nde chiffres et d'au moins un caractère spécial."
print "Le mot de passe tapé ne sera pas écrit à l'écran."
print "Taper Ctrl-D pour abandonner"
try :
while 1 :
mdp = getpass.getpass('Nouveau mot de passe : ')
### Test du mdp
## 1 - Longueur
if len(mdp) < 6 :
if len(mdp) < 6 :
cprint(u'Mot de passe trop court', 'rouge')
continue
@ -68,8 +68,8 @@ Il ne doit pas être basé sur un mot du dictionnaire.""", 'jaune')
if "'" in mdp:
cprint(u'Les accents ou caractères bizarres ne sont pas autorisés (mais #!@*&%{}| le sont !)',
'rouge')
continue
continue
## 3 - assez de caractères de types différents ?
chiffres = 0
majuscules = 0
@ -89,33 +89,33 @@ Il ne doit pas être basé sur un mot du dictionnaire.""", 'jaune')
(not majuscules and not minuscules) :
cprint(u'Mot de passe trop simple.', 'rouge')
continue
## 4 - Cracklib
test = commands.getoutput("echo '%s' | /usr/sbin/crack_testlib" % mdp)
if test.split(':')[-1] != ' ok' :
commentaire = {
commentaire = {
' it does not contain enough DIFFERENT characters': u'Il y a trop de caractères identiques.' ,
' it is based on a dictionary word': u'Le mot de passe est basé sur un mot du dictionnaire' ,
' it is too simplistic/systematic': u'Le mot de passe est trop simple/répétitif'
}.get(test.split(':')[-1],test.split(':')[-1])
cprint(commentaire, 'rouge')
continue
### On redemande le mot de passe
mdp1 = getpass.getpass('Retaper mot de passe : ')
if mdp != mdp1 :
cprint(u'Les deux mots de passe entrés sont différents, réesayer', 'rouge')
continue
break
# Changement mdp
if os.system("/usr/bin/ldappasswd -H '%s' -x -D '%s' -w '%s' '%s' -s '%s' > /dev/null" % (uri, ldap_auth_dn, ldap_password, dn, mdp) ):
cprint(u'Erreur lors du changement de mot de passe', 'rouge')
syslog.syslog("LDAP password changed for dn=%s" % dn)
else :
cprint(u'Changement effectué avec succès', u'vert')
except KeyboardInterrupt :
cprint(u'\nAbandon', 'rouge')
pass
@ -130,7 +130,7 @@ if __name__ == '__main__' :
# Changement de son mot de passe
login = getuser()
self_mode = True
elif '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) != 2 :
print "%s <login>" % sys.argv[0].split('/')[-1].split('.')[0]
print "Changement du mot de passe du compte choisi."
@ -143,11 +143,11 @@ if __name__ == '__main__' :
if not c.isalnum() and not c=='-' :
cprint(u'Login incorrect', 'rouge')
sys.exit(1)
if getuser() == login :
cprint(u'Utiliser passwd pour changer son propre mot de passe', 'rouge')
sys.exit(2)
if self_mode :
s = commands.getoutput('sudo -u respbats ldap_whoami')
else :
@ -155,13 +155,13 @@ if __name__ == '__main__' :
if not s :
cprint(u'Login non trouvé dans la base LDAP', 'rouge')
sys.exit(3)
# Ca a l'air bon
if s.find('\n\n') != -1 :
# Plusieurs trouvé : pas normal
cprint(u'Erreur lors de la recherche du login : plusieurs occurences !', 'rouge')
sys.exit(4)
s = s.split('\n')
try :
dn = s[0].split()[1]
@ -172,13 +172,13 @@ if __name__ == '__main__' :
except :
cprint(u'Erreur lors de la recherche du login', 'rouge')
sys.exit(5)
if self_mode :
# Il faut vérifier l'ancien mot de passe
ldap_auth_dn = dn
ldap_password = getpass.getpass('Mot de passe actuel : ')
s = commands.getoutput("/usr/bin/ldapwhoami -H '%s' -x -D '%s' -w '%s'" % ( uri, ldap_auth_dn, ldap_password ) ).strip()
try :
try :
resultat = s.split('\n')[0].split(':')[1].strip()
except :
cprint(u"Erreur lors de l'authentification", 'rouge')
@ -186,7 +186,7 @@ if __name__ == '__main__' :
if resultat != dn :
cprint({ 'Invalid credentials (49)': u'Mot de passe invalide' }.get(resultat, resultat), 'rouge')
sys.exit(8)
elif len(s) > 3 and os.getuid()!=0 :
# Adhérent avec droits et on est pas root
From = 'roots@crans.org'

View file

@ -1,9 +1,9 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
""" Changement du shell de l'utilisateur lancant le programme
""" Changement du shell de l'utilisateur lancant le programme
DOIT ETRE LANCE PAR SUDO
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
@ -31,7 +31,7 @@ adh = s['adherent']
if len(adh) != 1 :
print 'Erreur fatale lors de la consultation de la base LDAP'
sys.exit(3)
adh = adh[0]
shell = prompt(u'Nouveau shell :')
fd=open('/etc/shells')

View file

@ -171,7 +171,7 @@ Si tu as des questions, contacte disconnect@crans.org
NB : L'upload consiste en l'envoi de données vers des machines n'étant
pas branchées sur le CRANS.
--
--
Disconnect team"""
message_hard = u"""From: %(from)s
@ -199,7 +199,7 @@ Si tu as des questions, contacte disconnect@crans.org
NB: L'upload consiste en l'envoi de données vers des machines n'étant
pas branchées sur le CRANS.
--
--
Disconnect team"""
@ -210,7 +210,7 @@ Content-Type: text/plain; charset="iso-8859-15"
%(proprio)s uploade actuellement %(upload)s Mo.
--
--
Message créé par deconnexion.py"""
message_disconnect_hard = u"""From: %(from)s
@ -220,7 +220,7 @@ Content-Type: text/plain; charset="iso-8859-15"
%(proprio)s a été déconnecté pour upload (%(upload)s Mo).
--
--
Message créé par deconnexion.py"""
message_disconnect_multi = u"""From: %(from)s
@ -234,23 +234,23 @@ L'adhérent %(proprio)s a été déconnecté %(nbdeco)d fois pour upload en un m
Le PS a été généré et se trouve sur zamok :
%(ps)s
--
Message créé par deconnexion.py"""
--
Message créé par deconnexion.py"""
message_demenagement = u"""From: %(from)s
To: %(to)s
Subject: =?iso-8859-1?Q?D=E9m=E9nagement=20non=20d=E9clar=E9?=
Subject: =?iso-8859-1?Q?D=E9m=E9nagement=20non=20d=E9clar=E9?=
Content-Type: text/plain; charset="iso-8859-15"
Bonjour,
Il semble que tu étais inscrit dans la chambre %(chambre), mais un
Il semble que tu étais inscrit dans la chambre %(chambre), mais un
autre adhérent s'y est inscrit, ce qui suppose que tu n'y es plus.
Pourrais tu nous préciser ta nouvelle chambre ou adresse stp ?
Si tu es parti du campus, souhaites-tu garder tes machines ?
--
--
Merci par avance,
Les membres actifs du Crans"""
@ -306,7 +306,7 @@ La machine %(hostname)s a été déconnectée pendant 24h pour
utilisation du protocole %(protocole)s.
Nombre de paquets : %(nb_paquets)s paquets depuis le %(datedebut)s.
--
--
Message créé par deconnexion.py"""
deconnexion = u"""From: %(From)s
To: %(To)s
@ -332,7 +332,7 @@ il pénalise l'ensemble des adhérents de l'association.
Tu seras donc déconnecté 24h.
--
--
Disconnect Team"""
message_disconnect_multi = u"""From: %(from)s
@ -345,7 +345,7 @@ L'adhérent %(proprio)s a été déconnecté %(nbdeco)d fois pour p2p en un an !
Le PS a été généré et se trouve sur zamok :
%(ps)s
--
--
Message créé par deconnexion.py"""
@ -449,7 +449,7 @@ Pour y avoir accés depuis l'extérieur il faut utiliser un mot de passe:
Sur ce, bienvenue au Cr@ns !
PS: Il t'est conseillé de conserver ce mail à toutes fin utiles
--
--
Les membres actifs."""
@ -469,7 +469,7 @@ de les utiliser est disponible sur la page :
Si tu as des questions, n'hésite pas à les poser
à d'autres câbleurs ou aux nounous.
--
--
Les nounous"""
pages_infos_droits = { "Cableur" : "https://wiki.crans.org/%C3%8AtreC%C3%A2bleur",
@ -496,7 +496,7 @@ https://wiki.crans.org/CransAdministratif?action=AttachFile&do=get&target=charte
Merci par avance,
--
--
Le bureau du CRANS"""
txt_menage_cableurs = u"""From: Crans <%(From)s>
@ -515,5 +515,5 @@ heureux de te les remettre.
Cordialement,
--
--
Le bureau du CRANS"""

View file

@ -3,7 +3,7 @@
###############################################################################
# config_mail : gestion du .forward et .procmailrc des adhérents
###############################################################################
# The authors of this code are
# The authors of this code are
# Etienne Chové <etienne.chove@crans.org>
#
# Copyright (C) 2006 Etienne Chové
@ -95,14 +95,14 @@ class MailConfigError(ReferenceError):
def _IsMail(mail):
"""
Dit si la chaine fournie est une adresse mail valide
"""
"""
return bool(re.match('[a-z0-9_\.-]+@[a-z0-9_-]+(\.[a-z0-9_-]+)+',mail.lower()))
def _Clean(texte):
"""
Nettoie une chaine de caractère/liste en supprimant les lignes vides/commentés,
et retourne une liste
"""
"""
if type(texte) != list:
texte = texte.split('\n')
return [ x.strip() for x in texte if x.strip() and x[0]!='#' ]
@ -123,31 +123,31 @@ def _GetConfig():
# forward simple
if _IsMail(fic_forward):
return {'forward':fic_forward, 'spam':'accepte'}
# utilisation de procmail
if fic_forward != _Clean(forward_procmail)[0]:
raise MailConfigError, 'Fichier forward non compréhensible'
## lecture du .procmailrc
fic_procmail = _Clean( open('%s/.procmailrc'%home).readlines() )
# forward
if _IsMail( fic_procmail[-1][1:].strip() ) and fic_procmail[-2] == ":0" :
forward = fic_procmail[-1][1:].strip()
fic_procmail = fic_procmail[:-2]
else:
forward = ''
# forward simple dans le procmailrc
if not fic_procmail:
return {'forward':forward, 'spam':'accepte'}
# marquage des spams
tmp = _Clean( procmail_mark )
if fic_procmail[:len(tmp)] != tmp:
raise MailConfigError, 'Fichier de procmail non compréhensible'
fic_procmail = fic_procmail[len(tmp):]
# suppression des spams ?
if not fic_procmail:
return {'forward':forward, 'spam':'marque'}
@ -158,7 +158,7 @@ def _GetConfig():
def _SetConfig(forward = None, spam= None):
""" Modifie la configuration de l'utilisateur courant """
# variable new_spam
if spam in ['accepte','supprime','marque']:
new_spam = spam
@ -166,7 +166,7 @@ def _SetConfig(forward = None, spam= None):
new_spam = _GetConfig()['spam']
else:
raise ValueError, 'Valeur interdite pour le paramètre spam'
# variable forward
if forward == None:
new_forward = _GetConfig()['forward']
@ -174,7 +174,7 @@ def _SetConfig(forward = None, spam= None):
new_forward = forward
else:
raise ValueError, 'Adresse mail invalide'
# génération des fichiers
if new_spam=='accepte':
# suppression du .procmailrc
@ -210,17 +210,17 @@ def _Sudo(uid, forward=None, spam=None):
c += " --forward=%s" % forward
if spam!=None:
c += " --spam=%s" % spam
# execution de la commande
status, output = getstatusoutput(c)
# code d'erreur
if status:
sys.stderr.write("Erreur sudo : %s\n"%c)
sys.stderr.write(output)
sys.stderr.flush()
sys.exit(status)
# valeurs de retour
res = {}
for line in output.split('\n'):
@ -230,37 +230,37 @@ def _Sudo(uid, forward=None, spam=None):
##
############################################################################
def MailConfig(uid=None, forward=None, spam=None):
""" Modifie ou retourne la configuration mail de l'utilisateur
user = utilisateur à configurer, si None configure l'utilisateur courant
forward = adresse vers laquelle rediriger les mails, chaine vide si pas de redirection
spam = action à effectuer sur les spams (accepte, supprime, marque)
Pour les champs forward et spam, la valeur None ne touche pas au champ.
Retourne un dictionnaire { 'forward':'', 'spam':'' }
"""
## demande pour un autre utilisateur
if uid:
return _Sudo(uid=uid, forward=forward, spam=spam)
## nettoyage des variables
cfg = _GetConfig()
if forward == cfg['forward']:
forward = None
if spam == cfg['spam']:
spam = None
## modifications
if forward!=None or spam!=None:
_SetConfig(forward=forward, spam=spam)
## on renvoie la configuration
return _GetConfig()
if __name__=="__main__":
## parsage des arguments
forward = None
@ -271,10 +271,10 @@ if __name__=="__main__":
forward = v
elif o == '--spam':
spam = v
## execution de MailConfig
res = MailConfig(forward=forward, spam=spam)
## affichage des résultats
for i in res.items():
print "%s=%s" % i

View file

@ -20,10 +20,10 @@ def format_sender(sender, header_charset='ISO-8859-15'):
"""
from email.Header import Header
from email.Utils import parseaddr, formataddr
# Split real name (which is optional) and email address parts
sender_name, sender_addr = parseaddr(sender)
# We must always pass Unicode strings to Header, otherwise it will
# use RFC 2047 encoding even on plain ASCII strings.
sender_name = str(Header(unicode(sender_name), header_charset))
@ -71,14 +71,14 @@ def send_email(sender, recipient, subject, body, server='localhost', cc=None, de
sender = format_sender(sender)
recipient = format_sender(recipient)
if cc: cc = format_sender(cc)
# Create the message ('plain' stands for Content-Type: text/plain)
msg = MIMEText(body.encode(body_charset), 'plain', body_charset)
msg['From'] = sender
msg['To'] = recipient
msg['Subject'] = Header(unicode(subject), header_charset)
if cc: msg['Cc'] = cc
if debug:
actual_recipient = [debug]
else:
@ -89,7 +89,7 @@ def send_email(sender, recipient, subject, body, server='localhost', cc=None, de
actual_sender = format_sender(actual_sender)
else:
actual_sender = sender
# Send the message
if isinstance(server, SMTP):
server.sendmail(actual_sender, actual_recipient, msg.as_string())
@ -113,23 +113,23 @@ def parse_mail_template(fichier):
"""
if not isinstance(fichier, file):
fichier = file(fichier)
encoding = fichier.readline()
matched = re.search(r'^Encoding:\s*([^\r\n]*)', encoding)
if matched:
encoding = matched.group(1)
else:
raise SyntaxError("Encoding manquant dans template")
subject = fichier.readline()
matched = re.search(r'^Subject:\s*([^\r\n]*)', subject)
if matched:
subject = matched.group(1).decode(encoding)
else:
raise SyntaxError("Subject manquant dans template")
if fichier.readline() not in ['\r\n', '\n']:
raise SyntaxError("le fichier n'a pas le bon format")
body = fichier.read().decode(encoding)
return subject, body

View file

@ -1,10 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
"""
Fichier de base d'interface avec les switchs manageables.
Donne la classe switch qui permet d'effectuer les opérations
Donne la classe switch qui permet d'effectuer les opérations
élémentaires sur les switchs manageable HP 26xx.
Frédéric PAUGET
@ -52,7 +52,7 @@ import pexpect
class ssh :
""" Ouverture d'une connexion ssh, envoi de commandes et récupération du résultat """
def __init__(self,host) :
""" Ouverture d'une connexion ssh vers le switch choisi """
self.switch = host.split('.')[0] # On garde pas le fqdn
@ -68,14 +68,14 @@ class ssh :
traceback.print_exc()
print "Contenu du buffer :"
print self.ssh.before
def __del__(self) :
"""Ferme la connexion : envoi logout et attend """
self.ssh.sendline("logout")
self.ssh.send("y")
self.ssh.send("y")
self.ssh.close()
def send_cmd(self,cmd,timeout=15):
""" Envoi une commande, attend le prompt et retourne la réponse """
# Envoi de la commande
@ -89,7 +89,7 @@ class ssh :
'%s\(config\)# ' % self.switch,
'quit: Control-C'],
timeout=timeout)
self.__sshout = self.__sshout + self.ssh.before
if index == 0:
# On répond oui
@ -100,7 +100,7 @@ class ssh :
elif index == 2:
# On doit continuer, on envoie espace
self.ssh.send(" ")
return self.__sshout
except pexpect.TIMEOUT:
print "Timeout !"
@ -115,7 +115,7 @@ class snmp :
""" host doit être la machine sur laquelle se connecter
version est la verion du protocole snmp à utiliser : 1, 2c ou 3
le reste des données doit être ou non fourni suivant la version
pour v1 et v2c seule la communauté est prise en compte
pour v3 authentication_protocol, authentication_pass et username sont requis si accès en authNoPriv
si privacy_pass est fourni accès en authPriv
@ -127,7 +127,7 @@ class snmp :
self._machine = cl.search("host=%(host)s" % {'host': host})["machineCrans"][0]
except IndexError:
raise ValueError(u"Cette machine n'est pas un switch du Cr@ns")
# l'engineid du switch n'est que sa mac avec quelques fioritures autour...
mac = self._machine.mac().replace(':', '')
self._engineid = '0000000b0000%(mac)s0' % {'mac': mac[:-1]}
@ -136,25 +136,25 @@ class snmp :
self.options = "-v %s -c '%s' %s " % ( version, community, host )
elif version =='3' :
self.options = "-v 3 -e %s -u %s -a %s -A '%s' -l authNoPriv" % ( self._engineid, username, authentication_protocol, authentication_pass )
if privacy_pass :
if privacy_pass :
self.options += " -x DES -X '%s' -l authPriv" % privacy_pass
self.options += " %s " % host
else :
raise ValueError('Version incorrecte')
def __exec(self,cmd) :
s, r = getstatusoutput(cmd)
if s :
r=r.replace('snmpget: ','')
raise ConversationError(r,cmd)
return r
def get(self,oid) :
""" Retourne le résultat correspondant à l'oid demandé """
return self.__exec('snmpget -O vq %s %s ' % ( self.options, oid ) )
def get_string(self,oid) :
""" Retourne le resultat convertit en String correspondant à l'oid demandé. Fonctionne avec les types de depart String, Integer, Hex-String. Raise ValueError sinon. """
""" Retourne le resultat convertit en String correspondant à l'oid demandé. Fonctionne avec les types de depart String, Integer, Hex-String. Raise ValueError sinon. """
s= self.__exec('snmpget -O v %s %s ' % ( self.options, oid ) )
if s=="\"\"":
return ""
@ -168,16 +168,16 @@ class snmp :
return unicode(var)
else:
raise ValueError('Type inconnu')
def set(self,oid,typ,val) :
""" Change la valeur le l'oid donné.
""" Change la valeur le l'oid donné.
type est le type de la valeur
val est la valeur à écrire
"""
return self.__exec('snmpset -O vq %s %s %s %s' % (self.options, oid, typ, val ) )
def walk(self,base_oid) :
""" Retourne le résultat de snmpwalk
""" Retourne le résultat de snmpwalk
le retour est un dictionnaire { oid : valeur }
"""
lignes = self.__exec('snmpwalk -O q %s %s' % (self.options, base_oid ) ).split('\n')
@ -190,14 +190,14 @@ class snmp :
#############################################################################################
### Gestion des switchs proprement dite
class hpswitch :
""" Classe pour l'administration des switchs HP. """
# Variables internes
__debug=0
__logDest=stderr
# Variables internes
switch = None # nom du switch
prise = ''
@ -207,17 +207,17 @@ class hpswitch :
""" Switch doit être le nom du switch """
if self.__debug : self.__logDest.write("HP DEBUG : __init__(switch=%s)\n" % switch )
self.switch = switch.lower()
# Config snmp
self.get, self.set, self.walk = config_snmp_secrete(snmp,switch)
def send_cmd(self,cmd,timout=15) :
""" Envoi une commande par ssh au switch """
if not self.__conn_ssh :
self.__conn_ssh = ssh(self.switch)
return self.__conn_ssh.send_cmd(cmd,timout)
def show_prise_mac(self,prise='') :
""" Retourne le(s) adresse(s) MAC présentes sur la prise."""
if not prise : prise = self.prise
@ -248,18 +248,18 @@ class hpswitch :
# On a rien trouvé
return None
def __scp(self,destination,fichier) :
if self.__debug :
self.__logDest.write("HP DEBUG : scp(%s,%s,%s)\n" % (fichier, self.switch, destination))
if exists(fichier):
system('scp %s %s:%s' % (fichier, self.switch, destination))
def update(self,file=None) :
""" Upload le fichier de config fourni
Fait rebooter le switch """
self.__scp('cfg/startup-config',file)
def upgrade(self,file=None) :
""" Changement de firmware,
le firmware est dans le fichier fourni en argument
@ -270,18 +270,18 @@ class hpswitch :
""" Ajoute la clef publique ssh aux clefs autorises
par le switch """
self.scp('ssh/mgr_keys',file)
def multicast(self,ip='') :
""" Donne la liste des ports du swich inscrits au flux multicast donné
Si aucun flux donné teste tous les flux multicast possibles.
""" Donne la liste des ports du swich inscrits au flux multicast donné
Si aucun flux donné teste tous les flux multicast possibles.
Retourne un dictionnaire : { adresse du flux : [ ports inscrits ] }
"""
if self.__debug : self.__logDest.write("HP DEBUG : multicast(ip=%s)\n" % ip)
if ip :
if ip :
data = self.walk('STATISTICS-MIB::hpIgmpStatsPortIndex2.%s' % ip)
return { ip : data.values() }
else :
data = self.walk('STATISTICS-MIB::hpIgmpStatsPortIndex2')
result = {}
@ -291,14 +291,14 @@ class hpswitch :
except : continue
result.setdefault(ip,[])
result[ip].append(port)
return result
def nb_prises(self) :
""" Retourne le nombre de prises du switch """
if self.__debug : self.__logDest.write("HP DEBUG : nb_prises()\n")
return int(findall(r'Switch 26([0-9]{2})', self.version())[0])
def version(self) :
""" Retourne la version du firmware du switch """
if self.__debug : self.__logDest.write("HP DEBUG : version()\n")
@ -309,13 +309,13 @@ class hpswitch :
if not prise : prise = self.prise
if self.__debug : self.__logDest.write("HP DEBUG : enable(prise=%s)\n" % prise)
return self.set('IF-MIB::ifAdminStatus.%d' % int(prise), 'i', 1)
def disable(self,prise=0) :
""" Désactive une prise """
if not prise : prise = self.prise
if self.__debug : self.__logDest.write("HP DEBUG : disable(prise=%s)\n" % prise)
return self.set('IF-MIB::ifAdminStatus.%d' % int(prise), 'i', 2)
def __is(self,oid,prise) :
if not prise : prise = self.prise
prise = str(prise)
@ -328,7 +328,7 @@ class hpswitch :
return nb
prise = prise.replace('-','')
return self.get(oid + '.' + prise) == 'up'
def is_enable(self,prise=0) :
""" Retoune True ou False suivant si la prise est activée ou non
Si prise=all retourne le nombre de prises activées sur le switch """
@ -340,7 +340,7 @@ class hpswitch :
Si prise=all retourne le nombre de prises up sur le switch """
if prise != 'all': prise = int(prise)
return self.__is('IF-MIB::ifOperStatus',prise)
def nom(self,nom=None,prise=0) :
""" Retourne ou attribue le nom à la prise fournie """
if not prise : prise = self.prise
@ -349,19 +349,19 @@ class hpswitch :
return self.get(oid)
else :
self.set(oid, 's' , nom)
def eth_mode(self,mode=None,prise=0) :
""" Fixe ou retourne le mode d'une prise
mode est un tuple : (vitesse, duplex) ou simplement "auto"
""" Fixe ou retourne le mode d'une prise
mode est un tuple : (vitesse, duplex) ou simplement "auto"
vitesse est : 10 100 ou 1000
duplex est FD, HD ou auto
"""
if not prise : prise = self.prise
oid = 'CONFIG-MIB::hpSwitchPortFastEtherMode.%d' % int(prise)
if mode == None :
return self.get(oid)
# Conversion du mode
if mode == 'auto' :
code = 5
@ -391,7 +391,7 @@ class hpswitch :
return result
class sw_chbre(hpswitch) :
def __init__(self,chbre) :
# On retrouve la chbre dans l'annuaire
@ -410,20 +410,20 @@ class sw_chbre(hpswitch) :
self.prise10Mb = False
except :
raise ValueError('Chambre %s inconnue' % chbre)
# Config snmp
self.get, self.set, self.walk = config_snmp_secrete(snmp,self.switch)
def reconfigure(self) :
""" Reconfigure la prise (nom et vitesse) """
in10 = self.eth_mode().find('10Mbits') != 1
if self.prise10Mb and not in10 :
self.eth_mode(('10','auto'))
elif not self.prise10Mb and in10 :
self.eth_mode('auto')
nom = 'Chambre_%s' % self.chbre.capitalize()
if nom != self.nom() :
self.nom(nom)
@ -448,13 +448,13 @@ class sw_prise(sw_chbre):
if __name__ == '__main__' :
import sys, getopt, sre
try :
options, arg = getopt.getopt(sys.argv[1:], 'U:hc:', [ 'help', 'snmp' ])
except getopt.error, msg :
print msg
sys.exit(1)
cmds = []
firmware=''
wait=True
@ -466,17 +466,17 @@ if __name__ == '__main__' :
print "L'envoi de firmware ne fait pas rebooter le switch"
print "L'option --snmp ajoute les commandes de reconfiguration snmp"
sys.exit(0)
elif opt=='-c' :
elif opt=='-c' :
cmds.append(val)
elif opt=='-U' :
firmware=val
elif opt=='--snmp' :
cmds.append(reconf_snmp)
cmds.append("write memory")
# Quels switchs ?
switchs=[]
if arg :
@ -484,20 +484,20 @@ if __name__ == '__main__' :
for sw in all_switchs() :
if re.match(sw) :
switchs.append(sw)
if not switchs :
print "Aucun switch trouvé"
print "Note : il faut une _regex_ (!= wilcards au sens du shell)"
sys.exit(3)
if not cmds and not firmware :
cmds=map(str.strip,sys.stdin.readlines())
# Ce que l'on va faire
print "Commandes :\n\t", '\n\t'.join(cmds)
print "\nSwitchs : ", ' '.join(switchs)
print "\nSwitchs : ", ' '.join(switchs)
print
try:
raw_input("Appuyer sur entrée pour continuer")
except EOFError:
@ -511,8 +511,8 @@ if __name__ == '__main__' :
s = hpswitch(sw)
if firmware :
s.upgrade(firmware)
for cmd in cmds :
for cmd in cmds :
print s.send_cmd(cmd)
except :
print 'ERREUR'

View file

@ -14,7 +14,7 @@ from config import NETs_regexp
__QuadToDecDone = {}
def QuadToDec(ip) :
"""
"""
Retourne la représentation décimale d'une ip
ip est de la forme xxx.xxx.xxx.xxx
"""
@ -33,9 +33,9 @@ def QuadToDec(ip) :
# Pour accélérer DecToQuad
__DecToQuadDone = {}
def DecToQuad(ip_dec) :
"""
"""
Retourne la représentation habituelle d'une ip (xxx.xxx.xxx.xxx)
ip_dec est l'IP en base 10
"""
@ -62,7 +62,7 @@ def param(net, raw=False) :
'netmask' : yyy.yyy.yyy.yyy ,
'broadcast' : zzz.zzz.zzz.zzz }
sinon retourne {}
Si raw = False, alors, on ne convertit pas les résultats sous forme pointée.
Ils restent sous forme d'un entier.
"""
@ -70,18 +70,18 @@ def param(net, raw=False) :
return __paramDone[net]
reseau = {}
ip, mask = net.split('/')
try :
mask = int(mask)
dec_ip = QuadToDec(ip)
if dec_ip == -1 : raise
except :
return {}
# Calcul du netmask
non_dec_netmask = netmask(mask, dec=False)
dec_netmask = netmask(mask)
reseau = { 'netmask' : dec_netmask,
'network' : dec_ip & dec_netmask,
'broadcast' : dec_ip | non_dec_netmask }
@ -91,7 +91,7 @@ def param(net, raw=False) :
else:
__paramDone[net] = reseau
return reseau
def AddrInNet(ip,net) :
"""
ip est de la forme xxx.xxx.xxx.xxx
@ -101,7 +101,7 @@ def AddrInNet(ip,net) :
Note : retourne False si l'IP est une adresse de réseau ou broadcast
"""
if type(net)==str : net = [ net ]
r = False
ip = QuadToDec(ip)
for ne in net :
@ -109,11 +109,11 @@ def AddrInNet(ip,net) :
if ip == n['broadcast'] or ip == n['network'] :
return False
r = r or n['netmask'] & ip == n['network']
return r
def AddrInNets(ip,nets) :
""" Vérifie si l'ip est au moins dans un des réseaux
""" Vérifie si l'ip est au moins dans un des réseaux
de la liste nets (voir AddrInNet) """
for net in nets :
if AddrInNet(ip,net) :

View file

@ -3,7 +3,7 @@
# ldap_passwd.py : manipulation des mots de passes LDAP
# $Id: ldap_passwd.py,v 1.7 2006-05-04 17:46:58 chove Exp $
###############################################################################
# The authors of this code are
# The authors of this code are
# Bjorn Ove Grotan <bgrotan@grotan.com>
# Etienne Chové <etienne.chove@crans.org>
#
@ -27,19 +27,19 @@
###############################################################################
# For extra strength passwords, we wanted SSHA in our LDAP-environment
# as the standard python-module 'sha' does not support ssha, but this can
# easily be implemented with a few extra functions.
# as the standard python-module 'sha' does not support ssha, but this can
# easily be implemented with a few extra functions.
#
# SSHA can be described as:
# the SHA1-digest of a password with a sequence of "salt" bytes, where
# the bytes are randomly chosen - followed by the same salt bytes
# For LDAP-use, the SHA1 and SSHA-digest has to be base64-encoded.
# For LDAP-use, the SHA1 and SSHA-digest has to be base64-encoded.
#
# Example-LDIF:
# {SSHA}oaEG3PJ10sHxGcSxsDRRooTifL55/2NOdN3nU1VEV+NFzc9Q
#
#
# This package should now support passwords compatible with [1] Samba using the [2]
# smbpasswd module for [3] Python. The samba compability is added for use with Samba
# smbpasswd module for [3] Python. The samba compability is added for use with Samba
# as PDC with storing user and host-information in LDAP.
#
# [1] http://www.samba.org
@ -70,7 +70,7 @@ if smb:
algos['lmpassword'] = 'lan man hash'
algos['ntpassword'] = 'nt hash'
def getsalt(chars=string.letters+string.digits, length=16):
''' Generate a random salt. Default length is 16 '''
salt = ''
@ -79,18 +79,18 @@ def getsalt(chars=string.letters+string.digits, length=16):
return salt
def mkpasswd(pwd, sambaver=3, algo='SSHA', salt=None):
''' Make a given password cryptated, possibly with different
crypt-algorihtms. This module was written for use with
''' Make a given password cryptated, possibly with different
crypt-algorihtms. This module was written for use with
LDAP - so default is seeded sha
'''
if salt == None:
salt = getsalt()
algo = algo.lower()
if algo not in algos.keys():
raise TypeError, 'Algorithm <%s> not supported in this version.' % algo
if algo == 'ssha':
pwdhash = "{SSHA}" + base64.encodestring(sha.new(str(pwd) + salt).digest() + salt)
elif algo =='sha':
@ -117,7 +117,7 @@ def mkpasswd(pwd, sambaver=3, algo='SSHA', salt=None):
def checkpwd(pwd, pwdhash):
''' Check if the password matches the hash '''
algo = pwdhash[1:].split('}')[0]
algo = algo.lower()
@ -134,5 +134,5 @@ def checkpwd(pwd, pwdhash):
salt = base64.decodestring(pwdhash.split('}')[1])[20:]
else:
salt = None
return mkpasswd(pwd, sambaver=sambaver, algo=algo, salt=salt) == pwdhash

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" Gestion de lock
""" Gestion de lock
Copyright (C) Frédéric Pauget
Licence : GPLv2
@ -51,7 +51,7 @@ def wait_lock(lock_name, lock_comment='', d=None, retry=0.2):
return d
def make_lock(lock_name, lock_comment='',nowait=0, quiet=False) :
""" Création d'un lock
""" Création d'un lock
si nowait=1 fait un sys.exit(254) quand un ancien lock actif est rencontré
"""
return
@ -76,7 +76,7 @@ def make_lock(lock_name, lock_comment='',nowait=0, quiet=False) :
raise AssertionError('In critical section')
else:
sys.stderr.write('\tpropriétaire : inconnu\n\tpid : inconnu\n\tdémarré depuis inconnu\n')
sys.exit(254)
sys.exit(254)
else:
# La procédure de lock est deja en cours d'execution, on essaie un peu plus tard
time.sleep(0.5)
@ -87,13 +87,13 @@ def make_lock(lock_name, lock_comment='',nowait=0, quiet=False) :
if os.path.isfile(lock_file) :
### Lock existant
# Lecture du lock
fd = open(lock_file, "r")
pid= fd.readline().strip()
user = fd.readline().strip()
fd.close()
# Informations sur le processus lockant
if os.system( "ps ax | grep -q '^%s '" % pid ) :
# Le script lockant ne tourne plus
@ -103,16 +103,16 @@ def make_lock(lock_name, lock_comment='',nowait=0, quiet=False) :
sys.stderr.write('Lock : %s\n' % lock_file)
l=getoutput("ps ax -o pid,etime | awk '($1 == %s)'" % pid)
data = [ user , pid , l.strip() ]
# Formatate de etime
s = data[-1].split('-')
if len(s)==2 :
txt = '%s jour(s) ' % s[0]
s=s[1]
else :
else :
txt = ''
s=s[0]
s = s.split(':')
if len(s) == 3 :
txt = '%sh%smin%ss' % tuple(s)
@ -140,7 +140,7 @@ def make_lock(lock_name, lock_comment='',nowait=0, quiet=False) :
lockf(lock_fd_dl, LOCK_UN)
lock_fd_dl.close()
return make_lock(lock_name, lock_comment)
### Prise du lock
lock_fd = file(lock_file,"w")
try:
@ -153,7 +153,7 @@ def make_lock(lock_name, lock_comment='',nowait=0, quiet=False) :
# On enleve le verrou système
lockf(lock_fd_dl, LOCK_UN)
lock_fd_dl.close()
def remove_lock( lock_name ) :
""" Destruction du lock """

View file

@ -44,14 +44,14 @@ def reconf_postfix():
if limit == "yes":
# Si oui, on demande à l'utilisateur si on y touche
negatif = ["N", "n", ""]
positif = ["O", "o", "Y", "y"]
poursuivre = "x"
while not (poursuivre in negatif + positif):
poursuivre = raw_input("Il y a trop de destinataires, il faut réécrire la conf de postfix [o/N] ")
if poursuivre in negatif:
print "Ok, on ne touche pas au fichier..."
sys.exit(0)
@ -69,7 +69,7 @@ def reconf_postfix():
sys.exit(1)
reload_postfix()
return(limit,lines)
if __name__ == "__main__":
@ -121,7 +121,7 @@ par le script.
except IOError:
print "Impossible d'ouvrir le fichier à envoyer, merci, au revoir."
sys.exit(1)
echecs = []
s = smtplib.SMTP()
s.connect('smtp.crans.org')
@ -141,14 +141,14 @@ par le script.
else:
# Tout va bien
pass
if echecs:
print "\nIl y a eu des erreurs :"
print echecs
s.close()
# On rétablit la conf de postfix
# On rétablit la conf de postfix
finally:
if limit == "yes":
try:

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
"""
Script de mise a jour des firmwares de switchs
J.Benoist Leger
@ -52,7 +52,7 @@ if not file_tftp :
print "Pas de fichier donné"
sys.exit(1)
# On fait les tests uniquement sur batv-3
# On fait les tests uniquement sur batv-3
# switchs=["batv-3.adm.crans.org"]
@ -87,7 +87,7 @@ for bestiole in switchs :
ssh.sendline("no ip ssh filetransfer")
ssh.sendline("tftp client")
ssh.sendline("exit")
try:
ssh.expect("%s# " % host, timeout=40)
except pexpect.TIMEOUT:

View file

@ -15,8 +15,8 @@ from iptools import AddrInNet
repertoire = '/usr/scripts/var/numeros_disponibles/'
"""
Un petit hack de rien du tout pour s'assurer qu'on n'attribue
pas ces adresses. Certains services risquent de continuer
Un petit hack de rien du tout pour s'assurer qu'on n'attribue
pas ces adresses. Certains services risquent de continuer
d'essayer de se connecter a ces adresses
"""
ancien_vlan_adm = ['10.231.136.0/24']

View file

@ -90,7 +90,7 @@ class spawn:
p = pexpect.spawn ('/usr/bin/ssh', ['user@example.com'])
p = pexpect.spawn ('ls', ['-latr', '/tmp'])
After this the child application will be created and
will be ready to talk to. For normal use, see expect() and
will be ready to talk to. For normal use, see expect() and
send() and sendline().
If the command parameter is an integer AND a valid file descriptor
@ -116,7 +116,7 @@ class spawn:
self.__child_fd_owner = None
self.exitstatus = None
self.pid = None
self.log_file = None
self.log_file = None
self.before = None
self.after = None
self.match = None
@ -124,21 +124,21 @@ class spawn:
self.name = '' # File-like object.
self.flag_eof = 0
# NEW -- to support buffering -- the ability to read more than one
# NEW -- to support buffering -- the ability to read more than one
# byte from a TTY at a time. See setmaxread() method.
self.buffer = ''
self.maxread = 1 # Maximum to read at a time
### IMPLEMENT THIS FEATURE!!!
# anything before maxsearchsize point is preserved, but not searched.
#self.maxsearchsize = 1000
# If command is an int type then it must represent an open file descriptor.
if type (command) == type(0):
try: # Command is an int, so now check if it is a file descriptor.
os.fstat(command)
except OSError:
raise ExceptionPexpect, 'Command is an int type, yet is not a valid file descriptor.'
self.pid = -1
self.pid = -1
self.child_fd = command
self.__child_fd_owner = 0 # Sets who is reponsible for the child_fd
self.args = None
@ -273,7 +273,7 @@ class spawn:
def setlog (self, fileobject):
"""This sets logging output to go to the given fileobject.
Set fileobject to None to stop logging.
Set fileobject to None to stop logging.
Example:
child = pexpect.spawn('some_command')
fout = file('mylog.txt','w')
@ -300,13 +300,13 @@ class spawn:
If a log file was set using setlog() then all data will
also be written to the log file.
Notice that if this method is called with timeout=None
Notice that if this method is called with timeout=None
then it actually may block.
This is a non-blocking wrapper around os.read().
It uses select.select() to implement a timeout.
It uses select.select() to implement a timeout.
"""
if self.child_fd == -1:
raise ValueError ('I/O operation on closed file')
@ -320,7 +320,7 @@ class spawn:
if not r:
self.flag_eof = 1
raise EOF ('End Of File (EOF) in read(). Braindead platform.')
r, w, e = select.select([self.child_fd], [], [], timeout)
if not r:
raise TIMEOUT('Timeout exceeded in read().')
@ -338,21 +338,21 @@ class spawn:
if s == '':
self.flag_eof = 1
raise EOF('End Of File (EOF) in read(). Empty string style platform.')
if self.log_file != None:
self.log_file.write (s)
self.log_file.flush()
return s
raise ExceptionPexpect('Reached an unexpected state in read().')
def read (self, size = -1): # File-like object.
"""This reads at most size bytes from the file
(less if the read hits EOF before obtaining size bytes).
If the size argument is negative or omitted,
read all data until EOF is reached.
The bytes are returned as a string object.
"""This reads at most size bytes from the file
(less if the read hits EOF before obtaining size bytes).
If the size argument is negative or omitted,
read all data until EOF is reached.
The bytes are returned as a string object.
An empty string is returned when EOF is encountered immediately.
"""
if size == 0:
@ -366,16 +366,16 @@ class spawn:
# I would catch any bugs early and ensure consistant behavior.
# It's a little less efficient, but there is less for me to
# worry about if I have to later modify read() or expect().
cre = re.compile('.{%d}' % size, re.DOTALL)
cre = re.compile('.{%d}' % size, re.DOTALL)
index = self.expect ([cre, EOF])
if index == 0:
return self.after ### self.before should be ''. Should I assert this?
return self.before
def readline (self, size = -1): # File-like object.
"""This reads and returns one entire line. A trailing newline is kept in
the string, but may be absent when a file ends with an incomplete line.
Note: This readline() looks for a \\r\\n pair even on UNIX because this is
the string, but may be absent when a file ends with an incomplete line.
Note: This readline() looks for a \\r\\n pair even on UNIX because this is
what the pseudo tty device returns. So contrary to what you may be used to
you will receive a newline as \\r\\n.
An empty string is returned when EOF is hit immediately.
@ -404,9 +404,9 @@ class spawn:
return result
def readlines (self, sizehint = -1): # File-like object.
"""This reads until EOF using readline() and returns a list containing
"""This reads until EOF using readline() and returns a list containing
the lines thus read. The optional sizehint argument is ignored.
"""
"""
lines = []
while 1:
line = self.readline()
@ -422,7 +422,7 @@ class spawn:
def writelines (self, sequence): # File-like object.
"""This calls write() for each element in the sequence.
The sequence can be any iterable object producing strings,
The sequence can be any iterable object producing strings,
typically a list of strings. This does not add line separators
There is no return value.
"""
@ -449,8 +449,8 @@ class spawn:
buffer to be sent to the waiting child program without
waiting for end-of-line. If it is the first character of the
line, the read() in the user program returns 0, which
signifies end-of-file. This means to work as expected
a sendeof() has to be called at the begining of a line.
signifies end-of-file. This means to work as expected
a sendeof() has to be called at the begining of a line.
This method does not send a newline. It is the responsibility
of the caller to ensure the eof is sent at the beginning of a line.
"""
@ -487,7 +487,7 @@ class spawn:
# If this class was created from an existing file descriptor then
# I just check to see if the file descriptor is still valid.
if self.pid == -1 and not self.__child_fd_owner:
if self.pid == -1 and not self.__child_fd_owner:
try:
os.fstat(self.child_fd)
return 1
@ -535,7 +535,7 @@ class spawn:
def compile_pattern_list(self, patterns):
"""This compiles a pattern-string or a list of pattern-strings.
Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or
Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or
a list of those.
This is used by expect() when calling expect_list().
@ -569,7 +569,7 @@ class spawn:
raise TypeError, 'Argument must be one of StringType, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p))
return compiled_pattern_list
def expect(self, pattern, timeout = -1):
"""This seeks through the stream until a pattern is matched.
The pattern is overloaded and may take several types including a list.
@ -654,14 +654,14 @@ class spawn:
#ED# incoming = ''
incoming = self.buffer
while 1: # Keep reading until exception or return.
#ED# c = self.read_nonblocking (1, timeout)
#ED# c = self.read_nonblocking (1, timeout)
#ED# incoming = incoming + c
# Sequence through the list of patterns and look for a match.
index = -1
for str_target in pattern_list:
index = index + 1
if str_target is EOF or str_target is TIMEOUT:
if str_target is EOF or str_target is TIMEOUT:
continue # The Exception patterns are handled differently.
match_index = incoming.find (str_target)
if match_index >= 0:
@ -670,9 +670,9 @@ class spawn:
self.buffer = incoming [match_index + len(str_target):]
self.match = None
return index
c = self.read_nonblocking (self.maxread, timeout)
c = self.read_nonblocking (self.maxread, timeout)
incoming = incoming + c
except EOF:
self.before = incoming
self.after = EOF
@ -695,10 +695,10 @@ class spawn:
self.match = None
self.buffer = ''
raise
def expect_list(self, pattern_list, timeout = -1):
"""
This takes a list of compiled regular expressions and returns
This takes a list of compiled regular expressions and returns
the index into the pattern_list that matched the child's output.
This is called by expect(). It is similar to the expect() method
except that expect_list() is not overloaded and it does not have to
@ -711,7 +711,7 @@ class spawn:
if timeout == -1:
timeout = self.timeout
try:
#ED# incoming = ''
incoming = self.buffer
@ -723,7 +723,7 @@ class spawn:
index = -1
for cre in pattern_list:
index = index + 1
if cre is EOF or cre is TIMEOUT:
if cre is EOF or cre is TIMEOUT:
continue # The patterns for PexpectExceptions are handled differently.
match = cre.search(incoming)
if match is not None:
@ -735,7 +735,7 @@ class spawn:
# Read more data
c = self.read_nonblocking (self.maxread, timeout)
incoming = incoming + c
except EOF:
self.before = incoming
self.after = EOF
@ -859,7 +859,7 @@ def _which (filename):
# Oddly enough this was the one line that made Pexpect
# incompatible with Python 1.5.2.
#pathlist = p.split (os.pathsep)
#pathlist = p.split (os.pathsep)
pathlist = string.split (p, os.pathsep)
for path in pathlist:
@ -907,7 +907,7 @@ def _split_command_line(command_line):
if c != '\\'and state_esc: # escape mode lasts for one character.
state_esc = 0
# Handle last argument.
# Handle last argument.
if arg != '':
arg_list.append(arg)
return arg_list
@ -936,7 +936,7 @@ def _split_command_line(command_line):
# Nonblocking on Win32?
# Reasearch this as a way to maybe make pipe work for Win32.
# http://groups.google.com/groups?q=setraw+tty&hl=en&selm=uvgpvisvk.fsf%40roundpoint.com&rnum=7
#
#
# if istty:
# if os.name=='posix':
# import tty
@ -983,10 +983,10 @@ def _split_command_line(command_line):
## self.buffer = ''
##
## def read(self, n, timeout = None):
## """This does a read restricted by a timeout and
## it includes any cached data from previous calls.
## """This does a read restricted by a timeout and
## it includes any cached data from previous calls.
## This is a non-blocking wrapper around os.read.
## it uses select.select to supply a timeout.
## it uses select.select to supply a timeout.
## Note that if this is called with timeout=None (the default)
## then this actually MAY block.
## """

View file

@ -292,7 +292,7 @@ def ressuscite(adh, oldmachine):
strmachine = u"Cette machine était une machine Wifi."
strdef = 'Wifi'
else:
strmachine = u"L'ancien type de cette machine est inconnu ..."
strmachine = u"L'ancien type de cette machine est inconnu ..."
strdef = ''
# Certaines fois, on peut vouloir transformer une machine Fixe en Wifi,

View file

@ -23,7 +23,7 @@ def load(file) :
except :
print "Impossible d'ouvrir le fichier demandé."
sys.exit(1)
obj = cPickle.load(fd)
try :
@ -39,10 +39,10 @@ def load(file) :
### Modifs pour permettre une restauration
# On supprime les infos de aid, mid ou cid
obj.dn = obj.dn.split(',',1)[1]
# On supprime les infos du init_data
obj._init_data={}
return obj
if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) != 2 :
@ -61,7 +61,7 @@ def restore_adher(adh) :
prompt(u'Appuyez sur ENTREE pour continuer')
adh._data['mail'] = []
modif_adher(adh)
def restore_machine(machine) :
try :
obj.proprio # crash si l'adhérent est encore dans la base
@ -77,7 +77,7 @@ def restore_machine(machine) :
def restore_club(club) :
modif_club(club)
# Restauration ?
q = prompt(u'Restaurer cette entrée ? [O/N]')
if q not in 'oO' :

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" Fonctions de tests sur l'utilisateur
""" Fonctions de tests sur l'utilisateur
Copyright (C) Frédéric Pauget
Licence : GPLv2
@ -15,15 +15,15 @@ def getuser() :
if not user :
user = pwd.getpwuid(os.getuid())[0]
return user
def groups(login='') :
""" Retourne la liste des droits qu'a l'utilisateur fourni, si aucun
utilisateur n'est fourni prend l'utilisateur loggué """
if login == '':
login = getuser()
if login == 'root':
groups = ['0']
else:
@ -37,9 +37,9 @@ def groups(login='') :
groups = []
else:
groups = adh.droits()
return groups
def isadm(login='') :
""" Retourne True si l'utilisateur est dans le groupe 4 (adm)

View file

@ -9,29 +9,29 @@ 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
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 :
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 :
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
-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
-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)
@ -39,7 +39,7 @@ d'historique affichées (défaut %(limit_aff_historique)i)
try:
import sys,locale
loc = locale.getdefaultlocale()
if loc[1]:
sys.reallysetdefaultencoding(loc[1])
@ -64,22 +64,22 @@ 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 :
(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
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':
elif t == 'mid':
if mtech:
cprint(list_machines(qqch))
else:
@ -98,11 +98,11 @@ def aff(qqch,mtech=0) :
cprint(machine_details(c).strip())
elif t == 'cid':
cprint(club_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 :
@ -116,7 +116,7 @@ def adhers_brief(adhers) :
# 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'
@ -128,23 +128,23 @@ def adhers_brief(adhers) :
else: paid = ok
elif isinstance(a,Adherent) and not a.adherentPayant(): paid = coul('G', 'bleu')
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():
if 'c' in a.controle(): carte = ook
else: carte = ok
else : carte = 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'
elif isinstance(machine, MachineWifi): k = 'cyan'
else : k= ''
if machines : machines += ', ' + coul(nom,k)
else : machines = coul(nom,k)
@ -155,7 +155,7 @@ def adhers_brief(adhers) :
data.append([a.id(), a.Nom(), a.chbre(), paid, carte, machines])
else:
data.append([a.id(), a.Nom(), a.chbre(), paid, carte])
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" + \
@ -172,7 +172,7 @@ def adhers_brief(adhers) :
titre = [u'aid', u'Prénom Nom', u'Chbre', u'P', u'C'],
largeur = [5, '*', 5, 1, 1],
alignement = ['d', 'c', 'g', 'c', 'c'])
def machines_brief(machines) :
"""
Formatage sous forme d'un tableau des propriétés de la liste de machine :
@ -188,14 +188,14 @@ def machines_brief(machines) :
# 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 ann_scol not in a.paiement() or ann_scol not in a.carteEtudiant():
if isinstance(a, Adherent) and not a.adherentPayant():
@ -205,7 +205,7 @@ def machines_brief(machines) :
# 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, en bleu une inscription gratuite\n" + \
tableau(data,
titre = [u'mid', u'Type', u'Nom de machine', u'Propriétaire', u'Chbre', u'Limitation'],
@ -225,7 +225,7 @@ def clubs_brief(clubs) :
# 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'
@ -236,10 +236,10 @@ def clubs_brief(clubs) :
if 'p' in c.controle(): paid = ook
else: 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() :
@ -248,13 +248,13 @@ def clubs_brief(clubs) :
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(data,
@ -262,7 +262,7 @@ def clubs_brief(clubs) :
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 :
@ -281,10 +281,10 @@ def list_machines(machines) :
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(data,
titre = [u'mid', u'Type', u'Nom de machine', u'Adresse IP', u'Adresse MAC', u'Limitation'],
largeur = [5, 4, '*', 17, 19, 10],
@ -313,7 +313,7 @@ def list_bornes(bornes) :
for b in bornes :
t, bl = __bases_machines(b)
if t != 'born' : continue
# Données
try :
l = [x for x in b.info() if not x[0]=='<'][0]
@ -321,19 +321,19 @@ def list_bornes(bornes) :
l = l[0:11]
except :
l = u'????'
if borne_etat(b.nom()):
etat = ok
else:
etat = nok
if '-' in b.puissance() :
puiss = coul(b.puissance(),'rouge')
else :
puiss = b.puissance()
data.append([b.id(), b.nom().split('.')[0], b.ip(), b.mac(), etat, b.canal(), puiss, b.prise(), l])
return u"Can=canaux, P=puissance, E=état\n" + \
tableau(data,
titre = [u'mid', u'Nom', u'Adresse IP', u'Adresse MAC', u'E', u'Can', u'P', u'Pris', u'Lieu'],
@ -349,10 +349,10 @@ def adher_details(adher) :
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().find(u'@')!=-1 :
if adher.mail().find(u'@')!=-1 :
f += coul(u'Adresse mail : ','gras')
if adher.mail_invalide():
f += coul(adher.mail(),'rouge')
@ -371,7 +371,7 @@ def adher_details(adher) :
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',':
@ -416,11 +416,11 @@ def adher_details(adher) :
else:
f += coul(u"cotisation %s/%d non réglée"% (ann_scol, ann_scol+1 ),'violet')
jour = 0
if jour :
if jour :
f += coul(u"à jour",'vert')
f += '\n'
# Telephone
tel = adher.tel()
if tel != 'inconnu' :
@ -429,13 +429,13 @@ def adher_details(adher) :
except :
pass
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
addr = adher.adresse()
if addr[0] :
if addr[0] :
f += coul(u'Adresse : ','gras')
f += addr[0] + u'\n'
if addr[1] != ' ' : f += u' ' + addr[1] + u'\n'
@ -446,13 +446,13 @@ def adher_details(adher) :
# Chambre + prise (d'après annuaire)
etat, vlans = prise_etat(adher.chbre())
f += coul(u'Chambre : ','gras') + u"%s " % chbre
f += u'(%s)' % etat
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" % \
@ -460,7 +460,7 @@ def adher_details(adher) :
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 :
@ -470,20 +470,20 @@ def adher_details(adher) :
else :
f += str(solde).replace('.',',')
f += u" Euros\n"
# Role dans l'assoce
d = adher.droits()
if d :
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'
# Paiement
if adher.paiement() :
if len(adher.paiement()) == 1 :
f += coul(u'Cotisation payée pour l\'année scolaire :','gras')
else :
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 )
@ -508,7 +508,7 @@ def adher_details(adher) :
f += _blacklist(adher)
f += _info(adher)
f += _hist(adher)
# Formatage des machines aussi
f += coul(u'Machine(s) : ','gras')
m = adher.machines()
@ -532,34 +532,34 @@ def ipsec_ok(machine) :
clients = [x.split("/")[0] for x in commands.getoutput("%snetstat -r -f encap | awk '($2 == \"0\") {print $6}'" % prefix).split("\n")]
globals()['clients_ipsec'] = clients
return machine.nom() in clients
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\n" % machine.nom()
# 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()
# Propriétaire
f+= coul(u'Propriétaire : ','gras')
try :
try :
f += machine.proprio + coul(' (adhérent détruit)', 'jaune')
a = AssociationCrans()
except :
@ -567,12 +567,12 @@ def machine_details(machine) :
f += "%s" % a.Nom()
if a.chbre() in ['EXT', '????']:
f += ' (%s = %s)' % (a.idn, a.id())
elif a.chbre() != 'CRA':
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:
@ -580,12 +580,12 @@ def machine_details(machine) :
f += "%d\n" % n
# Adhérent blacklisté ?
bl = a.blacklist_actif()
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')
@ -602,10 +602,10 @@ def machine_details(machine) :
f += coul(u'VLANs : ', 'gras')
from hptools import sw_prise
f += ', '.join(sw_prise(machine.prise()).vlans())
f += '\n'
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')
@ -654,11 +654,11 @@ def machine_details(machine) :
if machine.nvram():
f += coul(u'NVRAM : ', 'gras')
f += ', '.join(machine.nvram()) + '\n'
if aff_ipsec and isinstance(machine, MachineWifi):
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') + ' '.join(machine.portTCPin()) + '\n'
@ -676,7 +676,7 @@ def machine_details(machine) :
f += _blacklist(machine)
f += _info(machine)
f += _hist(machine)
return f
def club_details(club) :
@ -688,7 +688,7 @@ def club_details(club) :
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()
@ -704,11 +704,11 @@ def club_details(club) :
if not jour : f += ' et '
f += coul(u"charte %s/%d non signée"% (ann_scol, ann_scol+1 ),'violet')
jour = 0
if jour :
if jour :
f += coul(u"à jour",'vert')
f += '\n'
# Chambre + prise
etat, vlans = prise_etat(club.chbre())
f += coul(u'Local : ','gras') + "%s " % club.local()
@ -718,7 +718,7 @@ def club_details(club) :
if vlans :
f += coul(u'VLAN : ','gras') + u'%s' % vlans
f += u'\n'
# Paiement
if club.paiement() :
f += coul(u'Charte signée pour les années scolaires :','gras')
@ -733,7 +733,7 @@ def club_details(club) :
if login :
f += coul(u'Login : ','gras') + login
alias = club.alias()
if alias :
if alias :
f += coul(u'\tAlias : ','gras') + ', '.join(alias)
f+= u'\n'
@ -781,14 +781,14 @@ def _blacklist(clas):
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] # 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''
@ -812,7 +812,7 @@ def _hist(clas) :
a = h[i] # Produit une erreur si i trop grand
if i !=0 : f += ' '
try:
# on force l'encodage ici sinon il est fait au moment de l'impression a
# 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.encode("iso-8859-15").decode("iso-8859-15")
except Exception, e:
@ -826,25 +826,25 @@ def _hist(clas) :
if h[i+1] : f += ' [...]\n'
except :
None
return f
def __bases_machines(m) :
""" Retourne [ type de la machines, blacklist ] """
#Type
#Type
if isinstance(m, MachineWifi): t = 'wifi'
elif isinstance(m, BorneWifi): t = 'born'
else : t='fixe'
# Déconnectée ?
b = m.blacklist_actif()
if not b :
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) :
@ -937,7 +937,7 @@ def prise_etat(chbre) :
count += 1
elif not prise.is_enable() :
f+= u', ' + coul(u'prise désactivée','rouge')
else :
else :
f+= u', activée, lien non détecté'
except ConversationError, r:
# Switch non manageable ou down
@ -975,12 +975,12 @@ def __usage() :
for c in base.auto_search_champs.values():
for champ in c:
if champ not in champs:
champs.append(champ)
champs.append(champ)
for champ in champs:
coul_champ = coul(champ, "bleu")
if accu == "":
accu = coul_champ
longueur = len(champ)
longueur = len(champ)
elif longueur + 2 + len(champ) < 80:
longueur += 2 + len(champ)
accu += ", " + coul_champ
@ -994,7 +994,7 @@ def __usage() :
for c in base.non_auto_search_champs.values():
for champ in c:
if champ not in champs:
champs.append(champ)
champs.append(champ)
for champ in champs:
if longueur + 2 + len(champ) < 80:
longueur += 2 + len(champ)
@ -1006,7 +1006,7 @@ def __usage() :
# 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,
@ -1018,7 +1018,7 @@ 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
@ -1028,8 +1028,8 @@ def __recherche() :
options, arg = getopt.getopt(sys.argv[1:], 'hamctbil:L:', [ 'debug', 'help', 'adherent', 'machine', 'club' , 'tech', 'bornes', 'limit=', 'limit-historique=', 'ipsec', 'crans' ])
except getopt.error, msg :
__usage_brief(unicode(msg))
# Traitement des options
only_adh=0
only_mac=0
@ -1037,7 +1037,7 @@ def __recherche() :
only_bornes=0
only_crans=0
mtech = 0
for opt, val in options :
if opt == '-h' or opt=='--help' :
__usage()
@ -1075,7 +1075,7 @@ def __recherche() :
# Recherche initiale sans critère
arg = [ 'canal=*&host=*.crans.org']
elif arg[0].find('=')!=-1 :
# Recherche avec critères
# Recherche avec critères
arg += [ '&canal=*' ]
elif opt in [ '-t', '--tech' ] :
# Format affichage des machines
@ -1083,7 +1083,7 @@ def __recherche() :
elif opt in [ '-i', '--ipsec' ] :
# Affichage des clefs ipsec
aff_ipsec = 1
if only_adh + only_mac + only_club + only_bornes > 1 :
__usage_brief(u'Options utilisées incompatibles')
@ -1110,7 +1110,7 @@ def __recherche() :
#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' : [] }
@ -1121,7 +1121,7 @@ def __recherche() :
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']:
# Pas de résultat dans la base
@ -1160,14 +1160,14 @@ def __recherche() :
elif res['club'] :
cprint(u'Aucun résultat à afficher')
sys.exit(4)
elif only_mac :
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 :
elif only_club :
if res['club'] : aff(res['club'])
elif res['machine'] :
to_aff=[]
@ -1196,15 +1196,15 @@ def __recherche() :
if res['club']:
cprint(u"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 :
@ -1215,7 +1215,7 @@ if __name__ == '__main__' :
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
Pour l'amélioration de ce programme merci de prévenir nounou en spécifiant la
marche à suivre pour reproduire cette erreur.""")
if debug :
cprint('-'*40)