#!/usr/bin/python3
#-*- coding: utf-8 -*-

import argparse
import re
import ipaddress
from os.path import isfile
from creole.loader import creole_loader
from json import load
from sys import exit


CONFFILE = '/var/lib/eole/config/dhcptool.json'
if not isfile(CONFFILE):
    print(f'Impossible de trouver le fichier de configuration {CONFFILE}')
    exit(1)
with open(CONFFILE) as fh:
    cfg = load(fh)



def get_ranges():
    range_specifications = cfg['ranges']

    ranges = {r[1]: {'first': int(ipaddress.ip_address(r[2])),
                     'last': int(ipaddress.ip_address(r[3])),
                     'restricted': r[4],
                     'static': r[5],
                     'warning_threshold': r[6],
                     'error_if_full': r[7],
                     'subnet': ipaddress.ip_network(r[0])}
              for r in range_specifications}
    return ranges


def subnet_host_is_in(ip, ranges):
    """Return subnet and range the host is in.
    :param host: host description
    :type host: dict('ip'=str, 'rangename'=str)
    """
    ip = int(ipaddress.ip_address(ip))
    rangename = None
    for r in ranges:
        if ranges[r]['first'] <= ip <= ranges[r]['last']:
            return r
    return 'NA'


LEASES_DATABASE = cfg['lease_database']
DHCP_LEASES_ATTRS = {
        'starts': re.compile(r'^\s*starts\s+[0-9]\s+(?P<value>.*);', re.M),
        'ends': re.compile(r'^\s*ends\s+[0-9]\s+(?P<value>.*);', re.M),
        'cltt': re.compile(r'^\s*cltt\s+[0-9]\s+(?P<value>.*);', re.M),
        'binding state': re.compile(r'^\s*binding state\s+(?P<value>.*);', re.M),
        'next binding state': re.compile(r'^\s*next binding state\s+(?P<value>\w+);', re.M),
        'rewind binding state': re.compile(r'^\s*rewind binding state\s+(?P<value>\w+);', re.M),
        'hardware ethernet': re.compile(r'^\s*hardware ethernet\s+(?P<value>[a-f0-9]{2}(:[a-f0-9]{2}){5});', re.M),
        'client-hostname': re.compile(r'^\s*client-hostname\s+"?(?P<value>\w+)"?;', re.M)
        }
leases_re = re.compile(r'^(?P<lease>lease (?P<ip>[0-9]{1,3}(?:\.[0-9]{1,3}){3})\s+\{.*?\})', re.M|re.S)


def parse_attrs(lease):
   attrs = {}
   for attribute in DHCP_LEASES_ATTRS:
     r = DHCP_LEASES_ATTRS[attribute].search(lease)
     if r:
       attrs[attribute] = r.group('value')
   return attrs


def get_leases(leases):
   l = leases_re.finditer(leases)
   return {lease.group('ip'): parse_attrs(lease.group('lease')) for lease in l}


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Expose dhcp information')
    parser.add_argument('--detailed', action='store_true')
    args = parser.parse_args()

    with open(LEASES_DATABASE, 'r') as leases_db:
        leases = leases_db.read()

    leases = get_leases(leases)
    ranges = get_ranges()
    for l, lease in leases.items():
        subnet = subnet_host_is_in(l, ranges)
        ranges[subnet].setdefault('leases', [])
        ranges[subnet]['leases'].append((l,lease))
    for r in ranges:
        if ranges[r]['static']:
            continue
        current_leases = [lease for lease in ranges[r].get('leases', []) if lease[1]['binding state'] == 'active']
        total_leases = ranges[r]['last'] - ranges[r]['first'] + 1
        print(r, '{} {} {} {}'.format(total_leases - len(current_leases), total_leases, ranges[r]['warning_threshold'], ranges[r]['error_if_full']))
        if args.detailed:
            for rn in ranges[r].get('leases', []):
                print('\t', rn[1].get('hardware ethernet', 'NA'), rn[0], rn[1].get('client-hostname', 'NA'), rn[1]['binding state'])
