Mini Projet : Collecteur de Métriques WiFi
Objectifs
À la fin de cette séance, vous serez capables de :
- Implémenter les bases du scraper SNMP
- Utiliser les environnements virtuels et gérer les dépendances
- Configurer le scraper de manière flexible (pas de hardcoding)
- Mettre en place le logging pour tracer l’exécution
- Utiliser le debugger pour tester votre code
Introduction
À partir de cette séance, vous démarrez un mini projet : un scraper SNMP pour des points d’accès Schneider Electric (les APs utilisés dans nos déploiements résidentiels).
Ce projet représente un vrai défi que vous rencontrerez en tant que technicien réseau :
- Les APs Schneider ont des métriques SNMP non-standard
- Vous disposez d’une MIB file qui documente les OID disponibles
- Vous apprendrez à lire une MIB et déboguer du matériel réseau réel
Ce projet regroupe tout ce que vous avez appris dans les 3 séances précédentes :
- Environnements virtuels (séance 2)
- Logging (séance 2)
- Debugging (séance 3)
- Fonctions et bonnes pratiques (séance 1)
Sur les 3 prochaines séances, vous allez construire progressivement :
- Séance 4 : Le scraper de base et découverte des OID (ce que vous faites aujourd’hui)
- Séance 5 : Finalisation et robustesse du scraper
- Séance 6 : Dashboard pour présenter les données collectées
Fichiers fournis :
- Mib OIDs: MIB file documentant les OID des APs Schneider
- Structure de base du projet: Structure de base du projet avec les fichiers nécessaires (config.py, scraper.py vide, etc.)
1. Contexte et cas d’usage
1.1 Le problème réel
Schneider déploie des APs dans des résidences. L’administrateur réseau a besoin de :
- Savoir combien de clients sont connectés en temps réel
- Avoir un historique pour identifier les pics d’utilisation
- Surveiller la santé des APs (CPU, mémoire, signal, etc.)
Situation : Les APs Schneider Electric n’utilisent pas les OID SNMP standard. Vous disposez d’une MIB file (SE_mibs.csv) qui documente tous les OID disponibles.
Solution : Un scraper customisé qui interroge les APs Schneider via SNMP en utilisant les OID spécifiques définis dans la MIB.
1.2 SNMP et les OID non-standard
SNMP (Simple Network Management Protocol) est un protocole d’interrogation d’équipements réseau. Les données sont identifiées par des OID (Object Identifier).
OID Standard (Cisco, par exemple) :
OID: 1.3.6.1.4.1.9.9.46.1.6.1.1.6
Signification: Nombre de clients WiFi
2. Architecture du scraper
2.1 Structure générale
Organisez votre projet de la manière suivante :
scraper_wifi/
├── venv/ # Environnement virtuel
├── scraper.py # Code principal du scraper
├── config.py # Configuration (hosts, OID, etc.)
├── data/
│ └── metrics.csv # Données collectées
├── logs/
│ └── scraper.log # Logs d'exécution
├── requirements.txt # Dépendances
└── README.md # Documentation
Principes :
- Les credentials et config ne sont JAMAIS dans le code principal
- Les données sont séparées du code
- Les logs permettent de déboguer
2.2 Flux général du scraper
1. Charger configuration (hosts, credentials, OID)
↓
2. Pour chaque Access Point :
- Se connecter via SNMP
- Récupérer le nombre de clients
- Sauvegarder dans le CSV
↓
3. Logger chaque opération
↓
4. Attendre et recommencer
3. Gestion des credentials et configuration
3.1 Pourquoi externaliser la configuration ?
# ❌ MAUVAIS : Secrets en dur dans le code
def scraper_principal():
host = "192.168.1.1"
username = "snmp_user"
auth_password = "auth_pass"
privacy_password = "priv_pass"
# ...
Risques :
- Les credentials se retrouvent dans Git
- Les mots de passe sont visibles dans le code source
- Impossible de changer de serveur sans modifier le code
- Sécurité compromise
3.2 Solution : Fichier config.py
Créez un fichier config.py :
# config.py - Configuration du scraper
# Serveurs SNMP à interroger (SNMPv3)
SNMP_HOSTS = [
{
"name": "AP1",
"host": "192.168.1.1",
"version": 3,
"username": "snmp_user",
"auth_protocol": "SHA",
"auth_password": "auth_password",
"privacy_protocol": "AES",
"privacy_password": "privacy_password"
},
]
# OID à interroger (nombre de clients WiFi)
OID_CLIENTS = "1.3.6.1.4.1.102.2"
# Fichiers de sortie
CSV_FILE = "data/metrics.csv"
LOG_FILE = "logs/scraper.log"
# Paramètres SNMP
SNMP_TIMEOUT = 5 # secondes
SNMP_RETRIES = 3
# Fréquence de scraping
SCRAPE_INTERVAL = 60 # secondes
Ainsi, pour changer de serveur, vous modifiez juste config.py, pas le code du scraper !
3.3 Dépendances requises
Créez un requirements.txt :
pysnmp
Installez avec :
pip install -r requirements.txt
Note : SNMPv3 nécessite les modules pysnmp et pyasn1 pour l’authentification SHA et le chiffrement AES.
3bis. Découverte des OID Schneider
3bis.1 Comment découvrir les OID disponibles ?
Sur un équipement non-standard (comme les APs Schneider), vous disposez d’une MIB file documentant tous les OID. Voici comment l’utiliser efficacement.
Snmpwalk
# Inspecter une branche spécifique avec SNMPv3
snmpwalk -v 3 -u username -a SHA -A password -x AES -X password 192.168.1.1 1.3.6.1.4.1
# Cela affiche tous les OID commençant par 1.3.6.1.4.1
3bis.2 Tester rapidement un OID
Une fois que vous avez trouvé un OID candidat, testez-le :
# Tester rapidement si c'est le bon OID avec SNMPv3
snmpget -v 3 -u username -a SHA -A password -x AES -X password 192.168.1.1 1.3.6.1.4.1.XXXX.X.X
# Si c'est le bon OID, vous verrez :
# SNMPv2-SMI::enterprises.XXXX.X.X.0 = INTEGER: 42
3bis.3 Enregistrer les OID trouvés
Une fois les OID découverts, les ajouter à config.py :
# Schneider AP-specific OID (from schneider_mib.csv)
OID_CLIENTS = "1.3.6.1.4.1.XXXX.1.1.1"
OID_SIGNAL = "1.3.6.1.4.1.XXXX.1.1.2"
OID_NAME = "1.3.6.1.4.1.XXXX.1.1.3"
Cela permet à votre scraper de rester flexible et facilement adaptable à d’autres APs.
4. Implémentation du scraper de base
4.1 Code de base (boilerplate)
Voici la structure de base de votre scraper.
# scraper.py
import logging
import csv
import os
from datetime import datetime
from pysnmp.hlapi import *
from config import *
# Configuration du logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(LOG_FILE),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
def recuperer_clients_wifi(host, username, auth_password, privacy_password, oid):
"""Récupère le nombre de clients WiFi via SNMPv3"""
logger.debug(f"Récupération de {oid} sur {host}")
try:
# À implémenter: Utiliser nextCmd() avec UsmUserData pour SNMPv3
# À implémenter: Compter les entrées retournées
pass
except Exception as e:
logger.error(f"Erreur SNMP sur {host}: {e}")
return -1
def sauvegarder_csv(timestamp, host, nombre_clients, status):
"""Sauvegarde une mesure dans le CSV"""
logger.debug(f"Sauvegarde des données pour {host}")
try:
os.makedirs(os.path.dirname(CSV_FILE), exist_ok=True)
# À implémenter: Ajouter la ligne au CSV
logger.info(f"Données sauvegardées pour {host}")
except IOError as e:
logger.error(f"Impossible d'écrire dans {CSV_FILE}: {e}")
def main():
"""Point d'entrée du scraper"""
logger.info("Démarrage du scraper WiFi")
for ap in SNMP_HOSTS:
try:
logger.info(f"Interrogation de {ap['name']}")
# À implémenter:
# - Récupérer les données avec recuperer_clients_wifi()
# - Sauvegarder avec sauvegarder_csv()
except Exception as e:
logger.error(f"Erreur sur {ap['name']}: {e}")
if __name__ == "__main__":
main()
À compléter :
recuperer_clients_wifi(): Implémenter la logique SNMPv3sauvegarder_csv(): Écrire au CSV avec les colonnes timestamp, host, nombre_clients, statusmain(): Boucler sur les APs, récupérer les données, les sauvegarder
4.2 Concepts clés
SNMPv3 avec pysnmp :
Pour compter les clients, vous devez “marcher” (walk) sur la table OID avec nextCmd():
from pysnmp.hlapi import *
UsmUserData(username, auth_password, privacy_password,
authProtocol=usmHMACSHAAuthProtocol,
privProtocol=usmAesCfb128Protocol)
Chaque entrée retournée par nextCmd() = 1 client WiFi connecté.
CSV Header :
timestamp,host,nombre_clients,status
5. Format attendu du CSV
Votre CSV doit avoir cette structure :
timestamp,host,nombre_clients,status
2026-03-12 14:30:00,AP1,42,success
2026-03-12 14:31:00,AP1,43,success
2026-03-12 14:32:00,AP1,43,success
2026-03-12 14:33:00,AP2,15,success
2026-03-12 14:33:00,AP2,-1,error
Colonnes :
timestamp: ISO 8601 (YYYY-MM-DD HH:MM:SS)host: Nom de l’AP (ex: “AP1”)nombre_clients: Nombre détecté (-1 si erreur)status: “success” ou “error”
6. Debugging du scraper
6.1 Scénarios de test
Testez votre scraper dans ces situations :
- Cas normal : Tous les APs répondent
- La connexion se fait
- Les données sont récupérées
- Le CSV est rempli correctement
- Host inaccessible : Un AP ne répond pas
- La fonction gère le timeout
- Une erreur est loggée
- Le scraper continue sur l’autre AP
- Authentification échouée (SNMPv3) : Mauvais identifiants SNMP (username/auth/priv)
- Une SNMPError devrait être levée
- Loggée correctement
- Le scraper continue
- OID invalide : L’OID demandé n’existe pas
- SNMP retourne “NoSuchObject”
- C’est géré comme une erreur
- Loggé
Exercice 1 : Mise en place de l'environnement et découverte des OID
Partie 1 : Infrastructure
- Téléchargez et extrayez
mini-projet.tar.gz(contient scraper.py, config.py skeleton, etc.) - Activez l’environnement virtuel :
source venv/bin/activate - Installez les dépendances :
pip install -r requirements.txt
Partie 2 : Découverte des OID
- Testez la connexion SNMPv3 à l’AP (IP fournie)
- Utilisez
snmpwalkavec vos credentials pour explorer :snmpwalk -v 3 -u <username> -a SHA -A <auth_pass> -x AES -X <priv_pass> <AP_IP> 1.3.6.1.4.1.102 - Identifiez l’OID pour les clients (1.3.6.1.4.1.102.2)
- Documentez votre découverte dans
config.py
Exercice 2 : Implémentation des fonctions
Complétez les TODO dans scraper.py :
recuperer_clients_wifi():- Utiliser
nextCmd()avecUsmUserData()pour SNMPv3 - Boucler sur les résultats et compter les entrées
- Retourner le count
- Utiliser
sauvegarder_csv():- Vérifier si le fichier existe
- Si non, créer l’en-tête :
timestamp,host,nombre_clients,status - Ajouter la ligne de données
main():- Pour chaque AP dans SNMP_HOSTS
- Appeler
recuperer_clients_wifi()avec ses paramètres - Appeler
sauvegarder_csv()avec le résultat - Logger les étapes
Test :
python scraper.py
# Vérifiez que data/metrics.csv est créé et rempli
7. Scraper amélioré : boucle infinie (Autonomie)
7.1 Lancer le scraper en continu
import time
def main():
logger.info("Démarrage du scraper WiFi")
while True:
try:
for ap in SNMP_HOSTS:
# Récupérer Les données
logger.info(f"Interrogation de {ap['name']}")
logger.debug(f"Prochain scraping dans {SCRAPE_INTERVAL} secondes")
time.sleep(SCRAPE_INTERVAL)
except KeyboardInterrupt:
logger.info("Arrêt du scraper (Ctrl+C)")
break
except Exception as e:
logger.error(f"Erreur inattendue: {e}")
time.sleep(SCRAPE_INTERVAL)