refactor
This commit is contained in:
+91
@@ -0,0 +1,91 @@
|
||||
"""DNS update helper and thread for heartbeat daemon."""
|
||||
from __future__ import annotations
|
||||
import threading
|
||||
import subprocess
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def create_nsupdate_payload(hostname: str, newip: str, dyndomain: str, dnsttl: str = "5") -> str:
|
||||
D = {"domain": dyndomain, "fqdn": f"{hostname}.dy.{dyndomain}", "dnsttl": dnsttl, "newip": newip, "ts": __import__("time").strftime("%Y-%m-%d.%H:%M:%S", __import__("time").gmtime())}
|
||||
if ":" in newip:
|
||||
nsup = (
|
||||
"""update delete %(fqdn)s AAAA
|
||||
update add %(fqdn)s %(dnsttl)s AAAA %(newip)s
|
||||
update delete %(fqdn)s TXT
|
||||
update add %(fqdn)s %(dnsttl)s TXT "Created: %(ts)s"
|
||||
send
|
||||
answer
|
||||
|
||||
""" % D
|
||||
)
|
||||
else:
|
||||
nsup = (
|
||||
"""update delete %(fqdn)s A
|
||||
update add %(fqdn)s %(dnsttl)s A %(newip)s
|
||||
update delete %(fqdn)s TXT
|
||||
update add %(fqdn)s %(dnsttl)s TXT "Created: %(ts)s"
|
||||
send
|
||||
answer
|
||||
|
||||
""" % D
|
||||
)
|
||||
return nsup
|
||||
|
||||
|
||||
def nsupdate(hostname: str, newip: str, dyndomain: str, nsupdate_bin: str = "/usr/local/bin/nsupdate", rndc_key: str = "/etc/dhcpc/rndc-key") -> Optional[str]:
|
||||
"""Perform DNS update via nsupdate command.
|
||||
|
||||
Returns None on success, else returns combined stdout/stderr as a string.
|
||||
"""
|
||||
nsup = create_nsupdate_payload(hostname, newip, dyndomain)
|
||||
cmd = [nsupdate_bin, "-k", rndc_key, "-v"]
|
||||
try:
|
||||
p = Popen(cmd, shell=False, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
|
||||
except OSError as e:
|
||||
return f"nsupdate: execution failed: {e}"
|
||||
except Exception as e:
|
||||
return f"nsupdate: some error occured: {e}"
|
||||
|
||||
(output, err) = p.communicate(nsup.encode())
|
||||
out = output.decode() if output else ""
|
||||
if out.find("status: NOERROR") >= 0:
|
||||
return None
|
||||
return out
|
||||
|
||||
|
||||
def dnsupdatethread(hbdclass, cfg: dict, log: Optional[callable] = None, email: Optional[callable] = None):
|
||||
"""Thread target: process dns update queue from hbdclass.Host.dnsQ.
|
||||
|
||||
hbdclass: module with Host class that exposes dnsQ queue
|
||||
cfg: configuration mapping with 'dyndomains' and 'nsupdate_bin'
|
||||
log: callable(host, message)
|
||||
email: callable(subject, message)
|
||||
"""
|
||||
while True:
|
||||
name, addr = hbdclass.Host.dnsQ.get()
|
||||
m = f"changed address to {addr}"
|
||||
for dyndomain in cfg.get("dyndomains", []):
|
||||
err = nsupdate(name, addr, dyndomain, nsupdate_bin=cfg.get("nsupdate_bin", "/usr/local/bin/nsupdate"))
|
||||
if err:
|
||||
m += f", DNS update failed: {err}"
|
||||
if email:
|
||||
try:
|
||||
email("error: nsupdate failed", f"{name}.dy.{dyndomain}: {m}")
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
m += ", DNS updated."
|
||||
hbdclass.Host.dnsQ.task_done()
|
||||
if log:
|
||||
try:
|
||||
log(name, m)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def start_dns_thread(hbdclass, cfg: dict, log: Optional[callable] = None, email: Optional[callable] = None) -> threading.Thread:
|
||||
t = threading.Thread(target=dnsupdatethread, args=(hbdclass, cfg, log, email))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
return t
|
||||
Reference in New Issue
Block a user