Skip to content

#70 — rapport-sla-mensuel

PLANIFIÉ

Priorité: 🟠 HAUTE · Type: C (Celery) · Conteneur: rgz-beat · Code: app/tasks/reports.pyDépendances: #43 sla-probe-engine, #10 rgz-beat, #63 email-notification


Description

Rapport SLA mensuel automatique généré à J+5 du mois suivant (ex. le 5 février pour janvier). Ce rapport synthétise la qualité de service fournie à chaque revendeur : uptime global du réseau, latence P95, incidents du mois, temps moyen de résolution, et éventuels crédits SLA dus en cas de violation du contrat.

Le rapport est généré en PDF beauté (avec logos, couleurs ACCESS) et envoyé automatiquement par email au revendeur (#63). Un tableau de bord JSON accompagne le PDF pour intégration dans les dashboards custom revendeur (#51).

Objectifs métier :

  • Transparence totale sur la qualité de service
  • Justification des crédits SLA automatiques (#25)
  • Evidence pour ARCEP (traceabilité uptime/incidents)
  • Engagement client : "nous mesons et rapportons"

Architecture Interne

Flux de Génération

Tous les jours 05:30 UTC:

Celery Beat déclenche rgz.reports.sla_monthly

Vérifier si J+5 du mois (ex. 5 février pour janvier)

SI OUI:
  1. Requête TimescaleDB:
     - SLA results: SELECT * FROM sla_results WHERE month=DATE_TRUNC('month', current_date - INTERVAL 1 MONTH)
     - Incidents: SELECT * FROM incidents WHERE created_at IN [month_start, month_end)
     - Uptime%: COUNT(success) / COUNT(*) * 100
     - Latency P95: PERCENTILE_CONT(0.95) latency_ms
  2. Calcul crédits:
     - SI uptime < 99.5% → crédit = (99.5 - uptime) * 0.05 * MIR
     - Enregistrer dans credits_sla table
  3. Rendu PDF avec WeasyPrint:
     - Template: templates/reports/sla_monthly.html
     - Injection données: uptime%, P95, incidents, crédits
     - Branding: logo revendeur (#18), couleurs ACCESS
  4. Email: SMTP → revendeur + NOC
  5. Archive: /var/reports/sla_monthly_{reseller_id}_{year}_{month}.pdf

Schéma de Données

sql
-- Table source (créée par #43)
TABLE sla_results:
  id UUID PK
  reseller_id UUID FK
  nas_id TEXT
  probe_timestamp TIMESTAMP
  response_time_ms INT
  is_success BOOLEAN
  probe_type (ICMP|TCP)

-- Table analytique (créée par #70)
TABLE sla_monthly_report:
  id UUID PK
  reseller_id UUID FK
  year INT
  month INT
  uptime_percent DECIMAL(5,2)
  latency_p95_ms INT
  incident_count INT
  incident_duration_total_minutes INT
  credit_fcfa DECIMAL(12,2)
  generated_at TIMESTAMP
  pdf_path TEXT
  email_sent_at TIMESTAMP
  UNIQUE(reseller_id, year, month)

-- Crédits SLA (consulté par #25)
TABLE credits_sla:
  id UUID PK
  reseller_id UUID FK
  sla_monthly_report_id UUID FK
  credit_amount_fcfa DECIMAL(12,2)
  reason TEXT (ex: "Uptime 98.2% < 99.5% threshold")
  applied_date DATE
  invoice_id UUID FK (lien vers facture)

Contenu du Rapport PDF

┌─────────────────────────────────────┐
│    RAPPORT SLA MENSUEL — JANVIER   │
│    Revendeur: Tech Connect Abomey   │
│    Période: 01/01 — 31/01/2026     │
└─────────────────────────────────────┘

📊 RÉSUMÉ EXÉCUTIF
  Uptime global:          99.8% ✓ (seuil: 99.5%)
  Latence P95:            42ms ✓
  Incidents:              2 (durée totale: 45 minutes)
  Crédit SLA appliqué:    0 FCFA (performance respectée)

📈 DÉTAILS PAR SITE
  ┌─────────────────────────────────┐
  │ Site: Abomey Centre             │
  │ NAS-ID: access_tech_connect_s1  │
  │ Uptime: 99.9% | P95: 38ms       │
  │ Incidents: 0 | Crédit: 0 FCFA  │
  └─────────────────────────────────┘

⚠️  INCIDENTS DU MOIS
  01/15 — 14:30 : Perte connectivité NAS (P1)
          Durée: 25 minutes | Cause: Perte route BGP
          Résolution: Basculement redundancy
  01/28 — 09:15 : Dégradation CPU NAS (P2)
          Durée: 20 minutes | Cause: Spike trafic
          Résolution: Throttle débit

📋 CONTRAT SLA
  Uptime garanti:    99.5% (3.6h downtime max/mois)
  Latence max P95:   150ms
  Temps réparation P1: 1h (RTO)
  Crédit:            5% MIR par 0.1% shortfall

✅ CONFORMITÉ
  SLA respecté ✓ | Incidents documentés ✓ | Crédits calculés ✓

─────────────────────────────────────
Généré: 2026-02-05 à 05:30 UTC
Prochaine révision: 2026-03-05

Configuration

env
# Rapports SLA — Génération
SLA_REPORT_DAY_OF_MONTH=5          # Jour de génération (J+5)
SLA_REPORT_TIME_UTC=05:30          # Heure déclenchement
SLA_UPTIME_THRESHOLD=99.5          # Seuil garanti (%)
SLA_LATENCY_P95_THRESHOLD_MS=150   # Latence max P95
SLA_CREDIT_PERCENTAGE=5             # Crédit par 0.1% shortfall

# Rapports — Hébergement
REPORTS_OUTPUT_DIR=/var/reports
REPORTS_RETENTION_DAYS=365
REPORT_TEMPLATE_DIR=/app/templates/reports

# Email
SMTP_HOST=smtp.rgz.local
SMTP_PORT=587
SMTP_USER=noreply@rgz.local
SMTP_PASSWORD=***
EMAIL_FROM=noreply@rgz.local
EMAIL_SUBJECT_SLA="Rapport SLA Mensuel — {reseller_name} — {month}/{year}"

Endpoints API

MéthodeRouteDescriptionRéponse
GET/api/v1/reports/sla?reseller_id={uuid}&month=1&year=2026Récupérer rapport JSON{uptime_percent, latency_p95_ms, incident_count, credit_fcfa, generated_at}
GET/api/v1/reports/sla/{report_id}Détails completsJSON complet
GET/api/v1/reports/sla/{report_id}/pdfTélécharger PDFPDF binary
GET/api/v1/resellers/{reseller_id}/reports/sla?months=12Historique SLA 12 moisList[SlaReportSummary]
POST/api/v1/reports/sla/regenerate?month=1&year=2026Régénérer rapport (admin)202 Accepted

Sécurité API:

  • SEC-01 IDOR : revendeur ne peut voir que ses rapports
  • Administrateur peut voir tous les rapports

Celery Task

ChampValeur
Task namergz.reports.sla_monthly
ScheduleDaily 05:30 UTC (cron exécution)
Queuergz.reports
Timeout600s (10 minutes)
Retry3x avec backoff exponentiel
LogINFO "SLA report generated" ou ERROR "SLA generation failed"

Logique Celery:

python
@app.task(name='rgz.reports.sla_monthly', bind=True)
def generate_sla_monthly_report(self):
    """
    Génère rapports SLA pour tous les revendeurs
    du mois précédent à J+5 du mois courant
    """
    # 1. Vérifier si J+5 du mois
    today = datetime.utcnow()
    if today.day != settings.SLA_REPORT_DAY:
        logger.info("Not SLA report day, skipping")
        return {'status': 'skipped'}

    # 2. Récupérer tous revendeurs actifs
    resellers = db.query(Reseller).filter(
        Reseller.status == ResellerStatus.active
    ).all()

    # 3. Pour chaque revendeur
    results = []
    for reseller in resellers:
        try:
            report = _generate_sla_report_for_reseller(
                reseller.id,
                month=today.month - 1,
                year=today.year
            )

            # 4. Générer PDF
            pdf_path = _render_sla_pdf(report, reseller)

            # 5. Envoyer email
            send_email.delay(
                to=reseller.primary_contact_email,
                subject=f"Rapport SLA {today.strftime('%B %Y')}",
                template='sla_monthly_email',
                context={'report': report}
            )

            results.append({'reseller_id': reseller.id, 'status': 'success'})
        except Exception as e:
            logger.error(f"Failed SLA report {reseller.id}: {e}")
            results.append({'reseller_id': reseller.id, 'status': 'failed'})
            self.retry(exc=e, countdown=300)  # Retry in 5min

    logger.info(f"SLA monthly: {len(results)} reports processed")
    return {'status': 'completed', 'reports': results}

Commandes Utiles

bash
# Déclencher SLA mensuel manuellement (admin)
docker-compose exec rgz-api celery -A app.celery_app call rgz.reports.sla_monthly

# Récupérer rapport JSON derniers 3 mois
curl -H "Authorization: Bearer {token}" \
  "http://api-rgz.duckdns.org/api/v1/reports/sla?reseller_id={uuid}&months=3"

# Télécharger PDF rapport janvier 2026
curl -H "Authorization: Bearer {token}" \
  "http://api-rgz.duckdns.org/api/v1/reports/sla/pdf?year=2026&month=1" \
  -o sla_janvier_2026.pdf

# Vérifier queue celery reports
docker-compose exec rgz-redis redis-cli llen celery:rgz.reports

# Logs dernières générations
docker-compose logs -f rgz-beat | grep "rgz.reports.sla_monthly"

# Archiver rapports anciens (>1 an)
find /var/reports/sla_* -mtime +365 -delete

Implémentation TODO

  • [ ] Schéma TimescaleDB sla_monthly_report + index month/reseller_id
  • [ ] Tâche Celery rgz.reports.sla_monthly dans app/tasks/reports.py
  • [ ] Template Jinja2 templates/reports/sla_monthly.html
  • [ ] WeasyPrint CSS pour PDF branding ACCESS
  • [ ] Endpoints API GET/POST /api/v1/reports/sla*
  • [ ] Fonction _generate_sla_report_for_reseller() (query TimescaleDB)
  • [ ] Fonction _render_sla_pdf() (WeasyPrint)
  • [ ] Intégration #63 send_email.delay() pour distribution
  • [ ] Tests: données synthétiques (uptime 99.8%, incidents, crédits)
  • [ ] Monitoring: alerte si rapport pas généré J+5
  • [ ] Documentation: schéma SLA, contrats, calcul crédits

Dernière mise à jour: 2026-02-21

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