fix 1223. Increment ip address on scaling vdus
[osm/RO.git] / RO / osm_ro / nfvo.py
index a28f57f..e346699 100644 (file)
@@ -20,7 +20,7 @@
 # For those usages not covered by the Apache License, Version 2.0 please
 # contact with: nfvlabs@tid.es
 ##
-
 '''
 NFVO engine, implementing all the methods for the creation, deletion and management of vnfs, scenarios and instances
 '''
@@ -29,12 +29,17 @@ __date__ ="$16-sep-2014 22:05:01$"
 
 # import imp
 import json
+import string
 import yaml
+from random import choice as random_choice
 from osm_ro import utils
 from osm_ro.utils import deprecated
 from osm_ro.vim_thread import vim_thread
 import osm_ro.console_proxy_thread as cli
-from osm_ro import vimconn
+from osm_ro_plugin.vim_dummy import VimDummyConnector
+from osm_ro_plugin.sdn_dummy import SdnDummyConnector
+from osm_ro_plugin.sdn_failing import SdnFailingConnector
+from osm_ro_plugin import vimconn, sdnconn
 import logging
 import collections
 import math
@@ -44,15 +49,7 @@ from osm_ro.db_base import db_base_Exception
 from osm_ro import nfvo_db
 from threading import Lock
 import time as t
-# TODO py3 BEGIN
-# from lib_osm_openvim import ovim as ovim_module
-# from lib_osm_openvim.ovim import ovimException
-from unittest.mock  import MagicMock
-ovim_module = MagicMock()
-class ovimException(Exception):
-   pass
-ovim_module.ovimException = ovimException
-# TODO py3 END
+from osm_ro.sdn import Sdn, SdnException as ovimException
 
 from Crypto.PublicKey import RSA
 
@@ -64,11 +61,9 @@ from pkg_resources import iter_entry_points
 
 
 # WIM
-import osm_ro.wim.wimconn as wimconn
-import osm_ro.wim.wim_thread as wim_thread
-from osm_ro.http_tools import errors as httperrors
-from osm_ro.wim.engine import WimEngine
-from osm_ro.wim.persistence import WimPersistence
+from .http_tools import errors as httperrors
+from .wim.engine import WimEngine
+from .wim.persistence import WimPersistence
 from copy import deepcopy
 from pprint import pformat
 #
@@ -77,7 +72,7 @@ global global_config
 # WIM
 global wim_engine
 wim_engine  = None
-global wimconn_imported
+global sdnconn_imported
 #
 global logger
 global default_volume_size
@@ -90,7 +85,7 @@ plugins = {}   # dictionary with VIM type as key, loaded module as value
 vim_threads = {"running":{}, "deleting": {}, "names": []}      # threads running for attached-VIMs
 vim_persistent_info = {}
 # WIM
-wimconn_imported = {}   # dictionary with WIM type as key, loaded module as value
+sdnconn_imported = {}   # dictionary with WIM type as key, loaded module as value
 wim_threads = {"running":{}, "deleting": {}, "names": []}      # threads running for attached-WIMs
 wim_persistent_info = {}
 #
@@ -101,17 +96,28 @@ last_task_id = 0.0
 db = None
 db_lock = Lock()
 
+worker_id = None
 
 class NfvoException(httperrors.HttpMappedError):
     """Common Class for NFVO errors"""
 
-def _load_vim_plugin(name):
+def _load_plugin(name, type="vim"):
+    # type can be vim or sdn
     global plugins
-    for v in iter_entry_points('osm_rovim.plugins', name):
-        plugins[name] = v.load()
+    try:
+        for v in iter_entry_points('osm_ro{}.plugins'.format(type), name):
+            plugins[name] = v.load()
+    except Exception as e:
+        logger.critical("Cannot load osm_{}: {}".format(name, e))
+        if name:
+            plugins[name] = SdnFailingConnector("Cannot load osm_{}: {}".format(name, e))
     if name and name not in plugins:
-        raise NfvoException("Unknown vim type '{}'. This plugin has not been registered".format(name),
-                            httperrors.Bad_Request)
+        error_text = "Cannot load a module for {t} type '{n}'. The plugin 'osm_{n}' has not been" \
+                     " registered".format(t=type, n=name)
+        logger.critical(error_text)
+        plugins[name] = SdnFailingConnector(error_text)
+        # raise NfvoException("Cannot load a module for {t} type '{n}'. The plugin 'osm_{n}' has not been registered".
+        #                     format(t=type, n=name), httperrors.Bad_Request)
 
 def get_task_id():
     global last_task_id
@@ -135,20 +141,31 @@ def new_task(name, params, depends=None):
 def is_task_id(id):
     return True if id[:5] == "TASK-" else False
 
-
-def get_non_used_vim_name(datacenter_name, datacenter_id, tenant_name, tenant_id):
-    name = datacenter_name[:16]
-    if name not in vim_threads["names"]:
-        vim_threads["names"].append(name)
-        return name
-    if tenant_name:
-        name = datacenter_name[:16] + "." + tenant_name[:16]
-        if name not in vim_threads["names"]:
-            vim_threads["names"].append(name)
-            return name
-    name = datacenter_id
-    vim_threads["names"].append(name)
-    return name
+def get_process_id():
+    """
+    Obtain a unique ID for this process. If running from inside docker, it will get docker ID. If not it
+    will provide a random one
+    :return: Obtained ID
+    """
+    # Try getting docker id. If fails, get pid
+    try:
+        with open("/proc/self/cgroup", "r") as f:
+            for text_id_ in f.readlines():
+                if "docker/" not in text_id_:
+                    continue
+                _, _, text_id = text_id_.rpartition("/")
+                text_id = text_id.replace("\n", "")[:12]
+                if text_id:
+                    return text_id
+    except Exception:
+        pass
+    # Return a random id
+    return "".join(random_choice("0123456789abcdef") for _ in range(12))
+
+def get_non_used_vim_name(datacenter_name, datacenter_id):
+    return "{}:{}:{}".format(
+        worker_id[:12], datacenter_id.replace("-", "")[:32], datacenter_name[:16]
+    )
 
 # -- Move
 def get_non_used_wim_name(wim_name, wim_id, tenant_name, tenant_id):
@@ -166,37 +183,24 @@ def get_non_used_wim_name(wim_name, wim_id, tenant_name, tenant_id):
 
 
 def start_service(mydb, persistence=None, wim=None):
-    global db, global_config, plugins
+    global db, global_config, plugins, ovim, worker_id
     db = nfvo_db.nfvo_db(lock=db_lock)
     mydb.lock = db_lock
     db.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
-    global ovim
 
     persistence = persistence or  WimPersistence(db)
 
-    # Initialize openvim for SDN control
-    # TODO: Avoid static configuration by adding new parameters to openmanod.cfg
-    # TODO: review ovim.py to delete not needed configuration
-    ovim_configuration = {
-        'logger_name': 'openmano.ovim',
-        'network_vlan_range_start': 1000,
-        'network_vlan_range_end': 4096,
-        'db_name': global_config["db_ovim_name"],
-        'db_host': global_config["db_ovim_host"],
-        'db_user': global_config["db_ovim_user"],
-        'db_passwd': global_config["db_ovim_passwd"],
-        'bridge_ifaces': {},
-        'mode': 'normal',
-        'network_type': 'bridge',
-        #TODO: log_level_of should not be needed. To be modified in ovim
-        'log_level_of': 'DEBUG'
-    }
     try:
+        worker_id = get_process_id()
+        if "rosdn_dummy" not in plugins:
+            plugins["rosdn_dummy"] = SdnDummyConnector
+        if "rovim_dummy" not in plugins:
+            plugins["rovim_dummy"] = VimDummyConnector
         # starts ovim library
-        ovim = ovim_module.ovim(ovim_configuration)
+        ovim = Sdn(db, plugins)
 
         global wim_engine
-        wim_engine = wim or WimEngine(persistence)
+        wim_engine = wim or WimEngine(persistence, plugins)
         wim_engine.ovim = ovim
 
         ovim.start_service()
@@ -221,38 +225,51 @@ def start_service(mydb, persistence=None, wim=None):
                 extra.update(yaml.load(vim["dt_config"], Loader=yaml.Loader))
             plugin_name = "rovim_" + vim["type"]
             if plugin_name not in plugins:
-                _load_vim_plugin(plugin_name)
+                _load_plugin(plugin_name, type="vim")
 
             thread_id = vim['datacenter_tenant_id']
             vim_persistent_info[thread_id] = {}
             try:
                 #if not tenant:
                 #    return -httperrors.Bad_Request, "You must provide a valid tenant name or uuid for VIM  %s" % ( vim["type"])
-                myvim = plugins[plugin_name].vimconnector(
+                myvim = plugins[plugin_name](
                     uuid=vim['datacenter_id'], name=vim['datacenter_name'],
                     tenant_id=vim['vim_tenant_id'], tenant_name=vim['vim_tenant_name'],
                     url=vim['vim_url'], url_admin=vim['vim_url_admin'],
                     user=vim['user'], passwd=vim['passwd'],
                     config=extra, persistent_info=vim_persistent_info[thread_id]
                 )
-            except vimconn.vimconnException as e:
+            except vimconn.VimConnException as e:
                 myvim = e
                 logger.error("Cannot launch thread for VIM {} '{}': {}".format(vim['datacenter_name'],
                                                                                vim['datacenter_id'], e))
             except Exception as e:
-                raise NfvoException("Error at VIM  {}; {}: {}".format(vim["type"], type(e).__name__, e),
-                                    httperrors.Internal_Server_Error)
-            thread_name = get_non_used_vim_name(vim['datacenter_name'], vim['datacenter_id'], vim['vim_tenant_name'],
-                                                vim['vim_tenant_id'])
-            new_thread = vim_thread(task_lock, plugins, thread_name, vim['datacenter_name'],
-                                    vim['datacenter_tenant_id'], db=db, db_lock=db_lock, ovim=ovim)
+                logger.critical("Cannot launch thread for VIM {} '{}': {}".format(vim['datacenter_name'],
+                                                                                  vim['datacenter_id'], e))
+                # raise NfvoException("Error at VIM  {}; {}: {}".format(vim["type"], type(e).__name__, e),
+                #                     httperrors.Internal_Server_Error)
+            thread_name = get_non_used_vim_name(vim['datacenter_name'], vim['datacenter_id'])
+            new_thread = vim_thread(task_lock, plugins, thread_name, None,
+                                    vim['datacenter_tenant_id'], db=db)
             new_thread.start()
             vim_threads["running"][thread_id] = new_thread
+        wims = mydb.get_rows(FROM="wim_accounts join wims on wim_accounts.wim_id=wims.uuid",
+                             WHERE={"sdn": "true"},
+                             SELECT=("wim_accounts.uuid as uuid", "type", "wim_accounts.name as name"))
+        for wim in wims:
+            plugin_name = "rosdn_" + wim["type"]
+            if plugin_name not in plugins:
+                _load_plugin(plugin_name, type="sdn")
 
+            thread_id = wim['uuid']
+            thread_name = get_non_used_vim_name(wim['name'], wim['uuid'])
+            new_thread = vim_thread(task_lock, plugins, thread_name, wim['uuid'], None, db=db)
+            new_thread.start()
+            vim_threads["running"][thread_id] = new_thread
         wim_engine.start_threads()
     except db_base_Exception as e:
         raise NfvoException(str(e) + " at nfvo.get_vim", e.http_code)
-    except ovim_module.ovimException as e:
+    except ovimException as e:
         message = str(e)
         if message[:22] == "DATABASE wrong version":
             message = "DATABASE wrong version of lib_osm_openvim {msg} -d{dbname} -u{dbuser} -p{dbpass} {ver}' "\
@@ -397,7 +414,7 @@ def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, da
             plugin_name = "rovim_" + vim["type"]
             if plugin_name not in plugins:
                 try:
-                    _load_vim_plugin(plugin_name)
+                    _load_plugin(plugin_name, type="vim")
                 except NfvoException as e:
                     if ignore_errors:
                         logger.error("{}".format(e))
@@ -414,7 +431,7 @@ def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, da
                     persistent_info = {}
                 #if not tenant:
                 #    return -httperrors.Bad_Request, "You must provide a valid tenant name or uuid for VIM  %s" % ( vim["type"])
-                vim_dict[vim['datacenter_id']] = plugins[plugin_name].vimconnector(
+                vim_dict[vim['datacenter_id']] = plugins[plugin_name](
                                 uuid=vim['datacenter_id'], name=vim['datacenter_name'],
                                 tenant_id=vim.get('vim_tenant_id',vim_tenant),
                                 tenant_name=vim.get('vim_tenant_name',vim_tenant_name),
@@ -427,7 +444,7 @@ def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, da
                     logger.error("Error at VIM  {}; {}: {}".format(vim["type"], type(e).__name__, str(e)))
                     continue
                 http_code = httperrors.Internal_Server_Error
-                if isinstance(e, vimconn.vimconnException):
+                if isinstance(e, vimconn.VimConnException):
                     http_code = e.http_code
                 raise NfvoException("Error at VIM  {}; {}: {}".format(vim["type"], type(e).__name__, str(e)), http_code)
         return vim_dict
@@ -457,7 +474,7 @@ def rollback(mydb,  vims, rollback_list):
                     vim.delete_network(item["uuid"])
                 elif item["what"]=="vm":
                     vim.delete_vminstance(item["uuid"])
-            except vimconn.vimconnException as e:
+            except vimconn.VimConnException as e:
                 logger.error("Error in rollback. Not possible to delete VIM %s '%s'. Message: %s", item['what'], item["uuid"], str(e))
                 undeleted_items.append("{} {} from VIM {}".format(item['what'], item["uuid"], vim["name"]))
             except db_base_Exception as e:
@@ -473,9 +490,9 @@ def rollback(mydb,  vims, rollback_list):
                 logger.error("Error in rollback. Not possible to delete %s '%s' from DB. Message: %s", item['what'], item["uuid"], str(e))
                 undeleted_items.append("{} '{}'".format(item['what'], item["uuid"]))
     if len(undeleted_items)==0:
-        return True,Rollback successful."
+        return True, "Rollback successful."
     else:
-        return False,Rollback fails to delete: " + str(undeleted_items)
+        return False, "Rollback fails to delete: " + str(undeleted_items)
 
 
 def check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1):
@@ -634,14 +651,14 @@ def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vi
                 vim_images = vim.get_image_list(filter_dict)
                 #logger.debug('>>>>>>>> VIM images: %s', str(vim_images))
                 if len(vim_images) > 1:
-                    raise vimconn.vimconnException("More than one candidate VIM image found for filter: {}".format(str(filter_dict)), httperrors.Conflict)
+                    raise vimconn.VimConnException("More than one candidate VIM image found for filter: {}".format(str(filter_dict)), httperrors.Conflict)
                 elif len(vim_images) == 0:
-                    raise vimconn.vimconnNotFoundException("Image not found at VIM with filter: '{}'".format(str(filter_dict)))
+                    raise vimconn.VimConnNotFoundException("Image not found at VIM with filter: '{}'".format(str(filter_dict)))
                 else:
                     #logger.debug('>>>>>>>> VIM image 0: %s', str(vim_images[0]))
                     image_vim_id = vim_images[0]['id']
 
-        except vimconn.vimconnNotFoundException as e:
+        except vimconn.VimConnNotFoundException as e:
             #Create the image in VIM only if image_dict['location'] or image_dict['new_location'] is not None
             try:
                 #image_dict['location']=image_dict.get('new_location') if image_dict['location'] is None
@@ -651,15 +668,15 @@ def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vi
                     image_created="true"
                 else:
                     #If we reach this point, then the image has image name, and optionally checksum, and could not be found
-                    raise vimconn.vimconnException(str(e))
-            except vimconn.vimconnException as e:
+                    raise vimconn.VimConnException(str(e))
+            except vimconn.VimConnException as e:
                 if return_on_error:
                     logger.error("Error creating image at VIM '%s': %s", vim["name"], str(e))
                     raise
                 image_vim_id = None
                 logger.warn("Error creating image at VIM '%s': %s", vim["name"], str(e))
                 continue
-        except vimconn.vimconnException as e:
+        except vimconn.VimConnException as e:
             if return_on_error:
                 logger.error("Error contacting VIM to know if the image exists at VIM: %s", str(e))
                 raise
@@ -797,7 +814,7 @@ def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_
             try:
                 vim.get_flavor(flavor_vim_id)
                 continue #flavor exist
-            except vimconn.vimconnException:
+            except vimconn.VimConnException:
                 pass
         #create flavor at vim
         logger.debug("nfvo.create_or_use_flavor() adding flavor to VIM %s", vim["name"])
@@ -805,14 +822,14 @@ def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_
             flavor_vim_id = None
             flavor_vim_id=vim.get_flavor_id_from_data(flavor_dict)
             flavor_created="false"
-        except vimconn.vimconnException as e:
+        except vimconn.VimConnException as e:
             pass
         try:
             if not flavor_vim_id:
                 flavor_vim_id = vim.new_flavor(flavor_dict)
                 rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"flavor","uuid":flavor_vim_id})
                 flavor_created="true"
-        except vimconn.vimconnException as e:
+        except vimconn.VimConnException as e:
             if return_on_error:
                 logger.error("Error creating flavor at VIM %s: %s.", vim["name"], str(e))
                 raise
@@ -970,6 +987,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
 
             # table nets (internal-vld)
             net_id2uuid = {}  # for mapping interface with network
+            net_id2index = {}  # for mapping interface with network
             for vld in vnfd.get("internal-vld").values():
                 net_uuid = str(uuid4())
                 uuid_list.append(net_uuid)
@@ -982,6 +1000,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                     "type": "bridge",   # TODO adjust depending on connection point type
                 }
                 net_id2uuid[vld.get("id")] = net_uuid
+                net_id2index[vld.get("id")] = len(db_nets)
                 db_nets.append(db_net)
                 # ip-profile, link db_ip_profile with db_sce_net
                 if vld.get("ip-profile-ref"):
@@ -1174,7 +1193,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                                 raise KeyError()
 
                             if vdu_id in vdu_id2cp_name:
-                                vdu_id2cp_name[vdu_id] = None  # more than two connecdtion point for this VDU
+                                vdu_id2cp_name[vdu_id] = None  # more than two connection point for this VDU
                             else:
                                 vdu_id2cp_name[vdu_id] = db_interface["external_name"]
 
@@ -1209,6 +1228,10 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                             if not icp:
                                 raise KeyError("is not referenced by any 'internal-vld'")
 
+                            # set network type as data
+                            if iface.get("virtual-interface") and iface["virtual-interface"].get("type") in \
+                                    ("SR-IOV", "PCI-PASSTHROUGH"):
+                                db_nets[net_id2index[icp_vld.get("id")]]["type"] = "data"
                             db_interface["net_id"] = net_id2uuid[icp_vld.get("id")]
                             if str(icp_descriptor.get("port-security-enabled")).lower() == "false":
                                 db_interface["port_security"] = 0
@@ -1348,7 +1371,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
 
             if vnfd["mgmt-interface"].get("ip-address"):
                 mgmt_access["ip-address"] = str(vnfd["mgmt-interface"].get("ip-address"))
-            if vnfd["mgmt-interface"].get("cp"):
+            if vnfd["mgmt-interface"].get("cp") and vnfd.get("vdu"):
                 if vnfd["mgmt-interface"]["cp"] not in cp_name2iface_uuid:
                     raise NfvoException("Error. Invalid VNF descriptor at 'vnfd[{vnf}]':'mgmt-interface':'cp'['{cp}']. "
                                         "Reference to a non-existing connection-point".format(
@@ -1525,7 +1548,7 @@ def new_vnf(mydb, tenant_id, vnf_descriptor):
         # Step 8. Adding the VNF to the NFVO DB
         vnf_id = mydb.new_vnf_as_a_whole(tenant_id,vnf_name,vnf_descriptor,VNFCDict)
         return vnf_id
-    except (db_base_Exception, vimconn.vimconnException, KeyError) as e:
+    except (db_base_Exception, vimconn.VimConnException, KeyError) as e:
         _, message = rollback(mydb, vims, rollback_list)
         if isinstance(e, db_base_Exception):
             error_text = "Exception at database"
@@ -1661,7 +1684,7 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor):
         # Step 8. Adding the VNF to the NFVO DB
         vnf_id = mydb.new_vnf_as_a_whole2(tenant_id,vnf_name,vnf_descriptor,VNFCDict)
         return vnf_id
-    except (db_base_Exception, vimconn.vimconnException, KeyError) as e:
+    except (db_base_Exception, vimconn.VimConnException, KeyError) as e:
         _, message = rollback(mydb, vims, rollback_list)
         if isinstance(e, db_base_Exception):
             error_text = "Exception at database"
@@ -1790,10 +1813,10 @@ def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None):
                     continue
                 try:
                     myvim.delete_flavor(flavor_vim["vim_id"])
-                except vimconn.vimconnNotFoundException:
+                except vimconn.VimConnNotFoundException:
                     logger.warn("VIM flavor %s not exist at datacenter %s", flavor_vim["vim_id"],
                                 flavor_vim["datacenter_vim_id"] )
-                except vimconn.vimconnException as e:
+                except vimconn.VimConnException as e:
                     logger.error("Not possible to delete VIM flavor %s from datacenter %s: %s %s",
                             flavor_vim["vim_id"], flavor_vim["datacenter_vim_id"], type(e).__name__, str(e))
                     undeletedItems.append("flavor {} from VIM {}".format(flavor_vim["vim_id"],
@@ -1823,9 +1846,9 @@ def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None):
                 myvim=vims[ image_vim["datacenter_id"] ]
                 try:
                     myvim.delete_image(image_vim["vim_id"])
-                except vimconn.vimconnNotFoundException as e:
+                except vimconn.VimConnNotFoundException as e:
                     logger.warn("VIM image %s not exist at datacenter %s", image_vim["vim_id"], image_vim["datacenter_id"] )
-                except vimconn.vimconnException as e:
+                except vimconn.VimConnException as e:
                     logger.error("Not possible to delete VIM image %s from datacenter %s: %s %s",
                             image_vim["vim_id"], image_vim["datacenter_id"], type(e).__name__, str(e))
                     undeletedItems.append("image {} from VIM {}".format(image_vim["vim_id"], image_vim["datacenter_id"] ))
@@ -1887,7 +1910,7 @@ def get_hosts(mydb, nfvo_tenant_id):
 
         #print 'datacenters '+ json.dumps(datacenter, indent=4)
         return datacenter
-    except vimconn.vimconnException as e:
+    except vimconn.VimConnException as e:
         raise NfvoException("Not possible to get_host_list from VIM: {}".format(str(e)), e.http_code)
 
 
@@ -2428,45 +2451,48 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
 
                 # table sce_interfaces (vld:vnfd-connection-point-ref)
                 for iface in vld.get("vnfd-connection-point-ref").values():
+                    # Check if there are VDUs in the descriptor
                     vnf_index = str(iface['member-vnf-index-ref'])
-                    # check correct parameters
-                    if vnf_index not in vnf_index2vnf_uuid:
-                        raise NfvoException("Error. Invalid NS descriptor at 'nsd[{}]':'vld[{}]':'vnfd-connection-point"
-                                            "-ref':'member-vnf-index-ref':'{}'. Reference to a non-existing index at "
-                                            "'nsd':'constituent-vnfd'".format(
-                                                str(nsd["id"]), str(vld["id"]), str(iface["member-vnf-index-ref"])),
-                                            httperrors.Bad_Request)
-
-                    existing_ifaces = mydb.get_rows(SELECT=('i.uuid as uuid', 'i.type as iface_type'),
-                                                    FROM="interfaces as i join vms on i.vm_id=vms.uuid",
-                                                    WHERE={'vnf_id': vnf_index2vnf_uuid[vnf_index],
-                                                           'external_name': get_str(iface, "vnfd-connection-point-ref",
-                                                                                    255)})
-                    if not existing_ifaces:
-                        raise NfvoException("Error. Invalid NS descriptor at 'nsd[{}]':'vld[{}]':'vnfd-connection-point"
-                                            "-ref':'vnfd-connection-point-ref':'{}'. Reference to a non-existing "
-                                            "connection-point name at VNFD '{}'".format(
-                                                str(nsd["id"]), str(vld["id"]), str(iface["vnfd-connection-point-ref"]),
-                                                str(iface.get("vnfd-id-ref"))[:255]),
-                                            httperrors.Bad_Request)
-                    interface_uuid = existing_ifaces[0]["uuid"]
-                    if existing_ifaces[0]["iface_type"] == "data":
-                        db_sce_net["type"] = "data"
-                    sce_interface_uuid = str(uuid4())
-                    uuid_list.append(sce_net_uuid)
-                    iface_ip_address = None
-                    if iface.get("ip-address"):
-                        iface_ip_address = str(iface.get("ip-address"))
-                    db_sce_interface = {
-                        "uuid": sce_interface_uuid,
-                        "sce_vnf_id": vnf_index2scevnf_uuid[vnf_index],
-                        "sce_net_id": sce_net_uuid,
-                        "interface_id": interface_uuid,
-                        "ip_address": iface_ip_address,
-                    }
-                    db_sce_interfaces.append(db_sce_interface)
-                if not db_sce_net["type"]:
-                    db_sce_net["type"] = "bridge"
+                    existing_vdus = mydb.get_rows(SELECT=('vms.uuid'), FROM="vms", WHERE={'vnf_id': vnf_index2vnf_uuid[vnf_index]})
+                    if existing_vdus:
+                        # check correct parameters
+                        if vnf_index not in vnf_index2vnf_uuid:
+                            raise NfvoException("Error. Invalid NS descriptor at 'nsd[{}]':'vld[{}]':'vnfd-connection-point"
+                                              "-ref':'member-vnf-index-ref':'{}'. Reference to a non-existing index at "
+                                              "'nsd':'constituent-vnfd'".format(
+                                                  str(nsd["id"]), str(vld["id"]), str(iface["member-vnf-index-ref"])),
+                                              httperrors.Bad_Request)
+
+                        existing_ifaces = mydb.get_rows(SELECT=('i.uuid as uuid', 'i.type as iface_type'),
+                                                      FROM="interfaces as i join vms on i.vm_id=vms.uuid",
+                                                      WHERE={'vnf_id': vnf_index2vnf_uuid[vnf_index],
+                                                             'external_name': get_str(iface, "vnfd-connection-point-ref",
+                                                                                      255)})
+                        if not existing_ifaces:
+                            raise NfvoException("Error. Invalid NS descriptor at 'nsd[{}]':'vld[{}]':'vnfd-connection-point"
+                                              "-ref':'vnfd-connection-point-ref':'{}'. Reference to a non-existing "
+                                              "connection-point name at VNFD '{}'".format(
+                                                  str(nsd["id"]), str(vld["id"]), str(iface["vnfd-connection-point-ref"]),
+                                                  str(iface.get("vnfd-id-ref"))[:255]),
+                                              httperrors.Bad_Request)
+                        interface_uuid = existing_ifaces[0]["uuid"]
+                        if existing_ifaces[0]["iface_type"] == "data":
+                            db_sce_net["type"] = "data"
+                        sce_interface_uuid = str(uuid4())
+                        uuid_list.append(sce_net_uuid)
+                        iface_ip_address = None
+                        if iface.get("ip-address"):
+                            iface_ip_address = str(iface.get("ip-address"))
+                        db_sce_interface = {
+                            "uuid": sce_interface_uuid,
+                            "sce_vnf_id": vnf_index2scevnf_uuid[vnf_index],
+                            "sce_net_id": sce_net_uuid,
+                            "interface_id": interface_uuid,
+                            "ip_address": iface_ip_address,
+                        }
+                        db_sce_interfaces.append(db_sce_interface)
+                        if not db_sce_net["type"]:
+                            db_sce_net["type"] = "bridge"
 
             # table sce_vnffgs (vnffgd)
             for vnffg in nsd.get("vnffgd").values():
@@ -2668,11 +2694,12 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
             myNetDict["type"] = myNetType
             myNetDict["tenant_id"] = myvim_tenant
             myNetIPProfile = sce_net.get('ip_profile', None)
+            myProviderNetwork = sce_net.get('provider_network', None)
             #TODO:
             #We should use the dictionary as input parameter for new_network
             #print myNetDict
             if not sce_net["external"]:
-                network_id, _ = myvim.new_network(myNetName, myNetType, myNetIPProfile)
+                network_id, _ = myvim.new_network(myNetName, myNetType, myNetIPProfile, provider_network_profile=myProviderNetwork)
                 #print "New VIM network created for scenario %s. Network id:  %s" % (scenarioDict['name'],network_id)
                 sce_net['vim_id'] = network_id
                 auxNetDict['scenario'][sce_net['uuid']] = network_id
@@ -2703,10 +2730,11 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
                 myNetDict["type"] = myNetType
                 myNetDict["tenant_id"] = myvim_tenant
                 myNetIPProfile = net.get('ip_profile', None)
+                myProviderNetwork = sce_net.get('provider_network', None)
                 #print myNetDict
                 #TODO:
                 #We should use the dictionary as input parameter for new_network
-                network_id, _  = myvim.new_network(myNetName, myNetType, myNetIPProfile)
+                network_id, _  = myvim.new_network(myNetName, myNetType, myNetIPProfile, provider_network_profile=myProviderNetwork)
                 #print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id)
                 net['vim_id'] = network_id
                 if sce_vnf['uuid'] not in auxNetDict:
@@ -2847,7 +2875,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
         instance_id = mydb.new_instance_scenario_as_a_whole(tenant_id,instance_scenario_name, instance_scenario_description, scenarioDict)
         return mydb.get_instance_scenario(instance_id)
 
-    except (db_base_Exception, vimconn.vimconnException) as e:
+    except (db_base_Exception, vimconn.VimConnException) as e:
         _, message = rollback(mydb, vims, rollbackList)
         if isinstance(e, db_base_Exception):
             error_text = "Exception at database"
@@ -2943,6 +2971,7 @@ def unify_cloud_config(cloud_config_preserve, cloud_config):
 
 
 def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_id=None):
+    global plugins
     datacenter_id = None
     datacenter_name = None
     thread = None
@@ -2950,7 +2979,7 @@ def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_i
         if datacenter_tenant_id:
             thread_id = datacenter_tenant_id
             thread = vim_threads["running"].get(datacenter_tenant_id)
-        else:
+        if not thread:
             where_={"td.nfvo_tenant_id": tenant_id}
             if datacenter_id_name:
                 if utils.check_valid_uuid(datacenter_id_name):
@@ -2962,7 +2991,7 @@ def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_i
             if datacenter_tenant_id:
                 where_["dt.uuid"] = datacenter_tenant_id
             datacenters = mydb.get_rows(
-                SELECT=("dt.uuid as datacenter_tenant_id",),
+                SELECT=("dt.uuid as datacenter_tenant_id, d.name as datacenter_name", "d.type as type"),
                 FROM="datacenter_tenants as dt join tenants_datacenters as td on dt.uuid=td.datacenter_tenant_id "
                      "join datacenters as d on d.uuid=dt.datacenter_id",
                 WHERE=where_)
@@ -2970,7 +2999,18 @@ def get_vim_thread(mydb, tenant_id, datacenter_id_name=None, datacenter_tenant_i
                 raise NfvoException("More than one datacenters found, try to identify with uuid", httperrors.Conflict)
             elif datacenters:
                 thread_id = datacenters[0]["datacenter_tenant_id"]
+                datacenter_name = datacenters[0]["datacenter_name"]
                 thread = vim_threads["running"].get(thread_id)
+                if not thread:
+                    datacenter_type = datacenters[0]["type"]
+                    plugin_name = "rovim_" + datacenter_type
+                    if plugin_name not in plugins:
+                        _load_plugin(plugin_name, type="vim")
+                    thread_name = get_non_used_vim_name(datacenter_name, datacenter_id)
+                    thread = vim_thread(task_lock, plugins, thread_name, None,
+                                        thread_id, db=mydb)
+                    thread.start()
+                    vim_threads["running"][thread_id] = thread
         if not thread:
             raise NfvoException("datacenter '{}' not found".format(str(datacenter_id_name)), httperrors.Not_Found)
         return thread_id, thread
@@ -2991,13 +3031,14 @@ def get_datacenter_uuid(mydb, tenant_id, datacenter_id_name):
                " dt on td.datacenter_tenant_id=dt.uuid"
     else:
         from_ = 'datacenters as d'
-    vimaccounts = mydb.get_rows(FROM=from_, SELECT=("d.uuid as uuid, d.name as name",), WHERE=WHERE_dict )
+    vimaccounts = mydb.get_rows(FROM=from_, SELECT=("d.uuid as uuid", "d.name as name", "d.type as type"),
+                                WHERE=WHERE_dict)
     if len(vimaccounts) == 0:
         raise NfvoException("datacenter '{}' not found".format(str(datacenter_id_name)), httperrors.Not_Found)
-    elif len(vimaccounts)>1:
-        #print "nfvo.datacenter_action() error. Several datacenters found"
+    elif len(vimaccounts) > 1:
+        # print "nfvo.datacenter_action() error. Several datacenters found"
         raise NfvoException("More than one datacenters found, try to identify with uuid", httperrors.Conflict)
-    return vimaccounts[0]["uuid"], vimaccounts[0]["name"]
+    return vimaccounts[0]["uuid"], vimaccounts[0]
 
 
 def get_datacenter_by_name_uuid(mydb, tenant_id, datacenter_id_name=None, **extra_filter):
@@ -3030,9 +3071,18 @@ def update(d, u):
     return d
 
 
+def _get_wim(db, wim_account_id):
+    # get wim from wim_account
+    wim_accounts = db.get_rows(FROM='wim_accounts', WHERE={"uuid": wim_account_id})
+    if not wim_accounts:
+        raise NfvoException("Not found sdn id={}".format(wim_account_id), http_code=httperrors.Not_Found)
+    return wim_accounts[0]["wim_id"]
+
+
 def create_instance(mydb, tenant_id, instance_dict):
     # print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
     # logger.debug("Creating instance...")
+
     scenario = instance_dict["scenario"]
 
     # find main datacenter
@@ -3094,6 +3144,7 @@ def create_instance(mydb, tenant_id, instance_dict):
     }
 
     # Auxiliary dictionaries from x to y
+    sce_net2wim_instance = {}
     sce_net2instance = {}
     net2task_id = {'scenario': {}}
     # Mapping between local networks and WIMs
@@ -3182,6 +3233,13 @@ def create_instance(mydb, tenant_id, instance_dict):
                     else:
                         update(scenario_net['ip_profile'], ipprofile_db)
 
+                if net_instance_desc.get('provider-network'):
+                    provider_network_db = net_instance_desc['provider-network']
+                    if 'provider_network' not in scenario_net:
+                        scenario_net['provider_network'] = provider_network_db
+                    else:
+                        update(scenario_net['provider_network'], provider_network_db)
+
             for vdu_id, vdu_instance_desc in vnf_instance_desc.get("vdus", {}).items():
                 for scenario_vm in scenario_vnf['vms']:
                     if vdu_id == scenario_vm['osm_id'] or vdu_id == scenario_vm["name"]:
@@ -3214,6 +3272,14 @@ def create_instance(mydb, tenant_id, instance_dict):
                             scenario_net['ip_profile'] = ipprofile_db
                         else:
                             update(scenario_net['ip_profile'], ipprofile_db)
+                    if 'provider-network' in net_instance_desc:
+                        provider_network_db = net_instance_desc['provider-network']
+
+                        if 'provider-network' not in scenario_net:
+                            scenario_net['provider_network'] = provider_network_db
+                        else:
+                            update(scenario_net['provider-network'], provider_network_db)
+
             for interface in net_instance_desc.get('interfaces', ()):
                 if 'ip_address' in interface:
                     for vnf in scenarioDict['vnfs']:
@@ -3226,10 +3292,13 @@ def create_instance(mydb, tenant_id, instance_dict):
         # logger.debug("Creating instance scenario-dict MERGED:\n%s",
         #              yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False))
 
+
         # 1. Creating new nets (sce_nets) in the VIM"
         number_mgmt_networks = 0
         db_instance_nets = []
+        db_instance_wim_nets = []
         for sce_net in scenarioDict['nets']:
+
             sce_net_uuid = sce_net.get('uuid', sce_net["name"])
             # get involved datacenters where this network need to be created
             involved_datacenters = []
@@ -3246,28 +3315,6 @@ def create_instance(mydb, tenant_id, instance_dict):
                 involved_datacenters.append(default_datacenter_id)
             target_wim_account = sce_net.get("wim_account", default_wim_account)
 
-            # --> WIM
-            # TODO: use this information during network creation
-            wim_account_id = wim_account_name = None
-            if len(involved_datacenters) > 1 and 'uuid' in sce_net:
-                if target_wim_account is None or target_wim_account is True:  # automatic selection of WIM
-                    # OBS: sce_net without uuid are used internally to VNFs
-                    # and the assumption is that VNFs will not be split among
-                    # different datacenters
-                    wim_account = wim_engine.find_suitable_wim_account(
-                        involved_datacenters, tenant_id)
-                    wim_account_id = wim_account['uuid']
-                    wim_account_name = wim_account['name']
-                    wim_usage[sce_net['uuid']] = wim_account_id
-                elif isinstance(target_wim_account, str):     # manual selection of WIM
-                    wim_account.persist.get_wim_account_by(target_wim_account, tenant_id)
-                    wim_account_id = wim_account['uuid']
-                    wim_account_name = wim_account['name']
-                    wim_usage[sce_net['uuid']] = wim_account_id
-                else:  # not WIM usage
-                    wim_usage[sce_net['uuid']] = False
-            # <-- WIM
-
             descriptor_net = {}
             if instance_dict.get("networks"):
                 if sce_net.get("uuid") in instance_dict["networks"]:
@@ -3286,6 +3333,7 @@ def create_instance(mydb, tenant_id, instance_dict):
                     if site.get("datacenter") and site["datacenter"] not in involved_datacenters:
                         involved_datacenters.append(site["datacenter"])
             sce_net2instance[sce_net_uuid] = {}
+            sce_net2wim_instance[sce_net_uuid] = {}
             net2task_id['scenario'][sce_net_uuid] = {}
 
             use_network = None
@@ -3299,14 +3347,39 @@ def create_instance(mydb, tenant_id, instance_dict):
                 )
                 if not target_instance_nets:
                     raise NfvoException(
-                        "Cannot find the target network at instance:networks[{}]:use-network".format(descriptor_net_name),
-                        httperrors.Bad_Request)
+                        "Cannot find the target network at instance:networks[{}]:use-network".format(
+                            descriptor_net_name), httperrors.Bad_Request)
                 else:
                     use_network = target_instance_nets[0]["related"]
 
             if sce_net["external"]:
                 number_mgmt_networks += 1
 
+            # --> WIM
+            # TODO: use this information during network creation
+            wim_account_id = wim_account_name = None
+            if len(involved_datacenters) > 1 and 'uuid' in sce_net:
+                urls = [myvims[v].url for v in involved_datacenters]
+                if len(set(urls)) < 2:
+                    wim_usage[sce_net['uuid']] = False
+                elif target_wim_account is None or target_wim_account is True:  # automatic selection of WIM
+                    # OBS: sce_net without uuid are used internally to VNFs
+                    # and the assumption is that VNFs will not be split among
+                    # different datacenters
+                    wim_account = wim_engine.find_suitable_wim_account(
+                        involved_datacenters, tenant_id)
+                    wim_account_id = wim_account['uuid']
+                    wim_account_name = wim_account['name']
+                    wim_usage[sce_net['uuid']] = wim_account_id
+                elif isinstance(target_wim_account, str):     # manual selection of WIM
+                    wim_account.persist.get_wim_account_by(target_wim_account, tenant_id)
+                    wim_account_id = wim_account['uuid']
+                    wim_account_name = wim_account['name']
+                    wim_usage[sce_net['uuid']] = wim_account_id
+                else:  # not WIM usage
+                    wim_usage[sce_net['uuid']] = False
+            # <-- WIM
+
             for datacenter_id in involved_datacenters:
                 netmap_use = None
                 netmap_create = None
@@ -3380,7 +3453,9 @@ def create_instance(mydb, tenant_id, instance_dict):
                 task_extra = {}
                 if create_network:
                     task_action = "CREATE"
-                    task_extra["params"] = (net_vim_name, net_type, sce_net.get('ip_profile', None), wim_account_name)
+                    task_extra["params"] = (net_vim_name, net_type, sce_net.get('ip_profile', None), False,
+                                            sce_net.get('provider_network', None), wim_account_name)
+
                     if lookfor_network:
                         task_extra["find"] = (lookfor_filter,)
                 elif lookfor_network:
@@ -3393,6 +3468,45 @@ def create_instance(mydb, tenant_id, instance_dict):
                 sce_net2instance[sce_net_uuid][datacenter_id] = net_uuid
                 if not related_network:   # all db_instance_nets will have same related
                     related_network = use_network or net_uuid
+                sdn_net_id = None
+                sdn_controller = vim.config.get('sdn-controller')
+                sce_net2wim_instance[sce_net_uuid][datacenter_id] = None
+                if sdn_controller and net_type in ("data", "ptp"):
+                    wim_id = _get_wim(mydb, sdn_controller)
+                    sdn_net_id = str(uuid4())
+                    sce_net2wim_instance[sce_net_uuid][datacenter_id] = sdn_net_id
+                    task_extra["sdn_net_id"] = sdn_net_id
+                    db_instance_wim_nets.append({
+                        "uuid": sdn_net_id,
+                        "instance_scenario_id": instance_uuid,
+                        "sce_net_id": sce_net.get("uuid"),
+                        "wim_id": wim_id,
+                        "wim_account_id": sdn_controller,
+                        'status': 'BUILD',  # if create_network else "ACTIVE"
+                        "related": related_network,
+                        'multipoint': True if net_type=="data" else False,
+                        "created": create_network, # TODO py3
+                        "sdn": True,
+                    })
+
+                    task_wim_extra = {"params": [net_type, wim_account_name]}
+                    # add sdn interfaces
+                    if sce_net.get('provider_network') and sce_net['provider_network'].get("sdn-ports"):
+                        task_wim_extra["sdn-ports"] = sce_net['provider_network'].get("sdn-ports")
+                    db_vim_action = {
+                        "instance_action_id": instance_action_id,
+                        "status": "SCHEDULED",
+                        "task_index": task_index,
+                        # "datacenter_vim_id": myvim_thread_id,
+                        "wim_account_id": sdn_controller,
+                        "action": task_action,
+                        "item": "instance_wim_nets",
+                        "item_id": sdn_net_id,
+                        "related": related_network,
+                        "extra": yaml.safe_dump(task_wim_extra, default_flow_style=True, width=256)
+                    }
+                    task_index += 1
+                    db_vim_actions.append(db_vim_action)
                 db_net = {
                     "uuid": net_uuid,
                     "osm_id": sce_net.get("osm_id") or sce_net["name"],
@@ -3404,7 +3518,8 @@ def create_instance(mydb, tenant_id, instance_dict):
                     "created": create_network,
                     'datacenter_id': datacenter_id,
                     'datacenter_tenant_id': myvim_thread_id,
-                    'status': 'BUILD' #  if create_network else "ACTIVE"
+                    'status': 'BUILD', #  if create_network else "ACTIVE"
+                    'sdn_net_id': sdn_net_id,
                 }
                 db_instance_nets.append(db_net)
                 db_vim_action = {
@@ -3451,6 +3566,7 @@ def create_instance(mydb, tenant_id, instance_dict):
             "task_index": task_index,
             "uuid_list": uuid_list,
             "db_instance_nets": db_instance_nets,
+            "db_instance_wim_nets": db_instance_wim_nets,
             "db_vim_actions": db_vim_actions,
             "db_ip_profiles": db_ip_profiles,
             "db_instance_vnfs": db_instance_vnfs,
@@ -3458,6 +3574,7 @@ def create_instance(mydb, tenant_id, instance_dict):
             "db_instance_interfaces": db_instance_interfaces,
             "net2task_id": net2task_id,
             "sce_net2instance": sce_net2instance,
+            "sce_net2wim_instance": sce_net2wim_instance,
         }
         # sce_vnf_list = sorted(scenarioDict['vnfs'], key=lambda k: k['name'])
         for sce_vnf in scenarioDict.get('vnfs', ()):  # sce_vnf_list:
@@ -3584,7 +3701,8 @@ def create_instance(mydb, tenant_id, instance_dict):
                             "source_ip": match["source_ip"],
                             "destination_ip": match["destination_ip"],
                             "source_port": match["source_port"],
-                            "destination_port": match["destination_port"]
+                            "destination_port": match["destination_port"],
+                            "logical_source_port": classifier["interface_id"]
                         }
                         db_vim_action = {
                             "instance_action_id": instance_action_id,
@@ -3655,7 +3773,7 @@ def create_instance(mydb, tenant_id, instance_dict):
             {"instance_sfs": db_instance_sfs},
             {"instance_classifications": db_instance_classifications},
             {"instance_sfps": db_instance_sfps},
-            {"instance_wim_nets": wan_links},
+            {"instance_wim_nets": db_instance_wim_nets + wan_links},
             {"vim_wim_actions": db_vim_actions + wim_actions}
         ]
 
@@ -3670,21 +3788,40 @@ def create_instance(mydb, tenant_id, instance_dict):
         returned_instance = mydb.get_instance_scenario(instance_uuid)
         returned_instance["action_id"] = instance_action_id
         return returned_instance
-    except (NfvoException, vimconn.vimconnException, wimconn.WimConnectorError, db_base_Exception) as e:
-        message = rollback(mydb, myvims, rollbackList)
+    except (NfvoException, vimconn.VimConnException, sdnconn.SdnConnectorError, db_base_Exception) as e:
+        _, message = rollback(mydb, myvims, rollbackList)
         if isinstance(e, db_base_Exception):
             error_text = "database Exception"
-        elif isinstance(e, vimconn.vimconnException):
+        elif isinstance(e, vimconn.VimConnException):
             error_text = "VIM Exception"
-        elif isinstance(e, wimconn.WimConnectorError):
+        elif isinstance(e, sdnconn.SdnConnectorError):
             error_text = "WIM Exception"
         else:
-            error_text = "Exception"
-        error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
+            error_text = "Exception " + str(type(e).__name__)
+        error_text += " {}. {}".format(e, message)
         # logger.error("create_instance: %s", error_text)
         logger.exception(e)
         raise NfvoException(error_text, e.http_code)
 
+def increment_ip_mac(ip_mac, vm_index=1):
+    if not isinstance(ip_mac, str):
+        return ip_mac
+    try:
+        # try with ipv4 look for last dot
+        i = ip_mac.rfind(".")
+        if i > 0:
+            i += 1
+            return "{}{}".format(ip_mac[:i], int(ip_mac[i:]) + vm_index)
+        # try with ipv6 or mac look for last colon. Operate in hex
+        i = ip_mac.rfind(":")
+        if i > 0:
+            i += 1
+            # format in hex, len can be 2 for mac or 4 for ipv6
+            return ("{}{:0" + str(len(ip_mac) - i) + "x}").format(ip_mac[:i], int(ip_mac[i:], 16) + vm_index)
+    except:
+        pass
+    return None
+
 
 def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
     default_datacenter_id = params["default_datacenter_id"]
@@ -3699,6 +3836,7 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
     task_index = params_out["task_index"]
     uuid_list = params_out["uuid_list"]
     db_instance_nets = params_out["db_instance_nets"]
+    db_instance_wim_nets = params_out["db_instance_wim_nets"]
     db_vim_actions = params_out["db_vim_actions"]
     db_ip_profiles = params_out["db_ip_profiles"]
     db_instance_vnfs = params_out["db_instance_vnfs"]
@@ -3706,15 +3844,19 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
     db_instance_interfaces = params_out["db_instance_interfaces"]
     net2task_id = params_out["net2task_id"]
     sce_net2instance = params_out["sce_net2instance"]
+    sce_net2wim_instance = params_out["sce_net2wim_instance"]
 
     vnf_net2instance = {}
+    vnf_net2wim_instance = {}
 
     # 2. Creating new nets (vnf internal nets) in the VIM"
     # For each vnf net, we create it and we add it to instanceNetlist.
     if sce_vnf.get("datacenter"):
+        vim = myvims[sce_vnf["datacenter"]]
         datacenter_id = sce_vnf["datacenter"]
         myvim_thread_id = myvim_threads_id[sce_vnf["datacenter"]]
     else:
+        vim = myvims[default_datacenter_id]
         datacenter_id = default_datacenter_id
         myvim_thread_id = myvim_threads_id[default_datacenter_id]
     for net in sce_vnf['nets']:
@@ -3731,12 +3873,30 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
             vnf_net2instance[sce_vnf['uuid']] = {}
         if sce_vnf['uuid'] not in net2task_id:
             net2task_id[sce_vnf['uuid']] = {}
-        net2task_id[sce_vnf['uuid']][net['uuid']] = task_index
 
         # fill database content
         net_uuid = str(uuid4())
         uuid_list.append(net_uuid)
         vnf_net2instance[sce_vnf['uuid']][net['uuid']] = net_uuid
+
+        sdn_controller = vim.config.get('sdn-controller')
+        sdn_net_id = None
+        if sdn_controller and net_type in ("data", "ptp"):
+            wim_id = _get_wim(mydb, sdn_controller)
+            sdn_net_id = str(uuid4())
+            db_instance_wim_nets.append({
+                "uuid": sdn_net_id,
+                "instance_scenario_id": instance_uuid,
+                "wim_id": wim_id,
+                "wim_account_id": sdn_controller,
+                'status': 'BUILD',  # if create_network else "ACTIVE"
+                "related": net_uuid,
+                'multipoint': True if net_type == "data" else False,
+                "created": True,  # TODO py3
+                "sdn": True,
+            })
+            vnf_net2wim_instance[net_uuid] = sdn_net_id
+
         db_net = {
             "uuid": net_uuid,
             "related": net_uuid,
@@ -3747,6 +3907,7 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
             "created": True,
             'datacenter_id': datacenter_id,
             'datacenter_tenant_id': myvim_thread_id,
+            'sdn_net_id': sdn_net_id,
         }
         db_instance_nets.append(db_net)
 
@@ -3761,7 +3922,25 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
         else:
             task_action = "CREATE"
             task_extra = {"params": (net_name, net_type, net.get('ip_profile', None))}
+        if sdn_net_id:
+            task_extra["sdn_net_id"] = sdn_net_id
 
+        if sdn_net_id:
+            task_wim_extra = {"params": [net_type, None]}
+            db_vim_action = {
+                "instance_action_id": instance_action_id,
+                "status": "SCHEDULED",
+                "task_index": task_index,
+                # "datacenter_vim_id": myvim_thread_id,
+                "wim_account_id": sdn_controller,
+                "action": task_action,
+                "item": "instance_wim_nets",
+                "item_id": sdn_net_id,
+                "related": net_uuid,
+                "extra": yaml.safe_dump(task_wim_extra, default_flow_style=True, width=256)
+            }
+            task_index += 1
+            db_vim_actions.append(db_vim_action)
         db_vim_action = {
             "instance_action_id": instance_action_id,
             "task_index": task_index,
@@ -3773,6 +3952,7 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
             "related": net_uuid,
             "extra": yaml.safe_dump(task_extra, default_flow_style=True, width=256)
         }
+        net2task_id[sce_vnf['uuid']][net['uuid']] = task_index
         task_index += 1
         db_vim_actions.append(db_vim_action)
 
@@ -3946,11 +4126,13 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
                         netDict['net_id'] = "TASK-{}".format(
                             net2task_id['scenario'][vnf_iface['sce_net_id']][datacenter_id])
                         instance_net_id = sce_net2instance[vnf_iface['sce_net_id']][datacenter_id]
+                        instance_wim_net_id = sce_net2wim_instance[vnf_iface['sce_net_id']][datacenter_id]
                         task_depends_on.append(net2task_id['scenario'][vnf_iface['sce_net_id']][datacenter_id])
                         break
             else:
                 netDict['net_id'] = "TASK-{}".format(net2task_id[sce_vnf['uuid']][iface['net_id']])
                 instance_net_id = vnf_net2instance[sce_vnf['uuid']][iface['net_id']]
+                instance_wim_net_id = vnf_net2wim_instance.get(instance_net_id)
                 task_depends_on.append(net2task_id[sce_vnf['uuid']][iface['net_id']])
             # skip bridge ifaces not connected to any net
             if 'net_id' not in netDict or netDict['net_id'] == None:
@@ -3960,9 +4142,11 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
                 # "uuid"
                 # 'instance_vm_id': instance_vm_uuid,
                 "instance_net_id": instance_net_id,
+                "instance_wim_net_id": instance_wim_net_id,
                 'interface_id': iface['uuid'],
                 # 'vim_interface_id': ,
                 'type': 'external' if iface['external_name'] is not None else 'internal',
+                'model': iface['model'],
                 'ip_address': iface.get('ip_address'),
                 'mac_address': iface.get('mac'),
                 'floating_ip': int(iface.get('floating-ip', False)),
@@ -3996,16 +4180,11 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
             av_index = None
         for vm_index in range(0, vm.get('count', 1)):
             vm_name = myVMDict['name'] + "-" + str(vm_index+1)
+            vm_networks = deepcopy(myVMDict['networks'])
             task_params = (vm_name, myVMDict['description'], myVMDict.get('start', None),
-                           myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'], cloud_config_vm,
+                           myVMDict['imageRef'], myVMDict['flavorRef'], vm_networks, cloud_config_vm,
                            myVMDict['disks'], av_index, vnf_availability_zones)
-            # put interface uuid back to scenario[vnfs][vms[[interfaces]
-            for net in myVMDict['networks']:
-                if "vim_id" in net:
-                    for iface in vm['interfaces']:
-                        if net["name"] == iface["internal_name"]:
-                            iface["vim_id"] = net["vim_id"]
-                            break
+
             vm_uuid = str(uuid4())
             uuid_list.append(vm_uuid)
             db_vm = {
@@ -4019,28 +4198,32 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
             }
             db_instance_vms.append(db_vm)
 
-            iface_index = 0
-            for db_vm_iface in db_vm_ifaces:
+            # put interface uuid back to scenario[vnfs][vms[[interfaces]
+            for net in vm_networks:
+                if "vim_id" in net:
+                    for iface in vm['interfaces']:
+                        if net["name"] == iface["internal_name"]:
+                            iface["vim_id"] = net["vim_id"]
+                            break
+
+                if vm_index > 0:
+                    if net.get("ip_address"):
+                        net["ip_address"] = increment_ip_mac(net.get("ip_address"), vm_index)
+                    if net.get("mac_address"):
+                        net["mac_address"] = increment_ip_mac(net.get("mac_address"), vm_index)
+
+            for iface_index, db_vm_iface in enumerate(db_vm_ifaces):
                 iface_uuid = str(uuid4())
                 uuid_list.append(iface_uuid)
                 db_vm_iface_instance = {
                     "uuid": iface_uuid,
-                    "instance_vm_id": vm_uuid
+                    "instance_vm_id": vm_uuid,
+                    "ip_address": vm_networks[iface_index].get("ip_address"),
+                    "mac_address": vm_networks[iface_index].get("mac_address")
                 }
                 db_vm_iface_instance.update(db_vm_iface)
-                if db_vm_iface_instance.get("ip_address"):  # increment ip_address
-                    ip = db_vm_iface_instance.get("ip_address")
-                    i = ip.rfind(".")
-                    if i > 0:
-                        try:
-                            i += 1
-                            ip = ip[i:] + str(int(ip[:i]) + 1)
-                            db_vm_iface_instance["ip_address"] = ip
-                        except:
-                            db_vm_iface_instance["ip_address"] = None
                 db_instance_interfaces.append(db_vm_iface_instance)
-                myVMDict['networks'][iface_index]["uuid"] = iface_uuid
-                iface_index += 1
+                vm_networks[iface_index]["uuid"] = iface_uuid
 
             db_vim_action = {
                 "instance_action_id": instance_action_id,
@@ -4349,6 +4532,23 @@ def delete_instance(mydb, tenant_id, instance_id):
         }
         task_index += 1
         db_vim_actions.append(db_vim_action)
+    for sdn_net in instanceDict['sdn_nets']:
+        if not sdn_net["sdn"]:
+            continue
+        extra = {}
+        db_vim_action = {
+            "instance_action_id": instance_action_id,
+            "task_index": task_index,
+            "wim_account_id": sdn_net["wim_account_id"],
+            "action": "DELETE",
+            "status": "SCHEDULED",
+            "item": "instance_wim_nets",
+            "item_id": sdn_net["uuid"],
+            "related": sdn_net["related"],
+            "extra": yaml.safe_dump(extra, default_flow_style=True, width=256)
+        }
+        task_index += 1
+        db_vim_actions.append(db_vim_action)
 
     db_instance_action["number_tasks"] = task_index
 
@@ -4383,18 +4583,19 @@ def get_instance_id(mydb, tenant_id, instance_id):
     #obtain data
 
     instance_dict = mydb.get_instance_scenario(instance_id, tenant_id, verbose=True)
-    for net in instance_dict["nets"]:
-        if net.get("sdn_net_id"):
-            net_sdn = ovim.show_network(net["sdn_net_id"])
-            net["sdn_info"] = {
-                "admin_state_up": net_sdn.get("admin_state_up"),
-                "flows": net_sdn.get("flows"),
-                "last_error": net_sdn.get("last_error"),
-                "ports": net_sdn.get("ports"),
-                "type": net_sdn.get("type"),
-                "status": net_sdn.get("status"),
-                "vlan": net_sdn.get("vlan"),
-            }
+    # TODO py3
+    # for net in instance_dict["nets"]:
+    #     if net.get("sdn_net_id"):
+    #         net_sdn = ovim.show_network(net["sdn_net_id"])
+    #         net["sdn_info"] = {
+    #             "admin_state_up": net_sdn.get("admin_state_up"),
+    #             "flows": net_sdn.get("flows"),
+    #             "last_error": net_sdn.get("last_error"),
+    #             "ports": net_sdn.get("ports"),
+    #             "type": net_sdn.get("type"),
+    #             "status": net_sdn.get("status"),
+    #             "vlan": net_sdn.get("vlan"),
+    #         }
     return instance_dict
 
 @deprecated("Instance is automatically refreshed by vim_threads")
@@ -4463,7 +4664,7 @@ def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenan
     #         try:
     #             vm_dict.update(myvims[datacenter_key].refresh_vms_status(vm_list[datacenter_key]) )
     #             failed = False
-    #         except vimconn.vimconnException as e:
+    #         except vimconn.VimConnException as e:
     #             logger.error("VIM exception %s %s", type(e).__name__, str(e))
     #             failed_message = str(e)
     #     if failed:
@@ -4525,7 +4726,7 @@ def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenan
     #         try:
     #             net_dict.update(myvims[datacenter_key].refresh_nets_status(net_list[datacenter_key]) )
     #             failed = False
-    #         except vimconn.vimconnException as e:
+    #         except vimconn.VimConnException as e:
     #             logger.error("VIM exception %s %s", type(e).__name__, str(e))
     #             failed_message = str(e)
     #     if failed:
@@ -4649,6 +4850,16 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
                         "extra": yaml.safe_dump({"params": vm_interfaces},
                                                 default_flow_style=True, width=256)
                     }
+                    # get affected instance_interfaces (deleted on cascade) to check if a wim_network must be updated
+                    deleted_interfaces = mydb.get_rows(
+                        SELECT=("instance_wim_net_id", ),
+                        FROM="instance_interfaces",
+                        WHERE={"instance_vm_id": vdu_id, "instance_wim_net_id<>": None},
+                    )
+                    for deleted_interface in deleted_interfaces:
+                        db_vim_actions.append({"TO-UPDATE": {}, "WHERE": {
+                            "item": "instance_wim_nets", "item_id": deleted_interface["instance_wim_net_id"]}})
+
                     task_index += 1
                     db_vim_actions.append(db_vim_action)
                     vm_result["deleted"].append(vdu_id)
@@ -4669,7 +4880,6 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
                 # TODO do the same for flavor and image when available
                 task_depends_on = []
                 task_params = extra["params"]
-                task_params_networks = deepcopy(task_params[5])
                 for iface in task_params[5]:
                     if iface["net_id"].startswith("TASK-"):
                         if "." not in iface["net_id"]:
@@ -4679,8 +4889,6 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
                                                                   iface["net_id"][5:])
                         else:
                             task_depends_on.append(iface["net_id"][5:])
-                    if "mac_address" in iface:
-                        del iface["mac_address"]
 
                 vm_ifaces_to_clone = mydb.get_rows(FROM="instance_interfaces", WHERE={"instance_vm_id": target_vm["uuid"]})
                 for index in range(0, vdu_count):
@@ -4707,26 +4915,26 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
                             "uuid": iface_uuid,
                             'instance_vm_id': vm_uuid,
                             "instance_net_id": vm_iface["instance_net_id"],
+                            "instance_wim_net_id": vm_iface["instance_wim_net_id"],
                             'interface_id': vm_iface['interface_id'],
                             'type': vm_iface['type'],
+                            'model': vm_iface['model'],
                             'floating_ip': vm_iface['floating_ip'],
                             'port_security': vm_iface['port_security']
                         }
                         db_instance_interfaces.append(db_vm_iface)
+                        if db_vm_iface["instance_wim_net_id"]:
+                            db_vim_actions.append({"TO-UPDATE": {}, "WHERE": {
+                                "item": "instance_wim_nets", "item_id": db_vm_iface["instance_wim_net_id"]}})
                     task_params_copy = deepcopy(task_params)
                     for iface in task_params_copy[5]:
                         iface["uuid"] = iface2iface[iface["uuid"]]
                         # increment ip_address
-                        if "ip_address" in iface:
-                            ip = iface.get("ip_address")
-                            i = ip.rfind(".")
-                            if i > 0:
-                                try:
-                                    i += 1
-                                    ip = ip[i:] + str(int(ip[:i]) + 1)
-                                    iface["ip_address"] = ip
-                                except:
-                                    iface["ip_address"] = None
+                        if iface.get("ip_address"):
+                            iface["ip_address"] = increment_ip_mac(iface.get("ip_address"), index+1)
+                        if iface.get("mac_address"):
+                            iface["mac_address"] = increment_ip_mac(iface.get("mac_address"), index+1)
+
                     if vm_name:
                         task_params_copy[0] = vm_name
                     db_vim_action = {
@@ -4800,10 +5008,9 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
                                                           action_dict['add_public_key'],
                                                           password=password, ro_key=priv_RO_key)
                                     vm_result[ vm['uuid'] ] = {"vim_result": 200,
-                                                       "description": "Public key injected",
-                                                       "name":vm['name']
+                                                    "description": "Public key injected",
+                                                    "name":vm['name']
                                                     }
-
                         except KeyError:
                             raise NfvoException("Unable to inject ssh key in vm: {} - Aborting".format(vm['uuid']),
                                                 httperrors.Internal_Server_Error)
@@ -4849,7 +5056,7 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
                     else:
                         vm_result[ vm['uuid'] ] = {"vim_result": 200, "description": "ok", "name":vm['name']}
                         vm_ok +=1
-            except vimconn.vimconnException as e:
+            except vimconn.VimConnException as e:
                 vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)}
                 vm_error+=1
 
@@ -4940,10 +5147,19 @@ def new_datacenter(mydb, datacenter_descriptor):
     datacenter_type = datacenter_descriptor.get("type", "openvim");
     # module_info = None
 
+    for url_field in ('vim_url', 'vim_url_admin'):
+        # It is common that users copy and paste the URL from the VIM website
+        # (example OpenStack), therefore a common mistake is to include blank
+        # characters at the end of the URL. Let's remove it and just in case,
+        # lets remove trailing slash as well.
+        url = datacenter_descriptor.get(url_field)
+        if url:
+            datacenter_descriptor[url_field] = url.strip(string.whitespace + '/')
+
     # load plugin
     plugin_name = "rovim_" + datacenter_type
     if plugin_name not in plugins:
-        _load_vim_plugin(plugin_name)
+        _load_plugin(plugin_name, type="vim")
 
     datacenter_id = mydb.new_row("datacenters", datacenter_descriptor, add_uuid=True, confidential_data=True)
     if sdn_port_mapping:
@@ -5023,56 +5239,60 @@ def delete_datacenter(mydb, datacenter):
 
 def create_vim_account(mydb, nfvo_tenant, datacenter_id, name=None, vim_id=None, vim_tenant=None, vim_tenant_name=None,
                        vim_username=None, vim_password=None, config=None):
+    global plugins
     # get datacenter info
     try:
         if not datacenter_id:
             if not vim_id:
                 raise NfvoException("You must provide 'vim_id", http_code=httperrors.Bad_Request)
             datacenter_id = vim_id
-        datacenter_id, datacenter_name = get_datacenter_uuid(mydb, None, datacenter_id)
+        datacenter_id, datacenter = get_datacenter_uuid(mydb, None, datacenter_id)
+        datacenter_name = datacenter["name"]
+        datacenter_type = datacenter["type"]
 
         create_vim_tenant = True if not vim_tenant and not vim_tenant_name else False
 
         # get nfvo_tenant info
         tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', nfvo_tenant)
-        if vim_tenant_name==None:
-            vim_tenant_name=tenant_dict['name']
+        if vim_tenant_name is None:
+            vim_tenant_name = tenant_dict['name']
 
-        tenants_datacenter_dict={"nfvo_tenant_id":tenant_dict['uuid'], "datacenter_id":datacenter_id }
+        tenants_datacenter_dict = {"nfvo_tenant_id": tenant_dict['uuid'], "datacenter_id": datacenter_id}
         # #check that this association does not exist before
         # tenants_datacenters = mydb.get_rows(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict)
         # if len(tenants_datacenters)>0:
-        #     raise NfvoException("datacenter '{}' and tenant'{}' are already attached".format(datacenter_id, tenant_dict['uuid']), httperrors.Conflict)
+        #     raise NfvoException("datacenter '{}' and tenant'{}' are already attached".format(
+        #                         datacenter_id, tenant_dict['uuid']), httperrors.Conflict)
 
-        vim_tenant_id_exist_atdb=False
+        vim_tenant_id_exist_atdb = False
         if not create_vim_tenant:
             where_={"datacenter_id": datacenter_id}
-            if vim_tenant!=None:
+            if vim_tenant is not None:
                 where_["vim_tenant_id"] = vim_tenant
-            if vim_tenant_name!=None:
+            if vim_tenant_name is not None:
                 where_["vim_tenant_name"] = vim_tenant_name
-            #check if vim_tenant_id is already at database
+            # check if vim_tenant_id is already at database
             datacenter_tenants_dict = mydb.get_rows(FROM='datacenter_tenants', WHERE=where_)
-            if len(datacenter_tenants_dict)>=1:
+            if len(datacenter_tenants_dict) >= 1:
                 datacenter_tenants_dict = datacenter_tenants_dict[0]
-                vim_tenant_id_exist_atdb=True
-                #TODO check if a field has changed and edit entry at datacenter_tenants at DB
-            else: #result=0
+                vim_tenant_id_exist_atdb = True
+                # TODO check if a field has changed and edit entry at datacenter_tenants at DB
+            else:  # result=0
                 datacenter_tenants_dict = {}
-                #insert at table datacenter_tenants
-        else: #if vim_tenant==None:
-            #create tenant at VIM if not provided
+                # insert at table datacenter_tenants
+        else:  # if vim_tenant==None:
+            # create tenant at VIM if not provided
             try:
-                _, myvim = get_datacenter_by_name_uuid(mydb, None, datacenter, vim_user=vim_username,
-                                                                   vim_passwd=vim_password)
+                _, myvim = get_datacenter_by_name_uuid(mydb, None, datacenter_id, vim_user=vim_username,
+                                                       vim_passwd=vim_password)
                 datacenter_name = myvim["name"]
                 vim_tenant = myvim.new_tenant(vim_tenant_name, "created by openmano for datacenter "+datacenter_name)
-            except vimconn.vimconnException as e:
-                raise NfvoException("Not possible to create vim_tenant {} at VIM: {}".format(vim_tenant_id, str(e)), httperrors.Internal_Server_Error)
-            datacenter_tenants_dict = {}
-            datacenter_tenants_dict["created"]="true"
+            except vimconn.VimConnException as e:
+                raise NfvoException("Not possible to create vim_tenant {} at VIM: {}".format(vim_tenant_name, e),
+                                    httperrors.Internal_Server_Error)
+            datacenter_tenants_dict = {"created": "true"}
 
-        #fill datacenter_tenants table
+        # fill datacenter_tenants table
         if not vim_tenant_id_exist_atdb:
             datacenter_tenants_dict["vim_tenant_id"] = vim_tenant
             datacenter_tenants_dict["vim_tenant_name"] = vim_tenant_name
@@ -5088,20 +5308,22 @@ def create_vim_account(mydb, nfvo_tenant, datacenter_id, name=None, vim_id=None,
             id_ = mydb.new_row('datacenter_tenants', datacenter_tenants_dict, add_uuid=True, confidential_data=True)
             datacenter_tenants_dict["uuid"] = id_
 
-        #fill tenants_datacenters table
+        # fill tenants_datacenters table
         datacenter_tenant_id = datacenter_tenants_dict["uuid"]
         tenants_datacenter_dict["datacenter_tenant_id"] = datacenter_tenant_id
         mydb.new_row('tenants_datacenters', tenants_datacenter_dict)
 
-        # create thread
-        thread_name = get_non_used_vim_name(datacenter_name, datacenter_id, tenant_dict['name'], tenant_dict['uuid'])
-        new_thread = vim_thread(task_lock, plugins, thread_name, datacenter_name, datacenter_tenant_id,
-                                db=db, db_lock=db_lock, ovim=ovim)
+        # load plugin and create thread
+        plugin_name = "rovim_" + datacenter_type
+        if plugin_name not in plugins:
+            _load_plugin(plugin_name, type="vim")
+        thread_name = get_non_used_vim_name(datacenter_name, datacenter_id)
+        new_thread = vim_thread(task_lock, plugins, thread_name, None, datacenter_tenant_id, db=db)
         new_thread.start()
         thread_id = datacenter_tenants_dict["uuid"]
         vim_threads["running"][thread_id] = new_thread
         return thread_id
-    except vimconn.vimconnException as e:
+    except vimconn.VimConnException as e:
         raise NfvoException(str(e), httperrors.Bad_Request)
 
 
@@ -5127,7 +5349,7 @@ def edit_vim_account(mydb, nfvo_tenant, datacenter_tenant_id, datacenter_id=None
     if config:
         original_config_dict = yaml.load(original_config, Loader=yaml.Loader)
         original_config_dict.update(config)
-        update["config"] = yaml.safe_dump(original_config_dict, default_flow_style=True, width=256)
+        update_["config"] = yaml.safe_dump(original_config_dict, default_flow_style=True, width=256)
     if name:
         update_['name'] = name
     if vim_tenant:
@@ -5180,7 +5402,7 @@ def delete_vim_account(mydb, tenant_id, vim_account_id, datacenter=None):
                 try:
                     datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
                     myvim.delete_tenant(vim_tenant_dict['vim_tenant_id'])
-                except vimconn.vimconnException as e:
+                except vimconn.VimConnException as e:
                     warning = "Not possible to delete vim_tenant_id {} from VIM: {} ".format(vim_tenant_dict['vim_tenant_id'], str(e))
                     logger.warn(warning)
         except db_base_Exception as e:
@@ -5202,14 +5424,14 @@ def datacenter_action(mydb, tenant_id, datacenter, action_dict):
     if 'check-connectivity' in action_dict:
         try:
             myvim.check_vim_connectivity()
-        except vimconn.vimconnException as e:
+        except vimconn.VimConnException as e:
             #logger.error("nfvo.datacenter_action() Not possible to get_network_list from VIM: %s ", str(e))
             raise NfvoException(str(e), e.http_code)
     elif 'net-update' in action_dict:
         try:
             nets = myvim.get_network_list(filter_dict={'shared': True, 'admin_state_up': True, 'status': 'ACTIVE'})
             #print content
-        except vimconn.vimconnException as e:
+        except vimconn.VimConnException as e:
             #logger.error("nfvo.datacenter_action() Not possible to get_network_list from VIM: %s ", str(e))
             raise NfvoException(str(e), httperrors.Internal_Server_Error)
         #update nets Change from VIM format to NFVO format
@@ -5268,7 +5490,7 @@ def datacenter_new_netmap(mydb, tenant_id, datacenter, action_dict=None):
 
     try:
         vim_nets = myvim.get_network_list(filter_dict=filter_dict)
-    except vimconn.vimconnException as e:
+    except vimconn.VimConnException as e:
         #logger.error("nfvo.datacenter_new_netmap() Not possible to get_network_list from VIM: %s ", str(e))
         raise NfvoException(str(e), httperrors.Internal_Server_Error)
     if len(vim_nets)>1 and action_dict:
@@ -5309,7 +5531,7 @@ def get_sdn_net_id(mydb, tenant_id, datacenter, network_id):
 
         datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
         network = myvim.get_network_list(filter_dict=filter_dict)
-    except vimconn.vimconnException as e:
+    except vimconn.VimConnException as e:
         raise NfvoException("Not possible to get_sdn_net_id from VIM: {}".format(str(e)), e.http_code)
 
     # ensure the network is defined
@@ -5480,7 +5702,7 @@ def vim_action_get(mydb, tenant_id, datacenter, item, name):
                  datacenter)
         else:
             return {item: content}
-    except vimconn.vimconnException as e:
+    except vimconn.VimConnException as e:
         print("vim_action Not possible to get_{}_list from VIM: {} ".format(item, str(e)))
         raise NfvoException("Not possible to get_{}_list from VIM: {}".format(item, str(e)), e.http_code)
 
@@ -5544,7 +5766,7 @@ def vim_action_delete(mydb, tenant_id, datacenter, item, name):
             content = myvim.delete_image(item_id)
         else:
             raise NfvoException(item + "?", httperrors.Method_Not_Allowed)
-    except vimconn.vimconnException as e:
+    except vimconn.VimConnException as e:
         #logger.error( "vim_action Not possible to delete_{} {}from VIM: {} ".format(item, name, str(e)))
         raise NfvoException("Not possible to delete_{} {} from VIM: {}".format(item, name, str(e)), e.http_code)
 
@@ -5565,7 +5787,10 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor):
             net_public = net.pop("shared", False)
             net_ipprofile = net.pop("ip_profile", None)
             net_vlan = net.pop("vlan", None)
-            content, _ = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net)
+            net_provider_network_profile = None
+            if net_vlan:
+                net_provider_network_profile = {"segmentation-id": net_vlan}
+            content, _ = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, provider_network_profile=net_provider_network_profile) #, **net)
 
             #If the datacenter has a SDN controller defined and the network is of dataplane type, then create the sdn network
             if get_sdn_controller_id(mydb, datacenter) != None and (net_type == 'data' or net_type == 'ptp'):
@@ -5603,19 +5828,35 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor):
             content = myvim.new_tenant(tenant["name"], tenant.get("description"))
         else:
             raise NfvoException(item + "?", httperrors.Method_Not_Allowed)
-    except vimconn.vimconnException as e:
+    except vimconn.VimConnException as e:
         raise NfvoException("Not possible to create {} at VIM: {}".format(item, str(e)), e.http_code)
 
     return vim_action_get(mydb, tenant_id, datacenter, item, content)
 
 def sdn_controller_create(mydb, tenant_id, sdn_controller):
-    data = ovim.new_of_controller(sdn_controller)
-    logger.debug('New SDN controller created with uuid {}'.format(data))
-    return data
+    try:
+        wim_id = ovim.new_of_controller(sdn_controller)
+
+        # Load plugin if not previously loaded
+        controller_type = sdn_controller.get("type")
+        plugin_name = "rosdn_" + controller_type
+        if plugin_name not in plugins:
+            _load_plugin(plugin_name, type="sdn")
+
+        thread_name = get_non_used_vim_name(sdn_controller['name'], wim_id)
+        new_thread = vim_thread(task_lock, plugins, thread_name, wim_id, None, db=db)
+        new_thread.start()
+        thread_id = wim_id
+        vim_threads["running"][thread_id] = new_thread
+        logger.debug('New SDN controller created with uuid {}'.format(wim_id))
+        return wim_id
+    except ovimException as e:
+        raise NfvoException(e) from e
 
 def sdn_controller_update(mydb, tenant_id, controller_id, sdn_controller):
     data = ovim.edit_of_controller(controller_id, sdn_controller)
     msg = 'SDN controller {} updated'.format(data)
+    vim_threads["running"][controller_id].insert_task("reload")
     logger.debug(msg)
     return msg
 
@@ -5661,20 +5902,25 @@ def datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, sdn_port_map
         #element = {"ofc_id": sdn_controller_id, "region": datacenter_id, "switch_dpid": switch_dpid}
         element = dict()
         element["compute_node"] = compute_node["compute_node"]
-        for port in compute_node["ports"]:
-            pci = port.get("pci")
-            element["switch_port"] = port.get("switch_port")
-            element["switch_mac"] = port.get("switch_mac")
-            if not element["switch_port"] and not element["switch_mac"]:
-                raise NfvoException ("The mapping must contain 'switch_port' or 'switch_mac'", httperrors.Bad_Request)
-            for pci_expanded in utils.expand_brackets(pci):
-                element["pci"] = pci_expanded
-                maps.append(dict(element))
-
-    return ovim.set_of_port_mapping(maps, ofc_id=sdn_controller_id, switch_dpid=switch_dpid, region=datacenter_id)
+        if compute_node["ports"]:
+            for port in compute_node["ports"]:
+                pci = port.get("pci")
+                element["switch_port"] = port.get("switch_port")
+                element["switch_mac"] = port.get("switch_mac")
+                element["switch_dpid"] = port.get("switch_dpid")
+                element["switch_id"] = port.get("switch_id")
+                if not element["switch_port"] and not element["switch_mac"]:
+                    raise NfvoException ("The mapping must contain 'switch_port' or 'switch_mac'", httperrors.Bad_Request)
+                for pci_expanded in utils.expand_brackets(pci):
+                    element["pci"] = pci_expanded
+                    maps.append(dict(element))
+
+    out = ovim.set_of_port_mapping(maps, sdn_id=sdn_controller_id, switch_dpid=switch_dpid, vim_id=datacenter_id)
+    vim_threads["running"][sdn_controller_id].insert_task("reload")
+    return out
 
 def datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id):
-    maps = ovim.get_of_port_mappings(db_filter={"region": datacenter_id})
+    maps = ovim.get_of_port_mappings(db_filter={"datacenter_id": datacenter_id})
 
     result = {
         "sdn-controller": None,
@@ -5703,24 +5949,25 @@ def datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id):
 
     ports_correspondence_dict = dict()
     for link in maps:
-        if result["sdn-controller"] != link["ofc_id"]:
+        if result["sdn-controller"] != link["wim_id"]:
             raise NfvoException("The sdn-controller specified for different port mappings differ", httperrors.Internal_Server_Error)
         if result["dpid"] != link["switch_dpid"]:
             raise NfvoException("The dpid specified for different port mappings differ", httperrors.Internal_Server_Error)
+        link_config = link["service_mapping_info"]
         element = dict()
-        element["pci"] = link["pci"]
+        element["pci"] = link.get("device_interface_id")
         if link["switch_port"]:
             element["switch_port"] = link["switch_port"]
-        if link["switch_mac"]:
-            element["switch_mac"] = link["switch_mac"]
+        if link_config["switch_mac"]:
+            element["switch_mac"] = link_config.get("switch_mac")
 
-        if not link["compute_node"] in ports_correspondence_dict:
+        if not link.get("interface_id") in ports_correspondence_dict:
             content = dict()
-            content["compute_node"] = link["compute_node"]
+            content["compute_node"] = link.get("interface_id")
             content["ports"] = list()
-            ports_correspondence_dict[link["compute_node"]] = content
+            ports_correspondence_dict[link.get("interface_id")] = content
 
-        ports_correspondence_dict[link["compute_node"]]["ports"].append(element)
+        ports_correspondence_dict[link["interface_id"]]["ports"].append(element)
 
     for key in sorted(ports_correspondence_dict):
         result["ports_mapping"].append(ports_correspondence_dict[key])
@@ -5728,7 +5975,7 @@ def datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id):
     return result
 
 def datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id):
-    return ovim.clear_of_port_mapping(db_filter={"region":datacenter_id})
+    return ovim.clear_of_port_mapping(db_filter={"datacenter_id":datacenter_id})
 
 def create_RO_keypair(tenant_id):
     """
@@ -5749,6 +5996,10 @@ def create_RO_keypair(tenant_id):
         private_key = key.exportKey(passphrase=tenant_id, pkcs=8)
     except (ValueError, NameError) as e:
         raise NfvoException("Unable to create private key: {}".format(e), httperrors.Internal_Server_Error)
+    if isinstance(public_key, bytes):
+        public_key = public_key.decode(encoding='UTF-8')
+    if isinstance(private_key, bytes):
+        private_key = private_key.decode(encoding='UTF-8')
     return public_key, private_key
 
 def decrypt_key (key, tenant_id):
@@ -5765,6 +6016,8 @@ def decrypt_key (key, tenant_id):
         unencrypted_key = key.exportKey('PEM')
         if isinstance(unencrypted_key, ValueError):
             raise NfvoException("Unable to decrypt the private key: {}".format(unencrypted_key), httperrors.Internal_Server_Error)
+        if isinstance(unencrypted_key, bytes):
+            unencrypted_key = unencrypted_key.decode(encoding='UTF-8')
     except ValueError as e:
         raise NfvoException("Unable to decrypt the private key: {}".format(e), httperrors.Internal_Server_Error)
     return unencrypted_key