#!/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 ) * 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.7 ;-------------------------------------------------------- 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 ssh version 2 ip authorized-managers 10.231.136.0 255.255.255.0 ip ssh filetransfer ;------------------------------------------------------- Spanning-tree spanning-tree protocol-version rstp ; Config des uplinks no spanning-tree %(uplinks)s 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 edge-port spanning-tree %(non_uplinks)s point-to-point-mac auto spanning-tree %(non_uplinks)s priority 8 ; On active spanning-tree ;------------------------------------------------------- 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" 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)} 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] " % 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()