#
# Emulab support for Rhumba
#
#    Sander Vrijders  <sander.vrijders@intec.ugent.be>
#    Wouter Tavernier <wouter.tavernier@intec.ugent.be>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA  02110-1301  USA

import os
import time
import re
from ast import literal_eval
from ssh_support import *

import warnings
warnings.filterwarnings("ignore")

def ops_server(testbed):
    '''
    Return server name of the ops-server (is testbed specific)

    @param testbed: testbed info

    @return: server name of the ops-server
    '''
    return 'ops.' + testbed.url

def full_name(testbed, node_name):
    '''
    Return server name of a node

    @param node_name: name of the node
    @param testbed: testbed info

    @return: server name of the node
    '''
    return node_name + '.' + testbed.exp_name + '.' + \
        testbed.proj_name + '.' + testbed.url


def get_experiment_list(testbed, project_name = None):
    '''
    Get list of made emulab experiments accessible with your credentials

    @param testbed: testbed info
    @param project_name: optional filter on project

    @return: list of created experiments (strings)
    '''
    cmd = '/usr/testbed/bin/sslxmlrpc_client.py -m experiment getlist'
    out = execute_command(testbed, ops_server(testbed), cmd)

    try:
        if project_name != None:
            return  literal_eval(out)[project_name][project_name]
        else:
            return  literal_eval(out)
    except:
        return { project_name: { project_name: [] }}

def swap_exp_in(testbed):
    '''
    Swaps experiment in

    @param testbed: testbed info
    '''
    cmd = '/usr/testbed/bin/sslxmlrpc_client.py swapexp proj=' + \
          testbed.proj_name + \
          ' exp=' + \
          testbed.exp_name + \
          ' direction=in'

    output = execute_command(testbed, ops_server(testbed), cmd)

    return output

def create_experiment(testbed, nodes, links):
    '''
    Creates an emulab experiment

    @param testbed: testbed info
    @param nodes: holds the nodes in the experiment
    @param links: holds the links in the experiment
    '''
    proj_name = testbed.proj_name
    exp_name = testbed.exp_name

    exp_list = get_experiment_list(testbed)

    try:
        if exp_name in exp_list[proj_name][proj_name]:
            print("Experiment already exists.")
            return
    except:
        print("First experiment to be created for that project.")

    ns = generate_ns_script(testbed, nodes, links)
    dest_file_name = '/users/'+ testbed.username + \
                     '/temp_ns_file.%s.ns' % os.getpid()
    copy_file_to_testbed(testbed, ops_server(testbed), ns, dest_file_name)

    cmd = '/usr/testbed/bin/sslxmlrpc_client.py startexp ' + \
          'batch=false wait=true proj="' + proj_name + \
          '" exp="' + exp_name + '" noswapin=true ' + \
          'nsfilepath="' + dest_file_name + '"'

    execute_command(testbed, ops_server(testbed), cmd, time_out = None)
    execute_command(testbed, ops_server(testbed),'rm ' + dest_file_name)
    print("New experiment succesfully created.")

def generate_ns_script(testbed, nodes, p2plinks):
    '''
    Generate ns script based on network graph.
    Enables to customize default node image.

    @param nodes: holds the nodes in the experiment
    @param links: holds the links in the experiment
    @param testbed: testbed info

    @return: ns2 script for Emulab experiment
    '''

    ns2_script = "# ns script generated by Rhumba\n"
    ns2_script += "set ns [new Simulator]\n"
    ns2_script += "source tb_compat.tcl\n"

    for node in nodes:
            ns2_script += "set " + node.name + " [$ns node]\n"
            ns2_script += "tb-set-node-os $" + node.name + " " + \
                          testbed.image + "\n"

    for link in p2plinks:
            ns2_script += "set " + link.name + \
                          " [$ns duplex-link $" + \
                          link.node_a.name + " $" + \
                          link.node_b.name + " 1000Mb 0ms DropTail]\n"

    ns2_script += "$ns run\n"

    return ns2_script

def wait_until_nodes_up(testbed):
    '''
    Checks if nodes are up

    @param testbed: testbed info
    '''
    print("Waiting until all nodes are up")

    cmd = '/usr/testbed/bin/script_wrapper.py expinfo -e' + \
          testbed.proj_name + \
          ',' + \
          testbed.exp_name + \
          ' -a | grep State | cut -f2,2 -d " "'

    res = execute_command(testbed, ops_server(testbed), cmd)
    active = False
    if res == "active":
        active = True
    while active != True:
        res = execute_command(testbed, ops_server(testbed), cmd)
        if res == "active":
            active = True
        print("Still waiting")
        time.sleep(5)

def complete_experiment_graph(testbed, nodes, p2plinks):
    '''
    Gets the interface (ethx) to link mapping

    @param testbed: testbed info
    @param nodes: holds the nodes in the experiment
    @param links: holds the links in the experiment
    '''

    node_full_name = full_name(testbed, nodes[0].name)
    cmd = 'cat /var/emulab/boot/topomap'
    topomap = execute_command(testbed, node_full_name, cmd)
    # Almost as ugly as yo momma
    index = topomap.rfind("# lans")
    topo_array = topomap[:index].split('\\n')[1:-1]
    # Array contains things like 'r2b1,link7:10.1.6.3 link6:10.1.5.3'
    for item in topo_array:
        item_array = re.split(',? ?', item)
        node_name = item_array[0]
        for item2 in item_array[1:]:
            item2 = item2.split(':')
            link_name = item2[0]
            link_ip = item2[1]
            for link in p2plinks:
                if link.name == link_name:
                    if link.node_a.name == node_name:
                        link.int_a.ip = link_ip
                    elif link.node_b.name == node_name:
                        link.int_b.ip = link_ip

    for node in nodes:
        cmd = 'cat /var/emulab/boot/ifmap'
        node_full_name  = full_name(testbed, node.name)
        output = execute_command(testbed, node_full_name, cmd)
        output = re.split('\\\\n', output)
        for item in output:
            item = item.split()
            for link in p2plinks:
                if link.node_a.name == node.name and \
                   link.int_a.ip == item[1]:
                    link.int_a.name = item[0]
                elif link.node_b.name == node.name and \
                     link.int_b.ip == item[1]:
                    link.int_b.name =  item[0]
        node.full_name = full_name(testbed, node.name)
