#!/usr/bin/env python
# vim:ts=2:et:sw=2:ai
#
# Build topological network graph
# Rick van der Zwet <info@rickvanderzwet.nl>
import gformat
import sys
import ipaddress

from collections import defaultdict

__version__ = '$Id: syntax-checker.py 14025 2018-01-22 12:49:48Z rick $'

allowed_multi_use = map(lambda x: ipaddress.ip_network(x, strict=True), [
    u'192.168.0.0/22',
    u'192.168.0.0/16',
    u'192.168.0.0/24',
    u'192.168.1.0/24',
    u'192.168.178.0/24',
    ])





def check_double_ip():
  pool = defaultdict(list)
  try:
    for host in gformat.get_hostlist():
      print "## Processing host %-25s: " % host,
      datadump = gformat.get_yaml(host,add_version_info=False)
      masterip_addr = ipaddress.IPv4Interface(unicode(datadump['masterip']))
      masterip_is_used = False

      # Check syntax of defined variables
      _ = gformat.generate_wleiden_yaml(datadump)
      try:
        # Process interfaces
        iface_keys = [elem for elem in datadump.keys() if (elem.startswith('iface_') and not "lo0" in elem)]
        for iface_key in iface_keys:
          # Extra (descriptive entries) are ignored
          if '_extra' in iface_key:
            continue

          # Process actual and virtual IP (avoiding clashes with nanostation IP)
          for entry in ['ip', 'ns_ip']:
            if entry in datadump[iface_key]:
              addr = ipaddress.IPv4Interface(unicode(datadump[iface_key][entry]))
              if masterip_addr in addr.network:
                masterip_is_used = True
              pool[addr.network].append((host, iface_key, entry, addr))


        # Add masterip to the list if IP has not been defined at interface
        if not masterip_is_used:
            pool[masterip_addr.network].append((host, 'masterip', '', masterip_addr))

        print "OK"
      except (KeyError, ValueError), e:
        print "[ERROR] in '%s' interface '%s' (%s)" % (host, iface_key, e) 
        raise
  except Exception as e:
    raise
    sys.exit(1)

  errors = 0
  keys = sorted(pool.keys(),reverse=True)

  for i,network in enumerate(keys):
    if not network in allowed_multi_use:
      for network2 in keys[i+1:]: 
        if not network2 in allowed_multi_use and network2.overlaps(network):
          errors += 1
          print "[ERROR#%i] network %s overlaps with %s:" % (errors, network, network2)
          for (host, key, entry, addr) in sorted(pool[network] + pool[network2]):
            print "  - %-20s - %-20s - %-5s - %s" % (host, key, entry, addr)

      leden = sorted(pool[network])
      for i,lid in enumerate(leden):
        for lid2 in leden[i+1:]:
          if lid[3] == lid2[3]:
            errors += 1
            print "[ERROR#%i] Multiple usages of IP %s:" % (errors, lid[3])
            print "  - %-20s - %-20s - %-5s" % (lid[0], lid[1], lid[2])
            print "  - %-20s - %-20s - %-5s" % (lid2[0], lid2[1], lid2[2])
            
  if errors > 0:
    print "# %i Errors found" % errors
    return 1
  else:
    print "# No multiple usages of IPs found"
    return 0


if __name__ == "__main__":
  sys.exit(check_double_ip())

