- Add per-host rsync bandwidth limit overrides with inherit/unlimited semantics. - Store the effective bwlimit in run metadata/results and show it in host/run detail views. - Document recommended starting values for VPN and remote backups. ## Tests - `.venv/bin/python manage.py makemigrations --check --dry-run` - `.venv/bin/python manage.py test src.pobsync_backend.tests.test_django_config_source.DjangoConfigSourceTests.test_returns_effective_config_from_database src.pobsync_backend.tests.test_django_config_source.DjangoConfigSourceTests.test_host_can_disable_global_rsync_bandwidth_limit src.pobsync_backend.tests.test_configure_commands.ConfigureCommandsTests.test_configure_host_uses_global_retention_defaults src.pobsync_backend.tests.test_run_scheduled_config_source.RunScheduledConfigSourceTests.test_dry_run_applies_configured_bandwidth_limit src.pobsync_backend.tests.test_run_scheduled_config_source.RunScheduledConfigSourceTests.test_real_run_can_request_verbose_output_args --verbosity 2` - `.venv/bin/python manage.py test src.pobsync_backend.tests.test_views.ViewTests.test_create_host_config_form_creates_host src.pobsync_backend.tests.test_views.ViewTests.test_host_detail_renders_effective_config_preview src.pobsync_backend.tests.test_views.ViewTests.test_run_detail_renders_result_payload src.pobsync_backend.tests.test_views.ViewTests.test_host_config_form_updates_host_config --verbosity 2` - `.venv/bin/python manage.py check` Closes #51
102 lines
3.7 KiB
Python
102 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
|
|
from pobsync.config.schemas import GLOBAL_SCHEMA, HOST_SCHEMA
|
|
from pobsync.validate import validate_dict
|
|
|
|
from .models import GlobalConfig, HostConfig
|
|
|
|
|
|
class ConfigRepositoryError(RuntimeError):
|
|
pass
|
|
|
|
|
|
def _global_runtime_data(global_config: GlobalConfig) -> dict[str, Any]:
|
|
data = {
|
|
"backup_root": global_config.backup_root,
|
|
"ssh": {
|
|
"user": global_config.ssh_user,
|
|
"port": global_config.ssh_port,
|
|
"options": list(global_config.ssh_options or []),
|
|
},
|
|
"rsync": {
|
|
"binary": global_config.rsync_binary,
|
|
"args": list(global_config.rsync_args or []),
|
|
"timeout_seconds": global_config.rsync_timeout_seconds,
|
|
"bwlimit_kbps": global_config.rsync_bwlimit_kbps,
|
|
"extra_args": list(global_config.rsync_extra_args or []),
|
|
},
|
|
"defaults": {
|
|
"source_root": global_config.default_source_root,
|
|
"destination_subdir": global_config.default_destination_subdir,
|
|
},
|
|
"excludes_default": list(global_config.excludes_default or []),
|
|
"retention_defaults": {
|
|
"daily": global_config.retention_daily,
|
|
"weekly": global_config.retention_weekly,
|
|
"monthly": global_config.retention_monthly,
|
|
"yearly": global_config.retention_yearly,
|
|
},
|
|
}
|
|
return validate_dict(data, GLOBAL_SCHEMA, path="global")
|
|
|
|
|
|
def _host_runtime_data(host_config: HostConfig) -> dict[str, Any]:
|
|
data: dict[str, Any] = {
|
|
"host": host_config.host,
|
|
"address": host_config.address,
|
|
"includes": list(host_config.includes or []),
|
|
"retention": {
|
|
"daily": host_config.retention_daily,
|
|
"weekly": host_config.retention_weekly,
|
|
"monthly": host_config.retention_monthly,
|
|
"yearly": host_config.retention_yearly,
|
|
},
|
|
}
|
|
if host_config.ssh_user or host_config.ssh_port:
|
|
data["ssh"] = {}
|
|
if host_config.ssh_user:
|
|
data["ssh"]["user"] = host_config.ssh_user
|
|
if host_config.ssh_port is not None:
|
|
data["ssh"]["port"] = host_config.ssh_port
|
|
if host_config.source_root:
|
|
data["source_root"] = host_config.source_root
|
|
if host_config.excludes_replace is not None:
|
|
data["excludes_replace"] = list(host_config.excludes_replace or [])
|
|
else:
|
|
data["excludes_add"] = list(host_config.excludes_add or [])
|
|
if host_config.rsync_extra_args or host_config.rsync_bwlimit_kbps is not None:
|
|
data["rsync"] = {}
|
|
if host_config.rsync_extra_args:
|
|
data["rsync"]["extra_args"] = list(host_config.rsync_extra_args or [])
|
|
if host_config.rsync_bwlimit_kbps is not None:
|
|
data["rsync"]["bwlimit_kbps"] = host_config.rsync_bwlimit_kbps
|
|
return validate_dict(data, HOST_SCHEMA, path="host")
|
|
|
|
|
|
def global_config_object_data(global_config: GlobalConfig) -> dict[str, Any]:
|
|
return _global_runtime_data(global_config)
|
|
|
|
|
|
def host_config_object_data(host_config: HostConfig) -> dict[str, Any]:
|
|
return _host_runtime_data(host_config)
|
|
|
|
|
|
def global_config_data(name: str = "default") -> dict[str, Any]:
|
|
try:
|
|
global_config = GlobalConfig.objects.get(name=name)
|
|
except ObjectDoesNotExist as exc:
|
|
raise ConfigRepositoryError(f"Missing global config {name!r}") from exc
|
|
return _global_runtime_data(global_config)
|
|
|
|
|
|
def host_config_data(host: str) -> dict[str, Any]:
|
|
try:
|
|
host_config = HostConfig.objects.get(host=host, enabled=True)
|
|
except ObjectDoesNotExist as exc:
|
|
raise ConfigRepositoryError(f"Missing enabled host {host!r}") from exc
|
|
return _host_runtime_data(host_config)
|