switchos support

This commit is contained in:
Mateusz Gruszczyński
2026-04-13 11:59:17 +02:00
parent 5163704b59
commit 4d2356f60b
28 changed files with 1142 additions and 330 deletions

View File

@@ -8,6 +8,7 @@ class BackupResponse(BaseModel):
id: int
router_id: int
router_name: str | None = None
device_type: str = "routeros"
file_path: str
file_name: str
backup_type: str

View File

@@ -1,16 +1,19 @@
import re
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, Field, field_validator
from pydantic import BaseModel, Field, field_validator, model_validator
ALLOWED_NAME_REGEX = re.compile(r"^[A-Za-z0-9_-]+$")
DeviceType = Literal["routeros", "switchos"]
class RouterBase(BaseModel):
name: str = Field(min_length=1, max_length=120)
device_type: DeviceType = "routeros"
host: str = Field(min_length=1, max_length=255)
port: int = Field(default=22, ge=1, le=65535)
ssh_user: str = Field(default="admin", min_length=1, max_length=120)
port: int | None = Field(default=None, ge=1, le=65535)
ssh_user: str | None = Field(default=None, max_length=120)
ssh_key: str | None = None
ssh_password: str | None = None
@@ -21,6 +24,23 @@ class RouterBase(BaseModel):
raise ValueError("Only letters, digits, dashes and underscores are allowed")
return value
@field_validator("host", "ssh_user", "ssh_key", "ssh_password", mode="before")
@classmethod
def normalize_text(cls, value: str | None) -> str | None:
normalized = (value or "").strip()
return normalized or None
@model_validator(mode="after")
def apply_device_defaults(self):
if self.device_type == "routeros":
self.port = self.port or 22
self.ssh_user = self.ssh_user or "admin"
return self
self.port = self.port or 80
self.ssh_key = None
return self
class RouterCreate(RouterBase):
pass
@@ -28,18 +48,30 @@ class RouterCreate(RouterBase):
class RouterUpdate(BaseModel):
name: str | None = None
device_type: DeviceType | None = None
host: str | None = None
port: int | None = Field(default=None, ge=1, le=65535)
ssh_user: str | None = None
ssh_key: str | None = None
ssh_password: str | None = None
@field_validator("name", "host", "ssh_user", "ssh_key", "ssh_password", mode="before")
@classmethod
def normalize_text(cls, value: str | None) -> str | None:
normalized = (value or "").strip()
return normalized or None
class RouterResponse(RouterBase):
id: int
owner_id: int
effective_username: str | None = None
uses_global_ssh_key: bool = False
has_effective_ssh_key: bool = False
uses_global_switchos_credentials: bool = False
has_effective_password: bool = False
supports_export: bool = False
supports_restore_upload: bool = False
last_connection_status: bool | None = None
last_connection_tested_at: datetime | None = None
last_connection_error: str | None = None
@@ -47,6 +79,11 @@ class RouterResponse(RouterBase):
last_connection_model: str | None = None
last_connection_version: str | None = None
last_connection_uptime: str | None = None
last_connection_transport: str | None = None
last_connection_server: str | None = None
last_connection_auth_mode: str | None = None
last_connection_http_status: str | None = None
last_connection_backup_available: bool | None = None
created_at: datetime | None = None
model_config = {"from_attributes": True}
@@ -60,3 +97,8 @@ class RouterTestConnection(BaseModel):
hostname: str
version: str | None = None
error: str | None = None
transport: str | None = None
server: str | None = None
auth_mode: str | None = None
http_status: str | None = None
backup_available: bool | None = None

View File

@@ -15,6 +15,8 @@ class SettingsBase(BaseModel):
enable_auto_export: bool = False
connection_test_interval_minutes: int = Field(default=0, ge=0, le=1440)
global_ssh_key: str | None = None
default_switchos_username: str | None = None
default_switchos_password: str | None = None
pushover_token: str | None = None
pushover_userkey: str | None = None
notify_failures_only: bool = True
@@ -30,9 +32,9 @@ class SettingsBase(BaseModel):
def normalize_cron(cls, value: str | None) -> str:
return (value or '').strip()
@field_validator('global_ssh_key', mode='before')
@field_validator('global_ssh_key', 'default_switchos_username', 'default_switchos_password', mode='before')
@classmethod
def normalize_key(cls, value: str | None) -> str | None:
def normalize_secret_text(cls, value: str | None) -> str | None:
normalized = (value or '').strip()
return normalized or None
@@ -55,6 +57,7 @@ class SettingsUpdate(SettingsBase):
class SettingsResponse(SettingsBase):
id: int
has_global_ssh_key: bool = False
has_default_switchos_credentials: bool = False
model_config = {'from_attributes': True}