Découverte de topologie réseau pyATS
Démo en direct : découverte automatisée des ABR, cartographie par traceroute et enrichissement des noms d'hôte
Cette démo montre un flux de découverte automatisé que j'ai développé avec Python, pyATS et Genie. Le script se connecte aux routeurs Cisco, identifie les routeurs ABR OSPF, lance des traceroutes, puis enrichit les résultats avec des noms d'hôte issus d'une source d'inventaire externe. Ce dernier point compte plus qu'il n'y paraît: un traceroute rempli d'adresses IP reste utile, mais dès qu'on peut rattacher les sauts à des noms reconnaissables, la topologie devient beaucoup plus facile à lire, à vérifier et à partager. Les routeurs sources apparaissent à gauche, les ABR découverts à droite, et les sauts intermédiaires au centre. Vous pouvez basculer entre la vue IP et la vue nom d'hôte, cliquer sur un nœud pour le détail, ou lancer l'animation pour voir le flux complet.
Exemple de code Python
# Chargement de l'inventaire et du mapping depuis SharePoint / Excel inventory_df = pd.read_csv("Device Inventory.csv") hostname_df = pd.read_excel("Node_to_IP.xlsx") ip_to_name = dict(zip(hostname_df["IP"], hostname_df["Hostname"])) # Chargement du testbed pyATS et itération sur tous les routeurs sources testbed = load("testbed.yaml") all_abr_dfs, all_trace_dfs = [], [] for device_name, device in testbed.devices.items(): try: device.connect(log_stdout=False) # Étape 1: découverte des ABR depuis la table OSPF raw_abr = device.execute( "show ip ospf border-routers | i ABR" ) abr_df = abr_cmd_parser(raw_abr, device_name, ip_to_name) all_abr_dfs.append(abr_df) # Étape 2: traceroute vers chaque ABR découvert for _, row in abr_df.iterrows(): raw_trace = device.execute( f"traceroute {row['abr_ip']}" ) trace_df = traceroute_parser( raw_trace, ip_to_name, row["abr_ip"], row["abr_name"], device_name ) all_trace_dfs.append(trace_df) except Exception as e: print(f"Échec sur {device_name}: {e}") finally: device.disconnect() # Export des résultats enrichis vers Excel + SharePoint final_df = pd.concat(all_trace_dfs, ignore_index=True) final_df.to_excel("traceroute_results.xlsx", index=False)
def abr_cmd_parser(raw_output, device_name, ip_to_short_name_dict): """Analyse la sortie 'show ip ospf border-routers | i ABR'. Extrait les IP des ABR via regex, résout les noms d'hôte depuis le dict d'inventaire.""" ip_pattern = r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' records = [] for line in raw_output.splitlines(): ips = re.findall(ip_pattern, line) for ip in ips: hostname = ip_to_short_name_dict.get(ip, "INCONNU") records.append({ "device": device_name, "abr_ip": ip, "abr_name": hostname, }) return pd.DataFrame(records)
def traceroute_parser(traceroute_output, ip_to_short_name_dict, destination_ip, destination_abr, device_name): """Analyse la sortie traceroute. Enrichit chaque IP de saut avec le nom d'hôte provenant du dict d'inventaire, pour rendre les IP opaques plus lisibles.""" ip_pattern = r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' records = [] hop_num = 0 for line in traceroute_output.splitlines(): ips = re.findall(ip_pattern, line) if ips: hop_num += 1 hop_ip = ips[0] hostname = ip_to_short_name_dict.get(hop_ip, "INCONNU") records.append({ "device": device_name, "hop": hop_num, "hop_ip": hop_ip, "hop_name": hostname, "dest_ip": destination_ip, "dest_abr": destination_abr, }) return pd.DataFrame(records)