Optimize firewall state collection and improve daemon shutdown

- Replace per-zone --list-all calls with single --list-all-zones in _collect_firewall
- Add _parse_all_zones_output() parser with rich rules/rich-rules normalization
- Convert daemon shutdown to async with proper runner cleanup and socket unlink
- Add TimeoutStopSec=15 to vacuum-walld.service for graceful stop
- Fix exception handling in _collect_dnsmasq
- Remove management badge from proxy path rows
This commit is contained in:
2026-06-28 00:54:01 +00:00
parent 80dd4e3272
commit 25a1943fce
7 changed files with 190 additions and 33 deletions
+48 -1
View File
@@ -66,7 +66,9 @@ def _parse_interfaces(output: str) -> list[str]:
def _parse_zone_output(zone: str, output: str) -> dict[str, Any]:
"""Parse ``firewall-cmd --zone=Z --list-all`` output."""
"""Parse ``firewall-cmd --zone=Z --list-all`` or a zone block
from ``--list-all-zones`` output.
"""
info: dict[str, Any] = {"name": zone}
for line in output.splitlines():
line = line.strip()
@@ -76,6 +78,11 @@ def _parse_zone_output(zone: str, output: str) -> dict[str, Any]:
key = key.strip()
value = value.strip()
# --list-all-zones uses "rich rules" (space) while
# --zone=Z --list-all uses "rich-rules" (hyphen); normalize.
if key == "rich rules":
key = "rich-rules"
if not value:
if key in ("masquerade", "ics"):
info[key] = False
@@ -116,6 +123,45 @@ def _parse_zone_output(zone: str, output: str) -> dict[str, Any]:
return info
def _parse_all_zones_output(output: str) -> dict[str, dict[str, Any]]:
"""Parse the combined ``firewall-cmd --list-all-zones`` output.
Returns a dict mapping each zone name to its parsed info dict
(same structure as ``_parse_zone_output``).
"""
zones: dict[str, dict[str, Any]] = {}
current_name: str | None = None
current_lines: list[str] = []
for raw_line in output.splitlines():
if not raw_line.strip():
continue
# Non-indented line starts a new zone block
if raw_line[0].isspace():
if current_name is not None:
current_lines.append(raw_line.strip())
else:
# Finalize previous zone
if current_name is not None and current_lines:
zones[current_name] = _parse_zone_output(
current_name, "\n".join(current_lines)
)
# Extract zone name (discard trailing parenthetical metadata)
name = raw_line.strip().split()[0]
if "(" in name:
name = name[: name.index("(")]
current_name = name
current_lines = []
# Finalize last zone
if current_name is not None and current_lines:
zones[current_name] = _parse_zone_output(
current_name, "\n".join(current_lines)
)
return zones
# ---------------------------------------------------------------------------
# Helpers for parsing forward-port lines
# ---------------------------------------------------------------------------
@@ -347,6 +393,7 @@ __all__ = [
"_normalize_target",
"_now_iso",
"_parse_active_zones",
"_parse_all_zones_output",
"_parse_forward_ports",
"_parse_interfaces",
"_parse_zone_output",