diff --git a/ns8_backup_monitor/notifier.py b/ns8_backup_monitor/notifier.py index ed3fa0a..8c4a8b0 100644 --- a/ns8_backup_monitor/notifier.py +++ b/ns8_backup_monitor/notifier.py @@ -6,6 +6,11 @@ Formats a human-readable HTML + text email based on: - correlation outcome (SUCCESS / PARTIAL / REPO_FAILURE) - per-module statuses - repository check results (if run) + +SMTP configuration is resolved via smtp_config.resolve_smtp_config(): + 1. NS8 cluster Redis (cluster/mail_settings) - same relay used by NS8 itself + 2. config.yml [smtp] section + 3. localhost:25 fallback """ import logging @@ -15,9 +20,11 @@ from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from typing import Optional +from .smtp_config import resolve_smtp_config + log = logging.getLogger(__name__) -OUTCOME_EMOJI = { +OUTCOME_LABEL = { "SUCCESS": "OK", "PARTIAL": "WARNING", "REPO_FAILURE": "CRITICAL", @@ -33,7 +40,7 @@ OUTCOME_COLOR = { def _build_text(correlation: dict, repo_status: Optional[dict]) -> str: outcome = correlation["outcome"] lines = [ - f"NS8 Backup Monitor - {OUTCOME_EMOJI[outcome]}: {outcome}", + f"NS8 Backup Monitor - {OUTCOME_LABEL[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", @@ -60,10 +67,10 @@ def _build_text(correlation: dict, repo_status: Optional[dict]) -> str: return "\n".join(lines) -def _build_html(correlation: dict, repo_status: Optional[dict]) -> str: +def _build_html(correlation: dict, repo_status: Optional[dict], smtp_source: str) -> str: outcome = correlation["outcome"] color = OUTCOME_COLOR[outcome] - label = OUTCOME_EMOJI[outcome] + label = OUTCOME_LABEL[outcome] ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") plan_ids = ", ".join(correlation.get("backup_ids", [])) or "N/A" @@ -131,6 +138,7 @@ def _build_html(correlation: dict, repo_status: Optional[dict]) -> str: {rows} {repo_section} +

Sent via {smtp_source}

""" @@ -145,15 +153,20 @@ def send_notification( ): outcome = correlation["outcome"] mail_cfg = config.get("mail", {}) - smtp_cfg = config.get("smtp", {}) - - mail_from = mail_cfg.get("from", "ns8-backup-monitor@localhost") - mail_to = mail_cfg.get("to", []) subject_prefix = mail_cfg.get("subject_prefix", "[NS8 Backup]") - subject = f"{subject_prefix} {OUTCOME_EMOJI[outcome]}: {outcome} - {datetime.now(timezone.utc).strftime('%Y-%m-%d')}" + subject = f"{subject_prefix} {OUTCOME_LABEL[outcome]}: {outcome} - {datetime.now(timezone.utc).strftime('%Y-%m-%d')}" + + mail_to = mail_cfg.get("to", []) + if not mail_to: + log.error("No mail.to recipients configured in config.yml - cannot send notification") + return + + # Resolve SMTP: NS8 Redis relay -> config.yml -> localhost:25 + smtp_cfg, mail_from = resolve_smtp_config(config) + smtp_source = f"{smtp_cfg['host']}:{smtp_cfg['port']}" text_body = _build_text(correlation, repo_status) - html_body = _build_html(correlation, repo_status) + html_body = _build_html(correlation, repo_status, smtp_source) msg = MIMEMultipart("alternative") msg["Subject"] = subject @@ -180,6 +193,6 @@ def send_notification( smtp.login(username, password) smtp.sendmail(mail_from, mail_to, msg.as_string()) smtp.quit() - log.info(f"Notification sent: {subject} -> {mail_to}") + log.info(f"Notification sent: {subject} -> {mail_to} (via {smtp_source})") except Exception as e: - log.error(f"Failed to send notification: {e}") + log.error(f"Failed to send notification via {smtp_source}: {e}")