
Ignore-this: a8ca2ead5791d32a01f6e8f688b9ee0a darcs-hash:20110224232934-8d035-ef4fae60ca8b2d2a65e28e46d4a2a485cf2a5a9c.gz
551 lines
20 KiB
Python
551 lines
20 KiB
Python
#!/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>)
|
|
* activation du ssh (crypto key generate ssh)
|
|
* copie fichier de conf
|
|
pour les reconfiguration copier le fichier de conf
|
|
|
|
Dans tous les cas FAIRE LE SNMP A LA MAIN (script hptools)
|
|
"""
|
|
|
|
import sys, os, commands, smtplib, tempfile, getopt
|
|
|
|
sys.path.append('/usr/scripts/gestion')
|
|
from hptools import hpswitch, sw_chbre
|
|
from ldap_crans import crans_ldap, BorneWifi
|
|
from annuaires_pg import uplink_prises, reverse, bat_manuels, all_switchs, bat_switchs
|
|
from random import shuffle
|
|
from gen_confs import *
|
|
from time import localtime
|
|
import config
|
|
|
|
try:
|
|
any
|
|
except NameError:
|
|
def any(iterable):
|
|
for item in iterable:
|
|
if item:
|
|
return True
|
|
return False
|
|
|
|
class switch(gen_config) :
|
|
# Répertoire ou écire les fichiers de conf
|
|
CONF_REP='/tmp/' # avec un / derrière
|
|
|
|
config = """%(switch_config_header)s
|
|
|
|
hostname "%(switch)s"
|
|
%(module-type)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
|
|
;-------------------------------------------------------- Heure/date
|
|
time timezone 60
|
|
time daylight-time-rule Western-Europe
|
|
sntp server 10.231.136.10
|
|
timesync sntp
|
|
sntp unicast
|
|
;-------------------------------------------------------- Misc
|
|
console inactivity-timer 30
|
|
;-------------------------------------------------------- Logs
|
|
logging 10.231.136.12
|
|
;-------------------------------------------------------- IP du switch
|
|
ip default-gateway 10.231.136.4
|
|
vlan %(vlan_adherent)s
|
|
name "DEFAULT_VLAN"
|
|
untagged %(prises_default)s
|
|
no ip address
|
|
ip igmp
|
|
no ip igmp querier
|
|
exit
|
|
vlan %(vlan_adm)s
|
|
name "Adm"
|
|
%(prises_adm)s
|
|
ip address %(ip)s 255.255.255.0
|
|
exit
|
|
vlan %(vlan_wifi)s
|
|
name "Wifi"
|
|
%(prises_wifi)s
|
|
no ip address
|
|
exit
|
|
vlan %(vlan_hotspot)s
|
|
name "Hotspot"
|
|
%(prises_hotspot)s
|
|
no ip address
|
|
exit
|
|
vlan %(vlan_gratuit)s
|
|
name "Gratuit"
|
|
tagged %(prises_default)s
|
|
no ip address
|
|
exit
|
|
vlan %(vlan_accueil)s
|
|
name "Accueil"
|
|
tagged %(prises_default)s
|
|
no ip address
|
|
exit
|
|
vlan %(vlan_isolement)s
|
|
name "Isolement"
|
|
tagged %(prises_default)s
|
|
no ip address
|
|
exit
|
|
vlan %(vlan_appts)s
|
|
name "Appt ENS"
|
|
%(prises_appts)s
|
|
no ip address
|
|
exit
|
|
;-------------------------------------------------------- Logs
|
|
%(INTERFACES_CONF)s
|
|
;------------------------------------------------------- Accès d'administration
|
|
no telnet-server
|
|
no web-management
|
|
aaa authentication ssh login public-key none
|
|
aaa authentication ssh enable public-key none
|
|
ip ssh
|
|
ip authorized-managers 10.231.136.0 255.255.255.0
|
|
ip ssh filetransfer
|
|
;------------------------------------------------------- Protection contre les boucles
|
|
loop-protect disable-timer 30
|
|
loop-protect transmit-interval 3
|
|
loop-protect %(non_uplinks)s
|
|
;------------------------------------------------------- Serveurs radius
|
|
radius-server dead-time 2
|
|
radius-server key %(radius_key)s
|
|
%(radius-serveurs)s
|
|
;------------------------------------------------------- Filtrage mac
|
|
aaa port-access mac-based addr-format multi-colon
|
|
;------------------------------------------------------- Bricoles
|
|
no cdp run
|
|
no stack
|
|
"""
|
|
|
|
dhcp_snooping_template = """;------------------------------------------------------- DHCP Snooping
|
|
dhcp-snooping vlan %(vlan_adherent)s
|
|
dhcp-snooping trust %(uplinks)s
|
|
no dhcp-snooping trust %(non_uplinks)s
|
|
dhcp-snooping authorized-server %(dhcp)s
|
|
; Activation
|
|
dhcp-snooping"""
|
|
|
|
interface_template = """interface %(prise)i
|
|
enable
|
|
name "%(nom)s"
|
|
%(no_flowcontrol)sflow-control%(speed)s
|
|
no lacp
|
|
exit
|
|
"""
|
|
# Serveurs radius
|
|
rad_servs = [ '10.231.136.72', '10.231.136.9' ]
|
|
rad_template = "radius-server host %s\n"
|
|
|
|
# Serveur DHCP du vlan par défaut
|
|
dhcp_server = "138.231.136.9"
|
|
|
|
def __init__(self,truc):
|
|
""" truc est soit :
|
|
* une _liste_ de chambres => reconfig de ces chambres
|
|
* un _tulpe_ de noms de switch => reconfig de ces swiths"""
|
|
self.db = crans_ldap() # connexion LDAP
|
|
if type(truc) == list :
|
|
# On enlève les chambres "CRA", "????" et EXT qui n'ont pas besion de config
|
|
self.chbres = [ch for ch in truc if (ch not in [ "CRA", "????", "EXT" ]) ]
|
|
self.switch = None
|
|
else :
|
|
self.chbres = None
|
|
self.switch = truc
|
|
|
|
def __str__(self) :
|
|
return 'switchs'
|
|
|
|
def restart(self) :
|
|
if self.chbre :
|
|
# Tout est déja fait
|
|
return
|
|
####### Vu qu'il n'y a pas de serveur tftp ici
|
|
# on excécute pas le truc en dessous
|
|
#for switch in self.switch :
|
|
# self.aff = anim('\treboot de %s' % switch)
|
|
# sw = hptools.switch(switch)
|
|
# sw.update()
|
|
|
|
def gen_conf(self) :
|
|
if self.chbres :
|
|
self.chbres.sort()
|
|
for chbre in self.chbres :
|
|
self.configure_chbre(chbre)
|
|
elif self.switch :
|
|
for switch in self.switch :
|
|
self.configure_switch(switch)
|
|
|
|
def configure_chbre(self,chbre) :
|
|
""" Recontigure la chambre fournie chambre """
|
|
try :
|
|
bat = chbre[0].lower()
|
|
if bat in bat_switchs :
|
|
prise = sw_chbre(chbre)
|
|
prise.reconfigure() # Vitesse et nom (juste au cas ou ca aurait changé)
|
|
elif bat in bat_manuels :
|
|
class prise_non_manageable :
|
|
def __init__(self,chbre) :
|
|
self.chbre = chbre
|
|
|
|
def __mail(self,sujet) :
|
|
To = "clef%s@crans.org" % self.chbre[0].lower()
|
|
From = To
|
|
conn=smtplib.SMTP('localhost')
|
|
txt_mail = "From: Crans scripts <%(From)s>\n"
|
|
txt_mail+= "To: %(To)s\n"
|
|
txt_mail+= "Subject: (CRANS) %s\n\nMerci." % sujet
|
|
conn.sendmail(From, To , txt_mail % { 'From' : From, 'To' : To })
|
|
conn.quit()
|
|
|
|
def disable(self) :
|
|
self.__mail("Chambre %s à débrancher." % self.chbre)
|
|
def enable(self) :
|
|
self.__mail("Chambre %s à brancher." % self.chbre)
|
|
|
|
prise=prise_non_manageable(chbre)
|
|
|
|
else :
|
|
# Rien a faire
|
|
print OK
|
|
return True
|
|
|
|
|
|
a = self.db.search('chbre=%s&paiement=ok' % chbre)
|
|
a = a['adherent'] + a['club']
|
|
if a and 'bloq' not in a[0].blacklist_actif() :
|
|
# Il faut activer la prise
|
|
anim('\tactivation chbre %s' % chbre)
|
|
prise.enable()
|
|
else :
|
|
# Il faut désactiver la prise
|
|
anim('\tdésactivation chbre %s' % chbre)
|
|
prise.disable()
|
|
|
|
print OK
|
|
except :
|
|
print ERREUR
|
|
if self.debug :
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
return True
|
|
|
|
def configure_switch(self,switch) :
|
|
self.aff = anim('\tconfiguration de %s' % switch)
|
|
try:
|
|
warn = self.__configure_switch(switch)
|
|
self.aff.reinit()
|
|
if warn :
|
|
print WARNING
|
|
if self.debug :
|
|
sys.stderr.write(warn)
|
|
else :
|
|
print OK
|
|
except :
|
|
self.aff.reinit()
|
|
print ERREUR
|
|
self._restore()
|
|
return 1
|
|
|
|
def __configure_switch(self,switch) :
|
|
""" Génère le fichier de conf du switch donné """
|
|
### Récupération données du switch
|
|
# Batiment et numéro du switch
|
|
bat = switch[3].lower()
|
|
sw_num = int(switch[5])
|
|
dhcp_server = self.dhcp_server
|
|
# Conf radius
|
|
sys.path.append('/usr/scripts/gestion/secrets')
|
|
from secrets import radius_key
|
|
|
|
self.aff.cycle()
|
|
|
|
## On veut par défaut tout confier au serveur radius principal
|
|
#shuffle(self.rad_servs)
|
|
rad = self.rad_template * len(self.rad_servs)
|
|
|
|
|
|
params = { 'switch' : switch, 'bat' : bat.upper() ,
|
|
'radius_key' : radius_key ,
|
|
'radius-serveurs' : rad[:-1] % tuple(self.rad_servs),
|
|
'dhcp': dhcp_server}
|
|
|
|
self.aff.cycle()
|
|
|
|
options = [ opt for opt,arg in opts]
|
|
if '-g' in options or '--get-conf' in options:
|
|
old_config = NamedTemporaryFile()
|
|
res, msg = commands.getstatusoutput("scp bat%s-%i:cfg/startup-config %s" % (bat, sw_num, old_config.name))
|
|
|
|
if res != 0:
|
|
raise RuntimeError(u"Erreur : impossible de récupérer l'ancienne configuration du switch")
|
|
|
|
params['switch_config_header'] = old_config.readline()
|
|
old_config.close()
|
|
|
|
self.aff.cycle()
|
|
elif '--header' in options:
|
|
for opt, arg in opts:
|
|
if opt == '--header':
|
|
params['switch_config_header'] = arg
|
|
break
|
|
else:
|
|
params['switch_config_header']= '; J4899A Configuration Editor; Created on release #H.10.50'
|
|
|
|
model = params['switch_config_header'].split(' ', 2)[1]
|
|
if model == "J9145A":
|
|
params['module-type'] = 'module 1 type J9145A'
|
|
else:
|
|
params['module-type'] = ''
|
|
|
|
# IP
|
|
machine = self.db.search(switch)['machine'][0]
|
|
params['ip'] = str(machine.ip())
|
|
|
|
self.aff.cycle()
|
|
|
|
# Nombre de prises et modèle
|
|
nb_prises = machine.nombrePrises()
|
|
if nb_prises < 0 :
|
|
raise RuntimeError("Erreur : impossible de déterminer les caractéristiques du switch.")
|
|
|
|
has_dhcp_snooping = "2810" not in " ".join(machine.info())
|
|
|
|
### Configuration prises
|
|
params['INTERFACES_CONF'] = ''
|
|
|
|
# Dictionnaire prise -> chambre
|
|
prise_chbres = reverse(bat)
|
|
|
|
# Prises occupées par des machines du Cr@ns
|
|
crans_prises={}
|
|
for m in self.db.search('prise=%s%i*' % (bat.upper(), sw_num))['machine'] :
|
|
try: crans_prises[m.prise()].append(m)
|
|
except: crans_prises[m.prise()] = [ m ]
|
|
|
|
self.aff.iter = nb_prises+1
|
|
|
|
# Paramètres à affecter
|
|
for key in ( 'uplinks', 'non_uplinks' ) :
|
|
params[key] = []
|
|
|
|
vlans = { 'wifi_tagged' : [] , 'wifi_untagged' : [] ,
|
|
'hotspot_tagged' : [], 'hotspot_untagged' : [],
|
|
'adm_tagged' : [] , 'adm_untagged' : [] ,
|
|
'appts_tagged' : [], 'appts_untagged' : [],
|
|
|
|
# VLans pour le reste: le vlan des adhérents, des
|
|
# inconnus et de ceux qui ne paie pas
|
|
'default' : [] }
|
|
|
|
personnels_loges = self.db.search('etudes=Personnel ENS')['adherent']
|
|
prises_appartements= [ p.chbre() for p in personnels_loges ]
|
|
|
|
# Génération de la conf de chaque prise
|
|
for prise in range(1,nb_prises+1):
|
|
self.aff.cycle()
|
|
|
|
# Conf par défaut : activée, autonégociation
|
|
prise_params = { 'prise' : prise , 'speed' : '',
|
|
'etat' : '', 'no_flowcontrol': '' }
|
|
annu_prise = '%i%02i' % (sw_num, prise) # prise telle que notée dans l'annuaire
|
|
|
|
if uplink_prises[bat].has_key(int(annu_prise)) :
|
|
### Prise d'uplink
|
|
prise_params['nom'] = uplink_prises[bat][int(annu_prise)]
|
|
prise_params['no_flowcontrol'] = 'no '
|
|
params['uplinks'].append(prise)
|
|
vlans['default'].append(prise)
|
|
vlans['adm_tagged'].append(prise)
|
|
vlans['wifi_tagged'].append(prise)
|
|
vlans['hotspot_tagged'].append(prise)
|
|
vlans['appts_tagged'].append(prise)
|
|
params['INTERFACES_CONF'] += self.interface_template % prise_params
|
|
continue
|
|
|
|
params['non_uplinks'].append(prise)
|
|
|
|
if crans_prises.has_key("%s%s" % (bat.upper(), annu_prise)) :
|
|
### Prise réservée à l'association
|
|
wifi=0
|
|
adm=0
|
|
autres=0
|
|
for m in crans_prises["%s%s" % (bat.upper(), annu_prise)] :
|
|
if isinstance(m, BorneWifi): wifi += 1
|
|
elif m.Nom().find('.adm.crans.org')!=-1 : adm+=1
|
|
else : autres+=1
|
|
if autres==0 and adm==0 :
|
|
# Vlan wifi uniquement
|
|
if wifi == 1 :
|
|
prise_params['nom'] = "Wifi_%s" % m.nom().split(".")[0]
|
|
# Certaines bornes sont dans des chambres, est-ce le cas ?
|
|
if prise_chbres.has_key(annu_prise):
|
|
chbres = prise_chbres[annu_prise]
|
|
prise_params['nom'] += '+Chambre'
|
|
if len(chbres) > 1 : prise_params['nom'] += 's'
|
|
for chbre in chbres :
|
|
prise_params['nom'] += '_%s%s' % (bat.upper(), chbre)
|
|
vlans['default'].append(prise)
|
|
else :
|
|
prise_params['nom'] = "Wifi"
|
|
vlans['hotspot_tagged'].append(prise)
|
|
vlans['wifi_tagged'].append(prise)
|
|
elif wifi==0 and autres==0 :
|
|
# Vlan adm uniquement
|
|
if adm == 1 :
|
|
prise_params['nom'] = m.nom().split(".")[0]
|
|
else :
|
|
prise_params['nom'] = "Uplink_adm"
|
|
vlans['adm_untagged'].append(prise)
|
|
else :
|
|
# Tous les vlans
|
|
prise_params['nom'] = "Prise_crans"
|
|
vlans['default'].append(prise)
|
|
vlans['adm_tagged'].append(prise)
|
|
vlans['wifi_tagged'].append(prise)
|
|
vlans['hotspot_tagged'].append(prise)
|
|
|
|
params['INTERFACES_CONF'] += self.interface_template % prise_params
|
|
continue
|
|
|
|
# Quelle(s) chambre(s) est/sont sur la prise. A cause du
|
|
# cas PDJ il peux y avoir des prises avec plusieurs
|
|
# chambres.
|
|
chbres = prise_chbres.get(annu_prise, [])
|
|
|
|
# Combien de machines sont succeptibles d'etre sur la prise
|
|
nombre_de_machines = 0
|
|
# Pour chaque chambre sur cette prise
|
|
for chb in chbres:
|
|
# On selectionne les eventuels adherents y residant
|
|
for adherent in self.db.search("chbre=%s" % (chb))['adherent']:
|
|
# On selectionne les machines fixes de l'adherent, et on ajoute le nombre au quota
|
|
nombre_de_machines += len(adherent.machines_fixes())
|
|
|
|
# Authentification RADIUS, pas pour les clubs...
|
|
if not any("cl" in chbre.lower() for chbre in chbres):
|
|
# "unauth-vid" est le vlan sur lequel sont envoyés les machines
|
|
# quand l'authentification RADIUS échoue. On met le VLAN 1 pour
|
|
# éviter les problèmes quand LDAP se ch@#! dessus.
|
|
params['INTERFACES_CONF'] += """aaa port-access mac-based %(prise)s
|
|
aaa port-access mac-based %(prise)s addr-limit %(nbmac)s
|
|
aaa port-access mac-based %(prise)s logoff-period 3600
|
|
aaa port-access mac-based %(prise)s unauth-vid 1
|
|
""" % { 'nbmac': 2 + nombre_de_machines, 'prise': prise }
|
|
# On regle le nombre de machines connectables a la prise au nombre de machines
|
|
# sur cette prise dans l'annuaire plus 2
|
|
|
|
# On donne à la prise un nom qui dépend des chambres
|
|
# connectés dessus
|
|
if chbres :
|
|
prise_params['nom'] = 'Chambre'
|
|
if len(chbres) > 1 : prise_params['nom'] += 's'
|
|
for chbre in chbres :
|
|
prise_params['nom'] += '_%s%s' % (bat.upper(), chbre)
|
|
else :
|
|
prise_params['nom'] = 'Inconnu'
|
|
|
|
# Si c'est une chambre d'un personnel de l'ENS, on lui donne
|
|
# le VLAN 21 en untagged
|
|
if chbres:
|
|
if chbres[0] in prises_appartements: # il faudrait faire
|
|
# un truc moins sale
|
|
vlans['appts_untagged'].append(prise)
|
|
prise_params['nom'] += "(appartement ENS)"
|
|
|
|
params['INTERFACES_CONF'] += self.interface_template % prise_params
|
|
|
|
# Petite verif
|
|
if not params['uplinks'] or not params['non_uplinks'] :
|
|
raise RuntimeError('Switch sans uplink ou sans prise adhérent.')
|
|
|
|
def mk_list(liste_prise) :
|
|
"""
|
|
transforme une liste de prises en une chaine pour le switch
|
|
exemple : 1, 3, 4, 5, 6, 7, 9, 10, 11, 12 => 1,3-7,9-12
|
|
"""
|
|
if not liste_prise : return ''
|
|
liste_prise.sort()
|
|
|
|
# initialisation
|
|
i = liste_prise.pop(0)
|
|
groupe = [ i, i ]
|
|
result = []
|
|
|
|
liste_prise.append(9999) # apparaitra jamais dans la liste
|
|
|
|
while liste_prise :
|
|
nouveau = liste_prise.pop(0)
|
|
if nouveau == groupe[1] + 1 :
|
|
groupe[1] += 1
|
|
else :
|
|
# Ajout du groupe au résultat
|
|
if groupe[0] == groupe[1] :
|
|
result.append(str(groupe[0]))
|
|
else :
|
|
result.append('-'.join(map(str,groupe)))
|
|
# Réinit de groupe
|
|
groupe = [ nouveau, nouveau ]
|
|
|
|
return ','.join(result)
|
|
|
|
# Saut de ligne parasite
|
|
params['INTERFACES_CONF'] = params['INTERFACES_CONF'][:-1].encode('iso-8859-15')
|
|
|
|
# Conversion des listes
|
|
for key in [ 'uplinks', 'non_uplinks' ] :
|
|
params[key] = mk_list(params[key])
|
|
for key, prises in vlans.items() :
|
|
vlans[key]=mk_list(prises)
|
|
|
|
# Config des vlans spéciaux (adm, wifi et appartements)
|
|
for v in ('adm', 'wifi', 'hotspot', 'appts') :
|
|
params['prises_%s' % v] = ''
|
|
for t in ('tagged' , 'untagged') :
|
|
if vlans['%s_%s' % (v,t)] :
|
|
params['prises_%s' % v] += '\n %s %s' % (t, vlans['%s_%s' % (v,t)])
|
|
# Saut de ligne parasite
|
|
params['prises_%s' % v] = params['prises_%s' % v][4:]
|
|
|
|
params['prises_default'] = vlans['default']
|
|
|
|
for name, number in config.vlans.items():
|
|
params["vlan_%s" % name] = number
|
|
|
|
# Ecriture
|
|
fd = self._open_conf(self.CONF_REP + switch + '.conf')
|
|
my_config = self.config
|
|
if has_dhcp_snooping:
|
|
my_config += self.dhcp_snooping_template
|
|
fd.write(my_config % params)
|
|
fd.close()
|
|
|
|
if __name__ == '__main__' :
|
|
opts, args = getopt.getopt(sys.argv[1:], 'hga', ['get-conf', 'help', 'all', 'header=' ])
|
|
if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) == 1 :
|
|
print "%s [-g|--get-conf] <switch>" % sys.argv[0].split('/')[-1].split('.')[0]
|
|
print "Génération du fichier de configuration des switchs donnés."
|
|
sys.exit(255)
|
|
|
|
if args[0] == 'all' or 'a' in opts or '--all' in opts :
|
|
switchs = tuple(all_switchs())
|
|
else :
|
|
switchs = tuple(args)
|
|
sw = switch(switchs)
|
|
sw.debug = 1
|
|
sw.reconfigure()
|