Development
This commit is contained in:
parent
205d6889df
commit
58ab569e42
27 changed files with 2894 additions and 2605 deletions
136
routlin/mod_avahi.py
Normal file
136
routlin/mod_avahi.py
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
"""
|
||||
mod_avahi.py -- Avahi mDNS reflector configuration and management.
|
||||
|
||||
Patches avahi-daemon.conf with the correct interface list and manages
|
||||
the avahi-daemon service lifecycle.
|
||||
"""
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import mod_shared as shared
|
||||
import mod_validation as validation
|
||||
|
||||
AVAHI_CONF_FILE = Path("/etc/avahi/avahi-daemon.conf")
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# State helpers
|
||||
# ===================================================================
|
||||
|
||||
def avahi_enabled(data):
|
||||
"""Return True if at least one non-WireGuard VLAN has mdns_reflection enabled."""
|
||||
return any(v.get("mdns_reflection") is True
|
||||
for v in data.get("vlans", [])
|
||||
if not validation.is_wg(v))
|
||||
|
||||
def avahi_interfaces(data):
|
||||
"""Return list of interface names for VLANs with mdns_reflection enabled."""
|
||||
return [
|
||||
validation.derive_interface(v, data)
|
||||
for v in data.get("vlans", [])
|
||||
if v.get("mdns_reflection") is True and not validation.is_wg(v)
|
||||
]
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# Config generation
|
||||
# ===================================================================
|
||||
|
||||
def build_avahi_conf(data):
|
||||
"""Patch avahi-daemon.conf directives needed for cross-VLAN mDNS reflection.
|
||||
Reads the existing file (default or previously patched) and modifies only
|
||||
the specific directives we need, leaving everything else untouched.
|
||||
Returns the patched config as a string. Caller is responsible for ensuring
|
||||
the file exists before calling (apply_avahi guards this).
|
||||
"""
|
||||
ifaces = avahi_interfaces(data)
|
||||
content = AVAHI_CONF_FILE.read_text()
|
||||
|
||||
def set_directive(text, directive, value):
|
||||
"""Enable and set a directive, whether it is commented out or already set."""
|
||||
pattern = re.compile(
|
||||
rf"^#?\s*{re.escape(directive)}\s*=.*$", re.MULTILINE
|
||||
)
|
||||
replacement = f"{directive}={value}"
|
||||
if pattern.search(text):
|
||||
return pattern.sub(replacement, text)
|
||||
return text + f"\n{replacement}\n"
|
||||
|
||||
content = set_directive(content, "use-ipv6", "no")
|
||||
content = set_directive(content, "disallow-other-stacks", "yes")
|
||||
content = set_directive(content, "allow-interfaces", ",".join(ifaces))
|
||||
content = set_directive(content, "enable-reflector", "yes")
|
||||
content = set_directive(content, "disable-publishing", "yes")
|
||||
|
||||
return content
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# Apply
|
||||
# ===================================================================
|
||||
|
||||
def apply_avahi(data):
|
||||
"""Write avahi-daemon.conf and ensure service is running."""
|
||||
if not shutil.which("avahi-daemon"):
|
||||
print("avahi-daemon is not installed.")
|
||||
print(" -> Run: sudo python3 install.py")
|
||||
return
|
||||
|
||||
ifaces = avahi_interfaces(data)
|
||||
|
||||
if len(ifaces) < 2:
|
||||
print("mDNS reflection requires at least two VLANs in reflect_vlans. Skipping.")
|
||||
return
|
||||
|
||||
if not AVAHI_CONF_FILE.exists():
|
||||
print(f"WARNING: {AVAHI_CONF_FILE} not found. Run: sudo python3 install.py")
|
||||
return
|
||||
|
||||
content = build_avahi_conf(data)
|
||||
existing = AVAHI_CONF_FILE.read_text()
|
||||
changed = existing != content
|
||||
if changed:
|
||||
AVAHI_CONF_FILE.write_text(content)
|
||||
print(f"Written: {AVAHI_CONF_FILE}")
|
||||
print(f" Reflecting mDNS across: {', '.join(ifaces)}")
|
||||
else:
|
||||
print(f"Unchanged: {AVAHI_CONF_FILE}")
|
||||
|
||||
svc = "avahi-daemon"
|
||||
state = subprocess.run(
|
||||
["systemctl", "is-active", svc], capture_output=True, text=True
|
||||
).stdout.strip()
|
||||
|
||||
if state == "active":
|
||||
if changed:
|
||||
result = subprocess.run(["systemctl", "restart", svc],
|
||||
capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
print("avahi-daemon restarted.")
|
||||
else:
|
||||
shared.service_warning("restart", "avahi-daemon", result.stderr)
|
||||
else:
|
||||
print("avahi-daemon: running, config unchanged.")
|
||||
else:
|
||||
subprocess.run(["systemctl", "enable", svc], capture_output=True, text=True)
|
||||
result = subprocess.run(["systemctl", "start", svc],
|
||||
capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
print("avahi-daemon started.")
|
||||
else:
|
||||
shared.service_warning("start", "avahi-daemon", result.stderr)
|
||||
|
||||
def disable_avahi():
|
||||
"""Stop and disable avahi-daemon."""
|
||||
result = subprocess.run(
|
||||
["systemctl", "is-active", "avahi-daemon"], capture_output=True, text=True
|
||||
)
|
||||
if result.stdout.strip() == "active":
|
||||
subprocess.run(["systemctl", "disable", "--now", "avahi-daemon"],
|
||||
capture_output=True, text=True)
|
||||
print("avahi-daemon stopped and disabled.")
|
||||
else:
|
||||
print("avahi-daemon: not running, skipping.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue