#!/usr/bin/env python3

# 
#   Copyright 2016 RIFT.IO Inc
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#


'''
This script will go through the input conffiguration template and convert all the matching "regular expression" and "strings"
specified in xlate_cp_list & xlate_str_list with matching IP addresses passed in as dictionary to this script.

-i Configuration template
-o Output final configuration complete with IP addresses
-x Xlate(Translate dictionary in string format
-t TAGS to be translated

'''

import sys
import getopt
import ast
import re
import yaml
import netaddr

from inspect import getsourcefile
import os.path

xlate_dict = None

def xlate_cp_list(line, cp_list):
    for cp_string in cp_list:
        match = re.search(cp_string, line)
        if match is not None:
            # resolve IP address using Connection Point dictionary
            resolved_ip = xlate_dict[match.group(1)]
            if resolved_ip is None:
                print("No matching CP found: ", match.group(1))
                exit(2)
            else:
                line = line[:match.start()] + resolved_ip + line[match.end():]
    return line

def xlate_multi_colon_list(line, multi_colon_list):
    for ucp_string in multi_colon_list:
        #print("Searching :", ucp_string)
        match = re.search(ucp_string, line)
        if match is not None:
            #print("match :", match.group())
            # resolve IP address using Connection Point dictionary for specified member (unique) index
            ucp_str_list = match.group(1).split(':')
            print("matched = {}, split list = {}".format(match.group(1), ucp_str_list))
            if len(ucp_str_list) != 3:
                print("Invalid TAG in the configuration: ", match.group(1))
                exit(2)

            # Traslate given CP address & mask into netaddr
            if ucp_string.startswith('<rw_unique_index:rw_connection_point:masklen'):
                member_vnf_index = int(ucp_str_list[0])
                resolved_ip = xlate_dict[ucp_str_list[1]]
                masklen = ucp_str_list[2]
                if resolved_ip is None:
                    print("No matching CP found: ", ucp_str_list[1])
                    exit(2)
                if int(masklen) <= 0:
                    print("Invalid mask length: ", masklen)
                    exit(2)
                else:
                    # Generate netaddr
                    ip_str = resolved_ip + '/' + masklen
                    #print("ip_str:", ip_str)
                    ip = netaddr.IPNetwork(ip_str)
                    if ucp_string.startswith('<rw_unique_index:rw_connection_point:masklen_broadcast'):
                        # Traslate given CP address & mask into broadcast address
                        addr = ip.broadcast
                    if ucp_string.startswith('<rw_unique_index:rw_connection_point:masklen_network'):
                        # Traslate given CP address & mask into network address
                        addr = ip.network
                    line = line[:match.start()] + str(addr) + line[match.end():]
    return line



def xlate_colon_list(line, colon_list):
    for ucp_string in colon_list:
        #print("Searching :", ucp_string)
        match = re.search(ucp_string, line)
        if match is not None:
            #print("match :", match.group())
            # resolve IP address using Connection Point dictionary for specified member (unique) index
            ucp_str_list = match.group(1).split(':')
            #print("matched = {}, split list = {}".format(match.group(1), ucp_str_list))
            if len(ucp_str_list) != 2:
                print("Invalid TAG in the configuration: ", match.group(1))
                exit(2)

            # Unique Connection Point translation to IP
            if ucp_string.startswith('<rw_unique_index:'):
                member_vnf_index = int(ucp_str_list[0])
                resolved_ip = xlate_dict[member_vnf_index][ucp_str_list[1]]
                #print("member_vnf_index = {}, resolved_ip = {}", member_vnf_index, resolved_ip)
                if resolved_ip is None:
                    print("For Unique index ({}), No matching CP found: {}", ucp_str_list[0], ucp_str_list[1])
                    exit(2)
                else:
                    line = line[:match.start()] + resolved_ip + line[match.end():]

            # Traslate given CP address & mask into netaddr
            if ucp_string.startswith('<rw_connection_point:masklen'):
                resolved_ip = xlate_dict[ucp_str_list[0]]
                masklen = ucp_str_list[1]
                if resolved_ip is None:
                    print("No matching CP found: ", ucp_str_list[0])
                    exit(2)
                if int(masklen) <= 0:
                    print("Invalid mask length: ", masklen)
                    exit(2)
                else:
                    # Generate netaddr
                    ip_str = resolved_ip + '/' + masklen
                    #print("ip_str:", ip_str)
                    ip = netaddr.IPNetwork(ip_str)
                    
                    if ucp_string.startswith('<rw_connection_point:masklen_broadcast'):
                        # Traslate given CP address & mask into broadcast address
                        addr = ip.broadcast
                    if ucp_string.startswith('<rw_connection_point:masklen_network'):
                        # Traslate given CP address & mask into network address
                        addr = ip.network
                        
                    line = line[:match.start()] + str(addr) + line[match.end():]
    return line

def xlate_cp_to_tuple_list(line, cp_to_tuple_list):
    for cp_string in cp_to_tuple_list:
        match = re.search(cp_string, line)
        if match is not None:
            # resolve IP address using Connection Point dictionary
            resolved_ip = xlate_dict[match.group(1)]
            if resolved_ip is None:
                print("No matching CP found: ", match.group(1))
                exit(2)
            else:
                line = line[:match.start()] + match.group(1) + ':'  + resolved_ip + line[match.end():]
    return line

def xlate_str_list(line, str_list):
    for replace_tag in str_list:
        replace_string = replace_tag[1:-1]
        line = line.replace(replace_tag, xlate_dict[replace_string])
    return line

    
def main(argv=sys.argv[1:]):
    cfg_template = None
    cfg_file = None
    global xlate_dict
    try:
        opts, args = getopt.getopt(argv,"i:o:x:")
    except getopt.GetoptError:
        print("Check arguments {}".format(argv))
        sys.exit(2)
    for opt, arg in opts:
        if opt == '-i':
            cfg_template = arg
        elif opt in ("-o"):
            cfg_file = arg
        elif opt in ("-x"):
            xlate_arg = arg

    # Read TAGS from yaml file
    # Read the translation tags from yaml file
    yml_dir = os.path.dirname(os.path.abspath(getsourcefile(lambda:0)))
    tags_input_file = os.path.join(yml_dir, 'xlate_tags.yml')
    with open(tags_input_file, "r") as ti:
        xlate_tags = yaml.load(ti.read())

    # Need to work on the solution for multiple pattern of same type in single line.
    try:
        with open(xlate_arg, "r") as ti:
            xlate_dict = yaml.load(ti.read())
        try:
            with open(cfg_template, 'r') as r:
                try:
                    with open(cfg_file, 'w') as w:
                        # Traslate
                        try:
                            # For each line
                            for line in r:
                                if line.startswith("#"):
                                    # Skip comment lines
                                    continue
                                #print("1.Line : ", line)
                                # For each Connection Point translation to IP
                                line = xlate_cp_list(line, xlate_tags['xlate_cp_list'])
                                #print("2.Line : ", line)
                                
                                # For each colon(:) separated tag, i.e. 3 inputs in a tag.
                                line = xlate_multi_colon_list(line, xlate_tags['xlate_multi_colon_list'])
                                #print("2a.Line : ", line)

                                # For each colon(:) separated tag, i.e. 2 inputs in a tag.
                                line = xlate_colon_list(line, xlate_tags['xlate_colon_list'])
                                #print("3.Line : ", line)

                                # For each connection point to tuple replacement
                                line = xlate_cp_to_tuple_list(line, xlate_tags['xlate_cp_to_tuple_list'])
                                #print("4.Line : ", line)

                                # For each direct replacement (currently only management IP address for ping/pong)
                                line = xlate_str_list(line, xlate_tags['xlate_str_list'])
                                #print("5.Line : ", line)

                                # Finally write the modified line to the new config file
                                w.write(line)
                        except Exception as e:
                            print("Error ({}) on line: {}".format(str(e), line))
                            exit(2)
                except Exception as e:
                    print("Failed to open for write: {}, error({})".format(cfg_file, str(e)))
                    exit(2)
        except Exception as e:
            print("Failed to open for read: {}, error({})".format(cfg_template, str(e)))
            exit(2)
        print("Wrote configuration file", cfg_file)
    except Exception as e:
        print("Could not translate dictionary, error: ", str(e))

if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(str(e))
