#! /usr/bin/env python3
# Script that connects to OpenDaylight SDN controller and creates IRI messages
#
# Copyright (C) 2014 Barbora Frankova
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os
import time
import sys
import requests
import getopt
import json

sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
from modules.sockets.li import ptptAsClient

# list has O(n) / set has O(1)
activeComputers = []
#textMessages = ["Host known before running script", "Host connected a moment ago"]
textMessages = ["",""]
lastRun = None

def connectToController():
    """ Connect to IRI-IIF Controller
    Returns socket
    """
    return ptptAsClient("iricol")

def getHosts(url, login, passwd):
    """ Connect to SDN Opendaylight controller and use REST API to get list of active hosts."""
    r = requests.get(url, auth=(login, passwd))
    if r.status_code != 200:
        print('Opendaylight module: could not connect to controler, exiting. Status code', r.status_code)
        raise Exception()
    return(json.loads(r.text))

def diffActiveComputers(s, continueTimeLimit, url, login, passwd):
    """ Active:  list of all Hosts - both configured via PUT API and dynamically learnt on the network. """
    global activeComputers
    global lastRun
    foundHosts = []
    # flag for time limit of "CONTINUE" messages
    timeLimitReached = True if (lastRun == None or time.time() - lastRun >= continueTimeLimit) else False
    
    activeHosts = getHosts(url, login, passwd)
    # check if active hosts are in list of known computers
    for host in activeHosts["hostConfig"]:
        connector = [host["nodeType"], host["nodeId"], host["nodeConnectorId"]]
        pc = [host["networkAddress"], host["dataLayerAddress"], host["nodeConnectorId"], connector, host["vlan"]]
        # add all found hosts in list for bakwards exam.
        foundHosts.append(pc)
        # new host, add to list and send BEGIN
        if pc not in activeComputers:
            activeComputers.append(pc)
            # event description + vlan number
            textMsg = textMessages[0] if lastRun == None else textMessages[1]
            s.send(createIRI("BEGIN", textMsg + ", vlan: " + pc[4], pc))
            #print(createIRI("BEGIN", textMsg + ", vlan: " + pc[4], pc))
        # known host, send CONTINUE if time limit passed
        else:
            if timeLimitReached:
                s.send(createIRI("CONTINUE", "vlan: " + pc[4], pc))
                #print(createIRI("CONTINUE", "vlan: " + pc[4], pc))
    # check if known computers are still active
    for host in activeComputers:
        if host not in foundHosts:
            activeComputers.remove(host)
            s.send(createIRI("END", "vlan: " + host[4], host))
            #print(createIRI("END", "vlan: " + host[4], host))
    if timeLimitReached:
        lastRun = time.time()

def createIRI(iriType, textMessage, nid):
    """ Create IRI message to be sent to IRI-Core """
    nidList = []
    nidList.append(("IPv6" if ":" in nid[0] else "IPv4", nid[0]))
    nidList.append(("MAC", nid[1]))
    nidList.append(("sdnConnector", nid[3][0] + "_" + nid[3][1] + "_" + nid[3][2]))
    return ("SDN-opendaylight",
            time.time(),
            iriType,
            textMessage,
            nidList
            )

def usage():
    print("SDN Opendaylight module for IRI-IIF.\nUsage:  ./odl.py [pculwh]")
    print("  -p 1-3600 | --poll=1-3600\t\tinterval between GET requests in seconds, default 5 s")
    print("  -c 0-3600 | --continue=0-3600\t\tinterval between CONTINUE messages (0 = no CONTINUE messages), default 10 s")
    print("  -u sdn_controller_url | --url=sdn_controller_url\tdefault http://sec6net-mv19.fit.vutbr.cz:8080")
    print("  -l username | --login=username\tdefault \'admin\'")
    print("  -w password | --passwd=passwd\t\tdefault \'admin\'\n")
    print("Example: ./odl -p 10 -c 20 -u localhost")
    print("         ./odl --poll=10 --url=localhost --login=admin --passwd=admin")

def parseParams():
    """ Parse command line parameters. """
    # interval between GET requests
    interval = 5
    # interval between CONTINUE messages (0 means no msgs)
    cont = 10
    # controller url
    url = 'http://sec6net-mv19.fit.vutbr.cz:8080'
    # user credentials
    login = 'admin'
    passwd = 'admin'
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'p:c:i:u:l:w:h', ['poll=', 'continue=', 'url=', 'login=', 'passwd='])
    except getopt.GetoptError:
        sys.exit(2)
    for opt, arg in opts:
        if opt in ('-p', '--poll'):
            interval = arg
        elif opt in ('-c', '--continue'):
            cont = arg if float(arg) != 0 else "infinity"
        elif opt in ('-u', '--url'):
            url = arg
        elif opt in ('-l', '--login'):
            login = arg
        elif opt in ('-w', '--passwd'):
            passwd = arg
        elif opt == '-h':
            usage()
            sys.exit(0)
    return(interval, cont, url, login, passwd)   

def main():
    """ Main body of the program. """
    interval, cont, url, login, passwd = parseParams()
    url = url + '/controller/nb/v2/hosttracker/default/hosts/active'
    s = connectToController()
    #s = None
    while True:
        try:
            diffActiveComputers(s, cont, url, login, passwd)
        except:
            sys.exit()
        time.sleep(interval)

if __name__ == "__main__":
    main()
