Files
OPNexus/backend/app/schemas/notification.py
T
Julian Boldt 79783d6d62
Release / verify (push) Canceled after 0s
CI / checks (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / backend (push) Canceled after 0s
Release / test (push) Canceled after 0s
Release / publish (backend) (push) Canceled after 0s
Release / publish (frontend) (push) Canceled after 0s
Release / publish (proxy) (push) Canceled after 0s
Release / publish (updater) (push) Canceled after 0s
Release v1.17.0
Geplante Wartung: Fehler zeigen sich sofort statt erst um 3 Uhr nachts, und der Master lässt sich
unbeaufsichtigt aktualisieren. Auf einem Test-Cluster live geprüft (Neustart und ein echtes Update
mit Umschaltung), siehe Handbuch „Geplante Wartung“.

- **Rollenprüfung beim Einplanen:** Ein Host, der in einem Cluster gerade CARP-MASTER ist,
  lässt sich nicht mehr einplanen (Neustart/Update/Major-Upgrade). Bisher wurde der Plan
  angenommen und scheiterte erst zur Uhrzeit. Der Planungsdialog zeigt den Grund an und
  sperrt den Button. Ist die Rolle nicht feststellbar (Host gerade nicht erreichbar), wird
  weiter angenommen; die volle Prüfung läuft wie bisher zum Zündzeitpunkt.
- **Benachrichtigung „Geplante Wartung nicht ausgeführt“:** Eine fehlgeschlagene geplante
  Wartung meldet sich per Webhook/E-Mail (einzeln schaltbar in den Einstellungen, standardmäßig
  an). Offen für drei Tage nach dem geplanten Zeitpunkt, dann ohne „behoben“-Meldung geschlossen.
  Neue Spalte `notification_settings.scheduled_maintenance_failed_enabled` (Migration `0006`).
- **Automatische CARP-Umschaltung (Option pro Plan, standardmäßig aus):** Ein geplanter Neustart,
  Update oder Major-Upgrade auf dem MASTER schaltet vorher auf den Partner um, prüft die
  Übernahme, führt die Aktion aus, wartet auf die Rückkehr des Hosts und schaltet zurück. Fehler
  vor dem Start der Aktion stellen den Normalbetrieb wieder her; danach bleibt der Host bewusst im
  Wartungsmodus (Partner trägt den Verkehr) und es kommt eine Benachrichtigung. Der Ablauf steht in
  der Datenbank und überlebt einen Neustart des Backends. Neue Spalten an `scheduled_maintenance`
  (Migration `0007`).
- **Backup vor geplantem Update:** Ein geplantes Update oder Major-Upgrade zieht unmittelbar vor
  dem Start selbst ein Konfigurationsbackup (nur lesend auf der Firewall). Bisher scheiterte jeder
  Plan, der mehr als 24 Stunden in der Zukunft lag, an der Backup-Aktualität. Schlägt das Backup
  fehl, wird nicht aktualisiert.
2026-10-19 16:45:07 +02:00

102 lines
3.4 KiB
Python

import uuid
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field
from app.config import settings
SmtpSecurity = Literal["none", "starttls", "ssl"]
ProblemType = Literal[
"host_offline", "cert_expiry", "rule_drift", "revert_failed", "maintenance_unknown",
"carp_split_brain", "nat_drift", "carp_auto_revert_failed", "network_drift",
"scheduled_maintenance_failed",
]
class NotificationSettingsRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
webhook_enabled: bool
webhook_url: str | None
webhook_signing_secret_set: bool
email_enabled: bool
smtp_host: str | None
smtp_port: int
smtp_security: SmtpSecurity
smtp_username: str | None
smtp_password_set: bool
smtp_from_address: str | None
smtp_to_addresses: list[str]
host_offline_enabled: bool
cert_expiring_enabled: bool
rule_drift_enabled: bool
revert_failed_enabled: bool
maintenance_unknown_enabled: bool
carp_health_enabled: bool
nat_drift_enabled: bool
network_drift_enabled: bool = True
carp_auto_revert_failed_enabled: bool
scheduled_maintenance_failed_enabled: bool = True
poll_interval_seconds: int
updated_at: datetime
class NotificationSettingsUpdate(BaseModel):
"""Partial update — everything optional, only provided fields change.
Secret values (smtp_password/webhook_signing_secret) are write-only:
provide a new value to set it, or the matching clear_* flag to remove
it — omitting both leaves the currently stored secret untouched, same
disambiguation pattern as HostUpdate.clear_cluster_name."""
webhook_enabled: bool | None = None
webhook_url: str | None = Field(default=None, max_length=500)
webhook_signing_secret: str | None = Field(default=None, min_length=1)
clear_webhook_signing_secret: bool = False
email_enabled: bool | None = None
smtp_host: str | None = Field(default=None, max_length=255)
smtp_port: int | None = Field(default=None, ge=1, le=65535)
smtp_security: SmtpSecurity | None = None
smtp_username: str | None = Field(default=None, max_length=255)
smtp_password: str | None = Field(default=None, min_length=1)
clear_smtp_password: bool = False
smtp_from_address: str | None = Field(default=None, max_length=255)
smtp_to_addresses: list[str] | None = None
host_offline_enabled: bool | None = None
cert_expiring_enabled: bool | None = None
rule_drift_enabled: bool | None = None
revert_failed_enabled: bool | None = None
maintenance_unknown_enabled: bool | None = None
carp_health_enabled: bool | None = None
nat_drift_enabled: bool | None = None
network_drift_enabled: bool | None = None
carp_auto_revert_failed_enabled: bool | None = None
scheduled_maintenance_failed_enabled: bool | None = None
poll_interval_seconds: int | None = Field(
default=None, ge=settings.notification_poll_min_interval_seconds
)
class NotificationProblemRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: uuid.UUID
problem_type: ProblemType
status: Literal["open", "resolved"]
context: dict
first_detected_at: datetime
last_seen_at: datetime
resolved_at: datetime | None
last_notified_at: datetime | None
resolved_notified_at: datetime | None
class TestNotificationRequest(BaseModel):
channel: Literal["webhook", "email"]