Development

This commit is contained in:
Matthew Grotke 2026-05-21 03:23:31 -04:00
parent d04e6ca8cc
commit 21db91d512
2 changed files with 520 additions and 54 deletions

View file

@ -73,7 +73,6 @@ Validation:
must be tcp, udp, or both.
Usage:
sudo python3 core.py --install Check and interactively install required packages
sudo python3 core.py --apply Apply config fast: restart running services only
sudo python3 core.py --update-blocklists Refresh blocklists and apply (used by timer)
sudo python3 core.py --status Show service and timer status
@ -183,11 +182,11 @@ def setup_logging(max_kb, errors_only):
# ===================================================================
def service_warning(action, svc, stderr):
"""Print a service start/restart warning, adding --install hint if unit not found."""
"""Print a service start/restart warning, adding install hint if unit not found."""
msg = stderr.strip()
print(f"WARNING: Failed to {action} {svc}: {msg}")
if "not found" in msg.lower() or "not-found" in msg.lower():
print(f" -> Package may not be installed. Run: sudo python3 core.py --install")
print(f" -> Package may not be installed. Run: sudo python3 install.py")
def die(msg):
@ -802,34 +801,6 @@ def ensure_resolv_conf(data):
RESOLV_CONF.write_text(wanted)
print(f"Updated /etc/resolv.conf: nameserver {nameserver}")
def check_dependencies():
"""Check required packages are installed; prompt to install missing ones via apt."""
import shutil
checks = [
("dnsmasq", "dnsmasq", "DHCP server and DNS resolver"),
("nft", "nftables", "firewall and NAT"),
("chronyd", "chrony", "NTP server"),
("freeradius", "freeradius", "RADIUS server for dynamic VLAN assignment"),
("avahi-daemon", "avahi-daemon", "mDNS reflector for cross-VLAN service discovery"),
]
missing = [(pkg, purpose) for binary, pkg, purpose in checks if not shutil.which(binary)]
if not missing:
return
print("The following required packages are not installed:")
for pkg, purpose in missing:
print(f" {pkg:<16} {purpose}")
print()
while True:
choice = input("Install them now via apt? [y/N]: ").strip().lower()
if choice in ("y", "yes"):
break
if choice in ("n", "no", ""):
die("Cannot continue without required packages. Install them and retry.")
result = subprocess.run(["apt-get", "install", "-y"] + [p for p, _ in missing])
if result.returncode != 0:
die("Package installation failed. Install manually and retry.")
def disable_systemd_resolved():
"""Stop and disable systemd-resolved if it is active."""
@ -2137,7 +2108,7 @@ def apply_avahi(data):
import shutil
if not shutil.which("avahi-daemon"):
print("avahi-daemon is not installed.")
print(" -> Run: sudo python3 core.py --install")
print(" -> Run: sudo python3 install.py")
return
ifaces = avahi_interfaces(data)
@ -2147,7 +2118,7 @@ def apply_avahi(data):
return
if not AVAHI_CONF_FILE.exists():
print(f"WARNING: {AVAHI_CONF_FILE} not found. Run: sudo python3 core.py --install")
print(f"WARNING: {AVAHI_CONF_FILE} not found. Run: sudo python3 install.py")
return
content = build_avahi_conf(data)
@ -3063,20 +3034,6 @@ def cmd_disable(data, dry_run=False):
# ===================================================================
def cmd_install(data):
"""--install: check and interactively install required packages."""
check_root()
check_dependencies()
print("All required packages are installed.")
install_dashboard_timer()
# Create blank dotfiles for dashboard updates
for dotfile in (DASHB_QUEUE_FILE, DASHB_DONE_FILE, DASHB_LAST_RUN_FILE, DASHB_LOCK_FILE):
if not dotfile.exists():
dotfile.touch()
chown_to_script_dir_owner(dotfile)
print(f"Created: {dotfile}")
def cmd_apply(data, dry_run=False):
"""--apply: full apply. Handles conflicting services, networkd (if changed),
dnsmasq confs, start/restart all services whose interface is up, nftables,
@ -3230,7 +3187,6 @@ def main():
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=(
"examples:\n"
" sudo python3 core.py --install Install required packages\n"
" sudo python3 core.py --apply Apply full config (idempotent, safe to re-run)\n"
" sudo python3 core.py --update-blocklists Refresh blocklists and apply\n"
" sudo python3 core.py --status Show service and timer status\n"
@ -3246,7 +3202,6 @@ def main():
" sudo python3 core.py --disable --dry-run\n"
)
)
parser.add_argument("--install", action="store_true", help="Check and interactively install required packages")
parser.add_argument("--apply", action="store_true", help="Apply full config: services, networkd, dnsmasq, nftables, timer, boot service")
parser.add_argument("--update-blocklists", action="store_true", help="Refresh blocklists and apply (used by timer)")
parser.add_argument("--dry-run", action="store_true", help="Preview all actions without making changes (combine with --apply or --disable)")
@ -3264,7 +3219,7 @@ def main():
update_blocklists_flag = getattr(args, "update_blocklists", False)
if not any([args.install, args.apply, update_blocklists_flag,
if not any([args.apply, update_blocklists_flag,
args.dry_run, args.status, args.view_configs, args.view_leases,
args.view_rules, args.disable, args.view_metrics,
args.reset_leases]):
@ -3320,10 +3275,6 @@ def main():
cmd_disable(data, dry_run=args.dry_run)
return
if args.install:
cmd_install(data)
return
if update_blocklists_flag:
cmd_update_blocklists(data)
return