Skip to content

#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 check

Configuration

Variables d'environnement

env
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 deploiements

Format inventory.csv

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_point

Model SQLAlchemy

python
# 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éthodeRouteRéponseAuth
POST/api/v1/network/cpe/batch-deploy202 {job_id, status}Admin
GET/api/v1/network/cpe/deployments?reseller_id=...200 {items: [...], total}Admin
POST/api/v1/network/cpe/{cpe_ip}/rollback202 {status}Admin
GET/api/v1/network/cpe/report?from=...&to=...200 HTML reportAdmin

Commandes Utiles

bash
# 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.html

Implémentation TODO

  • [ ] Classe CpeInventory pour charger et valider CSV
  • [ ] Classe CpeConfigurator pour générer JSON config par CPE
  • [ ] Classe CpeSshClient pour 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 CpeDeploymentReporter pour 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

PROJET MOSAÏQUE — 81 outils, 22 conteneurs, 500+ revendeurs WiFi Zone