#!/usr/bin/python3
from keystoneauth1 import identity
from keystoneauth1 import session
from neutronclient.v2_0 import client
from subprocess import DEVNULL 
import subprocess
import sys
import os
import ipaddress
import concurrent.futures

username=os.environ['OS_USERNAME']
password=os.environ['OS_PASSWORD']
project_name=os.environ['OS_PROJECT_NAME']
project_id=os.environ['OS_PROJECT_ID']
user_domain_name=os.environ['OS_USER_DOMAIN_NAME']
auth_url=os.environ['OS_AUTH_URL']

err = 0
out = []
ok_counter = 0

auth = identity.Password(auth_url=auth_url,
                         username=username,
                         password=password,
                         project_name=project_name,
                         project_id=project_id,
                         user_domain_name=user_domain_name)
sess = session.Session(auth=auth)
neutron = client.Client(session=sess)

def ping(ip):
    subprocess.run(['/bin/ping', '-c1', '-n', '-W1', ip], check=True, stdout=DEVNULL)

def check_active_routers(router_id):
    alive_agents = 0
    active_agents = 0
    res = neutron.list_l3_agent_hosting_routers(router_id)
    for agent in res['agents']:
        if agent['alive'] == True:
            alive_agents += 1

        if agent['ha_state'] == 'active':
            active_agents += 1

    if active_agents > 1:
      raise Exception(str(active_agents) + " active agents")

    if alive_agents < 2:
      raise Exception("only " + str(alive_agents) + " alive agents")

    return(res)

#
# fill router_ips
#
router_ips = []
routers = neutron.list_routers()
for router in routers['routers']:
    if router['external_gateway_info'] is not None:
        router_ip = router['external_gateway_info']['external_fixed_ips'][0]['ip_address']
        if ipaddress.ip_address(router_ip).is_global:
                router_ips.append(router_ip)

#
# check for DualActive L3-Agents
#
router_ids = []
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
  for router in routers['routers']:
    router_ids.append(router['id'])

  l3_calls = {executor.submit(check_active_routers, router_id): router_id for router_id in router_ids}
  for l3_call in l3_calls:
        router_id = l3_calls[l3_call]
        try:
            l3_res = l3_call.result()
        except Exception as exc:
            out.append("%r failed: %r" % (router_id, exc))
            err = 2

out.append("%s l3-agents checked" % len(router_ids))

#
# ping all public IPs
#
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
     ping_calls = {executor.submit(ping, ip): ip for ip in router_ips}
     for ping_call in ping_calls:
         ip = ping_calls[ping_call]
         try:
           ping_res = ping_call.result()
         except Exception as exc:
             out.append("%r failed: %s" % (ip, exc))
             err = 2
         else:
             ok_counter += 1

out.append("%s routers of %s routers reachable" % (ok_counter, len(router_ips)))

print ('\n'.join(out))
sys.exit(err);
