sync: add cross-subsystem event bus for config consistency
Add EventBus with loop guards to keep firewall, dnsmasq, wireguard, and network configs consistent. Handlers emit SyncEvent after mutations; subscribers compute diffs and write JSON without manual cascade loops.
This commit is contained in:
+75
-15
@@ -26,7 +26,8 @@ from daemon.iface import (
|
||||
POST_DNSMASQ_UPSTREAMS,
|
||||
)
|
||||
from daemon.server import NotFoundError, refresh_state, registry
|
||||
from lib.common import deep_merge, ensure_dirs, load_json, run, run_proc, save_json
|
||||
from lib.common import deep_merge, ensure_dirs, load_json, run, save_json
|
||||
from lib.sync import SyncEvent, bus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -156,7 +157,10 @@ def save_config_handler(_request: Any, body: dict[str, Any] | None) -> dict[str,
|
||||
if not body:
|
||||
raise ValueError("Request body required")
|
||||
_save_config(body)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "config_saved"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"config_saved": True}
|
||||
|
||||
|
||||
@@ -172,7 +176,10 @@ def patch_config(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
current = _get_config()
|
||||
merged = deep_merge(current, body)
|
||||
_save_config(merged)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "config_patched"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"config_saved": True}
|
||||
|
||||
|
||||
@@ -198,8 +205,11 @@ def apply_config(_request: Any, _body: Any) -> dict[str, Any]:
|
||||
cfg_after = _get_config()
|
||||
cfg_after[_APPLY_HASH_KEY] = _config_hash(cfg_after)
|
||||
_save_config(cfg_after)
|
||||
refresh_state(["dnsmasq"])
|
||||
return {"applied": True}
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "config_applied"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"applied": True, "synced": sync_result.affected_subsystems}
|
||||
|
||||
|
||||
@registry.register(GET_DNSMASQ_STATUS)
|
||||
@@ -260,7 +270,12 @@ def set_dhcp_range(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]
|
||||
entry["dns"] = body["dns"]
|
||||
ranges.append(entry)
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "range_added", "interface": iface}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"interface": iface, "start": start, "end": end}
|
||||
|
||||
|
||||
@@ -295,7 +310,12 @@ def remove_dhcp_range(_request: Any, body: dict[str, Any] | None) -> dict[str, A
|
||||
f"DHCP range for interface '{iface}' ({start}-{end}) not found"
|
||||
)
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "range_removed", "interface": iface}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"interface": iface, "start": start, "end": end}
|
||||
|
||||
|
||||
@@ -334,14 +354,26 @@ def add_static_lease(_request: Any, body: dict[str, Any] | None) -> dict[str, An
|
||||
if hostname is not None:
|
||||
leases[i]["hostname"] = hostname
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq",
|
||||
"config_saved",
|
||||
{"action": "static_lease_added", "mac": mac},
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"mac": mac, "ip": ip, "hostname": hostname}
|
||||
entry: dict[str, Any] = {"mac": mac, "ip": ip}
|
||||
if hostname:
|
||||
entry["hostname"] = hostname
|
||||
leases.append(entry)
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "static_lease_added", "mac": mac}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"mac": mac, "ip": ip, "hostname": hostname}
|
||||
|
||||
|
||||
@@ -366,7 +398,12 @@ def remove_static_lease(_request: Any, body: dict[str, Any] | None) -> dict[str,
|
||||
if len(cfg["dhcp"]["static_leases"]) == before:
|
||||
raise NotFoundError(f"Static lease for MAC '{mac}' not found")
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "static_lease_removed", "mac": mac}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"mac": mac}
|
||||
|
||||
|
||||
@@ -392,14 +429,26 @@ def add_dns_record(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]
|
||||
if hostname is not None:
|
||||
records[i]["hostname"] = hostname
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq",
|
||||
"config_saved",
|
||||
{"action": "dns_record_added", "name": name},
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"name": name, "address": address, "hostname": hostname}
|
||||
entry: dict[str, Any] = {"name": name, "address": address}
|
||||
if hostname:
|
||||
entry["hostname"] = hostname
|
||||
records.append(entry)
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "dns_record_added", "name": name}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"name": name, "address": address, "hostname": hostname}
|
||||
|
||||
|
||||
@@ -422,7 +471,12 @@ def remove_dns_record(_request: Any, body: dict[str, Any] | None) -> dict[str, A
|
||||
if len(cfg["dns"]["custom_records"]) == before:
|
||||
raise NotFoundError(f"DNS record '{name}' not found")
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "dns_record_removed", "name": name}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"name": name}
|
||||
|
||||
|
||||
@@ -438,7 +492,10 @@ def set_upstreams(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
cfg = _get_config()
|
||||
cfg["dns"]["upstreams"] = list(body["servers"])
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "upstreams_set"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"upstreams": cfg["dns"]["upstreams"]}
|
||||
|
||||
|
||||
@@ -455,5 +512,8 @@ def set_domain(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
cfg = _get_config()
|
||||
cfg["dns"]["domain"] = domain if domain else None
|
||||
_save_config(cfg)
|
||||
refresh_state(["dnsmasq"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "domain_set"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"domain": cfg["dns"]["domain"]}
|
||||
|
||||
Reference in New Issue
Block a user