matrice de routage ajouté | reformatage
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
Cet outil permet de **parser les configurations de différents types de firewalls** (Palo Alto, Stormshield, Forcepoint) et de **convertir ces informations en un format JSON normalisé basé sur des modèles OpenConfig en YANG**.
|
Cet outil permet de **parser les configurations de différents types de firewalls** (Palo Alto, Stormshield, Forcepoint) et de **convertir ces informations en un format JSON normalisé basé sur des modèles OpenConfig en YANG**.
|
||||||
|
|
||||||
Il fournit également la possibilité de générer une **matrice de flux au format Excel** pour visualiser les communications et règles de trafic dans l’infrastructure.
|
Il fournit également la possibilité de générer une **matrice de flux au format Excel** pour visualiser les communications et règles de trafic dans l’infrastructure.
|
||||||
|
Il founit également la possibilité de générer une **matrice de routage au format Excel** pour visualiser les routes statiques dans l’infrastructure.
|
||||||
|
|
||||||
## Fonctionnalités principales
|
## Fonctionnalités principales
|
||||||
|
|
||||||
@@ -41,6 +42,10 @@ Il fournit également la possibilité de générer une **matrice de flux au form
|
|||||||
- les règles de sécurité
|
- les règles de sécurité
|
||||||
- les communications entre objets et groupes d’adresses/services.
|
- les communications entre objets et groupes d’adresses/services.
|
||||||
|
|
||||||
|
6. **Génération de matrices de routage**
|
||||||
|
- Script Python qui utilise le JSON normalisé pour générer automatiquement une matrice Excel détaillant :
|
||||||
|
- les routes statiques
|
||||||
|
|
||||||
## Utilisation
|
## Utilisation
|
||||||
|
|
||||||
### Pré-requis
|
### Pré-requis
|
||||||
@@ -50,21 +55,20 @@ python -m venv .venv
|
|||||||
|
|
||||||
pip install -r .\src\requierements.txt
|
pip install -r .\src\requierements.txt
|
||||||
```
|
```
|
||||||
- Mettre le/les fichier(s) de configurations dans le dossier `/src/input/`
|
|
||||||
- Modifier le fichier `site.json` de données dans `/src/data/`
|
|
||||||
|
|
||||||
#### Commandes principales
|
#### Commandes principales
|
||||||
```bash
|
```bash
|
||||||
python3 .\src\main.py stormshield .\src\input\backup\ -m
|
python3 .\src\main.py stormshield .\src\input\backup\ -f -r
|
||||||
python3 .\src\main.py paloalto .\src\input\nomfichier -m
|
python3 .\src\main.py paloalto .\src\input\nomfichier -f -r
|
||||||
python3 .\src\main.py forcepoint .\src\input\nomfichier -m
|
python3 .\src\main.py forcepoint .\src\input\nomfichier -f -r
|
||||||
```
|
```
|
||||||
#### Options
|
#### Options
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| -o [nom_fichier] | Spécifie le nom du fichier JSON de sortie (optionnel)
|
| -o [nom_fichier] | Spécifie le nom du fichier JSON de sortie (optionnel)
|
||||||
| -m | Génère un rapport Excel de type matrice de flux (optionnel)
|
| -f | Génère un rapport Excel de type matrice de flux (optionnel)
|
||||||
|
| -r | Génère un rapport Excel de type matrice de routage (optionnel)
|
||||||
---
|
---
|
||||||
|
|
||||||
## Arborescence du projet
|
## Arborescence du projet
|
||||||
|
|||||||
@@ -4,21 +4,23 @@ import time
|
|||||||
from scripts.json_PaloAlto import generate_json_paloalto
|
from scripts.json_PaloAlto import generate_json_paloalto
|
||||||
from scripts.json_Stormshield import generate_json_stormshield
|
from scripts.json_Stormshield import generate_json_stormshield
|
||||||
from scripts.json_Forcepoint import generate_json_forcepoint
|
from scripts.json_Forcepoint import generate_json_forcepoint
|
||||||
from scripts.export_excel import export_to_excel
|
from scripts.export_matrice_flux import export_to_excel as export_flux_to_excel
|
||||||
|
from scripts.export_matrice_routage import export_to_excel as export_routing_to_excel
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) < 3:
|
if len(sys.argv) < 3:
|
||||||
print("Usage: python3 src/main.py <firewall_type>[paloalto|stormshield|forcepoint] <input_directory/file> [-o <output_file>] [-m]")
|
print("Usage: python3 src/main.py <firewall_type>[paloalto|stormshield|forcepoint] <input_directory/file> [-o <output_file>] [-f] [-r]")
|
||||||
print("Options:")
|
print("Options:")
|
||||||
print(" <firewall_type> Type of firewall to process (paloalto|stormshield|forcepoint)")
|
print(" <firewall_type> Type of firewall to process (paloalto|stormshield|forcepoint)")
|
||||||
print(" -o <output_file> Specify output JSON file name (optional)")
|
print(" -o <output_file> Specify output JSON file name (optional)")
|
||||||
print(" -m Generate Excel report (optional)")
|
print(" -f Generate matrix flux report (optional)")
|
||||||
|
print(" -r Generate routing matrix report (optional)")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
firewall_type = sys.argv[1].lower()
|
firewall_type = sys.argv[1].lower()
|
||||||
input_data = sys.argv[2]
|
input_data = sys.argv[2]
|
||||||
|
input_path = "src/input/"
|
||||||
output_path = "src/output/"
|
output_path = "src/output/"
|
||||||
|
|
||||||
if "-o" in sys.argv:
|
if "-o" in sys.argv:
|
||||||
o_index = sys.argv.index("-o")
|
o_index = sys.argv.index("-o")
|
||||||
if o_index + 1 < len(sys.argv):
|
if o_index + 1 < len(sys.argv):
|
||||||
@@ -41,20 +43,36 @@ def main():
|
|||||||
print("Erreur: type de firewall inconnu. Utilisez 'paloalto', 'stormshield' ou 'forcepoint'.")
|
print("Erreur: type de firewall inconnu. Utilisez 'paloalto', 'stormshield' ou 'forcepoint'.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if "-m" in sys.argv:
|
if "-f" in sys.argv:
|
||||||
print(f"\nGénération de l'export Excel...")
|
print(f"\nGénération de l'export la matrice flux...")
|
||||||
if "-o" in sys.argv:
|
if "-o" in sys.argv:
|
||||||
o_index = sys.argv.index("-o")
|
o_index = sys.argv.index("-o")
|
||||||
if o_index + 1 < len(sys.argv):
|
if o_index + 1 < len(sys.argv):
|
||||||
output_file_excel = os.path.join(f"{output_path}matrice_{firewall_type}_{sys.argv[o_index + 1]}.xlsx")
|
output_file_excel = os.path.join(f"{output_path}matrice_flux_{firewall_type}_{sys.argv[o_index + 1]}.xlsx")
|
||||||
else:
|
else:
|
||||||
print("Erreur: nom de fichier de sortie manquant après '-o'.")
|
print("Erreur: nom de fichier de sortie manquant après '-o'.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
timestamp = time.strftime("%Y%m%d")
|
timestamp = time.strftime("%Y%m%d")
|
||||||
output_file_excel = f"{output_path}matrice_{firewall_type}_{timestamp}.xlsx"
|
output_file_excel = f"{output_path}matrice_flux_{firewall_type}_{timestamp}.xlsx"
|
||||||
|
|
||||||
excel_file = export_to_excel(output_file_json, output_file_excel)
|
excel_file = export_flux_to_excel(output_file_json, output_file_excel)
|
||||||
|
print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file_json}\n - Excel: {excel_file}")
|
||||||
|
|
||||||
|
if "-r" in sys.argv:
|
||||||
|
print(f"\nGénération de l'export la matrice routage...")
|
||||||
|
if "-o" in sys.argv:
|
||||||
|
o_index = sys.argv.index("-o")
|
||||||
|
if o_index + 1 < len(sys.argv):
|
||||||
|
output_file_excel = os.path.join(f"{output_path}matrice_routage_{firewall_type}_{sys.argv[o_index + 1]}.xlsx")
|
||||||
|
else:
|
||||||
|
print("Erreur: nom de fichier de sortie manquant après '-o'.")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
timestamp = time.strftime("%Y%m%d")
|
||||||
|
output_file_excel = f"{output_path}matrice_routage_{firewall_type}_{timestamp}.xlsx"
|
||||||
|
|
||||||
|
excel_file = export_routing_to_excel(output_file_json, output_file_excel)
|
||||||
print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file_json}\n - Excel: {excel_file}")
|
print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file_json}\n - Excel: {excel_file}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import json
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import ipaddress
|
||||||
|
import time
|
||||||
|
from collections import deque
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
from scripts.style_excel.style_matrice_routage import style_matrice_routage
|
||||||
|
|
||||||
|
def is_pure_cidr(value):
|
||||||
|
try:
|
||||||
|
ipaddress.ip_network(value, strict=False)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def export_to_excel(json_file_path, output_file_excel):
|
||||||
|
"""
|
||||||
|
Export firewall data from JSON to Excel
|
||||||
|
Args:
|
||||||
|
json_file_path: Path to the JSON file to process
|
||||||
|
output_file_excel: Path to the output Excel file
|
||||||
|
"""
|
||||||
|
|
||||||
|
with open(json_file_path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
address_objects = {}
|
||||||
|
addr_objs = data.get(
|
||||||
|
"custom-firewall-objects:firewall-objects", {}
|
||||||
|
).get("address", [])
|
||||||
|
|
||||||
|
for obj in addr_objs:
|
||||||
|
name = obj.get("name")
|
||||||
|
ip_netmask = obj.get("config", {}).get("ip_netmask")
|
||||||
|
|
||||||
|
if name and ip_netmask:
|
||||||
|
address_objects[name] = ip_netmask
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
network_instances = data.get(
|
||||||
|
"openconfig-network-instance:network-instances", {}
|
||||||
|
).get("network-instance", [])
|
||||||
|
|
||||||
|
for ni in network_instances:
|
||||||
|
equipement = ni.get("name", "")
|
||||||
|
|
||||||
|
protocols = ni.get("protocols", {}).get("protocol", [])
|
||||||
|
for proto in protocols:
|
||||||
|
if proto.get("identifier") != "STATIC":
|
||||||
|
continue
|
||||||
|
|
||||||
|
static_routes = proto.get("static-routes", {}).get("static", [])
|
||||||
|
for route in static_routes:
|
||||||
|
prefix = route.get("prefix", "")
|
||||||
|
|
||||||
|
# Découpage réseau / masque
|
||||||
|
resolved_prefix = prefix
|
||||||
|
if not is_pure_cidr(prefix):
|
||||||
|
resolved_prefix = address_objects.get(prefix, prefix)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
net = ipaddress.ip_network(resolved_prefix, strict=False)
|
||||||
|
reseau = str(net.network_address)
|
||||||
|
masque = str(net.netmask)
|
||||||
|
except ValueError:
|
||||||
|
reseau = resolved_prefix
|
||||||
|
masque = ""
|
||||||
|
|
||||||
|
next_hops = route.get("next-hops", {}).get("next-hop", [])
|
||||||
|
for nh in next_hops:
|
||||||
|
nh_config = nh.get("config", {})
|
||||||
|
|
||||||
|
rows.append({
|
||||||
|
"Equipement": equipement,
|
||||||
|
"Réseau destination": reseau,
|
||||||
|
"Masque": masque,
|
||||||
|
"Next Hop": nh_config.get("next-hop", ""),
|
||||||
|
"Metrique": nh_config.get("metric", ""),
|
||||||
|
"Commentaire": prefix
|
||||||
|
})
|
||||||
|
|
||||||
|
df = pd.DataFrame(rows)
|
||||||
|
|
||||||
|
with pd.ExcelWriter(output_file_excel, engine="openpyxl") as writer:
|
||||||
|
df.to_excel(writer,sheet_name="Routes statiques",index=False,startrow=1)
|
||||||
|
|
||||||
|
wb = load_workbook(output_file_excel)
|
||||||
|
if "Sheet1" in wb.sheetnames:
|
||||||
|
del wb["Sheet1"]
|
||||||
|
|
||||||
|
style_matrice_routage(wb["Routes statiques"])
|
||||||
|
|
||||||
|
wb.save(output_file_excel)
|
||||||
|
print(f"✓ Export Excel OK: {output_file_excel}")
|
||||||
|
return output_file_excel
|
||||||
@@ -196,59 +196,95 @@ class StormshieldParser(ParserMixin):
|
|||||||
if not os.path.exists(route_path):
|
if not os.path.exists(route_path):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
virtual_routers = {}
|
||||||
|
default_route = None
|
||||||
|
|
||||||
section = None
|
section = None
|
||||||
with open(route_path, encoding="utf-8", errors="ignore") as f:
|
with open(route_path, encoding="utf-8", errors="ignore") as f:
|
||||||
for raw_line in f:
|
for raw_line in f:
|
||||||
line = raw_line.strip()
|
line = raw_line.strip()
|
||||||
if not line or line.startswith("#"):
|
if not line or line.startswith("off") or line.startswith("#"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if line.startswith("[") and line.endswith("]"):
|
if line.startswith("[") and line.endswith("]"):
|
||||||
section = line.strip("[]")
|
section = line.strip("[]")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# ===== Default route =====
|
||||||
if section == "Config" and line.startswith("DefaultRoute="):
|
if section == "Config" and line.startswith("DefaultRoute="):
|
||||||
self.config["default_route"] = line.split("=", 1)[1].strip()
|
default_route = line.split("=", 1)[1].strip()
|
||||||
|
|
||||||
elif section == "StaticRoutes" and not line.startswith("#"):
|
# ===== Static routes =====
|
||||||
parts = line.split(",")
|
elif section == "StaticRoutes":
|
||||||
if len(parts) >= 2:
|
parts = [p.strip() for p in line.split(",")]
|
||||||
self.config["static_routes"].append({
|
if len(parts) < 2:
|
||||||
"destination": parts[0],
|
continue
|
||||||
"interface": parts[1],
|
|
||||||
"extra": parts[2:] if len(parts) > 2 else []
|
|
||||||
})
|
|
||||||
|
|
||||||
static_routes = []
|
destination = parts[0]
|
||||||
if self.config["default_route"]:
|
|
||||||
static_routes.append(
|
vr_name = None
|
||||||
|
interface = None
|
||||||
|
next_hop_ip = None
|
||||||
|
|
||||||
|
# Format : VR->NextHop
|
||||||
|
if "->" in parts[1]:
|
||||||
|
vr_name, next_hop_ip = map(str.strip, parts[1].split("->", 1))
|
||||||
|
interface = vr_name
|
||||||
|
else:
|
||||||
|
vr_name = parts[1]
|
||||||
|
interface = parts[1]
|
||||||
|
|
||||||
|
# Création VR si absent
|
||||||
|
if vr_name not in virtual_routers:
|
||||||
|
virtual_routers[vr_name] = {
|
||||||
|
"interfaces": set(),
|
||||||
|
"static_routes": []
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual_routers[vr_name]["interfaces"].add(interface)
|
||||||
|
|
||||||
|
static_route = StaticRoute(
|
||||||
|
name=f"{vr_name}-static-{len(virtual_routers[vr_name]['static_routes']) + 1}",
|
||||||
|
destination=destination,
|
||||||
|
metric=None,
|
||||||
|
next_vr=None,
|
||||||
|
next_hop_ip=next_hop_ip,
|
||||||
|
interface=interface,
|
||||||
|
bfd_profile=None
|
||||||
|
)
|
||||||
|
|
||||||
|
virtual_routers[vr_name]["static_routes"].append(static_route)
|
||||||
|
|
||||||
|
# ===== Default VR (route par défaut) =====
|
||||||
|
if default_route:
|
||||||
|
vr_name = "default"
|
||||||
|
virtual_routers.setdefault(vr_name, {
|
||||||
|
"interfaces": set(),
|
||||||
|
"static_routes": []
|
||||||
|
})
|
||||||
|
|
||||||
|
virtual_routers[vr_name]["static_routes"].insert(
|
||||||
|
0,
|
||||||
StaticRoute(
|
StaticRoute(
|
||||||
name="default-route",
|
name="default-route",
|
||||||
destination="0.0.0.0/0",
|
destination="0.0.0.0/0",
|
||||||
metric=1,
|
metric=1,
|
||||||
next_hop_ip=self.config["default_route"],
|
next_vr=None,
|
||||||
interface=None
|
next_hop_ip=default_route,
|
||||||
|
interface=None,
|
||||||
|
bfd_profile=None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
for idx, route in enumerate(self.config["static_routes"]):
|
# ===== Construction finale =====
|
||||||
static_routes.append(
|
self.config["virtual_routers"] = [
|
||||||
StaticRoute(
|
VirtualRouter(
|
||||||
name=f"static-{idx+1}",
|
name=vr_name,
|
||||||
destination=route["destination"],
|
interfaces=list(data["interfaces"]),
|
||||||
metric=1,
|
static_routes=data["static_routes"]
|
||||||
next_hop_ip=None,
|
|
||||||
interface=route["interface"]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
for vr_name, data in virtual_routers.items()
|
||||||
vr = VirtualRouter(
|
]
|
||||||
name="default-vr",
|
|
||||||
interfaces=[r["interface"] for r in self.config["static_routes"]],
|
|
||||||
static_routes=static_routes
|
|
||||||
)
|
|
||||||
|
|
||||||
self.config["virtual_routers"] = [vr]
|
|
||||||
|
|
||||||
# def _parse_slotinfo_file(self):
|
# def _parse_slotinfo_file(self):
|
||||||
# path = os.path.join(self.base_dir, "Filter", "slotinfo")
|
# path = os.path.join(self.base_dir, "Filter", "slotinfo")
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
from openpyxl.styles import Alignment, PatternFill, Font, Border, Side
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
def style_matrice_routage(ws):
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
"Equipement",
|
||||||
|
"Réseau destination",
|
||||||
|
"Masque",
|
||||||
|
"Next Hop",
|
||||||
|
"Metrique",
|
||||||
|
"Commentaire"
|
||||||
|
]
|
||||||
|
|
||||||
|
header_fill = PatternFill("solid", fgColor="1F4E78")
|
||||||
|
header_font = Font(color="FFFFFF", bold=True)
|
||||||
|
header_alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
|
||||||
|
cell_alignment_left = Alignment(horizontal="left", vertical="center")
|
||||||
|
cell_alignment_center = Alignment(horizontal="center", vertical="center")
|
||||||
|
|
||||||
|
thin_border = Border(
|
||||||
|
left=Side(style="thin"),
|
||||||
|
right=Side(style="thin"),
|
||||||
|
top=Side(style="thin"),
|
||||||
|
bottom=Side(style="thin")
|
||||||
|
)
|
||||||
|
|
||||||
|
for col_idx, header in enumerate(headers, start=1):
|
||||||
|
cell = ws.cell(row=1, column=col_idx, value=header)
|
||||||
|
cell.fill = header_fill
|
||||||
|
cell.font = header_font
|
||||||
|
cell.alignment = header_alignment
|
||||||
|
cell.border = thin_border
|
||||||
|
|
||||||
|
column_widths = {
|
||||||
|
"A": 22, # Equipement
|
||||||
|
"B": 22, # Réseau destination
|
||||||
|
"C": 18, # Masque
|
||||||
|
"D": 20, # Next Hop
|
||||||
|
"E": 12, # Metrique
|
||||||
|
"F": 30 # Commentaire
|
||||||
|
}
|
||||||
|
|
||||||
|
for col_letter, width in column_widths.items():
|
||||||
|
ws.column_dimensions[col_letter].width = width
|
||||||
|
|
||||||
|
for row in ws.iter_rows(min_row=2, max_row=ws.max_row, max_col=len(headers)):
|
||||||
|
for cell in row:
|
||||||
|
cell.border = thin_border
|
||||||
|
|
||||||
|
if cell.column in (1, 2, 4, 6):
|
||||||
|
cell.alignment = cell_alignment_left
|
||||||
|
else:
|
||||||
|
cell.alignment = cell_alignment_center
|
||||||
|
|
||||||
|
ws.freeze_panes = "A2"
|
||||||
|
ws.auto_filter.ref = ws.dimensions
|
||||||
57
Parseurs_logs_Switch/README.md
Normal file
57
Parseurs_logs_Switch/README.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Parseur de Logs HPE 5130
|
||||||
|
|
||||||
|
Ce projet permet d'extraire automatiquement des informations pertinentes depuis les logs de switchs **HPE 5130**, notamment les connexions entre les switchs d'accès et le cœur de réseau. Les résultats sont exportés sous deux formats :
|
||||||
|
|
||||||
|
- Un **fichier Excel** listant les interfaces côté accès et cœur.
|
||||||
|
- Un **diagramme Mermaid** simplifié représentant les connexions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧰 Fonctionnalités
|
||||||
|
|
||||||
|
- Extraction des données utiles depuis les fichiers `.log` au format **JSON**.
|
||||||
|
- Génération d’un **rapport Excel (`uplink_report.xlsx`)** contenant :
|
||||||
|
- Les informations des interfaces côté accès connectées au cœur.
|
||||||
|
- Les informations des interfaces du cœur.
|
||||||
|
- Création d’un **fichier Mermaid (`mermaid.md`)** représentant graphiquement les connexions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Lancement
|
||||||
|
|
||||||
|
Pour lancer l’outil, placez-vous dans le dossier du projet et exécutez la commande suivante :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python .\src\main.py [fichier logs du cœur]
|
||||||
|
```
|
||||||
|
#### 💡 Exemple : python .\src\main.py .\src\logs\core_switch.log
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Arborescence du projet
|
||||||
|
```makefile
|
||||||
|
C:.
|
||||||
|
└───src
|
||||||
|
├───logs # Contient les fichiers .log des switchs (à déposer ici)
|
||||||
|
├───output # Contient les fichiers générés (Excel et Mermaid)
|
||||||
|
├───scripts # Contient les scripts de traitement
|
||||||
|
│ ├───extract_json.py # Extraction des données en JSON
|
||||||
|
│ ├───format.py # Formatage des données
|
||||||
|
│ ├───mermaid.py # Formatage et génération du fichier Mermaid
|
||||||
|
│ └───parse_uplinks.py # Création du fichier Excel
|
||||||
|
└───main.py # Programme de lancement
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Instructions d'utilisation
|
||||||
|
1. Déposer les fichiers .log dans le dossier src/logs.
|
||||||
|
2. Lancer le script main.py avec en argument le log du switch cœur.
|
||||||
|
3. Les résultats seront disponibles dans le dossier src/output :
|
||||||
|
- uplink_report.xlsx
|
||||||
|
- mermaid.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ Remarque
|
||||||
|
- Bien penser à **mettre les fichiers de logs avec l'extension `.log`** dans le dossier `src/logs`.
|
||||||
@@ -7,7 +7,7 @@ from datetime import datetime
|
|||||||
|
|
||||||
class InterfaceParser:
|
class InterfaceParser:
|
||||||
"""
|
"""
|
||||||
Parses 'display interface' command output to extract interface details.
|
Parse les données de la commande 'display interface' pour extraire les informations des interfaces.
|
||||||
"""
|
"""
|
||||||
def parse(self, content: str) -> dict:
|
def parse(self, content: str) -> dict:
|
||||||
interfaces = {}
|
interfaces = {}
|
||||||
@@ -72,11 +72,11 @@ class InterfaceParser:
|
|||||||
|
|
||||||
class LinkAggregationParser:
|
class LinkAggregationParser:
|
||||||
"""
|
"""
|
||||||
Parses 'display link-aggregation verbose' command output.
|
Parse les données de la commande 'display link-aggregation verbose'.
|
||||||
Extracts link counts, MAC mappings, and bridge mappings.
|
Extrait les comptes de liens, les mappages MAC et les mappages de pont.
|
||||||
"""
|
"""
|
||||||
def _convert_port_name(self, port_name: str) -> str:
|
def _convert_port_name(self, port_name: str) -> str:
|
||||||
"""Converts the port name to a standardized format."""
|
"""Convertit le nom du port en un format standardisé."""
|
||||||
port_name = re.sub(r'\([A-Z]\)', '', port_name)
|
port_name = re.sub(r'\([A-Z]\)', '', port_name)
|
||||||
patterns = [
|
patterns = [
|
||||||
(r'^XGE(\d+/\d+/\d+)$', r'Ten-GigabitEthernet\1'),
|
(r'^XGE(\d+/\d+/\d+)$', r'Ten-GigabitEthernet\1'),
|
||||||
@@ -175,11 +175,11 @@ class LinkAggregationParser:
|
|||||||
|
|
||||||
class ConfigurationParser:
|
class ConfigurationParser:
|
||||||
"""
|
"""
|
||||||
Parses 'display current-configuration' command output.
|
Parse les données de la commande 'display current-configuration'.
|
||||||
Extracts interface configurations and general switch configurations.
|
Extrait les configurations des interfaces et les configurations générales du commutateur.
|
||||||
"""
|
"""
|
||||||
def _parse_vlan_ranges(self, vlan_string: str) -> list:
|
def _parse_vlan_ranges(self, vlan_string: str) -> list:
|
||||||
"""Parses a VLAN string and returns a list of unique VLANs."""
|
"""Parse un string de VLAN et renvoie une liste de VLANs uniques."""
|
||||||
vlans = set()
|
vlans = set()
|
||||||
parts = vlan_string.split()
|
parts = vlan_string.split()
|
||||||
i = 0
|
i = 0
|
||||||
@@ -370,7 +370,7 @@ class ConfigurationParser:
|
|||||||
|
|
||||||
class DeviceInfoParser:
|
class DeviceInfoParser:
|
||||||
"""
|
"""
|
||||||
Parses 'display device manuinfo' command output to extract MAC addresses.
|
Parse les données de la commande 'display device manuinfo' pour extraire les adresses MAC.
|
||||||
"""
|
"""
|
||||||
def parse(self, content: str) -> list[str]:
|
def parse(self, content: str) -> list[str]:
|
||||||
mac_addresses = []
|
mac_addresses = []
|
||||||
@@ -394,8 +394,8 @@ class DeviceInfoParser:
|
|||||||
|
|
||||||
class LogFileProcessor:
|
class LogFileProcessor:
|
||||||
"""
|
"""
|
||||||
Manages the parsing of a single log file, coordinating different parsers
|
Manage le parsing d'un fichier journal unique, en coordonnant différents analyseurs
|
||||||
and merging their results into a unified JSON structure.
|
et en fusionnant leurs résultats dans une structure JSON unifiée.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.interface_parser = InterfaceParser()
|
self.interface_parser = InterfaceParser()
|
||||||
@@ -404,7 +404,7 @@ class LogFileProcessor:
|
|||||||
self.device_info_parser = DeviceInfoParser()
|
self.device_info_parser = DeviceInfoParser()
|
||||||
|
|
||||||
def _extract_sections(self, file_content: str) -> dict:
|
def _extract_sections(self, file_content: str) -> dict:
|
||||||
"""Extracts different sections from the log file content."""
|
"""Extrait les sections pertinentes du contenu du fichier journal en fonction des motifs définis."""
|
||||||
sections = {}
|
sections = {}
|
||||||
current_section = None
|
current_section = None
|
||||||
lines = file_content.splitlines()
|
lines = file_content.splitlines()
|
||||||
@@ -441,14 +441,14 @@ class LogFileProcessor:
|
|||||||
return sections
|
return sections
|
||||||
|
|
||||||
def _extract_switch_name(self, content: str) -> str | None:
|
def _extract_switch_name(self, content: str) -> str | None:
|
||||||
"""Extracts the switch name from the log file content."""
|
"""Extrait le nom du commutateur à partir du contenu du fichier journal."""
|
||||||
sysname_match = re.search(r"(?m)^\s*sysname\s+(.+)$", content)
|
sysname_match = re.search(r"(?m)^\s*sysname\s+(.+)$", content)
|
||||||
if sysname_match:
|
if sysname_match:
|
||||||
return sysname_match.group(1).strip()
|
return sysname_match.group(1).strip()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _merge_configuration_into_interfaces(self, interfaces: dict, interfaces_config: dict):
|
def _merge_configuration_into_interfaces(self, interfaces: dict, interfaces_config: dict):
|
||||||
"""Merges configuration details into the parsed interface data."""
|
"""Merge les détails de configuration dans les données d'interface analysées."""
|
||||||
for interface_name, config in interfaces_config.items():
|
for interface_name, config in interfaces_config.items():
|
||||||
if interface_name in interfaces:
|
if interface_name in interfaces:
|
||||||
interfaces[interface_name].update({
|
interfaces[interface_name].update({
|
||||||
@@ -480,13 +480,13 @@ class LogFileProcessor:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _merge_link_aggregation_counts(self, interfaces: dict, link_counts: dict):
|
def _merge_link_aggregation_counts(self, interfaces: dict, link_counts: dict):
|
||||||
"""Merges link aggregation counts into the interface data."""
|
"""Merge le nombre de link aggregation dans les données d'interface."""
|
||||||
for interface_name, count in link_counts.items():
|
for interface_name, count in link_counts.items():
|
||||||
if interface_name in interfaces:
|
if interface_name in interfaces:
|
||||||
interfaces[interface_name]["nb_liens"] = count
|
interfaces[interface_name]["nb_liens"] = count
|
||||||
|
|
||||||
def _merge_mac_mappings(self, interfaces: dict, mac_mappings: dict):
|
def _merge_mac_mappings(self, interfaces: dict, mac_mappings: dict):
|
||||||
"""Merges MAC address mappings into the interface data."""
|
"""Merge les mappages d'adresses MAC dans les données d'interface."""
|
||||||
for interface_name, mac_address in mac_mappings.items():
|
for interface_name, mac_address in mac_mappings.items():
|
||||||
if interface_name in interfaces:
|
if interface_name in interfaces:
|
||||||
interfaces[interface_name]["mac_destination"] = mac_address
|
interfaces[interface_name]["mac_destination"] = mac_address
|
||||||
@@ -509,15 +509,15 @@ class LogFileProcessor:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _merge_bridge_mappings(self, interfaces: dict, bridge_mappings: dict):
|
def _merge_bridge_mappings(self, interfaces: dict, bridge_mappings: dict):
|
||||||
"""Merges bridge aggregation mappings into existing interfaces."""
|
"""Merge les bridge agreggation dans les interfaces existantes."""
|
||||||
for interface_name, bridge_name in bridge_mappings.items():
|
for interface_name, bridge_name in bridge_mappings.items():
|
||||||
if interface_name in interfaces:
|
if interface_name in interfaces:
|
||||||
interfaces[interface_name]["bridge_name"] = bridge_name
|
interfaces[interface_name]["bridge_name"] = bridge_name
|
||||||
|
|
||||||
def process_file(self, filepath: str) -> dict | None:
|
def process_file(self, filepath: str) -> dict | None:
|
||||||
"""
|
"""
|
||||||
Processes a single log file to extract and consolidate network device data.
|
Gère le parsing d'un fichier journal unique pour extraire et consolider les données des appareils réseau.
|
||||||
Returns a dictionary containing metadata and parsed data, or None on error.
|
Renvoie un dictionnaire contenant des métadonnées et des données analysées, ou None en cas d'erreur.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with open(filepath, 'r', encoding='latin-1', errors='ignore') as f:
|
with open(filepath, 'r', encoding='latin-1', errors='ignore') as f:
|
||||||
@@ -580,7 +580,7 @@ class LogFileProcessor:
|
|||||||
|
|
||||||
class DataExporter:
|
class DataExporter:
|
||||||
"""
|
"""
|
||||||
Handles the export of processed data to a JSON file.
|
Gère l'export des données traitées vers un fichier JSON.
|
||||||
"""
|
"""
|
||||||
def export(self, data: dict, output_filepath: str):
|
def export(self, data: dict, output_filepath: str):
|
||||||
try:
|
try:
|
||||||
@@ -596,7 +596,8 @@ class DataExporter:
|
|||||||
|
|
||||||
class MainApplication:
|
class MainApplication:
|
||||||
"""
|
"""
|
||||||
Main application class to orchestrate the log file processing.
|
Application principale pour coordonner le traitement des fichiers journaux,
|
||||||
|
l'export des données et la gestion des erreurs.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.processor = LogFileProcessor()
|
self.processor = LogFileProcessor()
|
||||||
@@ -625,8 +626,8 @@ class MainApplication:
|
|||||||
|
|
||||||
def process_file_return_json(filepath: str) -> dict | None:
|
def process_file_return_json(filepath: str) -> dict | None:
|
||||||
"""
|
"""
|
||||||
Standalone function to process a single file and return JSON object,
|
Fonction autonome pour traiter un fichier unique et renvoyer un objet JSON,
|
||||||
useful for external calls that don't need the full CLI application.
|
utile pour les appels externes.
|
||||||
"""
|
"""
|
||||||
processor = LogFileProcessor()
|
processor = LogFileProcessor()
|
||||||
return processor.process_file(filepath)
|
return processor.process_file(filepath)
|
||||||
56
gui.py
56
gui.py
@@ -68,7 +68,7 @@ class ToolTip:
|
|||||||
# Fenêtre principale
|
# Fenêtre principale
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
root.title("Analyse Réseau")
|
root.title("Analyse Réseau")
|
||||||
root.geometry("650x300")
|
root.geometry("650x350")
|
||||||
root.resizable(False, False)
|
root.resizable(False, False)
|
||||||
|
|
||||||
|
|
||||||
@@ -89,7 +89,8 @@ def open_firewall_gui():
|
|||||||
input_var = tk.StringVar()
|
input_var = tk.StringVar()
|
||||||
output_var = tk.StringVar()
|
output_var = tk.StringVar()
|
||||||
|
|
||||||
excel_var = tk.BooleanVar()
|
matrice_flux = tk.BooleanVar()
|
||||||
|
matrice_routage = tk.BooleanVar()
|
||||||
|
|
||||||
def browse_input():
|
def browse_input():
|
||||||
fw_type = firewall_var.get()
|
fw_type = firewall_var.get()
|
||||||
@@ -114,17 +115,24 @@ def open_firewall_gui():
|
|||||||
|
|
||||||
if output_var.get():
|
if output_var.get():
|
||||||
f_json = os.path.join(OUTPUT_DIR, f"{fw}_{output_var.get()}.json")
|
f_json = os.path.join(OUTPUT_DIR, f"{fw}_{output_var.get()}.json")
|
||||||
f_excel = os.path.join(OUTPUT_DIR, f"matrice_{fw}_{output_var.get()}.xlsx")
|
f_flux = os.path.join(OUTPUT_DIR, f"matrice_flux_{fw}_{output_var.get()}.xlsx")
|
||||||
|
f_routage = os.path.join(OUTPUT_DIR, f"matrice_routage_{fw}_{output_var.get()}.xlsx")
|
||||||
else:
|
else:
|
||||||
dt = datetime.now().strftime("%Y%m%d")
|
dt = datetime.now().strftime("%Y%m%d")
|
||||||
f_json = os.path.join(OUTPUT_DIR, f"{fw}_{dt}.json")
|
f_json = os.path.join(OUTPUT_DIR, f"{fw}_{dt}.json")
|
||||||
f_excel = os.path.join(OUTPUT_DIR, f"matrice_{fw}_{dt}.xlsx")
|
f_flux = os.path.join(OUTPUT_DIR, f"matrice_flux_{fw}_{dt}.xlsx")
|
||||||
|
f_routage = os.path.join(OUTPUT_DIR, f"matrice_routage_{fw}_{dt}.xlsx")
|
||||||
if not excel_var.get():
|
|
||||||
output_label_var.set("Fichier de sortie :\n" + f_json)
|
|
||||||
else:
|
|
||||||
output_label_var.set("Fichiers de sortie :\n" + f_json + "\n" + f_excel)
|
|
||||||
|
|
||||||
|
if not matrice_flux.get():
|
||||||
|
if not matrice_routage.get():
|
||||||
|
output_label_var.set("Fichier de sortie :\n" + f_json)
|
||||||
|
else:
|
||||||
|
output_label_var.set("Fichiers de sortie :\n" + f_json + "\n" + f_routage)
|
||||||
|
else:
|
||||||
|
if not matrice_routage.get():
|
||||||
|
output_label_var.set("Fichiers de sortie :\n" + f_json + "\n" + f_flux)
|
||||||
|
else:
|
||||||
|
output_label_var.set("Fichiers de sortie :\n" + f_json + "\n" + f_flux + "\n" + f_routage)
|
||||||
app.update_idletasks()
|
app.update_idletasks()
|
||||||
|
|
||||||
def open_output_folder():
|
def open_output_folder():
|
||||||
@@ -172,8 +180,11 @@ def open_firewall_gui():
|
|||||||
if output_var.get():
|
if output_var.get():
|
||||||
cmd.extend(["-o", output_var.get()])
|
cmd.extend(["-o", output_var.get()])
|
||||||
|
|
||||||
if excel_var.get():
|
if matrice_flux.get():
|
||||||
cmd.append("-m")
|
cmd.append("-f")
|
||||||
|
|
||||||
|
if matrice_routage.get():
|
||||||
|
cmd.append("-r")
|
||||||
|
|
||||||
print("Commande exécutée :", " ".join(cmd))
|
print("Commande exécutée :", " ".join(cmd))
|
||||||
print("Dossier courant (cwd) :", FIREWALL_DIR)
|
print("Dossier courant (cwd) :", FIREWALL_DIR)
|
||||||
@@ -274,15 +285,23 @@ def open_firewall_gui():
|
|||||||
ttk.Checkbutton(
|
ttk.Checkbutton(
|
||||||
app,
|
app,
|
||||||
text="Générer la matrice de flux en Excel",
|
text="Générer la matrice de flux en Excel",
|
||||||
variable=excel_var
|
variable=matrice_flux
|
||||||
).pack(anchor="w", padx=10, pady=10)
|
).pack(anchor="w", padx=10, pady=(10, 0))
|
||||||
excel_var.set(True)
|
matrice_flux.set(True)
|
||||||
|
|
||||||
|
ttk.Checkbutton(
|
||||||
|
app,
|
||||||
|
text="Générer la matrice de routage en Excel (route statique uniquement)",
|
||||||
|
variable=matrice_routage
|
||||||
|
).pack(anchor="w", padx=10, pady=(0, 10))
|
||||||
|
matrice_routage.set(True)
|
||||||
|
|
||||||
output_label_var = tk.StringVar()
|
output_label_var = tk.StringVar()
|
||||||
ttk.Label(app, textvariable=output_label_var).pack(anchor="w", padx=10)
|
ttk.Label(app, textvariable=output_label_var).pack(anchor="w", padx=10)
|
||||||
firewall_var.trace_add("write", update_output_label)
|
firewall_var.trace_add("write", update_output_label)
|
||||||
output_var.trace_add("write", update_output_label)
|
output_var.trace_add("write", update_output_label)
|
||||||
excel_var.trace_add("write", update_output_label)
|
matrice_flux.trace_add("write", update_output_label)
|
||||||
|
matrice_routage.trace_add("write", update_output_label)
|
||||||
update_output_label()
|
update_output_label()
|
||||||
|
|
||||||
ttk.Button(
|
ttk.Button(
|
||||||
@@ -321,8 +340,9 @@ ttk.Button(
|
|||||||
|
|
||||||
ttk.Label(
|
ttk.Label(
|
||||||
root,
|
root,
|
||||||
text="(Mise des données au format normalisé Yang dans un fichier JSON)" \
|
text="(Convertir les données au format normalisé Yang dans un fichier JSON)" \
|
||||||
"\n + possibilité de générer une matrice de flux en Excel",
|
"\n + possibilité de générer une matrice de flux en Excel" \
|
||||||
|
"\n + possibilité de générer une matrice de routage en Excel (route statique uniquement)",
|
||||||
font=("Arial", 9, "italic"),
|
font=("Arial", 9, "italic"),
|
||||||
anchor="center",
|
anchor="center",
|
||||||
justify="center"
|
justify="center"
|
||||||
@@ -339,7 +359,7 @@ ttk.Button(
|
|||||||
|
|
||||||
ttk.Label(
|
ttk.Label(
|
||||||
root,
|
root,
|
||||||
text="(Mise des données au format normalisé Yang dans un fichier JSON)" \
|
text="(Convertir les données au format normalisé Yang dans un fichier JSON)" \
|
||||||
"\n + possibilité de générer un schéma réseau",
|
"\n + possibilité de générer un schéma réseau",
|
||||||
font=("Arial", 9, "italic"),
|
font=("Arial", 9, "italic"),
|
||||||
anchor="center",
|
anchor="center",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
Cet outil permet de **récupérer les données des configurations de différents types de firewalls** (Palo Alto, Stormshield, Forcepoint) et de **convertir ces informations en un format JSON normalisé basé sur des modèles OpenConfig en YANG**.
|
Cet outil permet de **récupérer les données des configurations de différents types de firewalls** (Palo Alto, Stormshield, Forcepoint) et de **convertir ces informations en un format JSON normalisé basé sur des modèles OpenConfig en YANG**.
|
||||||
|
|
||||||
Il fournit également la possibilité de générer une **matrice de flux au format Excel** pour visualiser les communications et règles de trafic dans l’infrastructure.
|
Il fournit également la possibilité de générer une **matrice de flux au format Excel** pour visualiser les communications et règles de trafic dans l’infrastructure.
|
||||||
|
Il founit également la possibilité de générer une **matrice de routage au format Excel** pour visualiser les routes statiques dans l’infrastructure.
|
||||||
|
|
||||||
## Utilisation
|
## Utilisation
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ cd .\Parseurs_config_Firewall\
|
|||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
.\.venv\Scripts\activate
|
.\.venv\Scripts\activate
|
||||||
|
|
||||||
pip install -r .\src\requierements.txt
|
pip install -r .\Parseurs_config_Firewall\src\requirements.txt
|
||||||
```
|
```
|
||||||
- Mettre le/les fichier(s) et/ou dossier(s) de configurations dans le dossier `/Parseurs_config_Firewall/src/input/`
|
- Mettre le/les fichier(s) et/ou dossier(s) de configurations dans le dossier `/Parseurs_config_Firewall/src/input/`
|
||||||
- Modifier le fichier `site.json` de données dans `/Parseurs_config_Firewall/src/data/`
|
- Modifier le fichier `site.json` de données dans `/Parseurs_config_Firewall/src/data/`
|
||||||
Reference in New Issue
Block a user