switchs2.py: mode "vérification"
This commit is contained in:
parent
f7cf7bdf4b
commit
7c4d8dcbb0
1 changed files with 158 additions and 44 deletions
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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:])
|
||||
|
||||
if options.check:
|
||||
check_conf_ldap(options.hostname)
|
||||
else:
|
||||
print(conf_switch(options.hostname))
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue