unbound+DHCP: Read existing leases from unbound

This allows us to restart unbound and all DHCP leases
will be re-imported even if the unbound-dhcp-leases-bridge is
not restarted.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
This commit is contained in:
Michael Tremer
2016-09-14 15:54:36 +01:00
parent 077ea717e0
commit b8dd42b9a6

View File

@@ -257,10 +257,10 @@ class Lease(object):
def rrset(self): def rrset(self):
return [ return [
# Forward record # Forward record
(self.fqdn, LOCAL_TTL, "IN A", self.ipaddr), (self.fqdn, "%s" % LOCAL_TTL, "IN A", self.ipaddr),
# Reverse record # Reverse record
(self.ipaddr, LOCAL_TTL, "IN PTR", self.fqdn), (self.ipaddr, "%s" % LOCAL_TTL, "IN PTR", self.fqdn),
] ]
@@ -268,33 +268,63 @@ class UnboundConfigWriter(object):
def __init__(self, path): def __init__(self, path):
self.path = path self.path = path
self._cached_leases = [] @property
def existing_leases(self):
local_data = self._control("list_local_data")
ret = {}
for line in local_data.splitlines():
try:
hostname, ttl, x, record_type, content = line.split("\t")
except ValueError:
continue
# Ignore everything that is not A or PTR
if not record_type in ("A", "PTR"):
continue
if hostname.endswith("."):
hostname = hostname[:-1]
if content.endswith("."):
content = content[:-1]
if record_type == "A":
ret[hostname] = content
elif record_type == "PTR":
ret[content] = hostname
return ret
def update_dhcp_leases(self, leases): def update_dhcp_leases(self, leases):
# Strip all non-active or expired leases # Strip all non-active or expired leases
leases = [l for l in leases if l.active and not l.expired] leases = [l for l in leases if l.active and not l.expired]
# Find any leases that have expired or do not exist any more # Find any leases that have expired or do not exist any more
removed_leases = [l for l in self._cached_leases if l.expired or l not in leases] removed_leases = []
for fqdn, address in self.existing_leases.items():
if not fqdn in (l.fqdn for l in leases):
removed_leases += [fqdn, address]
# Find any leases that have been added # Find any leases that have been added
new_leases = [l for l in leases if l not in self._cached_leases] new_leases = [l for l in leases
if l.fqdn not in self.existing_leases]
# End here if nothing has changed # End here if nothing has changed
if not new_leases and not removed_leases: if not new_leases and not removed_leases:
return return
self._cached_leases = leases
# Write out all leases # Write out all leases
self.write_dhcp_leases(leases) self.write_dhcp_leases(leases)
# Update unbound about changes # Update unbound about changes
for l in removed_leases: for hostname in removed_leases:
self._control("local_data_remove", l.fqdn) log.debug("Removing all records for %s" % hostname)
self._control("local_data_remove", hostname)
for l in new_leases: for l in new_leases:
for rr in l.rrset: for rr in l.rrset:
log.debug("Adding new record %s" % " ".join(rr))
self._control("local_data", *rr) self._control("local_data", *rr)
@@ -305,11 +335,11 @@ class UnboundConfigWriter(object):
f.write("local-data: \"%s\"\n" % " ".join(rr)) f.write("local-data: \"%s\"\n" % " ".join(rr))
def _control(self, *args): def _control(self, *args):
command = ["unbound-control", "-q"] command = ["unbound-control"]
command.extend(args) command.extend(args)
try: try:
subprocess.check_call(command) return subprocess.check_output(command)
# Log any errors # Log any errors
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e: