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:
+64
-12
@@ -43,6 +43,7 @@ from lib.firewall import (
|
||||
from lib.firewall import (
|
||||
save_backup as _save_backup,
|
||||
)
|
||||
from lib.sync import SyncEvent, bus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -350,7 +351,10 @@ def save_config_handler(_request: Any, body: dict[str, Any] | None) -> dict[str,
|
||||
raise ValueError("'zones' must be a dict")
|
||||
_save_config(body)
|
||||
logger.info("Firewall config saved (%d zones)", len(body["zones"]))
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("firewall", "config_saved", {"action": "config_saved"})
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"config_saved": True}
|
||||
|
||||
|
||||
@@ -364,7 +368,10 @@ def patch_config(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
merged = deep_merge(current, body)
|
||||
_save_config(merged)
|
||||
logger.info("Firewall config patched: %s", sorted(body.keys()))
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("firewall", "config_saved", {"action": "config_patched"})
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"config_saved": True}
|
||||
|
||||
|
||||
@@ -378,7 +385,11 @@ def config_pending_handler(_request: Any, _body: Any) -> dict[str, Any]:
|
||||
def config_apply(_request: Any, _body: Any) -> dict[str, Any]:
|
||||
result = _config_apply()
|
||||
logger.info("Firewall config applied: %s", result.get("applied_zones", []))
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("firewall", "config_saved", {"action": "config_applied"})
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
result["synced"] = sync_result.affected_subsystems
|
||||
return result
|
||||
|
||||
|
||||
@@ -404,7 +415,12 @@ def create_zone(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
)
|
||||
_reload()
|
||||
logger.info("Zone '%s' created (target=%s)", zone_name, target)
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"firewall", "config_saved", {"action": "zone_created", "zone": zone_name}
|
||||
)
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone_name}
|
||||
|
||||
|
||||
@@ -419,7 +435,10 @@ def delete_zone(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
run(["firewall-cmd", f"--zone={zone}", "--delete", "--permanent"], sudo=True)
|
||||
_reload()
|
||||
logger.info("Zone '%s' deleted", zone)
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("firewall", "config_saved", {"action": "zone_deleted", "zone": zone})
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone}
|
||||
|
||||
|
||||
@@ -489,7 +508,12 @@ def set_zone_interfaces(_request: Any, body: dict[str, Any] | None) -> dict[str,
|
||||
_save_config(cfg)
|
||||
|
||||
logger.info("Zone '%s' interfaces set to %s", zone, interfaces)
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"firewall", "config_saved", {"action": "interfaces_set", "zone": zone}
|
||||
)
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone, "interfaces": interfaces}
|
||||
|
||||
|
||||
@@ -528,7 +552,10 @@ def set_zone_services(_request: Any, body: dict[str, Any] | None) -> dict[str, A
|
||||
sudo=True,
|
||||
)
|
||||
_reload()
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("firewall", "config_saved", {"action": "services_set", "zone": zone})
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone, "services": services}
|
||||
|
||||
|
||||
@@ -559,7 +586,12 @@ def add_rich_rule(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
entry = {"id": rule_id, "rule": rule}
|
||||
cfg["zones"][zone]["rich_rules"].append(entry)
|
||||
_save_config(cfg)
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"firewall", "config_saved", {"action": "rich_rule_added", "zone": zone}
|
||||
)
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone, "id": rule_id, "rule": rule}
|
||||
|
||||
|
||||
@@ -597,7 +629,12 @@ def remove_rich_rule(_request: Any, body: dict[str, Any] | None) -> dict[str, An
|
||||
r for r in zone_cfg.get("rich_rules", []) if r.get("id") != rule_id
|
||||
]
|
||||
_save_config(cfg)
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"firewall", "config_saved", {"action": "rich_rule_removed", "zone": zone}
|
||||
)
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone, "id": rule_id}
|
||||
|
||||
|
||||
@@ -632,7 +669,12 @@ def set_masquerade(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]
|
||||
action = "--add-masquerade" if enable else "--remove-masquerade"
|
||||
run(["firewall-cmd", f"--zone={zone}", action, "--permanent"], sudo=True)
|
||||
_reload()
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"firewall", "config_saved", {"action": "masquerade_set", "zone": zone}
|
||||
)
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone, "masquerade": bool(enable)}
|
||||
|
||||
|
||||
@@ -675,7 +717,12 @@ def add_forward_port(_request: Any, body: dict[str, Any] | None) -> dict[str, An
|
||||
cfg.setdefault("zones", {}).setdefault(zone, {}).setdefault("forward_ports", [])
|
||||
cfg["zones"][zone]["forward_ports"].append(entry)
|
||||
_save_config(cfg)
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"firewall", "config_saved", {"action": "forward_port_added", "zone": zone}
|
||||
)
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone, "id": fp_id, "port": int(port), "proto": proto}
|
||||
|
||||
|
||||
@@ -722,7 +769,12 @@ def remove_forward_port(_request: Any, body: dict[str, Any] | None) -> dict[str,
|
||||
fp for fp in fps if not (fp.get("port") == port and fp.get("proto") == proto)
|
||||
]
|
||||
_save_config(cfg)
|
||||
refresh_state(["firewall"])
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"firewall", "config_saved", {"action": "forward_port_removed", "zone": zone}
|
||||
)
|
||||
)
|
||||
refresh_state(["firewall", *sync_result.affected_subsystems])
|
||||
return {"zone": zone, "port": int(port), "proto": proto}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user