#33 — Configuration CPE Batch
PLANIFIÉ
Priorité: 🔴 CRITIQUE · Type: TYPE G · Conteneur: rgz-tools · Code: tools/cpe_preconfig/Dépendances: #31 vlan-allocation
Description
Configuration automatisée de 160+ CPE LiteBeam via SSH. Le service lit une liste d'équipements (IP, MAC, reseller_id, site_number) depuis un fichier CSV, génère les configurations pour chaque CPE (SSID, VLAN, IP réseau, RADIUS, QoS profiles), puis les applique via SSH avec parallélisation (10 CPE max simultanément pour limiter la charge).
Chaque CPE est configuré avec : SSID et NAS-ID depuis la table reseller_sites via #31, accès au serveur RADIUS #6 avec son secret, adresse IP statique dans le subnet VLAN alloué, et trois profiles QoS (Interactive, Video, Bulk) dérivés de #27 HTB et #28 DSCP. Une vérification d'intégrité est faite après chaque configuration, et un rollback manuel est possible via l'outil de CLI.
L'outil génère des rapports HTML listant les CPE configurés, les erreurs, et l'historique des rollback. Un mode dry-run permet de tester sans appliquer les configurations.
Architecture Interne
1. Phase préparation:
└─> Charger inventory.csv:
├─ Colonnes: ip,mac,reseller_id,site_number,cpe_model,device_type
├─ MAC: validation via OUI IEEE (LL#20 MAC validation)
├─ IP: vérification format IPv4, ping ICMP timeout 2s
└─ reseller_id: lookup DB pour récupérer vlan_id, nas_id, ssid, gateway
2. Génération config par CPE:
└─> Pour chaque CPE:
├─> Récupérer de reseller_sites:
│ ├─ vlan_id, nas_id, ssid, subnet, gateway
│ └─ radius_secret (depuis envvar $RADIUS_SECRET)
├─> Générer LiteBeam config (pseudo):
│ {
│ "network": {
│ "vlan_id": 150,
│ "ip": "10.150.0.2",
│ "netmask": "255.255.255.0",
│ "gateway": "10.150.0.1",
│ "dns": ["10.0.0.9", "8.8.8.8"]
│ },
│ "wireless": {
│ "ssid": "ACCESS Tech Connect",
│ "mode": "access_point",
│ "freq": "5GHz",
│ "power": "23dBm"
│ },
│ "radius": {
│ "server": "10.0.0.6",
│ "secret": $RADIUS_SECRET,
│ "nas_id": "access_tech_s1",
│ "timeout": 3
│ },
│ "qos": {
│ "profiles": ["interactive", "video", "bulk"]
│ }
│ }
└─> Écrire dans /tmp/cpe_{ip}.json
3. Phase SSH (parallélisé, max 10):
└─> Pour chaque CPE:
├─> SSH connect: ssh -i $SSH_KEY $CPE_USER@$ip
├─> Vérifier status: "/usr/bin/cpe-status" → JSON parsing
├─> Upload config: scp /tmp/cpe_{ip}.json $ip:/etc/config.json
├─> Redémarrer réseau: ssh ... "/usr/bin/cpe-restart-network"
├─> Vérifier après 30s: ping, HTTP status /api/health
├─> Si KO: rollback → scp old_config.json restore, restart
└─> Log success/failure → DB (cpe_deployments table)
4. Rapport:
└─> Générer HTML:
├─ Table: CPE | IP | MAC | Status | VLAN | Timestamp
├─ Statistiques: X succès, Y échecs, Z rollback
├─ Détail erreur: timeout, SSH error, health check KO
└─> Envoyer à admin via #63 email
5. Rollback:
└─> CLI: python3 tools/cpe_preconfig/cli.py rollback --cpe-ip=10.150.0.2
├─> SSH vers CPE
├─> Récupérer config précédente depuis DB
├─> Appliquer + restart
└─> Confirmer health checkConfiguration
Variables d'environnement
CPE_SSH_USER=root # Utilisateur SSH CPE
CPE_SSH_KEY_PATH=/app/cpe_key # Clé privée SSH (volume monté)
CPE_SSH_TIMEOUT=30 # Timeout SSH (secondes)
CPE_HEALTH_CHECK_RETRIES=3 # Tentatives health check après restart
CPE_PARALLEL_JOBS=10 # Max CPE simultanés
RADIUS_SECRET=shared_secret_rgz # Secret RADIUS (partagé avec #6)
CPE_GATEWAY_IP=10.0.0.1 # IP gateway pour ARP
INVENTORY_CSV=/data/cpe_inventory.csv # Chemin fichier inventaire
DRY_RUN=false # Mode simulation sans appliquer
DATABASE_URL=postgresql://... # Pour log deploiementsFormat inventory.csv
ip,mac,reseller_id,site_number,cpe_model,device_type
10.150.0.2,AA:BB:CC:DD:EE:01,550e8400-e29b-41d4-a716-446655440000,1,LiteBeam-5AC-Gen2,access_point
10.150.0.3,AA:BB:CC:DD:EE:02,550e8400-e29b-41d4-a716-446655440000,1,LiteBeam-5AC-Gen2,access_point
10.160.0.2,AA:BB:CC:DD:EE:03,660f9511-f3ae-52e5-b827-556766551111,1,LiteBeam-5AC,access_pointModel SQLAlchemy
# app/models/monitoring.py (ou tools/cpe_preconfig/models.py)
class CpeDeployment(Base):
__tablename__ = "cpe_deployments"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
cpe_ip = Column(String(15), nullable=False)
cpe_mac = Column(String(17), nullable=False)
reseller_id = Column(UUID(as_uuid=True), ForeignKey("resellers.id"), nullable=False)
vlan_id = Column(Integer, nullable=False)
nas_id = Column(String(50), nullable=False)
status = Column(String(20), nullable=False,
CheckConstraint("status IN ('pending', 'deployed', 'failed', 'rollback')"))
error_message = Column(Text, nullable=True)
config_json = Column(JSON, nullable=True) # Config appliquée
old_config_json = Column(JSON, nullable=True) # Backup avant modif
health_check_result = Column(JSON, nullable=True) # {ssh: true, ping: true, http: 200}
deployed_at = Column(DateTime, nullable=True)
rolled_back_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, default=utcnow, nullable=False)
updated_at = Column(DateTime, default=utcnow, onupdate=utcnow)Endpoints API (indirects)
| Méthode | Route | Réponse | Auth |
|---|---|---|---|
| POST | /api/v1/network/cpe/batch-deploy | 202 {job_id, status} | Admin |
| GET | /api/v1/network/cpe/deployments?reseller_id=... | 200 {items: [...], total} | Admin |
| POST | /api/v1/network/cpe/{cpe_ip}/rollback | 202 {status} | Admin |
| GET | /api/v1/network/cpe/report?from=...&to=... | 200 HTML report | Admin |
Commandes Utiles
# Valider inventory CSV avant déploiement
python3 /tools/cpe_preconfig/validate_inventory.py \
--csv /data/cpe_inventory.csv \
--db postgresql://... \
--dry-run
# Déployer batch (mode dry-run)
python3 /tools/cpe_preconfig/cli.py deploy \
--inventory /data/cpe_inventory.csv \
--dry-run \
--log-file /tmp/deploy.log
# Déployer batch (réel)
python3 /tools/cpe_preconfig/cli.py deploy \
--inventory /data/cpe_inventory.csv \
--parallel 5
# Rollback un CPE spécifique
python3 /tools/cpe_preconfig/cli.py rollback \
--cpe-ip 10.150.0.2 \
--confirm
# Voir status d'un CPE
ssh -i /app/cpe_key root@10.150.0.2 /usr/bin/cpe-status | jq
# Générer rapport HTML
python3 /tools/cpe_preconfig/cli.py report \
--from 2026-02-15 \
--to 2026-02-21 \
--output /tmp/cpe_report.htmlImplémentation TODO
- [ ] Classe
CpeInventorypour charger et valider CSV - [ ] Classe
CpeConfiguratorpour générer JSON config par CPE - [ ] Classe
CpeSshClientpour connexion SSH parallelisée (ThreadPoolExecutor) - [ ] Fonction
validate_mac()avec OUI IEEE lookup - [ ] Fonction
generate_config()qui lit reseller_sites et construit JSON - [ ] Fonction
deploy_cpe()pour un CPE unique (upload + restart + health check) - [ ] Fonction
health_check()(ping, SSH /api/health, HTTP 200) - [ ] Fonction
rollback_cpe()pour restaurer old_config - [ ] Classe
CpeDeploymentReporterpour générer HTML report - [ ] Mode dry-run pour afficher config sans appliquer
- [ ] Parallélisation max 10 CPE simultanés via ThreadPoolExecutor
- [ ] Logging DB: INSERT cpe_deployments pour chaque opération
- [ ] Tests: CSV valide, SSH mock, health check, rollback
- [ ] Intégration #31 VLAN: lookup reseller_sites par reseller_id
- [ ] Intégration #63 email: rapport après déploiement
- [ ] CLI avec argparse (deploy, rollback, status, report)
Dernière mise à jour: 2026-02-21