#!/usr/bin/env python

from pyeole import ihm
from pyeole import process
from creole.client import CreoleClient
from tempfile import mkstemp

import sys
import os
import csv
import logging
import re

LOG_FILE = '/var/log/one/eole-one-node.log'

#= Configure Logger ===
logger = logging.getLogger(__name__)
#std_handler = logging.StreamHandler(sys.stdout)
file_handler = logging.FileHandler(LOG_FILE)

logger.setLevel(logging.INFO)
#std_handler.setLevel(logging.INFO)
file_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
#std_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

#logger.addHandler(std_handler)
logger.addHandler(file_handler)
#= End Logger ===


class RunCmdError(Exception):
    pass


class OneClient():

    def __init__(self, user):
        self.user = None
        self.auth = None
        self.root = '/var/lib/one'

        if user:
            self.user = user
        else:
            self.user = 'oneadmin'

        user_info = process.system_out(['getent', 'passwd', user])[1]
        if user_info:
            self.root = user_info.split(':')[5]

        command = ['cat', u'{0}/.one/one_auth'.format(self.root)]
        res = process.system_out(command)
        if res[0] == 0:
            self.auth = res[1].split(':')

    def __run_cmd__(self, cmd):
        command = list(cmd)
        command.extend(['--user', self.auth[0]])
        command.extend(['--password', self.auth[1]])

        res = process.system_out(command)
        if res[0] == 0:
            if 'list' in cmd:
                out_lines = []
                if res:
                    for line in res[1].split('\n'):
                        if len(line) == 0:
                            next
                        elif re.match('^.*ID ', line):
                            next
                        else:
                            out_lines.append(line.split())
                return out_lines
            else:
                return res
        else:
            return False

    def get_hosts(self):
        """ get the list of hosts
        """
        cmd = ['onehost', 'list']
        cmd.extend(['-l', 'ID,NAME'])
        res = self.__run_cmd__(cmd)
        return(res)

    def get_clusters(self):
        """ get the cluster list
        """
        cmd = ['onecluster', 'list']
        cmd.extend(['-l', 'ID,NAME'])
        return self.__run_cmd__(cmd)

    def get_networks(self):
        """ get the virtual network list
        """
        cmd = ['onevnet', 'list']
        cmd.extend(['-l', 'ID,NAME'])
        return self.__run_cmd__(cmd)

    def get_cluster_id_by_name(self, name):
        cmd = ['onecluster', 'list']
        cmd.extend(['-f', 'NAME={0}'.format(name)])
        res = self.__run_cmd__(cmd)
        ID = res[0][0]
        return ID

    def get_vnet_id_by_name(self, name):
        cmd = ['onevnet', 'list']
        cmd.extend(['-f', 'NAME={0}'.format(name)])
        res = self.__run_cmd__(cmd)
        ID = res[0][0]
        return ID

    def create_network(self, templatefile, cluster, vnet_name):
        """ Create a network
        """
        cmd = ['onevnet', 'create']
        cmd.extend(['--user', self.auth[0]])
        cmd.extend(['--password', self.auth[1][:-1]])
        #cmd.extend(['-c', cluster])
        cmd.append(templatefile)
        res = process.system_out(cmd)
        if res[0] == 0:
            clt_id = self.get_cluster_id_by_name(cluster)
            vnet_id = self.get_vnet_id_by_name(vnet_name)
            res = self.__run_cmd__(['onecluster', 'addvnet', clt_id, vnet_id])
            os.remove(templatefile)
            if not res:
                print("Error attaching {0} vnet to {1} cluster".format(vnet_name, cluster))
                return False
            else:
                return True
        else:
            logger.error("Creation of virtual network with template {0} failed".format(templatefile))
            return False

    def update_network(self, templatefile, cluster, vnet_name):
        """ Update a network
        """
        vnet_id = self.get_vnet_id_by_name(vnet_name)
        cmd = ['onevnet', 'update']
        cmd.extend(['--user', self.auth[0]])
        cmd.extend(['--password', self.auth[1][:-1]])
        cmd.extend([vnet_id, templatefile])

        res = process.system_out(cmd)
        if res[0] == 0:
            os.remove(templatefile)
            return True
        else:
            logger.error("Update of virtual network with template {0} failed".format(templatefile))
            return False

    def delete_network(self, vnet_id):
        cmd = ['onevnet', 'delete']
        cmd.extend(['--user', self.auth[0]])
        cmd.extend(['--password', self.auth[1][:-1]])
        cmd.append(vnet_id)

        res = process.system_out(cmd)
        if res[0] == 0:
            ihm.print_line("Network {0} deleted".format(vnet_id))
            return True
        else:
            logger.error("Error deleting network {0}".format(vnet_id))
            ihm.print_line("Error deleting network {0}".format(vnet_id))
            return False


class OneNetwork():
    def create(self, one_client):
        tmpl_file = self.create_template()
        if one_client.create_network(tmpl_file, self.cluster, self.zone):
            ihm.print_line("Virtual network {0} created".format(self.zone))
            return True
        else:
            ihm.print_line("Error Creating virtual network {0}".format(self.zone))
            return False

    def update(self, one_client):
        tmpl_file = self.create_template(True)
        if one_client.update_network(tmpl_file, self.cluster, self.zone):
            ihm.print_line("Virtual network {0} updated".format(self.zone))
            return True
        else:
            ihm.print_line("Error Updating virtual network {0}".format(self.zone))
            return False

    def manage(self, one_client):
        found = False
        vnet = one_client.get_networks()
        network_name = self.zone
        for net in vnet:
            if network_name in net:
                found = True
                break

        if not found:
            return self.create(one_client)
        else:
            return self.update(one_client)

class OneNetworkL3(OneNetwork):
    def __init__(self, net_info, cluster):
        self.swname = net_info[0]
        self.zone = u'{0}{1}'.format(net_info[10], net_info[1])
        self.vlan = net_info[2]
        self.vnet_addr = net_info[3]
        self.vnet_mask = net_info[4]
        self.vnet_gw = net_info[5]
        self.vnet_rg_start = net_info[6]
        self.vnet_rg_size = net_info[7]
        self.vnet_dns = net_info[8]
        self.vnet_trunk = net_info[9]
        self.cluster = cluster

    def create_template(self, update=False):
        fd, tmp_path = mkstemp(prefix='oneVnet-')
        template = open(tmp_path, 'w')
        template.write('NAME = "{0}"\n'.format(self.zone))
        if (update is False):
            if self.vnet_rg_start and self.vnet_rg_size:
                template.write('AR=[\n')
                template.write('TYPE = "IP4",\n')
                template.write('IP = "{0}",\n'.format(self.vnet_rg_start))
                template.write('SIZE = "{0}"\n'.format(self.vnet_rg_size))
                template.write(']\n')
            else:
                template.write('TYPE = FIXED\n')

        if self.vlan:
            template.write('VLAN = yes\n')
            template.write('VLAN_ID = {0}\n'.format(self.vlan))

        if self.vnet_trunk:
            template.write('VLAN_TAGGED_ID = {0}\n'.format(self.vnet_trunk))

        template.write('BRIDGE = {0}\n'.format(self.swname))
        template.write('NETWORK_ADDRESS = {0}\n'.format(self.vnet_addr))
        template.write('NETWORK_MASK = {0}\n'.format(self.vnet_mask))
        template.write('GATEWAY = {0}\n'.format(self.vnet_gw))
        template.write('DNS = {0}\n'.format(self.vnet_dns))
        template.close()
        return tmp_path


class OneNetworkL2(OneNetwork):
    def __init__(self, net_info, cluster):
        self.swname = net_info[0]
        self.zone = u'{0}{1}'.format(net_info[6], net_info[1])
        self.net_size = net_info[2]
        self.first_mac = net_info[3]
        self.tag = net_info[4]
        self.trunk = net_info[5]
        self.cluster = cluster

    def create_template(self,update=False):
        fd, tmp_path = mkstemp(prefix='oneVnet-')
        template = open(tmp_path, 'w')
        template.write('NAME = "{0}"\n'.format(self.zone))

        if self.tag:
            template.write('VLAN = yes\n')
            template.write('VLAN_ID = "{0}"\n'.format(self.tag))

        if self.trunk:
            template.write('VLAN_TAGGED_ID = "{0}""\n'.format(self.trunk))

        template.write('BRIDGE = {0}\n'.format(self.swname))
        if update is False and self.net_size:
            template.write("AR=[\n")
            template.write('  TYPE = "ETHER",\n')

            if self.first_mac:
                template.write('  MAC = "{0}",\n'.format(self.first_mac))

            template.write('  SIZE = "{0}"\n'.format(self.net_size))
            template.write("]\n")
        template.close()
        return tmp_path

def main():
    client = CreoleClient()
    one_client = OneClient('oneadmin')
    networks = []
    cluster = client.get_creole('one_cluster_name')
    swname = client.get_creole('ovs_sw_name')
    zones = client.get_creole('vnets')
    vlans = client.get_creole('vnet_vlan_tag')
    vnet_addr = client.get_creole('vnet_network_addr')
    vnet_mask = client.get_creole('vnet_network_mask')
    vnet_dns = client.get_creole('vnet_network_dns')
    vnet_gw = client.get_creole('vnet_network_gw')
    vnet_rg_start = client.get_creole('vnet_range_start')
    vnet_rg_size = client.get_creole('vnet_range_size')
    vnet_trunk = client.get_creole('vnet_vlan_trunk')

    l2_vnet = client.get_creole('l2_vnets')
    l2_vnet_size = client.get_creole('l2_vnet_size')
    l2_vnet_vlan_tag = client.get_creole('l2_vnet_vlan_tag')
    l2_vnet_vlan_trunk = client.get_creole('l2_vnet_vlan_trunk')
    l2_vnet_first_mac = client.get_creole('l2_vnet_first_mac')

    net_prefix = "CR_"

    processed = []
    for cpt in range(len(zones)):
        if zones[cpt] not in processed:
            info = []
            info.append(swname)
            info.append(zones[cpt])
            info.append(vlans[cpt])
            info.append(vnet_addr[cpt])
            info.append(vnet_mask[cpt])
            info.append(vnet_gw[cpt])
            info.append(vnet_rg_start[cpt])
            info.append(vnet_rg_size[cpt])
            info.append(vnet_dns[cpt])
            info.append(vnet_trunk[cpt])
            info.append(net_prefix)
            networks.append(OneNetworkL3(info, cluster))
            processed.append(zones[cpt])

    for i in range(len(l2_vnet)):
        if l2_vnet[i] not in processed:
            net_info = []
            net_info.append(swname)
            net_info.append(l2_vnet[i])
            net_info.append(l2_vnet_size[i])
            net_info.append(l2_vnet_first_mac[i])
            net_info.append(l2_vnet_vlan_tag[i])
            net_info.append(l2_vnet_vlan_trunk[i])
            net_info.append(net_prefix)
            networks.append(OneNetworkL2(net_info, cluster))
            processed.append(l2_vnet[i])

    if client.get_creole('activer_openvswitch'):
        for network in networks:
            if not network.manage(one_client):
                exit(1)
    else:
        ihm.print_line(u'Open vSwitch disabled no need to configure virtual networks')

    networks = one_client.get_networks()
    for net in networks:
        name = net[1]
        if name.startswith(net_prefix):
            if not name[3:] in zones and not name[3:] in l2_vnet:
                one_client.delete_network(net[0])

    exit(0)

main()