#!/usr/bin/env python3 """ notifier.py - Sends a single classified notification email. Formats a human-readable HTML + text email based on: - correlation outcome (SUCCESS / PARTIAL / REPO_FAILURE) - per-module statuses - repository check results (if run) """ import logging import smtplib from datetime import datetime, timezone from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from typing import Optional log = logging.getLogger(__name__) OUTCOME_EMOJI = { "SUCCESS": "OK", "PARTIAL": "WARNING", "REPO_FAILURE": "CRITICAL", } OUTCOME_COLOR = { "SUCCESS": "#2e7d32", "PARTIAL": "#e65100", "REPO_FAILURE": "#b71c1c", } def _build_text(correlation: dict, repo_status: Optional[dict]) -> str: outcome = correlation["outcome"] lines = [ f"NS8 Backup Monitor - {OUTCOME_EMOJI[outcome]}: {outcome}", f"Time: {datetime.now(timezone.utc).isoformat()}", f"Plans checked: {', '.join(correlation.get('backup_ids', []))}", f"Modules: {correlation['succeeded']} OK / {correlation['failed']} FAILED / {correlation['total']} total", "", ] if correlation["failed_modules"]: lines.append("Failed modules:") for m in correlation["failed_modules"]: lines.append(f" - [{m['module_id']}] backup_id={m['backup_id']}: {m.get('error', 'unknown error')}") lines.append("") if repo_status: lines.append("Repository check:") for dest in repo_status.get("destinations", []): lines.append(f" - [{dest['repo_id']}] {dest['status']}: {dest.get('error', '')}") if repo_status.get("note"): lines.append(f" NOTE: {repo_status['note']}") lines.append("") if correlation.get("note"): lines.append(f"Note: {correlation['note']}") return "\n".join(lines) def _build_html(correlation: dict, repo_status: Optional[dict]) -> str: outcome = correlation["outcome"] color = OUTCOME_COLOR[outcome] label = OUTCOME_EMOJI[outcome] ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") plan_ids = ", ".join(correlation.get("backup_ids", [])) or "N/A" rows = "" for m in correlation.get("modules", []): bg = "#e8f5e9" if m["result"] == "success" else "#ffebee" icon = "✓" if m["result"] == "success" else "✗" rows += ( f'
| Repo ID | Status | Detail |
|---|
{repo_status["note"]}
' html = f"""{correlation['succeeded']} module(s) OK | {correlation['failed']} FAILED | {correlation['total']} total
| Module | Backup ID | Timestamp | Error |
|---|