switchs2.py: mode "vérification"

This commit is contained in:
Daniel STAN 2014-12-09 19:47:57 +01:00
parent f7cf7bdf4b
commit 7c4d8dcbb0

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""
Génération de la configuration d'un switch.
@ -26,16 +26,20 @@ from socket import gethostbyname
import netaddr
import argparse
sys.path.append('/usr/scripts/')
if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts')
import gestion.secrets_new as secrets
from lc_ldap.shortcuts import lc_ldap_readonly as make_ldap_conn
import gestion.annuaires_pg as annuaire
import gestion.config as config
capture_model = re.compile(r'\((.*)\)')
import lc_ldap.objets as ldap_classes
from gestion.hptools import snmp
GIGABIT_MODELS = ['J9021A', 'J9145A']
MIB_PRISE_VLAN = 'SNMPv2-SMI::enterprises.11.2.14.11.5.1.7.1.15.3.1.1'
MIB_PRISE_MAC = 'SNMPv2-SMI::enterprises.11.2.14.11.5.1.9.4.2'
ldap = make_ldap_conn()
# états possibles
@ -43,11 +47,17 @@ V_TAGGED = 1
V_UNTAGGED = 2
V_NO = 3
# Vlans disponibles
ENABLED_VLANS = ['adherent', 'adm', 'wifi', 'v6only', 'accueil', 'isolement',
'appts', 'event']
def vlan_id(name):
"""Vlan id of a name (filtre jinja)"""
return config.vlans[name]
def net_of_vlan_name(name):
"""Renvoie le nom du réseau (ip) d'un vlan donné. C'est utile pour
lister les IPs de serveurs dhcp à autoriser (entre autres)"""
net_name = {
'adherent': 'fil',
'appts': 'personnel-ens',
@ -72,11 +82,20 @@ class Port(object):
# : Liste de noms de chambres
chambres = None
# : Liste des macs vues
seen_macs = None
# : Liste des vlans vus
seen_vlans = None
def __init__(self, num):
self.num = num
self.servers = list()
self.bornes = list()
self.chambres = list()
self.seen_macs = list()
self.seen_vlans = list()
def __str__(self):
if self.uplink:
@ -85,13 +104,13 @@ class Port(object):
labels = []
if self.servers:
labels.append('Srv_' +
','.join(s['host'][0].value.split('.',1)[0]
','.join(s['host'][0].value.split('.', 1)[0]
for s in self.servers))
if self.chambres:
labels.append('Ch_' + ','.join(self.chambres))
if self.bornes:
labels.append('Wifi_' +
','.join(b['host'][0].value.split('.',1)[0]
','.join(b['host'][0].value.split('.', 1)[0]
for b in self.bornes))
return ",".join(labels) or "Inconnu"
@ -108,6 +127,7 @@ class Port(object):
return 'speed-duplex auto-10-100'
def flowcontrol(self):
"""Est-ce que le flowcontrol est activé sur ce port ?"""
if self.uplink or self.servers:
return 'no flow-control'
else:
@ -188,7 +208,7 @@ class PortList(list):
liste.sort()
sortie = []
groupe = [-99999,-99999]
groupe = [-99999, -99999]
for x in itertools.chain(liste, [99999]):
if x > groupe[1]+1: # Nouveau groupe !
if groupe[0] == groupe[1]:
@ -212,10 +232,128 @@ class PortList(list):
untagged.append(port)
return (tagged, untagged)
def get_port_dict(switch):
"""Renvoie le dictionnaire prise->objet Port"""
# Build ports !
ports = {}
for num in xrange(1, 1+int(switch['nombrePrises'][0].value)):
ports[num] = Port(num)
bat, sw_num = get_bat_num(unicode(switch['host'][0]))
# Remplit les machines ayant une prise spécifiée
# (normalement uniquement les serveurs et les bornes)
for machine in ldap.search(u'prise=%s%i*' % (bat, sw_num)):
port = ports[int(machine['prise'][0].value[2:])]
classe = machine['objectClass'][0].value
if classe == 'machineCrans':
port.servers.append(machine)
elif classe == 'borneWifi':
port.bornes.append(machine)
# On remplit les chambres
for prise, chbres in annuaire.reverse(bat).iteritems():
# TODO rajouter un arg à reverse
if int(prise[0]) != int(sw_num):
continue
port = ports[int(prise[1:])]
# (below) beware: type(num) == str (ex: 302g)
port.chambres += [bat.upper() + num for num in chbres]
# Remplit les uplinks
for num_prise, label in annuaire.uplink_prises[bat].iteritems():
if num_prise/100 != int(sw_num):
continue
port = ports[num_prise % 100]
port.uplink = label
return ports
def fill_port_infos(hostname, port_dict):
"""Rajoute des infos sur les ports d'un switch"""
conn = snmp(hostname, version='1', community='public')
prise_vlan = conn.walk(MIB_PRISE_VLAN, bin_comp=True)
for res in prise_vlan:
res = res.split('.')
port = int(res[-2])
vlan = int(res[-3])
port_dict[port].seen_vlans.append(vlan)
prise_mac = conn.walk(MIB_PRISE_MAC, bin_comp=True)
for mib in prise_mac:
port = int(mib.split('.')[-8])
mib = mib.split('.')
mac = ':'.join('%02x' % int(mib[i]) for i in xrange(-7, -1))
port_dict[port].seen_macs.append(mac)
def check_conf_ldap(hostname):
"""Vérifie la conf du switch, la base ldap et les macs/prises associées"""
bat, sw_num = get_bat_num(hostname)
switch = ldap.search(u'host=bat%s-%d.adm.crans.org' % (bat, sw_num))[0]
port_dict = get_port_dict(switch)
fill_port_infos(hostname, port_dict)
for port in port_dict.itervalues():
print("* Checking port %d (%s)" % (port.num, port))
# Nombres de vlans
pr_nb_vlans = len(port.seen_vlans)
th_nb_vlans = sum( port.vlan_member(vname) != V_NO for vname in ENABLED_VLANS )
if not th_nb_vlans and port.radius_auth():
th_nb_vlans = 1
if port.bornes and port.vlan_member('adherent') == V_NO:
th_nb_vlans += 1
if th_nb_vlans != pr_nb_vlans:
print(" Wrong vlan number (%d,%d)" % (th_nb_vlans, pr_nb_vlans))
print(port.seen_vlans)
print(list( vname for vname in ENABLED_VLANS if port.vlan_member(vname) != V_NO ))
# Les macs
if port.uplink:
if len(port.seen_macs) < 20:
print(" Uplink but few macs (%d)" % len(port.seen_macs))
elif port.radius_auth(): # On vérifie que c'est la bonne chambre
th_prises_set = set()
for mac in set(port.seen_macs):
res = ldap.search(u'macAddress=%s' % mac)
if not res:
continue
chbre = unicode(res[0].proprio().get('chbre', ['EXT'])[0])
th_prise = chbre[0].lower()
th_prise += annuaire.chbre_prises(chbre[0], chbre[1:])
th_prises_set.add(th_prise)
pr_prise = bat.lower() + '%d%02d' % (sw_num, port.num)
if th_prises_set and pr_prise not in th_prises_set:
print(" Aucune machine de chbre. Candidats: %r" % th_prises_set)
else:
for mac in set(port.seen_macs):
res = ldap.search(u'macAddress=%s' % mac)
if not res:
print(" Unknown mac %s" % mac)
continue
machine = res[0]
owner = machine.proprio()
if isinstance(owner, ldap_classes.AssociationCrans):
the = unicode(machine.get('prise', ['N/A'])[0])
the = the.lower()
pra = bat.lower() + '%d%02d' % (sw_num, port.num)
if the != pra:
print(" Machine %s sur mauvaise prise (%s,%s)" %
(machine, the, pra))
elif isinstance(machine, ldap_classes.machineWifi):
if not port.bornes:
print(" Machine %s sur prise sans borne ?" % machine)
def get_bat_num(hostname):
"""Renvoie un tuple (bat, num) où bat est la lettre du bâtiment et
num l'entier numéro du switch"""
return (hostname[3].lower(), int(hostname[5]))
def conf_switch(hostname):
"""Affiche la configuration d'un switch"""
bat = hostname[3].lower()
sw_num = int(hostname[5])
bat, sw_num = get_bat_num(hostname)
switch = ldap.search(u'host=bat%s-%d.adm.crans.org' % (bat, sw_num))[0]
@ -240,8 +378,7 @@ def conf_switch(hostname):
'dhcp_rid_servers': [34, 160],
# vlans activés (cf data.vlans)
'vlan_names': ['adherent', 'adm', 'wifi', 'v6only', 'accueil',
'isolement', 'appts', 'event'],
'vlan_names': ENABLED_VLANS,
# réseaux où on fait du dhcp snooping (cf data.NETs)
'dhcp_snooping_vlan_names': ['adherent', 'wifi', 'accueil',
@ -277,37 +414,7 @@ def conf_switch(hostname):
data['gigabit'] = True
# Build ports !
ports = {}
for x in xrange(1, 1+int(switch['nombrePrises'][0].value)):
ports[x] = Port(x)
# Remplit les machines ayant une prise spécifiée
# (normalement uniquement les serveurs et les bornes)
for machine in ldap.search(u'prise=%s%i*' % (bat.upper(), sw_num)):
port = ports[int(machine['prise'][0].value[2:])]
classe = machine['objectClass'][0].value
if classe == 'machineCrans':
port.servers.append(machine)
elif classe == 'borneWifi':
port.bornes.append(machine)
# On remplit les chambres
for prise, chbres in annuaire.reverse(bat).iteritems():
# TODO rajouter un arg à reverse
if int(prise[0]) != int(sw_num):
continue
port = ports[int(prise[1:])]
# (below) beware: type(num) == str (ex: 302g)
port.chambres += [bat.upper() + num for num in chbres]
# Remplit les uplinks
for num_prise, label in annuaire.uplink_prises[bat].iteritems():
if num_prise/100 != int(sw_num):
continue
port = ports[num_prise % 100]
port.uplink = label
ports_list = PortList(ports.itervalues())
ports_list = PortList(get_port_dict(switch).itervalues())
data['ports'] = ports_list
data['vlans'] = []
# On remplit les objets vlans (nom, id, tagged, untagged etc)
@ -350,7 +457,14 @@ if __name__ == "__main__":
"switch.")
parser.add_argument('hostname', help="Nom du switch à regénérer " +
"(ex: batg-4)")
parser.add_argument('-c', '--check', action='store_true', default=False,
help="Vérifie la conf par rapport aux macs et vlans effectivement" +\
"présents sur le switch")
options = parser.parse_args(sys.argv[1:])
print(conf_switch(options.hostname))
if options.check:
check_conf_ldap(options.hostname)
else:
print(conf_switch(options.hostname))