Version plus pythonesque de HPTools. Pleinement fonctionnelle sous jessie.

* Les requêtes de type lecture seule marchent très bien tout court ;
 * Celles de type écriture sont sans effet sous wheezy. C'est a priori
 un bug dans python-netsnmp
This commit is contained in:
Pierre-Elliott Bécue 2015-04-24 01:55:52 +02:00
parent cd5ae8aaa5
commit a0f0c80ead
8 changed files with 932 additions and 0 deletions

View file

@ -0,0 +1,11 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
import os
from .switch import HPSwitch
from .tools import trace_mac
import gestion.config.snmp as config_snmp
os.environ["MIBS"] = ":".join([mib for mib in config_snmp.PRELOAD_MIBS])

View file

@ -0,0 +1,53 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""Ce sont les variables utiles pour les autres scripts du
module"""
OPERSTATUS = {
1: 'up',
2: 'down',
3: 'testing',
4: 'unknown',
5: 'dormant',
6: 'notPresent',
7: 'lowerLayerDown',
}
ADMINSTATUS = {
1: 'up',
2: 'down',
3: 'testing',
}
ETHSPEED = {
'HD': {
0: '5',
10: '1',
100: '2',
1000: '5',
},
'FD': {
0: '5',
10: '3',
100: '4',
1000: '6',
},
'AUTO': {
0: '5',
10: '7',
100: '8',
1000: '9',
},
}
REV_ETHSPEED = {
'1': '10 Mbs Half Duplex',
'2': '100 Mbs Half Duplex',
'3': '10 Mbs Full Duplex',
'4': '100 Mbs Full Duplex',
'6': '1000 Mbs Full Duplex',
'5': 'auto',
'7': '10 Mbs auto',
'8': '100 Mbs auto',
'9': '1000 Mbs auto',
}

79
gestion/hptools2/mac.py Normal file
View file

@ -0,0 +1,79 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""Contient les outils pour manipuler des adresses MAC
dans le module hptools"""
import binascii
import netaddr
def bin_to_mac(raw):
"""Convertit une OctetString en une MAC"""
return format_mac(binascii.hexlify(raw))
def format_mac(raw):
"""Formatte la mac en aa:bb:cc:dd:ee:ff"""
return str(netaddr.EUI(raw)).replace('-', ':').lower()
class MACFactory(object):
"""Factory stockant les macs"""
__macs = {}
@classmethod
def register_mac(cls, mac, parent=None):
"""Enregistre une mac dans la factory et
retourne une instance de MACAddress si besoin."""
if cls.__macs.get(mac, None) is None:
cls.__macs[mac] = MACAddress(mac, parent)
else:
cls.__macs[mac].append_parent(parent)
return cls.__macs[mac]
@classmethod
def get_mac(cls, mac):
"""Récupère une mac dans la factory"""
return cls.__macs.get(mac, None)
@classmethod
def get_macs(cls):
"""Récupère l'ensemble des MACS de la factory"""
return cls.__macs
class MACAddress(object):
"""Classe représentant une adresse MAC"""
def __init__(self, value, parent=None):
"""Stocke l'adresse mac quelque part et le parent"""
self.__value = value
if parent is not None:
self.__parents = {parent.name() : parent}
@property
def value(self):
"""Property pour lire la valeur d'une MAC"""
return self.__value
@property
def parents(self):
"""Retourne les parents"""
return self.__parents
def append_parent(self, parent):
"""Ajoute un parent à la MAC si parent n'est pas None"""
if parent is not None:
if self.__parents.get(parent.name(), None) is None:
self.__parents[parent.name()] = parent
def remove_parent(self, parent):
"""Retire le parent référencé à la MAC"""
if parent is not None:
if self.__parents.get(parent.name(), None) is not None:
_ = self.__parents.pop(parent.name())

139
gestion/hptools2/port.py Normal file
View file

@ -0,0 +1,139 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""Contient la définition et les outils pour bosser avec
les ports.
C'est essentiellement une couche d'abstraction, les fonctions
utiles sont appelées avec les switches."""
import netaddr
from .mac import MACFactory, bin_to_mac
class HPSwitchPort(object):
"""Classe représentant le port d'un switch"""
def __init__(self, num, parent, ptype=None):
"""Permet de lier un port au switch parent."""
self.__num = num
self.__ptype = ptype
self.__parent = parent
self.__macs = []
self.__multicast = []
self.__vlans = []
self.__oper = False
self.__admin = False
self.__alias = None
self.__eth = None
def name(self):
"""Retourne le nom du port"""
return "%s%02d" % (self.__parent.name(), self.__num)
def get_vlans(self):
"""Retourne les vlans du port"""
return self.__vlans
def add_vlan(self, vlan):
"""Ajoute le vlan à la liste"""
self.__vlans.append(vlan)
def purge_vlans(self):
"""Purge la liste des vlans connus du port"""
self.__vlans = []
def get_eth(self):
"""Récupère l'alias du port"""
if self.__eth is None:
self.__parent.update_eth_speed()
return self.__eth
def set_eth(self, val):
"""Affecte le nom"""
self.__eth = val
def get_alias(self):
"""Récupère l'alias du port"""
if self.__alias is None:
self.__parent.update_ports_aliases()
return self.__alias
def set_alias(self, alias):
"""Affecte le nom"""
self.__alias = alias
def append_mac(self, mac):
"""Ajoute une mac au port"""
self.__macs.append(MACFactory.register_mac(bin_to_mac(mac), self))
def get_macs(self, update=False):
"""Récupère les adresses mac depuis le parent"""
if not self.__macs or update:
# On boucle sur les macs et on les sépare du parent actuel (vu
# qu'on va régénérer sa liste de macs).
self.flush_macs()
__ret = self.__parent.client.walk('hpSwitchPortFdbAddress.%d' % (self.__num,))
self.__macs = [MACFactory.register_mac(bin_to_mac(ret['val']), self) for ret in __ret]
return self.__macs
def flush_macs(self):
"""Vire les macs"""
if not self.__macs:
return True
for mac in self.__macs:
mac.remove_parent(self)
self.__macs = []
return True
def append_multicast(self, multi_ip):
"""Ajoute l'IP aux multicasts"""
self.__multicast.append(netaddr.IPAddress(multi_ip))
@property
def multicast(self):
"""Retourne les ip multicast liées au port."""
return self.__multicast
def flush_multicast(self):
"""Vire les infos sur le multicast."""
self.__multicast = []
@property
def parent(self):
"""Property sur __parent"""
return self.__parent
@property
def oper(self):
"""Retourne l'oper status"""
return self.__oper
@property
def admin(self):
"""Retourne l'admin status"""
return self.__admin
@admin.setter
def admin(self, stat):
"""Met à jour l'admin status. Si stat n'est pas bon, met 3 (testing)"""
try:
stat = int(stat)
except TypeError:
stat = 3
self.__admin = stat
@oper.setter
def oper(self, stat):
"""Met à jour l'oper status. Si stat n'est pas bon, met 4 (unknown)"""
try:
stat = int(stat)
except TypeError:
stat = 4
self.__oper = stat

124
gestion/hptools2/snmp.py Normal file
View file

@ -0,0 +1,124 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""Ce fichier propose un client snmp basique"""
import netsnmp
import socket
import gestion.secrets_new as secrets_new
class SNMPClient(object):
"""Classe de base définissant un client SNMP."""
def __init__(self, host):
"""Crée une session pointant vers le serveur SNMP, et
peuple les variables utiles."""
# Le fait se gérer si c'est .adm.crans.org, .crans.org, ou
# si le nom est un fqdn ou pas est du ressort du DNS (dans la
# mesure où de toute façon, si on a pas de dns, contacter les
# switches dont on doit résoudre l'IP va être tendu).
try:
self.host = socket.gethostbyname_ex(host)[0]
except socket.gaierror:
self.host = host
self.__session = None
self.__version3 = False
self.__snmp_community = None
self.__snmp_version = None
self.__snmp_seclevel = None
self.__snmp_authprotocol = None
self.__snmp_authpassword = None
self.__snmp_secname = None
self.__snmp_privprotocol = None
self.__snmp_privpassword = None
def __get_session(self, version3=False):
"""Crée une session en cas de besoin, en vérifiant qu'une
session répondant aux besoins n'existe pas déjà."""
if version3 and self.__version3 and self.__session:
return self.__session
if not version3 and not self.__version3 and self.__session:
return self.__session
if version3 and (not self.__version3 or not self.__session):
self.__snmp_community = 'private'
self.__snmp_version = 3
self.__snmp_seclevel = 'authPriv'
self.__snmp_authprotocol = 'SHA'
self.__snmp_authpassword = secrets_new.get('snmp_authentication_pass')
self.__snmp_secname = 'crans'
self.__snmp_privprotocol = 'DES'
self.__snmp_privpassword = secrets_new.get('snmp_privacy_pass')
if not version3 and (self.__version3 or not self.__session):
self.__snmp_community = 'public'
self.__snmp_version = 1
self.__snmp_seclevel = 'noAuthNoPriv'
self.__snmp_authprotocol = 'DEFAULT'
self.__snmp_authpassword = ''
self.__snmp_secname = 'initial'
self.__snmp_privprotocol = 'DEFAULT'
self.__snmp_privpassword = ''
self.__version3 = version3
session = netsnmp.Session(Version=self.__snmp_version, DestHost=self.host,
Community=self.__snmp_community, SecLevel=self.__snmp_seclevel,
SecName=self.__snmp_secname, PrivProto=self.__snmp_privprotocol,
PrivPass=self.__snmp_privpassword, AuthProto=self.__snmp_authprotocol,
AuthPass=self.__snmp_authpassword)
return session
def walk(self, attribute):
"""Fait un walk.
Exemple:
Si je demande hpSwitchPortFdbAddress, le retour contiendra
des entrées ayant pour tag hpSwitchPortFdbAddress, pour iid
une éventuelle valeur (si pertinent), et pour val la valeur
associée."""
self.__session = self.__get_session()
# Crée une variable netsnmp exploitable pour walk.
__varbind = netsnmp.Varbind(attribute)
# La stocke dans une liste.
__varlist = netsnmp.VarList(__varbind)
# __varlist est modifiée en place par la méthode walk.
_ = self.__session.walk(__varlist)
return [
{
'tag': ret.tag,
'iid': ret.iid,
'val': ret.val,
}
for ret in __varlist
]
def set(self, list_of_vars):
"""Met à jour un attribut"""
# On passe en SNMPv3
self.__session = self.__get_session(True)
# On construit la varlist à balancer en SNMP
__varlist = [
netsnmp.Varbind(
tag=res['tag'],
iid=res['iid'],
val=res['val']
)
for res in list_of_vars
]
# Oui, c'est moche
__varlist = netsnmp.VarList(*__varlist)
__ret = self.__session.set(__varlist)
return __ret

392
gestion/hptools2/switch.py Normal file
View file

@ -0,0 +1,392 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""Outils principaux pour la description d'un switch"""
import socket
import netaddr
from .port import HPSwitchPort
from .snmp import SNMPClient
from .mac import format_mac, MACFactory
from .defaults import OPERSTATUS, ADMINSTATUS, ETHSPEED, REV_ETHSPEED
class HPSwitchFactory(object):
"""Factory stockant les switches"""
switches = {}
@classmethod
def get_switch(cls, switch):
"""Récupère un switch dans la factory"""
return cls.switches.get(switch, None)
@classmethod
def register_switch(cls, switch, switch_object):
"""Enregistre un switch dans la factory"""
cls.switches[switch] = switch_object
@classmethod
def get_switches(cls):
"""Récupère l'ensemble des switches dans la Factory"""
return cls.switches
class HPSwitch(object):
"""Classe décrivant un switch HP."""
def __new__(cls, switch):
"""Vérifie d'abord si un switch n'existe pas déjà.
L'idée est d'éviter de manipuler en parallèle des objets
dont les données deviendraient incohérentes. Donc,
lorsqu'on instancie un switch, celui-ci est référencé
par son hostname s'il existe, ou par le nom donné sinon."""
try:
__switch = socket.gethostbyname_ex(switch)[0]
except socket.gaierror:
__switch = switch
switch_object = HPSwitchFactory.get_switch(__switch)
if switch_object is None:
switch_object = super(HPSwitch, cls).__new__(cls)
HPSwitchFactory.register_switch(switch, switch_object)
return switch_object
def __init__(self, switch):
"""Récupère le nom, et un client snmp"""
self.switch = switch
self.client = SNMPClient(self.switch)
self.ports = {}
self.__build_ports_list()
def version(self):
"""Retourne les données relatives à la version du firmware"""
return self.client.walk('sysDescr')[0]['val']
def name(self):
"""Retourne un nom "standardisé" du switch
(aka g0, c4, )"""
return self.switch.split(".", 1)[0].replace('bat', '').replace('-', '').lower()
def __build_ports_list(self):
"""Construit via une requête SNMP la liste des ports pour le switch."""
__ret = self.client.walk('hpSwitchPhysicalPortEntry.2')
for entry in __ret:
self.ports[int(entry['iid'])] = HPSwitchPort(int(entry['iid']), self, int(entry['val']))
def flush_port(self, num):
"""Vide un port de ses MACs"""
if self.ports.get(num, None) is not None:
self.ports[num].flush_macs()
def flush_ports(self):
"""Vide un port de ses MACs"""
for port in self.ports.itervalues():
port.flush_macs()
def fetch_all_ports(self):
"""Récupère les données depuis les ports du switch, et
renvoie ce qui a un intérêt"""
self.flush_ports()
__ret = self.client.walk('hpSwitchPortFdbAddress')
__ret = [
(int(ret['iid'].split('.')[0]), ret['val'])
for ret in __ret
]
return __ret
def __populate_port(self, num):
"""Peuple le port numéro num"""
if self.ports.get(num, None) is not None:
_ = self.ports[num].get_macs(update=True)
def __populate_all_ports(self):
"""Peuple tous les ports."""
__ret = self.fetch_all_ports()
for (iid, val) in __ret:
if self.ports.get(iid, None) is not None:
self.ports[iid].append_mac(val)
def show_port_macs(self, num, populate=False):
"""Affiche les macs d'un port donné.
Si populate vaut True, fait le populate et affiche le
bousin."""
if populate:
self.__populate_port(num)
return (
[
mac.value
for mac in self.ports[num].get_macs()
],
self.ports[num].name()
)
def show_ports_macs(self, populate=False):
"""Affiche les ports et macs associées.
Si populate vaut True, fait le populate et affiche le
bousin."""
if populate:
self.__populate_all_ports()
return {
port.name(): [
mac.value
for mac in port.get_macs()
]
for port in self.ports.itervalues()
}
def find_mac(self, mac, populate=False):
"""Cherche une mac sur le switch"""
mac = format_mac(mac)
if populate:
self.__populate_all_ports()
# On boucle sur les macs dans la factory
__mac = MACFactory.get_mac(mac)
if __mac is not None:
# On boucle sur les parents (des ports) à la recherche
# de ceux qui appartiennent au switch courant.
__parents = []
for parent in __mac.parents.itervalues():
if parent.parent == self:
__parents.append(parent)
# Si on en a trouvé, on les retourne avec la mac.
if __parents:
return (__mac, __parents)
return None
def __flush_multicast(self):
"""Vide les infos de multicast sur les ports"""
for port in self.ports.itervalues():
port.flush_multicast()
def __update_multicast(self):
"""Fait la mise à jour des infos de multicast sur chaque port"""
# On commence par vider.
self.__flush_multicast()
# On fait un walk.
data = self.client.walk('hpIgmpStatsPortIndex2')
# Le dico est du format standard. L'ip est au milieu du champ iid, et
# le port est dans val.
for data_dict in data:
# En gros, le champ iid ressemble à 1.239.255.255.255.6, où 1 est un truc
# que je connais pas, et 6 le numéro du port.
data_ip = ".".join(data_dict['iid'].split('.')[1:5])
igmp_ip, igmp_port = netaddr.IPAddress(data_ip), int(data_dict['val'])
# Y a plus qu'à stocker
if self.ports.get(igmp_port, None) is not None:
self.ports[igmp_port].append_multicast(igmp_ip)
def get_multicast(self, multi_ip=None, update=True):
"""Permet de récupérer les informations sur les ports
pour lesquels le multicast est actif."""
__output = {}
if multi_ip is not None:
multi_ip = netaddr.IPAddress(multi_ip)
# En cas d'update
if update:
self.__update_multicast()
# On construit le résultat de façon identique dans le cas
# update ou non.
for port in self.ports.itervalues():
for multicast_ip in port.multicast:
__output.setdefault(multicast_ip, []).append(port)
# On filtre par l'ip si besoin.
if multi_ip is not None:
__output = __output[multi_ip]
return __output
def nb_prises(self):
"""Retourne le nombre de prises du switch.
On pourrait aussi faire un self.client.walk('mib-2.17.1.2')
et récupérer la clef "val" du premier élément de la liste
retournée."""
return len(self.ports)
def __update_oper_status(self):
"""Récupère le statut des ports du switch."""
__oper = self.client.walk('ifOperStatus')
for dico in __oper:
port, state = dico['iid'], dico['val']
if self.ports.get(int(port), None) is not None:
self.ports[int(port)].oper = state
def __update_admin_status(self):
"""Récupère l'état d'un port du switch."""
__admin = self.client.walk('ifAdminStatus')
for dico in __admin:
port, state = dico['iid'], dico['val']
if self.ports.get(int(port), None) is not None:
self.ports[int(port)].admin = state
def is_enabled(self, prise, update=True):
"""Vérifie si la prise est activée"""
if update:
self.__update_admin_status()
if self.ports.get(prise, None) is not None:
return ADMINSTATUS[self.ports[prise].admin]
else:
return {
port : ADMINSTATUS[port.admin]
for port in self.ports.itervalues()
}
def is_up(self, prise, update=True):
"""Vérifie si la prise est allumée actuellement
(en gros, s'il y a une mac dessus)"""
if update:
self.__update_oper_status()
if self.ports.get(prise, None) is not None:
return OPERSTATUS[self.ports[prise].oper]
else:
return {
port : OPERSTATUS[port.oper]
for port in self.ports.itervalues()
}
def set_enabled(self, prise, enabled=True):
"""Met le port à enabled/disabled"""
if enabled:
val = '1'
else:
val = '2'
command_dict = {
'iid': str(prise),
'tag': 'ifAdminStatus',
'val': val,
}
return self.client.set([
command_dict
])
def toggle_enabled(self, prise):
"""Alterne up/down"""
if self.is_enabled(prise) == ADMINSTATUS[1]:
enabled = False
else:
enabled = True
return self.set_enabled(prise, enabled)
def get_port_alias(self, prise):
"""Retourne le nom du port"""
if self.ports.get(prise, None) is None:
return ""
return self.ports[prise].get_alias()
def update_ports_aliases(self):
"""Récupère les aliases des ports et les affecte"""
data = self.client.walk('ifAlias')
for data_dict in data:
if self.ports.get(int(data_dict['iid']), None) is not None:
self.ports[int(data_dict['iid'])].set_alias(data_dict['val'])
def set_port_alias(self, prise, alias):
"""Affecte un nom au port"""
if self.ports.get(prise, None) is not None:
self.client.set([
{
'iid': str(prise),
'tag': 'ifAlias',
'val': alias,
}
])
self.ports[prise].set_alias(alias)
def get_eth_speed(self, prise):
"""Retourne le nom du port"""
if self.ports.get(prise, None) is None:
return ""
return REV_ETHSPEED.get(self.ports[prise].get_eth(), 'unknown')
def update_eth_speed(self):
"""Met à jour la vitesse de tous les ports"""
data = self.client.walk('hpSwitchPortFastEtherMode')
for data_dict in data:
if self.ports.get(int(data_dict['iid']), None) is not None:
self.ports[int(data_dict['iid'])].set_eth(data_dict['val'])
def set_eth_speed(self, prise, rate=0, dtype='AUTO'):
"""Affecte un nom au port"""
# On affecte une config spécifique en vitesse
if self.ports.get(prise, None) is not None:
self.client.set([
{
'iid': str(prise),
'tag': 'hpSwitchPortFastEtherMode',
'val': ETHSPEED[dtype][int(rate)],
}
])
self.ports[prise].set_eth(ETHSPEED[dtype][int(rate)])
def get_vlans(self, prise=None, update=True):
"""Récupère les vlans actifs sur une prise"""
# Si mise à jour
if update:
# On fait le ménage
for port in self.ports.itervalues():
port.purge_vlans()
# Et on recommence
data = self.client.walk('enterprises.11.2.14.11.5.1.7.1.15.3.1.1')
for data_dict in data:
vlan, iid = [int(res) for res in data_dict['iid'].split('.')]
if self.ports.get(iid, None) is not None:
self.ports[iid].add_vlan(vlan)
# Si la prise vaut none, on file tout, sinon juste elle.
if prise is not None:
if self.ports.get(prise, None) is not None:
return self.ports[prise].get_vlans()
else:
return {
iid: port.get_vlans()
for (iid, port) in self.ports.iteritems()
}

124
gestion/hptools2/tools.py Normal file
View file

@ -0,0 +1,124 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""Fournit des outils et fonctions appelables au besoin"""
from gestion import annuaires_pg
from multiprocessing import Process, Manager
from .switch import HPSwitch
from .mac import MACFactory, format_mac
def filter_uplink(switch, stuff):
"""Filtre les prises uplink d'un retour.
stuff est une liste de la forme
[(port_id, mac), ...]"""
sortie = []
# Retourne "batg", "4.adm.crans.org", par exemple.
bat, num = switch.split('-', 1)
# On filtre ce qui n'est pas utile.
bat = bat[-1]
num = int(num[0])
# On récupère les infos utiles.
uplink = annuaires_pg.uplink_prises[bat]
gen_num_prise = 100 * num
for (port_id, mac) in stuff:
num_prise = gen_num_prise + port_id
if not num_prise in uplink:
sortie.append((port_id, mac))
return sortie
# +--------------------------------------------------------+
# | Mac Tracking Functions |
# +--------------------------------------------------------+
"""Ces fonctions servent à tracker une mac sur le réseau.
La fonction trace_mac s'occupe de ce travail. Elle utilise
la librairie multiprocessing pour spawner un process par
switch, et aller récupérer auprès de ceux-ci la liste des
MACs connectées, et les ports allant bien.
Multiprocessing ne mettant pas en place du partage de variable
par défaut, les objets retournés le sont via un Manager, dans
un dico, sans structure complexe.
Une solution dans laquelle les switches seraient renvoyés dans
leur structure python existe, mais elle est plus coûteuse,
et peu utile dans notre cas. (l'overhead engendré par la méthode
à base de dicos et régénération dans le processus parent est
epsilonesque)"""
def fetch_all_ports(switch, output):
"""Récupère l'ensemble des ports d'un switch, avec les MACS
dessus."""
sw = HPSwitch(switch)
# output est un Manager().dict()
__stuff = sw.fetch_all_ports()
__stuff = filter_uplink(switch, __stuff)
output[switch] = __stuff
def populate_all_switches():
"""Remplit l'ensemble des switches avec les MACS qui sont
présentes sur leurs ports"""
switches = annuaires_pg.all_switchs()
hp_switches = {
switch : HPSwitch(switch)
for switch in switches
}
processes = {}
# La sortie des appels de fetch_all_ports sera écrite dans ce dico.
# On évitera la concurrence en utilisant le nom du switch comme
# séparateur
output = Manager().dict()
# Dans une première boucle, on crée les switches. Et on met
# les processes en mode actif.
for switch in switches:
hp_switches[switch].flush_ports()
processes[switch] = Process(target=fetch_all_ports, args=(switch, output), name=switch)
processes[switch].start()
# On fait la jointure des processes dans une seconde
# boucle, pour s'assurer que les processes ont bien
# tous été lancés avant de commencer à les sonder.
for switch in switches:
processes[switch].join()
for switch in switches:
if output[switch] is not None:
for (iid, val) in output[switch]:
if hp_switches[switch].ports.get(iid, None) is not None:
hp_switches[switch].ports[iid].append_mac(val)
else:
print "Output for switch %s is None." % (switch,)
def trace_mac(mac, in_all_switches=False):
"""Cherche une MAC. Si in_all_switches est à True, commence
par instancier tous les switches, et à les peupler.
Cette méthode est assez agressive, il faut l'utiliser avec
précaution."""
if in_all_switches:
populate_all_switches()
mac = format_mac(mac)
# On boucle sur les macs dans la factory
__mac = MACFactory.get_mac(mac)
if __mac is not None:
# On boucle sur les parents (des ports) à la recherche
# de ceux qui appartiennent au switch courant.
__parents = []
for parent in __mac.parents.itervalues():
__parents.append(parent)
# Si on en a trouvé, on les retourne avec la mac.
if __parents:
return (__mac, __parents)
return None