Files
truenas-stacks/pihole/bin/render_keepalived_conf.py
2026-05-15 03:17:56 +00:00

68 lines
2.2 KiB
Python
Executable File

#!/usr/bin/env python3
from __future__ import annotations
import os
import re
import sys
from pathlib import Path
STACK_DIR = Path(__file__).resolve().parent.parent
ENV_FILE = Path(os.environ.get('ENV_FILE', STACK_DIR / '.env'))
TEMPLATE = STACK_DIR / 'keepalived.conf.template'
OUTPUT = STACK_DIR / 'keepalived.conf'
def load_env(path: Path) -> dict[str, str]:
env = dict(os.environ)
if path.exists():
for raw in path.read_text(encoding='utf-8').splitlines():
line = raw.strip()
if not line or line.startswith('#') or '=' not in line:
continue
k, v = line.split('=', 1)
env.setdefault(k.strip(), v.strip().strip('"').strip("'"))
return env
def main() -> int:
env = load_env(ENV_FILE)
required = [
'LAN_INTERFACE', 'VIP_CIDR', 'KEEPALIVED_ROUTER_ID', 'KEEPALIVED_STATE',
'KEEPALIVED_PRIORITY', 'KEEPALIVED_PASSWORD', 'PD_PIHOLE_WEB_PORT'
]
missing = [k for k in required if not env.get(k)]
if missing:
print(f'Missing required vars in {ENV_FILE}: {", ".join(missing)}', file=sys.stderr)
return 1
keepalived_password = env['KEEPALIVED_PASSWORD']
if len(keepalived_password) > 8:
print('KEEPALIVED_PASSWORD must be 8 characters or fewer for VRRP PASS auth.', file=sys.stderr)
return 1
try:
web_port_hex = f"{int(env['PD_PIHOLE_WEB_PORT']):04X}"
except ValueError:
print('PD_PIHOLE_WEB_PORT must be an integer.', file=sys.stderr)
return 1
router_id_name = f"pihole_{re.sub(r'[^a-z0-9]+', '_', env['KEEPALIVED_STATE'].lower())}"
nopreempt_line = ' nopreempt' if env.get('KEEPALIVED_NOPREEMPT', 'true').lower() in {'1','true','yes','on'} else ''
rendered = TEMPLATE.read_text(encoding='utf-8')
replacements = {
'ROUTER_ID_NAME': router_id_name,
'PIHOLE_WEB_PORT_HEX': web_port_hex,
'NOPREEMPT_LINE': nopreempt_line,
}
replacements.update({k: env[k] for k in required})
for key, value in replacements.items():
rendered = rendered.replace('${' + key + '}', value)
OUTPUT.write_text(rendered.rstrip() + '\n', encoding='utf-8')
print(f'Rendered {OUTPUT}')
return 0
if __name__ == '__main__':
raise SystemExit(main())