return ".".join(parts)
class UnboundDHCPLeasesBridge(object):
- def __init__(self, dhcp_leases_file, unbound_leases_file):
+ def __init__(self, dhcp_leases_file, fix_leases_file, unbound_leases_file):
self.leases_file = dhcp_leases_file
+ self.fix_leases_file = fix_leases_file
self.unbound = UnboundConfigWriter(unbound_leases_file)
self.running = False
# Initially read leases file
self.update_dhcp_leases()
- i = inotify.adapters.Inotify([self.leases_file])
+ i = inotify.adapters.Inotify([self.leases_file, self.fix_leases_file])
for event in i.event_gen():
# End if we are requested to terminate
if "IN_MODIFY" in type_names:
self.update_dhcp_leases()
+ # If the file is deleted, we re-add the watcher
+ if "IN_IGNORED" in type_names:
+ i.add_watch(watch_path)
+
log.info("Unbound DHCP Leases Bridge terminated")
def update_dhcp_leases(self):
- log.info("Reading DHCP leases from %s" % self.leases_file)
+ leases = []
+
+ for lease in DHCPLeases(self.leases_file):
+ leases.append(lease)
+
+ for lease in FixLeases(self.fix_leases_file):
+ leases.append(lease)
- leases = DHCPLeases(self.leases_file)
self.unbound.update_dhcp_leases(leases)
def terminate(self):
return iter(self._leases)
def _parse(self):
+ log.info("Reading DHCP leases from %s" % self.path)
+
leases = []
with open(self.path) as f:
return properties
+class FixLeases(object):
+ cache = {}
+
+ def __init__(self, path):
+ self.path = path
+
+ self._leases = self.cache[self.path] = self._parse()
+
+ def __iter__(self):
+ return iter(self._leases)
+
+ def _parse(self):
+ log.info("Reading fix leases from %s" % self.path)
+
+ leases = []
+ now = datetime.datetime.utcnow()
+
+ with open(self.path) as f:
+ for line in f.readlines():
+ line = line.rstrip()
+
+ try:
+ hwaddr, ipaddr, enabled, a, b, c, hostname = line.split(",")
+ except ValueError:
+ log.warning("Could not parse line: %s" % line)
+ continue
+
+ # Skip any disabled leases
+ if not enabled == "on":
+ continue
+
+ l = Lease(ipaddr, {
+ "binding" : "state active",
+ "client-hostname" : hostname,
+ "hardware" : "ethernet %s" % hwaddr,
+ "starts" : now.strftime("%w %Y/%m/%d %H:%M:%S"),
+ "ends" : "never",
+ })
+ leases.append(l)
+
+ # Try finding any deleted leases
+ for lease in self.cache.get(self.path, []):
+ if lease in leases:
+ continue
+
+ # Free the deleted lease
+ lease.free()
+ leases.append(lease)
+
+ return leases
+
+
class Lease(object):
def __init__(self, ipaddr, properties):
self.ipaddr = ipaddr
state = state.split(" ", 1)
return state[1]
+ def free(self):
+ self._properties.update({
+ "binding" : "state free",
+ })
+
@property
def active(self):
return self.binding_state == "active"
metavar="PATH", help="Path to the DHCPd leases file")
parser.add_argument("--unbound-leases", default="/etc/unbound/dhcp-leases.conf",
metavar="PATH", help="Path to the unbound configuration file")
+ parser.add_argument("--fix-leases", default="/var/ipfire/dhcp/fixleases",
+ metavar="PATH", help="Path to the fix leases file")
# Parse command line arguments
args = parser.parse_args()
setup_logging(loglevel)
- bridge = UnboundDHCPLeasesBridge(args.dhcp_leases, args.unbound_leases)
+ bridge = UnboundDHCPLeasesBridge(args.dhcp_leases, args.fix_leases, args.unbound_leases)
ctx = daemon.DaemonContext(detach_process=args.daemon)
ctx.signal_map = {