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
Mehr Verwaltung pro Firewall und ein Abgleich der Cluster-Knoten. Alles über dieselbe abgesicherte Engine wie in 1.12.0 und live auf drei Test-Firewalls geprüft, siehe Handbuch „Erweiterung 1.13.0“: - **Netzwerk › Geräte:** LAGG, Bridge, Loopback und GRE. Dazu Gateway-Gruppen, DNS-Weiterleitungen (Unbound) und DHCP-Bereiche (dnsmasq). - **Neue Gruppe „Host-System“:** Tunables (immer mit Bestätigung, Vorgaben nur per „Überschreiben“), Cron, Syslog-Ziele und ZFS-Snapshots (anlegen und löschen; der aktive Snapshot ist geschützt, das Aktivieren bleibt OPNsense selbst überlassen). - **Netzwerk › Abgleich:** zeigt Unterschiede in der Netzwerk-Konfiguration zwischen den Knoten eines Clusters. Neuer Alarm „Netzwerk-Abweichung im Cluster“ (Einstellungen › Benachrichtigungen, Migration 0004). - **Auf den Partner übertragen:** Nach dem Anlegen eines Objekts lässt es sich mit einem Klick auch auf dem anderen Cluster-Knoten anlegen (eigene Vorschau, eigenes Backup). - **Kopfzeile:** Mit acht Gruppen zeigt die Navigation die Namen ab 1150 px, darunter nur Icons, damit Suche und Abmelden in einer Zeile bleiben.
56 lines
2.0 KiB
Python
56 lines
2.0 KiB
Python
import uuid
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.database import get_db
|
|
from app.errors import ErrorCode, api_error
|
|
from app.models.host import OpnsenseHost
|
|
from app.routers.hosts import _upstream_error
|
|
from app.services import snapshot_service
|
|
from app.services.host_maintenance_service import ensure_no_maintenance
|
|
from app.services.opnsense_client import OpnsenseAPIError
|
|
from app.services.policy_service import policy_lock
|
|
|
|
router = APIRouter(prefix="/hosts/{host_id}/snapshots", tags=["snapshots"])
|
|
|
|
|
|
class SnapshotCreate(BaseModel):
|
|
name: str = Field(min_length=1, max_length=64)
|
|
note: str = Field(default="", max_length=200)
|
|
|
|
|
|
async def _host(db: AsyncSession, host_id: uuid.UUID) -> OpnsenseHost:
|
|
host = await db.get(OpnsenseHost, host_id)
|
|
if not host:
|
|
raise api_error(404, ErrorCode.HOST_NICHT_GEFUNDEN)
|
|
return host
|
|
|
|
|
|
@router.get("")
|
|
async def list_snapshots(host_id: uuid.UUID, db: AsyncSession = Depends(get_db)):
|
|
host = await _host(db, host_id)
|
|
try:
|
|
return await snapshot_service.list_snapshots(host)
|
|
except OpnsenseAPIError as exc:
|
|
raise _upstream_error(host, exc) from exc
|
|
|
|
|
|
@router.post("", status_code=201)
|
|
async def create_snapshot(host_id: uuid.UUID, payload: SnapshotCreate, db: AsyncSession = Depends(get_db)):
|
|
await policy_lock(db)
|
|
host = await _host(db, host_id)
|
|
await ensure_no_maintenance(db, [host])
|
|
return await snapshot_service.create(host, payload.name, payload.note)
|
|
|
|
|
|
@router.delete("/{snapshot_uuid}", status_code=204)
|
|
async def delete_snapshot(host_id: uuid.UUID, snapshot_uuid: str, db: AsyncSession = Depends(get_db)):
|
|
await policy_lock(db)
|
|
host = await _host(db, host_id)
|
|
await ensure_no_maintenance(db, [host])
|
|
if not snapshot_uuid.replace("-", "").isalnum() or len(snapshot_uuid) > 64:
|
|
raise api_error(404, ErrorCode.NETWORK_ITEM_NOT_FOUND)
|
|
await snapshot_service.delete(host, snapshot_uuid)
|