
Ignore-this: cc9c1e80009975a99f12f7101a6d0b5f Nommément, les features sont : - Le DHCP Snooping (filtrage des requêtes/réponses DHCP par port) - La loop-detection (désactivation de prises lors de détection de boucle, plus simple que le STP) - Nettoyage pour générer des config compatibles avec les séries 2600 et 2610 Pour que la configuration fonctionne correctement sur les switches de la série 2600, l'OS H10.74 ou supérieur est préconisé. darcs-hash:20090830170259-ffbb2-5d6e0f26e23e246f2e915f82fbae480d3d98bf85.gz
522 lines
18 KiB
Python
522 lines
18 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 hpttols)
|
|
"""
|
|
|
|
import string, 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 import chbre_prises, uplink_prises, reverse, bat_manuels, all_switchs
|
|
from random import shuffle
|
|
from gen_confs import *
|
|
from time import localtime
|
|
import config
|
|
|
|
from annuaires import bat_switchs
|
|
|
|
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"
|
|
;-------------------------------------------------------- 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.3
|
|
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
|
|
;------------------------------------------------------- Spanning-tree
|
|
spanning-tree force-version rstp-operation
|
|
; Config des uplinks
|
|
no spanning-tree %(uplinks)s admin-edge-port
|
|
spanning-tree %(uplinks)s point-to-point-mac true
|
|
spanning-tree %(uplinks)s priority 6
|
|
; Config des prises adhérent
|
|
spanning-tree %(non_uplinks)s admin-edge-port
|
|
spanning-tree %(non_uplinks)s point-to-point-mac auto
|
|
spanning-tree %(non_uplinks)s priority 8
|
|
; On active
|
|
spanning-tree
|
|
;------------------------------------------------------- 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
|
|
;------------------------------------------------------- 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
|
|
"""
|
|
|
|
interface_template = """interface %(prise)i
|
|
enable
|
|
name "%(nom)s"
|
|
flow-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])
|
|
# 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': self.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 not res:
|
|
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()
|
|
else:
|
|
params['switch_config_header']= '; J4899A Configuration Editor; Created on release #H.10.50'
|
|
|
|
# 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.")
|
|
|
|
### 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' : '' }
|
|
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)]
|
|
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, [])
|
|
|
|
# "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 sur pegase.
|
|
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': 1+2*len(chbres), 'prise': prise }
|
|
|
|
|
|
# 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')
|
|
fd.write(self.config % params)
|
|
fd.close()
|
|
|
|
if __name__ == '__main__' :
|
|
opts, args = getopt.getopt(sys.argv[1:], 'hga', ['get-conf', 'help', 'all'])
|
|
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()
|