hswaw/checkinator: implement support for kea dhcp server

Also bump version number and fix typo in README.

Change-Id: I116ef706d99df4ace70ccceefc6a23d41fd1adb6
Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1942
Reviewed-by: q3k <q3k@hackerspace.pl>
diff --git a/hswaw/checkinator/README.rst b/hswaw/checkinator/README.rst
index e76b600..26de179 100644
--- a/hswaw/checkinator/README.rst
+++ b/hswaw/checkinator/README.rst
@@ -1,4 +1,4 @@
-`Warsaw Hackerspace`_  presence tracker hosted on https://at.hackersapce.pl. It
+`Warsaw Hackerspace`_  presence tracker hosted on https://at.hackerspace.pl. It
 uses dhcpd.leases file to track MAC adressess of devices connected to hs LAN
 network.
 
@@ -11,7 +11,7 @@
 -----
 .. code:: bash
 
-    cp config.yaml.dist config.yaml
+    cp config.dist.yaml config.yaml
     cp web-config.yaml.dist web-config.yaml
 
     # edit config files using your favourite editor
diff --git a/hswaw/checkinator/at/dhcp.py b/hswaw/checkinator/at/dhcp.py
index bee2ace..df40dae 100644
--- a/hswaw/checkinator/at/dhcp.py
+++ b/hswaw/checkinator/at/dhcp.py
@@ -140,6 +140,26 @@
             lease database. Once this file has been written to disk, the old
             file is renamed dhcpd.leases~, and the new file is renamed
             dhcpd.leases.
+
+        Kea documentation[0] suggests identical behavior:
+
+            For performance reasons, the server does not update the existing
+            client's lease in the file, as this would potentially require
+            rewriting the entire file. Instead, it simply appends the new lease
+            information to the end of the file; the previous lease entries for
+            the client are not removed.
+
+            [ ... ]
+
+            Lease file cleanup is performed by a separate process (in the
+            background) to avoid a performance impact on the server process. To
+            avoid conflicts between two processes using the same lease files,
+            the LFC process starts with Kea opening a new lease file; the actual
+            LFC process operates on the lease file that is no longer used by the
+            server. There are also other files created as a side effect of the
+            lease file cleanup
+
+        [0]: https://kea.readthedocs.io/en/latest/arm/dhcp4-srv.html#why-is-lease-file-cleanup-necessary
         """
         while True:
             try:
@@ -232,3 +252,16 @@
         offset, devices = parse_isc_dhcpd_leases(f)
         self.update(devices)
         return offset
+
+class KeaUpdater(MtimeUpdater):
+    def file_changed(self, f):
+        leases = ActiveDevices()
+        for line in f:
+            # header line
+            if(line.startswith('address,')):
+                continue
+            # taken directly from kea leases file header
+            address, hwaddr, client_id, valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, state, user_context, pool_id = line.split(',')
+            leases.add(DhcpLease(hwaddr, int(expire) - int(valid_lifetime), address, hostname))
+        self.update(leases)
+        return f.tell()
diff --git a/hswaw/checkinator/at/tracker.py b/hswaw/checkinator/at/tracker.py
index 4ca71fb..4ec9e70 100644
--- a/hswaw/checkinator/at/tracker.py
+++ b/hswaw/checkinator/at/tracker.py
@@ -1,4 +1,4 @@
-from at.dhcp import DhcpdUpdater, DhcpLease
+from at.dhcp import KeaUpdater, DhcpdUpdater, DhcpLease
 from pathlib import Path
 import yaml
 import grpc
@@ -76,11 +76,14 @@
 
 def server():
     args = parser.parse_args()
-    
+
     config = yaml.safe_load(args.config.read_text())
-    tracker = DhcpdUpdater(config['LEASE_FILE'], config['TIMEOUT'])
+    if config['DHCP_SERVER'] == "kea":
+        tracker = KeaUpdater(config['KEA_LEASE_FILE'], config['TIMEOUT'])
+    elif config['DHCP_SERVER'] == "isc" or "DHCP_SERVER" not in config:
+        tracker = DhcpdUpdater(config['LEASE_FILE'], config['TIMEOUT'])
     tracker.start()
-    
+
     server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
     add_DhcpTrackerServicer_to_server(DhcpTrackerServicer(tracker), server)
 
diff --git a/hswaw/checkinator/config.dist.yaml b/hswaw/checkinator/config.dist.yaml
index 2582168..7debe95 100644
--- a/hswaw/checkinator/config.dist.yaml
+++ b/hswaw/checkinator/config.dist.yaml
@@ -2,6 +2,9 @@
 DEBUG: false
 CAP_FILE: './dhcp-cap'
 LEASE_FILE: './dhcpd.leases'
+KEA_LEASE_FILE: './dhcp4.leases'
+# optional - specify which dhcp server flavor - isc or kea
+DHCP_SERVER: "isc"
 TIMEOUT: 1500
 
 WIKI_URL: 'https://wiki.hackerspace.pl/people:%(login)s:start'
diff --git a/hswaw/checkinator/default.nix b/hswaw/checkinator/default.nix
index c37f9fa..e079602 100644
--- a/hswaw/checkinator/default.nix
+++ b/hswaw/checkinator/default.nix
@@ -8,7 +8,7 @@
   }}" {};
 in pkgs.python3Packages.buildPythonPackage {
   pname = "checkinator";
-  version = "0.2";
+  version = "0.3";
 
   doCheck = false;
   src = ./.;
diff --git a/hswaw/checkinator/setup.py b/hswaw/checkinator/setup.py
index be35cc5..522f6a3 100644
--- a/hswaw/checkinator/setup.py
+++ b/hswaw/checkinator/setup.py
@@ -23,7 +23,7 @@
 
 setup(
     name='hswaw-at',
-    version='0.1',
+    version='0.3',
     description='warsaw hackerspace checkinator',
 
     packages=['at'],