import gevent
from gevent import monkey
monkey.patch_all()

from CloudProviderFramework import *
from libcloud.compute.types import Provider
from libcloud.compute.types import NodeState
from libcloud.compute.providers import get_driver
from libcloud.compute.base import NodeAuthPassword
from cloud_provider_plugin_base import CloudProviderPluginBase
from concurrent.futures import ThreadPoolExecutor
import threading
import sys
import re
import uuid
import requests
import json
import os

from gevent import getcurrent
from gevent.pool import Pool
from gevent.pool import Group

POOL_SIZE = 16

class CloudProviderPlugin(CloudProviderPluginBase):
    def __init__(self, pluginHolder):
        CloudProviderPluginBase.__init__(self, pluginHolder)
        self.cls = get_driver(Provider.EC2)
        self.pool = Pool(POOL_SIZE)
        self.user_id = None
        self.project_id = None
        self.key = None
        self.loc = None
        self.zone = None
        self.provider_name = ""
        self.multithreaded = True

    ### tools
    def get_exc_message(self):
        infos = sys.exc_info()
        message = '' 
        for info in infos:
            message = message + str(info) + ' '
        return message
    
    def create_driver(self, region):
        driver = None
        try:
            if region == None and self.loc != None and not len(self.loc) == 0:
                region = self.loc
                
            driver = self.cls(self.user_id, self.key, region=region)
        except:
            self.set_error(self.get_exc_message())
        
        return driver

    def spawn(self, func, *args, **kwargs):
            if self.multithreaded:
                parent = gevent.getcurrent()
                
                def on_error(subtask):
                    parent.throw(subtask.exception)
                
                g = self.pool.spawn(func, *args, **kwargs)
                g.link_exception(on_error)
            else:
               func(*args, **kwargs) 

    def handle_exception(self, greenlet):
        try:
            self.set_error("Error")
        except Exception as exc:
            pass
    
    # Get the driver for current thread.
    def get_driver(self, region=None):
        return self.create_driver(region)

    ###
    def initialize(self):
        self.user_id = self.subscriber_id()
        self.project_id = self.client_id()
        self.key = self.password()
        self.zone = self.location()
        lastChar = self.zone[-1:]
        match = re.search("[a-z]", lastChar)
        if (match != None):
            self.loc = self.zone[:-1]
        else:
            self.loc = self.zone
        self.provider_name = self.name()
    
    def update(self):
        self.pool.join(timeout=0.001)  
    ###

    ### tool functions
    def get_image(self, driver, id):
        images = driver.list_images()
        for image in images:
            if image.id == id:
                return image

    def get_size(self, driver, id):
        sizes = driver.list_sizes()
        for size in sizes:
            if size.id == id:
                return size
    
    def get_node(self, driver, vmName, resourceGroupName):
        nodes = driver.list_nodes(ex_filters={'tag:rrgroup': resourceGroupName, 'tag:Name': vmName})
        for node in nodes:
            if node.state != NodeState.TERMINATED:
                return node
            
        return None
    
    def list_nodes(self, driver, resourceGroupName):
        nodes = []
        nodes = driver.list_nodes(ex_filters={'tag:rrgroup': resourceGroupName})
        for node in nodes:
            if node.state == NodeState.TERMINATED:
                nodes.remove(node)
        return nodes
    
    # list locations
    def list_locations(self):
        self.spawn(self.process_list_locations)

    def process_list_locations(self):
        regions = ["us-east-2", "us-east-1", "us-west-1", \
                   "ap-south-1", "ap-northeast-3", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", \
                   "ap-northeast-1", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-south-1", "eu-west-3", \
                   "eu-north-1", "sa-east-1"]
                
        response = ListLocationsResponse()
        location_names = []
        fail_counter = 0
        for region in regions: 
            driver = self.get_driver(region)
            try:
                locations = driver.list_locations()
                for location in locations:
                    location_names.append(location.name)
            except:
                fail_counter += 1
                if (fail_counter > 3):
                    response.set_error_code('libcloud error')
                    response.set_error_message(self.get_exc_message())
                    self.list_locations_finished(response)
                    return
                #pass
                
        if (len(locations) == 0):
            response.set_error_code('libcloud error')
            response.set_error_message('Could not retreive locations.')
            self.list_locations_finished(response)
            return
                
        response.locations = location_names  
        self.list_locations_finished(response)

    # list resource groups (instance group in GCE)
    def list_resource_groups(self):
        self.spawn(self.process_list_resource_groups)
    
    def process_list_resource_groups(self):
        driver = self.get_driver()
        response = ListResourceGroupsResponse()
        try:
            groups = driver.ex_list_placement_groups()
            group_names = []
            for group in groups:
                group_names.append(group.name)
            response.resource_group_names = group_names
        except:
            response.set_error_code('libcloud error')
            response.set_error_message(self.loc + " " + self.get_exc_message())
            self.list_resource_groups_finished(response)
            return
        self.list_resource_groups_finished(response)

    # create a resource group (instance group in GCE)
    def create_resource_group(self, resourceGroupName, location, vmConfig):
        self.spawn(self.process_create_resource_group, resourceGroupName, location, vmConfig)
    
    def process_create_resource_group(self, resourceGroupName, location, vmConfig):
        driver = self.get_driver()
        response = CreateResourceGroupResponse()
        try:
            group = driver.ex_create_placement_group(resourceGroupName)
        except:
            response.set_error_code('libcloud error')
            response.set_error_message(self.get_exc_message())
            self.create_resource_group_finished(response)
            return
        self.create_resource_group_finished(response)
    
    # delete a resource group (placement group in GCE)
    def delete_resource_group(self, resourceGroupName):
        self.spawn(self.process_create_resource_group, resourceGroupName)
    
    def process_delete_resource_group(self, resourceGroupName):
        pass

    # list all available VM sizes (number of cores, RAM, HDD, etc.) in a location
    def list_skus(self, location):
        self.spawn(self.process_list_skus, location)

    def process_list_skus(self, location):
        driver = self.get_driver()
        skuResponse = ListSkusResponse()
        skuResponse.provider_name = self.provider_name
        priceResponse = ListVmPricesResponse()
        priceResponse.provider_name = self.provider_name

        priceDict = dict()
        #priceDict = self.get_pricing("API-KEY", "europe-west3")

        try:
            sizes = driver.list_sizes()
            sku_dict = dict()
            prices = []

            for size in sizes:
                sku_info = SkuInfo()
                price_info = VmPriceInfo()
                sku_info.name = size.name
                sku_info.memory_size = size.ram / 1024
                sku_info.os_disk_size = size.disk * 1024
                price_info.sku_name = size.name
                price_info.unit_of_measure = ""
                price_info.unit_price = -1
                sku_dict[size.name] = sku_info
                
                if len(priceDict) > 0:
                    prefixList = re.findall("([acemnfg][1,2]d?)-.+", size.name)
                    if len(prefixList) > 0:
                        prefix = prefixList[0]
                        ramPrices = priceDict["ram"]
                        corePrices = priceDict["core"]
                        # debugStr = "Debug: " + str(type(ramPrices)) + " " + str(type(corePrices)) + " " + prefix
                        if prefix in ramPrices and prefix in corePrices:
                            ram = size.ram / 1024
                            ramPricePerUnit = ramPrices[prefix]
                            cores = size.extra["guestCpus"]
                            corePricePerUnit = corePrices[prefix]
                            price_info.unit_price = ram * ramPricePerUnit + cores * corePricePerUnit

                
                prices.append(price_info)

            skuResponse.sku_info_map = sku_dict
            priceResponse.vm_prices = prices

        except:
            skuResponse.set_error_code('libcloud error')
            skuResponse.set_error_message(self.get_exc_message())
            #priceResponse.set_error_code('libcloud error')
            #priceResponse.set_error_message(self.get_exc_message())

        self.list_skus_finished(skuResponse)
        #self.list_vm_prices_finished(priceResponse)
        
    # list all available VM sizes (number of cores, RAM, HDD, etc.) in a location
    def list_vm_sizes(self, resourceGroupName, location):
        self.spawn(self.process_list_vm_sizes, resourceGroupName, location)
    
    def process_list_vm_sizes(self, resourceGroupName, location):
        driver = self.get_driver()
        response = VmSizesResponse()
        try:
            sizes = driver.list_sizes()
            size_names = []
            for size in sizes:
                size_names.append(size.name)

            response.vm_sizes = size_names
        except:
            response.set_error_code('libcloud error')
            response.set_error_message(self.get_exc_message())
        self.list_vm_sizes_finished(response)
        
    # list all VMs in a resource group (instance group for GCE)
    def list_vms(self, resourceGroupName):
        self.spawn(self.process_list_vms, resourceGroupName)
        
    def process_list_vms(self, resourceGroupName):
        driver = self.get_driver()
        response = VirtualMachinesResponse()
        response.resource_group_name = resourceGroupName
        nic_response = NetworkInterfacesResponse()
        try:
            #nodes = driver.list_nodes(ex_filters={'tag:rrgroup': resourceGroupName})
            nodes = self.list_nodes(driver, resourceGroupName)
        except:
            response.set_error_code('libcloud error')
            response.set_error_message(self.get_exc_message())
            self.list_vms_finished(response)
            return   
        vm_dict = dict()
        nic_dict = dict()
        for node in nodes:
            vm_info = VmInfo()
            vm_info.instance_name = node.name
            vm_info.resource_group_name = resourceGroupName
            vm_dict[node.name] = vm_info
            
            if (len(node.private_ips) > 0):
                nic_info = NicInfo()
                nic_info.name = node.name
                nic_info.ip_address = node.private_ips[0]
                nic_dict[node.name] = nic_info
        
        response.vm_info_map = vm_dict
        nic_response.nic_info_map = nic_dict
        
        self.list_vms_finished(response)
        self.list_nics_finished(nic_response)
    
    # list nics (private/public IPs) of VMs in a given resource group (instance group in GCE)
    def list_nics(self, resourceGroupName):
        self.spawn(self.process_list_nics, resourceGroupName)
    
    def process_list_nics(self, resourceGroupName):
        driver = self.get_driver()
        nic_response = NetworkInterfacesResponse()
        try:
            #nodes = driver.list_nodes(ex_filters={'tag:rrgroup': resourceGroupName})
            nodes = self.list_nodes(driver, resourceGroupName)
        except:
            nic_response.set_error_code('libcloud error')
            nic_response.set_error_message(self.get_exc_message())
            self.list_nics_finished(nic_response)
            return
            
        nic_dict = dict()
        for node in nodes:    
            if (len(node.private_ips) > 0):
                nic_info = NicInfo()
                nic_info.name = node.name
                nic_info.ip_address = node.private_ips[0]
                if ((len(node.public_ips) > 0) and (node.public_ips[0] != None)):
                    nic_info.ip_address_public = node.public_ips[0]
                nic_dict[node.name] = nic_info
        
        nic_response.nic_info_map = nic_dict
        self.list_nics_finished(nic_response)
        
    # get the state of a given VM (running, starting, stopping, etc.)
    def get_vm_info(self, vmName, resourceGroupName):
        self.spawn(self.process_get_vm_info, vmName, resourceGroupName)

    def process_get_vm_info(self, vmName, resourceGroupName):
        driver = self.get_driver()
        response = InstanceResponse()
        response.vm_name = vmName
        try:
            node = self.get_node(driver, vmName, resourceGroupName)
            if (node == None):
                return
            elif (node.state == NodeState.RUNNING):
                response.power_state = RestPowerState.PS_ReadyRole
                response.power_state_string = 'running'
            elif (node.state == NodeState.STARTING):
                response.power_state = RestPowerState.PS_StartingRole
                response.power_state_string = 'starting'
            elif (node.state == NodeState.REBOOTING):
                response.power_state = RestPowerState.PS_RestartingRole
                response.power_state_string = 'restarting'
            elif (node.state == NodeState.TERMINATED):
                response.power_state = RestPowerState.PS_UnresponsiveRole
                response.power_state_string = 'unresponsive'
            elif (node.state == NodeState.STOPPING):
                response.power_state = RestPowerState.PS_StoppingVM
                response.power_state_string = 'stopping'
            elif (node.state == NodeState.STOPPED):
                response.power_state = RestPowerState.PS_StoppedDeallocated
                response.power_state_string = 'deallocated'
            elif (node.state == NodeState.PENDING):
                response.power_state = RestPowerState.PS_Preparing
                response.power_state_string = 'preparing'
            elif (node.state == NodeState.SUSPENDED):
                response.power_state = RestPowerState.PS_UnresponsiveRole
                response.power_state_string = 'suspended'
            elif (node.state == NodeState.ERROR):
                response.power_state = RestPowerState.PS_FailedStartingRole
                response.power_state_string = 'failed'
            elif (node.state == NodeState.PAUSED ):
                response.power_state = RestPowerState.PS_StoppedVM
                response.power_state_string = 'stopped'
            elif (node.state == NodeState.RECONFIGURING  ):
                response.power_state = RestPowerState.PS_BusyRole
                response.power_state_string = 'busy'
            else:
                response.power_state = RestPowerState.PS_Unknown
                response.power_state_string = 'unknown'
            
        except:
            response.set_error_code('libcloud error')
            response.set_error_message(self.get_exc_message())
            
        self.get_vm_info_finished(response)
    
    # add new VMs to a given resource group (instance group in GCE)
    def add_vms(self, vmNames, vmConfig, resourceGroupName):
        self.spawn(self.process_add_vms, vmNames, vmConfig, resourceGroupName)
        
    def process_add_vms(self, vmNames, vmConfig, resourceGroupName):
        driver = self.get_driver()
        response = CreateVMsResponse()
        location = vmConfig.cloud_connector_config.provider_config.cloud_location
        
        try:
            networks = driver.ex_list_networks(filters={'tag:Name': vmConfig.virtual_network_name})
            net = networks[0]
            subnets = driver.ex_list_subnets(filters={'tag:Name': 'rendersubnet'})
            subnet = subnets[0]
            secGroups = driver.ex_get_security_groups(filters={'group-name': 'rendersubnet', 'vpc-id': net.id})
            secGroup = secGroups[0]

        except:
            response.set_error_code('libcloud error')
            response.set_error_message(self.get_exc_message())
            self.add_vms_finished(response)
            return

        nodes = []
        for vm_name in vmNames:
            try:
                nodeSize = self.get_size(driver, vmConfig.cloud_connector_config.vm_size)
                nodeImage = self.get_image(driver, vmConfig.cloud_connector_config.vm_image_name)
               
                keyname = None
                keyFilePath = vmConfig.cloud_connector_config.vm_admin_ssh_key
                keyFilePath.replace('\\', '/')
                keyFileName = os.path.basename(keyFilePath)
                keyFileName = os.path.splitext(keyFileName)[0]
                if len(keyFileName) > 0:
                    keyname = keyFileName

                node = driver.create_node(vm_name, nodeSize, nodeImage, location, ex_keyname=keyname, ex_security_group_ids=[secGroup.id], ex_subnet=subnet, ex_assign_public_ip=True)
                #node = driver.create_node(vm_name, nodeSize, nodeImage, location, ex_securitygroup="rendersubnet", ex_subnet=subnet, ex_spot=vmConfig.cloud_connector_config.vm_low_prio)
                driver.ex_create_tags(node, {'rrgroup': resourceGroupName})
                #node = driver.create_node(name=vm_name, image=vmConfig.cloud_connector_config.vm_image_name, size=vmConfig.cloud_connector_config.vm_size, location=location, ex_network=net, ex_subnetwork=subnet, external_ip=None, ex_can_ip_forward=True, ex_preemptible=vmConfig.cloud_connector_config.vm_low_prio)
                nodes.append(node)
            except:
                response.set_error_code('libcloud error')
                response.set_error_message(self.get_exc_message())
            
        self.add_vms_finished(response)
        
    # delete a VM from a given resource group (instance group in GCE)
    def delete_vm(self, vmName, resourceGroupName):
        self.process_delete_vm(vmName, resourceGroupName)
        #self.pool.spawn(self.process_delete_vm, vmName, resourceGroupName)
        
    def process_delete_vm(self, vmName, resourceGroupName):
        pass
        #response = DeleteVMResponse()
        #self.delete_vm_finished(response)
    
    # starts VMs in a given resource group (instance group in GCE)
    def start_vms(self, vmNames, resourceGroupName):
        self.spawn(self.process_start_vms, vmNames, resourceGroupName)
        
    def process_start_vms(self, vmNames, resourceGroupName):
        driver = self.get_driver()
        response = VirtualMachineNameResponse()
        for vm_name in vmNames:
            try:
                node = self.get_node(driver, vm_name, resourceGroupName)
                driver.start_node(node)
            except:
                response.set_error_code('libcloud error')
                response.set_error_message(self.get_exc_message())
        self.start_vms_finished(response)
    
    # shutdown VMs in a resource group (instance group in GCE)
    def shutdown_vms(self, vmNames, resourceGroupName):
        self.spawn(self.process_shutdown_vms, vmNames, resourceGroupName)
        
    def process_shutdown_vms(self, vmNames, resourceGroupName):
        driver = self.get_driver()
        response = VirtualMachineNameResponse()
        for vm_name in vmNames:
            try:
                node = self.get_node(driver, vm_name, resourceGroupName)
                driver.stop_node(node)
            except:
                response.set_error_code('libcloud error')
                response.set_error_message(self.get_exc_message())
        self.shutdown_vms_finished(response)

    # shutdown VMs in a resource group
    def simulate_vm_eviction(self, vmName, resourceGroupName):
        self.spawn(self.process_simulate_vm_eviction, vmName, resourceGroupName)
        
    def process_simulate_vm_eviction(self, vmName, resourceGroupName):
        self.process_shutdown_vms(vmName, resourceGroupName)
    
    def generalize_vm(self, vmName, resourceGroupName):
        self.process_generalize_vm(vmName, resourceGroupName)
        #self.pool.spawn(self.process_generalize_vm, vmName, resourceGroupName)
            
    def process_generalize_vm(self, vmName, resourceGroupName):
        pass
        #response = RestResponse()
        #self.generalize_vm_finished(response)
     
    def capture_image_from_vm(self, imageName, vmName, resourceGroupName):
        self.process_capture_image_from_vm(imageName, vmName, resourceGroupName)
        #self.pool.spawn(self.process_capture_image_from_vm, imageName, vmName, resourceGroupName)
        
    def process_capture_image_from_vm(self, imageName, vmName, resourceGroupName):
        pass
        #response = RestResponse()
        #self.capture_image_from_vm_finished(response)

    
    # create a new virtual network with a given config
    # this function creates the following resources:
    # 1. VPN with render subnet and VPN subnet
    # 2. Firewall entries allowing ssh and vpn traffic
    # 3. (optional) a new VM which hosts the OpenVPN server with static address
    # 4. (optional) additional routes
    def create_virtual_network(self, resourceGroupName, virtualNetworkConfig):
        self.spawn(self.process_create_virtual_network, resourceGroupName, virtualNetworkConfig)
        
    def process_create_virtual_network(self, resourceGroupName, virtualNetworkConfig):
        driver = self.get_driver()
        vn_name = virtualNetworkConfig.name
        vn_subnet = virtualNetworkConfig.subnet
        render_subnet = virtualNetworkConfig.render_subnet
        vpn_subnet = virtualNetworkConfig.vpn_subnet
        vpn_client_subnet = virtualNetworkConfig.vpn_client_subnet
        vpn_local_network = virtualNetworkConfig.vpn_local_network
        vpn_ssh_keyfile = virtualNetworkConfig.vpn_ssh_keyfile
        location = virtualNetworkConfig.location
        response = RestResponse()
        deployment_response = DeploymentInfoResponse()
        deployment_response.provisioning_state = 'succeeded'
        network = None

        try:
            network = driver.ex_create_network(vn_subnet, name=vn_name, instance_tenancy='default')
            gateway = driver.ex_create_internet_gateway(name=vn_name)
            driver.ex_attach_internet_gateway(gateway, network)
        except:
            response.set_error_code('libcloud error')
            response.set_error_message(self.get_exc_message())
            deployment_response.set_error_code('libcloud error')
            deployment_response.set_error_message(self.get_exc_message())
            deployment_response.provisioning_state = 'failed'
            self.create_virtual_network_finished(response)
            self.get_deployment_info_finished(deployment_response)
            return
            
        try:    
            #subnet = driver.ex_create_subnetwork('rendersubnet', render_subnet, network, region)
            subnetRender = driver.ex_create_subnet(network.id, render_subnet, self.zone, name='rendersubnet')
            secGroupRender = driver.ex_create_security_group("rendersubnet", "Royal Render security group", network.id)
            driver.ex_authorize_security_group_ingress(secGroupRender['group_id'], 22, 22, ["0.0.0.0/0"], description="Allow SSH")
            driver.ex_authorize_security_group_ingress(secGroupRender['group_id'], 3389, 3389, ["0.0.0.0/0"], description="Allow RDP")
            source_ranges_vpnsubnet = [vpn_subnet]
            driver.ex_authorize_security_group_ingress(secGroupRender['group_id'], 0, 65535, source_ranges_vpnsubnet, protocol='all', description="Allow VPN subnet")
            routeTableRender = driver.ex_create_route_table(network, "rendersubnet")
            driver.ex_associate_route_table(routeTableRender, subnetRender)
        except:
            response.set_error_code('libcloud error')
            response.set_error_message(self.get_exc_message())
            deployment_response.set_error_code('libcloud error')
            deployment_response.set_error_message(self.get_exc_message())
            deployment_response.provisioning_state = 'failed'
            self.create_virtual_network_finished(response)
            self.get_deployment_info_finished(deployment_response)
            return

        if virtualNetworkConfig.create_vpn:    
            try:    
                subnetVpn = driver.ex_create_subnet(network.id, vpn_subnet, self.zone, name='vpnsubnet')
                secGroupVpn = driver.ex_create_security_group("vpnsubnet", "Royal Render security group", network.id)
                driver.ex_authorize_security_group_ingress(secGroupVpn['group_id'], 22, 22, ["0.0.0.0/0"], description="Allow SSH")
                driver.ex_authorize_security_group_ingress(secGroupVpn['group_id'], 1194, 1194, ["0.0.0.0/0"], protocol='udp', description="Allow OpenVPN")
                source_ranges_rendersubnet = [render_subnet]
                driver.ex_authorize_security_group_ingress(secGroupVpn['group_id'], 0, 65535, source_ranges_rendersubnet, protocol='all', description="Allow render subnet")
                source_ranges_vpnsubnet = [vpn_subnet]
                driver.ex_authorize_security_group_ingress(secGroupVpn['group_id'], 0, 65535, source_ranges_vpnsubnet, protocol='all', description="Allow VPN subnet")
                source_ranges_vpnclients = [vpn_client_subnet]
                driver.ex_authorize_security_group_ingress(secGroupVpn['group_id'], 0, 65535, source_ranges_vpnclients, protocol='all', description="Allow VPN client subnet")
                routeTableVpn = driver.ex_create_route_table(network, "vpnsubnet")
                driver.ex_associate_route_table(routeTableVpn, subnetVpn)
                driver.ex_create_route(routeTableVpn, "0.0.0.0/0", internet_gateway=gateway)
            except:
                response.set_error_code('libcloud error')
                response.set_error_message(self.get_exc_message())
                deployment_response.set_error_code('libcloud error')
                deployment_response.set_error_message(self.get_exc_message())
                deployment_response.provisioning_state = 'failed'
                self.create_virtual_network_finished(response)
                self.get_deployment_info_finished(deployment_response)
                return
            
            debug_error = ''

            try:
                vm_name = 'vpnvm'
                nodeSize = self.get_size(driver, virtualNetworkConfig.vpn_vm_size)
                nodeImage = self.get_image(driver, virtualNetworkConfig.vpn_image_name)
                groupId = secGroupVpn['group_id']
                
                # extract key name from key file
                keyname = None
                keyFilePath = vpn_ssh_keyfile
                keyFilePath.replace('\\', '/')
                keyFileName = os.path.basename(keyFilePath)
                keyFileName = os.path.splitext(keyFileName)[0]
                if len(keyFileName) > 0:
                    keyname = keyFileName                

                #create vm
                node = driver.create_node(vm_name, nodeSize, nodeImage, virtualNetworkConfig.location, ex_keyname=keyname, ex_security_group_ids=[groupId], ex_subnet=subnetVpn, ex_assign_public_ip=True)
                driver.ex_create_tags(node, {'rrgroup': resourceGroupName})
                
                # wait until running (mandatory for next steps)
                nodesToWait = [node]
                driver.wait_until_running(nodes=nodesToWait, ssh_interface='private_ips')
                
                # create static ip
                elasticIp = driver.ex_allocate_address()
                driver.ex_associate_address_with_node(node, elasticIp)

                # create routes
                driver.ex_create_route(routeTableRender, vpn_client_subnet, node=node)
                driver.ex_create_route(routeTableRender, vpn_local_network, node=node)
                
            except:
                response.set_error_code('libcloud error')
                response.set_error_message(self.get_exc_message())
                deployment_response.set_error_code('libcloud error')
                deployment_response.set_error_message(self.get_exc_message())
                deployment_response.provisioning_state = 'failed'
                self.create_virtual_network_finished(response)
                self.get_deployment_info_finished(deployment_response)
                return
            
        self.create_virtual_network_finished(response)
        self.get_deployment_info_finished(deployment_response)
        
    def create_uuid(context):
        random_uuid = context + '_' + str(uuid.uuid4())
        return random_uuid
        
