bug 425 fix SR-IOV PCI-PASSTHROUGH interfaces
[osm/RO.git] / osm_ro / nfvo.py
index f6ec568..bd9d368 100644 (file)
@@ -38,6 +38,7 @@ import console_proxy_thread as cli
 import vimconn
 import logging
 import collections
+import math
 from uuid import uuid4
 from db_base import db_base_Exception
 
@@ -138,9 +139,14 @@ def start_service(mydb):
         'log_level_of': 'DEBUG'
     }
     try:
+        # starts ovim library
         ovim = ovim_module.ovim(ovim_configuration)
         ovim.start_service()
 
+        #delete old unneeded vim_actions
+        clean_db(mydb)
+
+        # starts vim_threads
         from_= 'tenants_datacenters as td join datacenters as d on td.datacenter_id=d.uuid join '\
                 'datacenter_tenants as dt on td.datacenter_tenant_id=dt.uuid'
         select_ = ('type', 'd.config as config', 'd.uuid as datacenter_id', 'vim_url', 'vim_url_admin',
@@ -224,6 +230,37 @@ def get_version():
     return  ("openmanod version {} {}\n(c) Copyright Telefonica".format(global_config["version"],
                                                                         global_config["version_date"] ))
 
+def clean_db(mydb):
+    """
+    Clean unused or old entries at database to avoid unlimited growing
+    :param mydb: database connector
+    :return: None
+    """
+    # get and delete unused vim_actions: all elements deleted, one week before, instance not present
+    now = t.time()-3600*24*7
+    instance_action_id = None
+    nb_deleted = 0
+    while True:
+        actions_to_delete = mydb.get_rows(
+            SELECT=("item", "item_id", "instance_action_id"),
+            FROM="vim_actions as va join instance_actions as ia on va.instance_action_id=ia.uuid "
+                    "left join instance_scenarios as i on ia.instance_id=i.uuid",
+            WHERE={"va.action": "DELETE", "va.modified_at<": now, "i.uuid": None,
+                   "va.status": ("DONE", "SUPERSEDED")},
+            LIMIT=100
+        )
+        for to_delete in actions_to_delete:
+            mydb.delete_row(FROM="vim_actions", WHERE=to_delete)
+            if instance_action_id != to_delete["instance_action_id"]:
+                instance_action_id = to_delete["instance_action_id"]
+                mydb.delete_row(FROM="instance_actions", WHERE={"uuid": instance_action_id})
+        nb_deleted += len(actions_to_delete)
+        if len(actions_to_delete) < 100:
+            break
+    if nb_deleted:
+        logger.debug("Removed {} unused vim_actions".format(nb_deleted))
+
+
 
 def get_flavorlist(mydb, vnf_id, nfvo_tenant=None):
     '''Obtain flavorList
@@ -816,9 +853,10 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
             vnf_uuid = str(uuid4())
             uuid_list.append(vnf_uuid)
             vnfd_uuid_list.append(vnf_uuid)
+            vnfd_id = get_str(vnfd, "id", 255)
             db_vnf = {
                 "uuid": vnf_uuid,
-                "osm_id": get_str(vnfd, "id", 255),
+                "osm_id": vnfd_id,
                 "name": get_str(vnfd, "name", 255),
                 "description": get_str(vnfd, "description", 255),
                 "tenant_id": tenant_id,
@@ -852,9 +890,10 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
             for vdu in vnfd.get("vdu").itervalues():
                 vm_uuid = str(uuid4())
                 uuid_list.append(vm_uuid)
+                vdu_id = get_str(vdu, "id", 255)
                 db_vm = {
                     "uuid": vm_uuid,
-                    "osm_id": get_str(vdu, "id", 255),
+                    "osm_id": vdu_id,
                     "name": get_str(vdu, "name", 255),
                     "description": get_str(vdu, "description", 255),
                     "vnf_id": vnf_uuid,
@@ -901,74 +940,6 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                                     device["image checksum"] = str(volume["image-checksum"])
                             devices.append(device)
 
-                # table flavors
-                db_flavor = {
-                    "name": get_str(vdu, "name", 250) + "-flv",
-                    "vcpus": int(vdu["vm-flavor"].get("vcpu-count", 1)),
-                    "ram": int(vdu["vm-flavor"].get("memory-mb", 1)),
-                    "disk": int(vdu["vm-flavor"].get("storage-gb", 1)),
-                }
-                # EPA  TODO revise
-                extended = {}
-                numa = {}
-                if devices:
-                    extended["devices"] = devices
-                if vdu.get("guest-epa"):   # TODO or dedicated_int:
-                    epa_vcpu_set = False
-                    if vdu["guest-epa"].get("numa-node-policy"):  # TODO or dedicated_int:
-                        numa_node_policy = vdu["guest-epa"].get("numa-node-policy")
-                        if numa_node_policy.get("node"):
-                            numa_node = numa_node_policy["node"]['0']
-                            if numa_node.get("num-cores"):
-                                numa["cores"] = numa_node["num-cores"]
-                                epa_vcpu_set = True
-                            if numa_node.get("paired-threads"):
-                                if numa_node["paired-threads"].get("num-paired-threads"):
-                                    numa["paired-threads"] = int(numa_node["paired-threads"]["num-paired-threads"])
-                                    epa_vcpu_set = True
-                                if len(numa_node["paired-threads"].get("paired-thread-ids")):
-                                    numa["paired-threads-id"] = []
-                                    for pair in numa_node["paired-threads"]["paired-thread-ids"].itervalues():
-                                        numa["paired-threads-id"].append(
-                                            (str(pair["thread-a"]), str(pair["thread-b"]))
-                                        )
-                            if numa_node.get("num-threads"):
-                                numa["threads"] = int(numa_node["num-threads"])
-                                epa_vcpu_set = True
-                            if numa_node.get("memory-mb"):
-                                numa["memory"] = max(int(numa_node["memory-mb"] / 1024), 1)
-                    if vdu["guest-epa"].get("mempage-size"):
-                        if vdu["guest-epa"]["mempage-size"] != "SMALL":
-                            numa["memory"] = max(int(db_flavor["ram"] / 1024), 1)
-                    if vdu["guest-epa"].get("cpu-pinning-policy") and not epa_vcpu_set:
-                        if vdu["guest-epa"]["cpu-pinning-policy"] == "DEDICATED":
-                            if vdu["guest-epa"].get("cpu-thread-pinning-policy") and \
-                                            vdu["guest-epa"]["cpu-thread-pinning-policy"] != "PREFER":
-                                numa["cores"] = max(db_flavor["vcpus"], 1)
-                            else:
-                                numa["threads"] = max(db_flavor["vcpus"], 1)
-                if numa:
-                    extended["numas"] = [numa]
-                if extended:
-                    extended_text = yaml.safe_dump(extended, default_flow_style=True, width=256)
-                    db_flavor["extended"] = extended_text
-                # look if flavor exist
-
-                temp_flavor_dict = {'disk': db_flavor.get('disk', 1),
-                                    'ram': db_flavor.get('ram'),
-                                    'vcpus': db_flavor.get('vcpus'),
-                                    'extended': db_flavor.get('extended')
-                                    }
-                existing_flavors = mydb.get_rows(FROM="flavors", WHERE=temp_flavor_dict)
-                if existing_flavors:
-                    flavor_uuid = existing_flavors[0]["uuid"]
-                else:
-                    flavor_uuid = str(uuid4())
-                    uuid_list.append(flavor_uuid)
-                    db_flavor["uuid"] = flavor_uuid
-                    db_flavors.append(db_flavor)
-                db_vm["flavor_id"] = flavor_uuid
-
                 # cloud-init
                 boot_data = {}
                 if vdu.get("cloud-init"):
@@ -996,10 +967,14 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                 db_vms_index += 1
 
                 # table interfaces (internal/external interfaces)
+                flavor_epa_interfaces = []
                 cp_name2iface_uuid = {}
                 cp_name2vm_uuid = {}
+                cp_name2db_interface = {}
+                vdu_id2cp_name = {}  # stored only when one external connection point is presented at this VDU
                 # for iface in chain(vdu.get("internal-interface").itervalues(), vdu.get("external-interface").itervalues()):
                 for iface in vdu.get("interface").itervalues():
+                    flavor_epa_interface = {}
                     iface_uuid = str(uuid4())
                     uuid_list.append(iface_uuid)
                     db_interface = {
@@ -1007,12 +982,15 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                         "internal_name": get_str(iface, "name", 255),
                         "vm_id": vm_uuid,
                     }
+                    flavor_epa_interface["name"] = db_interface["internal_name"]
                     if iface.get("virtual-interface").get("vpci"):
                         db_interface["vpci"] = get_str(iface.get("virtual-interface"), "vpci", 12)
+                        flavor_epa_interface["vpci"] = db_interface["vpci"]
 
                     if iface.get("virtual-interface").get("bandwidth"):
                         bps = int(iface.get("virtual-interface").get("bandwidth"))
-                        db_interface["bw"] = bps/1000
+                        db_interface["bw"] = int(math.ceil(bps/1000000.0))
+                        flavor_epa_interface["bandwidth"] = "{} Mbps".format(db_interface["bw"])
 
                     if iface.get("virtual-interface").get("type") == "OM-MGMT":
                         db_interface["type"] = "mgmt"
@@ -1022,11 +1000,13 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                     elif iface.get("virtual-interface").get("type") in ("SR-IOV", "PCI-PASSTHROUGH"):
                         db_interface["type"] = "data"
                         db_interface["model"] = get_str(iface.get("virtual-interface"), "type", 12)
+                        flavor_epa_interface["dedicated"] = "no" if iface["virtual-interface"]["type"] == "SR-IOV" \
+                            else "yes"
+                        flavor_epa_interfaces.append(flavor_epa_interface)
                     else:
                         raise NfvoException("Error. Invalid VNF descriptor at 'vnfd[{}]':'vdu[{}]':'interface':'virtual"
                                             "-interface':'type':'{}'. Interface type is not supported".format(
-                                                str(vnfd["id"])[:255], str(vdu["id"])[:255],
-                                                iface.get("virtual-interface").get("type")),
+                                                vnfd_id, vdu_id, iface.get("virtual-interface").get("type")),
                                             HTTP_Bad_Request)
 
                     if iface.get("external-connection-point-ref"):
@@ -1035,9 +1015,19 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                             db_interface["external_name"] = get_str(cp, "name", 255)
                             cp_name2iface_uuid[db_interface["external_name"]] = iface_uuid
                             cp_name2vm_uuid[db_interface["external_name"]] = vm_uuid
+                            cp_name2db_interface[db_interface["external_name"]] = db_interface
                             for cp_descriptor in vnfd_descriptor["connection-point"]:
                                 if cp_descriptor["name"] == db_interface["external_name"]:
                                     break
+                            else:
+                                raise KeyError()
+
+                            if vdu_id in vdu_id2cp_name:
+                                vdu_id2cp_name[vdu_id] = None  # more than two connecdtion point for this VDU
+                            else:
+                                vdu_id2cp_name[vdu_id] = db_interface["external_name"]
+
+                            # port security
                             if str(cp_descriptor.get("port-security-enabled")).lower() == "false":
                                 db_interface["port_security"] = 0
                             elif str(cp_descriptor.get("port-security-enabled")).lower() == "true":
@@ -1046,7 +1036,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                             raise NfvoException("Error. Invalid VNF descriptor at 'vnfd[{vnf}]':'vdu[{vdu}]':"
                                                 "'interface[{iface}]':'vnfd-connection-point-ref':'{cp}' is not present"
                                                 " at connection-point".format(
-                                                    vnf=vnfd["id"], vdu=vdu["id"], iface=iface["name"],
+                                                    vnf=vnfd_id, vdu=vdu_id, iface=iface["name"],
                                                     cp=iface.get("vnfd-connection-point-ref")),
                                                 HTTP_Bad_Request)
                     elif iface.get("internal-connection-point-ref"):
@@ -1067,13 +1057,82 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                             raise NfvoException("Error. Invalid VNF descriptor at 'vnfd[{vnf}]':'vdu[{vdu}]':"
                                                 "'interface[{iface}]':'vdu-internal-connection-point-ref':'{cp}' is not"
                                                 " referenced by any internal-vld".format(
-                                                    vnf=vnfd["id"], vdu=vdu["id"], iface=iface["name"],
+                                                    vnf=vnfd_id, vdu=vdu_id, iface=iface["name"],
                                                     cp=iface.get("vdu-internal-connection-point-ref")),
                                                 HTTP_Bad_Request)
                     if iface.get("position") is not None:
                         db_interface["created_at"] = int(iface.get("position")) - 1000
                     db_interfaces.append(db_interface)
 
+                # table flavors
+                db_flavor = {
+                    "name": get_str(vdu, "name", 250) + "-flv",
+                    "vcpus": int(vdu["vm-flavor"].get("vcpu-count", 1)),
+                    "ram": int(vdu["vm-flavor"].get("memory-mb", 1)),
+                    "disk": int(vdu["vm-flavor"].get("storage-gb", 1)),
+                }
+                # EPA  TODO revise
+                extended = {}
+                numa = {}
+                if devices:
+                    extended["devices"] = devices
+                if flavor_epa_interfaces:
+                    numa["interfaces"] = flavor_epa_interfaces
+                if vdu.get("guest-epa"):   # TODO or dedicated_int:
+                    epa_vcpu_set = False
+                    if vdu["guest-epa"].get("numa-node-policy"):  # TODO or dedicated_int:
+                        numa_node_policy = vdu["guest-epa"].get("numa-node-policy")
+                        if numa_node_policy.get("node"):
+                            numa_node = numa_node_policy["node"]['0']
+                            if numa_node.get("num-cores"):
+                                numa["cores"] = numa_node["num-cores"]
+                                epa_vcpu_set = True
+                            if numa_node.get("paired-threads"):
+                                if numa_node["paired-threads"].get("num-paired-threads"):
+                                    numa["paired-threads"] = int(numa_node["paired-threads"]["num-paired-threads"])
+                                    epa_vcpu_set = True
+                                if len(numa_node["paired-threads"].get("paired-thread-ids")):
+                                    numa["paired-threads-id"] = []
+                                    for pair in numa_node["paired-threads"]["paired-thread-ids"].itervalues():
+                                        numa["paired-threads-id"].append(
+                                            (str(pair["thread-a"]), str(pair["thread-b"]))
+                                        )
+                            if numa_node.get("num-threads"):
+                                numa["threads"] = int(numa_node["num-threads"])
+                                epa_vcpu_set = True
+                            if numa_node.get("memory-mb"):
+                                numa["memory"] = max(int(numa_node["memory-mb"] / 1024), 1)
+                    if vdu["guest-epa"].get("mempage-size"):
+                        if vdu["guest-epa"]["mempage-size"] != "SMALL":
+                            numa["memory"] = max(int(db_flavor["ram"] / 1024), 1)
+                    if vdu["guest-epa"].get("cpu-pinning-policy") and not epa_vcpu_set:
+                        if vdu["guest-epa"]["cpu-pinning-policy"] == "DEDICATED":
+                            if vdu["guest-epa"].get("cpu-thread-pinning-policy") and \
+                                            vdu["guest-epa"]["cpu-thread-pinning-policy"] != "PREFER":
+                                numa["cores"] = max(db_flavor["vcpus"], 1)
+                            else:
+                                numa["threads"] = max(db_flavor["vcpus"], 1)
+                if numa:
+                    extended["numas"] = [numa]
+                if extended:
+                    extended_text = yaml.safe_dump(extended, default_flow_style=True, width=256)
+                    db_flavor["extended"] = extended_text
+                # look if flavor exist
+                temp_flavor_dict = {'disk': db_flavor.get('disk', 1),
+                                    'ram': db_flavor.get('ram'),
+                                    'vcpus': db_flavor.get('vcpus'),
+                                    'extended': db_flavor.get('extended')
+                                    }
+                existing_flavors = mydb.get_rows(FROM="flavors", WHERE=temp_flavor_dict)
+                if existing_flavors:
+                    flavor_uuid = existing_flavors[0]["uuid"]
+                else:
+                    flavor_uuid = str(uuid4())
+                    uuid_list.append(flavor_uuid)
+                    db_flavor["uuid"] = flavor_uuid
+                    db_flavors.append(db_flavor)
+                db_vm["flavor_id"] = flavor_uuid
+
             # VNF affinity and antiaffinity
             for pg in vnfd.get("placement-groups").itervalues():
                 pg_name = get_str(pg, "name", 255)
@@ -1082,7 +1141,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                     if vdu_id not in vdu_id2db_table_index:
                         raise NfvoException("Error. Invalid VNF descriptor at 'vnfd[{vnf}]':'placement-groups[{pg}]':"
                                             "'member-vdus':'{vdu}'. Reference to a non-existing vdu".format(
-                                                vnf=vnfd["id"], pg=pg_name, vdu=vdu_id),
+                                                vnf=vnfd_id, pg=pg_name, vdu=vdu_id),
                                             HTTP_Bad_Request)
                     db_vms[vdu_id2db_table_index[vdu_id]]["availability_zone"] = pg_name
                     # TODO consider the case of isolation and not colocation
@@ -1091,22 +1150,30 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
             # VNF mgmt configuration
             mgmt_access = {}
             if vnfd["mgmt-interface"].get("vdu-id"):
-                if vnfd["mgmt-interface"]["vdu-id"] not in vdu_id2uuid:
+                mgmt_vdu_id = get_str(vnfd["mgmt-interface"], "vdu-id", 255)
+                if mgmt_vdu_id not in vdu_id2uuid:
                     raise NfvoException("Error. Invalid VNF descriptor at 'vnfd[{vnf}]':'mgmt-interface':'vdu-id':"
                                         "'{vdu}'. Reference to a non-existing vdu".format(
-                                            vnf=vnfd["id"], vdu=vnfd["mgmt-interface"]["vdu-id"]),
+                                            vnf=vnfd_id, vdu=mgmt_vdu_id),
                                         HTTP_Bad_Request)
                 mgmt_access["vm_id"] = vdu_id2uuid[vnfd["mgmt-interface"]["vdu-id"]]
+                # if only one cp is defined by this VDU, mark this interface as of type "mgmt"
+                if vdu_id2cp_name.get(mgmt_vdu_id):
+                    cp_name2db_interface[vdu_id2cp_name[mgmt_vdu_id]]["type"] = "mgmt"
+
             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"]["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(
-                                            vnf=vnfd["id"], cp=vnfd["mgmt-interface"]["cp"]),
+                                            vnf=vnfd_id, cp=vnfd["mgmt-interface"]["cp"]),
                                         HTTP_Bad_Request)
                 mgmt_access["vm_id"] = cp_name2vm_uuid[vnfd["mgmt-interface"]["cp"]]
                 mgmt_access["interface_id"] = cp_name2iface_uuid[vnfd["mgmt-interface"]["cp"]]
+                # mark this interface as of type mgmt
+                cp_name2db_interface[vnfd["mgmt-interface"]["cp"]]["type"] = "mgmt"
+
             default_user = get_str(vnfd.get("vnf-configuration", {}).get("config-access", {}).get("ssh-access", {}),
                                     "default-user", 64)
 
@@ -1469,8 +1536,7 @@ def get_vnf_id(mydb, tenant_id, vnf_id):
     content = mydb.get_rows(FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces on vms.uuid=interfaces.vm_id',\
                                     SELECT=('interfaces.uuid as uuid','interfaces.external_name as external_name', 'vms.name as vm_name', 'interfaces.vm_id as vm_id', \
                                             'interfaces.internal_name as internal_name', 'interfaces.type as type', 'interfaces.vpci as vpci','interfaces.bw as bw'),\
-                                    WHERE={'vnfs.uuid': vnf_id},
-                                    WHERE_NOT={'interfaces.external_name': None} )
+                                    WHERE={'vnfs.uuid': vnf_id, 'interfaces.external_name<>': None} )
     #print content
     data['vnf']['external-connections'] = content
 
@@ -1657,8 +1723,7 @@ def new_scenario(mydb, tenant_id, topo):
 
 #1.2: Check that VNF are present at database table vnfs. Insert uuid, description and external interfaces
     for name,vnf in vnfs.items():
-        where={}
-        where_or={"tenant_id": tenant_id, 'public': "true"}
+        where = {"OR": {"tenant_id": tenant_id, 'public': "true"}}
         error_text = ""
         error_pos = "'topology':'nodes':'" + name + "'"
         if 'vnf_id' in vnf:
@@ -1667,14 +1732,12 @@ def new_scenario(mydb, tenant_id, topo):
         if 'VNF model' in vnf:
             error_text += " 'VNF model' " +  vnf['VNF model']
             where['name'] = vnf['VNF model']
-        if len(where) == 0:
+        if len(where) == 1:
             raise NfvoException("Descriptor need a 'vnf_id' or 'VNF model' field at " + error_pos, HTTP_Bad_Request)
 
         vnf_db = mydb.get_rows(SELECT=('uuid','name','description'),
                                FROM='vnfs',
-                               WHERE=where,
-                               WHERE_OR=where_or,
-                               WHERE_AND_OR="AND")
+                               WHERE=where)
         if len(vnf_db)==0:
             raise NfvoException("unknown" + error_text + " at " + error_pos, HTTP_Not_Found)
         elif len(vnf_db)>1:
@@ -1684,7 +1747,7 @@ def new_scenario(mydb, tenant_id, topo):
         #get external interfaces
         ext_ifaces = mydb.get_rows(SELECT=('external_name as name','i.uuid as iface_uuid', 'i.type as type'),
             FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces as i on vms.uuid=i.vm_id',
-            WHERE={'vnfs.uuid':vnf['uuid']}, WHERE_NOT={'external_name':None} )
+            WHERE={'vnfs.uuid':vnf['uuid'], 'external_name<>': None} )
         for ext_iface in ext_ifaces:
             vnf['ifaces'][ ext_iface['name'] ] = {'uuid':ext_iface['iface_uuid'], 'type':ext_iface['type']}
 
@@ -1923,8 +1986,7 @@ def new_scenario_v02(mydb, tenant_id, scenario_dict, version):
 
     # 1: Check that VNF are present at database table vnfs and update content into scenario dict
     for name,vnf in scenario["vnfs"].iteritems():
-        where={}
-        where_or={"tenant_id": tenant_id, 'public': "true"}
+        where = {"OR": {"tenant_id": tenant_id, 'public': "true"}}
         error_text = ""
         error_pos = "'scenario':'vnfs':'" + name + "'"
         if 'vnf_id' in vnf:
@@ -1933,13 +1995,11 @@ def new_scenario_v02(mydb, tenant_id, scenario_dict, version):
         if 'vnf_name' in vnf:
             error_text += " 'vnf_name' " + vnf['vnf_name']
             where['name'] = vnf['vnf_name']
-        if len(where) == 0:
+        if len(where) == 1:
             raise NfvoException("Needed a 'vnf_id' or 'vnf_name' at " + error_pos, HTTP_Bad_Request)
         vnf_db = mydb.get_rows(SELECT=('uuid', 'name', 'description'),
                                FROM='vnfs',
-                               WHERE=where,
-                               WHERE_OR=where_or,
-                               WHERE_AND_OR="AND")
+                               WHERE=where)
         if len(vnf_db) == 0:
             raise NfvoException("Unknown" + error_text + " at " + error_pos, HTTP_Not_Found)
         elif len(vnf_db) > 1:
@@ -1950,7 +2010,7 @@ def new_scenario_v02(mydb, tenant_id, scenario_dict, version):
         # get external interfaces
         ext_ifaces = mydb.get_rows(SELECT=('external_name as name', 'i.uuid as iface_uuid', 'i.type as type'),
                                    FROM='vnfs join vms on vnfs.uuid=vms.vnf_id join interfaces as i on vms.uuid=i.vm_id',
-                                   WHERE={'vnfs.uuid':vnf['uuid']}, WHERE_NOT={'external_name': None} )
+                                   WHERE={'vnfs.uuid':vnf['uuid'], 'external_name<>': None} )
         for ext_iface in ext_ifaces:
             vnf['ifaces'][ ext_iface['name'] ] = {'uuid':ext_iface['iface_uuid'], 'type': ext_iface['type']}
         # TODO? get internal-connections from db.nets and their profiles, and update scenario[vnfs][internal-connections] accordingly
@@ -2139,7 +2199,8 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                 elif vld.get("provider-network").get("overlay-type") == "VLAN":
                     db_sce_net["type"] = "data"
                 else:
-                    db_sce_net["type"] = "bridge"
+                    # later on it will be fixed to bridge or data depending on the type of interfaces attached to it
+                    db_sce_net["type"] = None
                 db_sce_nets.append(db_sce_net)
 
                 # ip-profile, link db_ip_profile with db_sce_net
@@ -2163,7 +2224,7 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                                                 str(nsd["id"]), str(vld["id"]), str(iface["member-vnf-index-ref"])),
                                             HTTP_Bad_Request)
 
-                    existing_ifaces = mydb.get_rows(SELECT=('i.uuid as uuid',),
+                    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",
@@ -2176,6 +2237,8 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                                                 str(iface.get("vnfd-id-ref"))[:255]),
                                             HTTP_Bad_Request)
                     interface_uuid = existing_ifaces[0]["uuid"]
+                    if existing_ifaces[0]["iface_type"] == "data" and not db_sce_net["type"]:
+                        db_sce_net["type"] = "data"
                     sce_interface_uuid = str(uuid4())
                     uuid_list.append(sce_net_uuid)
                     db_sce_interface = {
@@ -2186,6 +2249,8 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                         # "ip_address": #TODO
                     }
                     db_sce_interfaces.append(db_sce_interface)
+                if not db_sce_net["type"]:
+                    db_sce_net["type"] = "bridge"
 
         db_tables = [
             {"scenarios": db_scenarios},
@@ -2404,7 +2469,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
                 else:
                     av_index = None
 
-                vm_id = myvim.new_vminstance(myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
+                vm_id, _ = myvim.new_vminstance(myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
                                              myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'],
                                              availability_zone_index=av_index,
                                              availability_zone_list=vnf_availability_zones)
@@ -2823,19 +2888,15 @@ def create_instance(mydb, tenant_id, instance_dict):
                     create_network = True
                     lookfor_network = False
 
-                if lookfor_network and create_network:
-                    # TODO create two tasks FIND + CREATE with their relationship
-                    task_action = "FIND"
-                    task_params = (lookfor_filter,)
-                    # task_action = "CREATE"
-                    # task_params = (net_vim_name, net_type, sce_net.get('ip_profile', None))
-                    # task
+                task_extra = {}
+                if create_network:
+                    task_action = "CREATE"
+                    task_extra["params"] = (net_vim_name, net_type, sce_net.get('ip_profile', None))
+                    if lookfor_network:
+                        task_extra["find"] = (lookfor_filter,)
                 elif lookfor_network:
                     task_action = "FIND"
-                    task_params = (lookfor_filter,)
-                elif create_network:
-                    task_action = "CREATE"
-                    task_params = (net_vim_name, net_type, sce_net.get('ip_profile', None))
+                    task_extra["params"] = (lookfor_filter,)
 
                 # fill database content
                 net_uuid = str(uuid4())
@@ -2860,7 +2921,7 @@ def create_instance(mydb, tenant_id, instance_dict):
                     "action": task_action,
                     "item": "instance_nets",
                     "item_id": net_uuid,
-                    "extra": yaml.safe_dump({"params": task_params}, default_flow_style=True, width=256)
+                    "extra": yaml.safe_dump(task_extra, default_flow_style=True, width=256)
                 }
                 net2task_id['scenario'][sce_net['uuid']][datacenter_id] = task_index
                 task_index += 1
@@ -3223,6 +3284,7 @@ def delete_instance(mydb, tenant_id, instance_id):
     myvims = {}
     myvim_threads = {}
     vimthread_affected = {}
+    net2vm_dependencies = {}
 
     task_index = 0
     instance_action_id = get_task_id()
@@ -3261,36 +3323,29 @@ def delete_instance(mydb, tenant_id, instance_id):
             if not myvim:
                 error_msg += "\n    VM id={} cannot be deleted because datacenter={} not found".format(vm['vim_vm_id'], sce_vnf["datacenter_id"])
                 continue
-            try:
-                db_vim_action = {
-                    "instance_action_id": instance_action_id,
-                    "task_index": task_index,
-                    "datacenter_vim_id": sce_vnf["datacenter_tenant_id"],
-                    "action": "DELETE",
-                    "status": "SCHEDULED",
-                    "item": "instance_vms",
-                    "item_id": vm["uuid"],
-                    "extra": yaml.safe_dump({"params": vm["interfaces"]},
-                                            default_flow_style=True, width=256)
-                }
-                task_index += 1
-                db_vim_actions.append(db_vim_action)
-
-            except vimconn.vimconnNotFoundException as e:
-                error_msg+="\n    VM VIM_id={} not found at datacenter={}".format(vm['vim_vm_id'], sce_vnf["datacenter_id"])
-                logger.warn("VM instance '%s'uuid '%s', VIM id '%s', from VNF_id '%s' not found",
-                    vm['name'], vm['uuid'], vm['vim_vm_id'], sce_vnf['vnf_id'])
-            except vimconn.vimconnException as e:
-                error_msg+="\n    VM VIM_id={} at datacenter={} Error: {} {}".format(vm['vim_vm_id'], sce_vnf["datacenter_id"], e.http_code, str(e))
-                logger.error("Error %d deleting VM instance '%s'uuid '%s', VIM_id '%s', from VNF_id '%s': %s",
-                    e.http_code, vm['name'], vm['uuid'], vm['vim_vm_id'], sce_vnf['vnf_id'], str(e))
+            db_vim_action = {
+                "instance_action_id": instance_action_id,
+                "task_index": task_index,
+                "datacenter_vim_id": sce_vnf["datacenter_tenant_id"],
+                "action": "DELETE",
+                "status": "SCHEDULED",
+                "item": "instance_vms",
+                "item_id": vm["uuid"],
+                "extra": yaml.safe_dump({"params": vm["interfaces"]},
+                                        default_flow_style=True, width=256)
+            }
+            db_vim_actions.append(db_vim_action)
+            for interface in vm["interfaces"]:
+                if not interface.get("instance_net_id"):
+                    continue
+                if interface["instance_net_id"] not in net2vm_dependencies:
+                    net2vm_dependencies[interface["instance_net_id"]] = []
+                net2vm_dependencies[interface["instance_net_id"]].append(task_index)
+            task_index += 1
 
     # 2.2 deleting NETS
     # net_fail_list=[]
     for net in instanceDict['nets']:
-        # TODO if not net['created']:
-        # TODO    continue #skip not created nets
-
         vimthread_affected[net["datacenter_tenant_id"]] = None
         datacenter_key = (net["datacenter_id"], net["datacenter_tenant_id"])
         if datacenter_key not in myvims:
@@ -3313,31 +3368,21 @@ def delete_instance(mydb, tenant_id, instance_id):
         if not myvim:
             error_msg += "\n    Net VIM_id={} cannot be deleted because datacenter={} not found".format(net['vim_net_id'], net["datacenter_id"])
             continue
-        try:
-            db_vim_action = {
-                "instance_action_id": instance_action_id,
-                "task_index": task_index,
-                "datacenter_vim_id": net["datacenter_tenant_id"],
-                "action": "DELETE",
-                "status": "SCHEDULED",
-                "item": "instance_nets",
-                "item_id": net["uuid"],
-                "extra": yaml.safe_dump({"params": (net['vim_net_id'], net['sdn_net_id'])},
-                                        default_flow_style=True, width=256)
-            }
-            task_index += 1
-            db_vim_actions.append(db_vim_action)
-
-        except vimconn.vimconnNotFoundException as e:
-            error_msg += "\n    NET VIM_id={} not found at datacenter={}".format(net['vim_net_id'], net["datacenter_id"])
-            logger.warn("NET '%s', VIM_id '%s', from VNF_net_id '%s' not found",
-                        net['uuid'], net['vim_net_id'], str(net['vnf_net_id']))
-        except vimconn.vimconnException as e:
-            error_msg += "\n    NET VIM_id={} at datacenter={} Error: {} {}".format(net['vim_net_id'],
-                                                                                    net["datacenter_id"],
-                                                                                    e.http_code, str(e))
-            logger.error("Error %d deleting NET '%s', VIM_id '%s', from VNF_net_id '%s': %s",
-                         e.http_code, net['uuid'], net['vim_net_id'], str(net['vnf_net_id']), str(e))
+        extra = {"params": (net['vim_net_id'], net['sdn_net_id'])}
+        if net2vm_dependencies.get(net["uuid"]):
+            extra["depends_on"] = net2vm_dependencies[net["uuid"]]
+        db_vim_action = {
+            "instance_action_id": instance_action_id,
+            "task_index": task_index,
+            "datacenter_vim_id": net["datacenter_tenant_id"],
+            "action": "DELETE",
+            "status": "SCHEDULED",
+            "item": "instance_nets",
+            "item_id": net["uuid"],
+            "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
     db_tables = [
@@ -3770,75 +3815,78 @@ def delete_datacenter(mydb, datacenter):
 
 def associate_datacenter_to_tenant(mydb, nfvo_tenant, datacenter, vim_tenant_id=None, vim_tenant_name=None, vim_username=None, vim_password=None, config=None):
     # get datacenter info
-    datacenter_id = get_datacenter_uuid(mydb, None, datacenter)
-
-    create_vim_tenant = True if not vim_tenant_id 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']
-
-    #check that this association does not exist before
-    tenants_datacenter_dict={"nfvo_tenant_id":tenant_dict['uuid'], "datacenter_id":datacenter_id }
-    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']), HTTP_Conflict)
-
-    vim_tenant_id_exist_atdb=False
-    if not create_vim_tenant:
-        where_={"datacenter_id": datacenter_id}
-        if vim_tenant_id!=None:
-            where_["vim_tenant_id"] = vim_tenant_id
-        if vim_tenant_name!=None:
-            where_["vim_tenant_name"] = vim_tenant_name
-        #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:
-            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
+    try:
+        datacenter_id = get_datacenter_uuid(mydb, None, datacenter)
+
+        create_vim_tenant = True if not vim_tenant_id 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']
+
+        #check that this association does not exist before
+        tenants_datacenter_dict={"nfvo_tenant_id":tenant_dict['uuid'], "datacenter_id":datacenter_id }
+        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']), HTTP_Conflict)
+
+        vim_tenant_id_exist_atdb=False
+        if not create_vim_tenant:
+            where_={"datacenter_id": datacenter_id}
+            if vim_tenant_id!=None:
+                where_["vim_tenant_id"] = vim_tenant_id
+            if vim_tenant_name!=None:
+                where_["vim_tenant_name"] = vim_tenant_name
+            #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:
+                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
+                datacenter_tenants_dict = {}
+                #insert at table datacenter_tenants
+        else: #if vim_tenant_id==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)
+                datacenter_name = myvim["name"]
+                vim_tenant_id = 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)), HTTP_Internal_Server_Error)
             datacenter_tenants_dict = {}
-            #insert at table datacenter_tenants
-    else: #if vim_tenant_id==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)
-            datacenter_name = myvim["name"]
-            vim_tenant_id = 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)), HTTP_Internal_Server_Error)
-        datacenter_tenants_dict = {}
-        datacenter_tenants_dict["created"]="true"
-
-    #fill datacenter_tenants table
-    if not vim_tenant_id_exist_atdb:
-        datacenter_tenants_dict["vim_tenant_id"] = vim_tenant_id
-        datacenter_tenants_dict["vim_tenant_name"] = vim_tenant_name
-        datacenter_tenants_dict["user"] = vim_username
-        datacenter_tenants_dict["passwd"] = vim_password
-        datacenter_tenants_dict["datacenter_id"] = datacenter_id
-        if config:
-            datacenter_tenants_dict["config"] = yaml.safe_dump(config, default_flow_style=True, width=256)
-        id_ = mydb.new_row('datacenter_tenants', datacenter_tenants_dict, add_uuid=True, confidential_data=True)
-        datacenter_tenants_dict["uuid"] = id_
-
-    #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
-    datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_dict['uuid'], datacenter_id)  # reload data
-    datacenter_name = myvim["name"]
-    thread_name = get_non_used_vim_name(datacenter_name, datacenter_id, tenant_dict['name'], tenant_dict['uuid'])
-    new_thread = vim_thread.vim_thread(myvim, task_lock, thread_name, datacenter_name, datacenter_tenant_id,
-                                       db=db, db_lock=db_lock, ovim=ovim)
-    new_thread.start()
-    thread_id = datacenter_tenants_dict["uuid"]
-    vim_threads["running"][thread_id] = new_thread
-    return datacenter_id
+            datacenter_tenants_dict["created"]="true"
+
+        #fill datacenter_tenants table
+        if not vim_tenant_id_exist_atdb:
+            datacenter_tenants_dict["vim_tenant_id"] = vim_tenant_id
+            datacenter_tenants_dict["vim_tenant_name"] = vim_tenant_name
+            datacenter_tenants_dict["user"] = vim_username
+            datacenter_tenants_dict["passwd"] = vim_password
+            datacenter_tenants_dict["datacenter_id"] = datacenter_id
+            if config:
+                datacenter_tenants_dict["config"] = yaml.safe_dump(config, default_flow_style=True, width=256)
+            id_ = mydb.new_row('datacenter_tenants', datacenter_tenants_dict, add_uuid=True, confidential_data=True)
+            datacenter_tenants_dict["uuid"] = id_
+
+        #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
+        datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_dict['uuid'], datacenter_id)  # reload data
+        datacenter_name = myvim["name"]
+        thread_name = get_non_used_vim_name(datacenter_name, datacenter_id, tenant_dict['name'], tenant_dict['uuid'])
+        new_thread = vim_thread.vim_thread(myvim, task_lock, thread_name, datacenter_name, datacenter_tenant_id,
+                                           db=db, db_lock=db_lock, ovim=ovim)
+        new_thread.start()
+        thread_id = datacenter_tenants_dict["uuid"]
+        vim_threads["running"][thread_id] = new_thread
+        return datacenter_id
+    except vimconn.vimconnException as e:
+        raise NfvoException(str(e), HTTP_Bad_Request)
 
 
 def edit_datacenter_to_tenant(mydb, nfvo_tenant, datacenter_id, vim_tenant_id=None, vim_tenant_name=None,
@@ -3880,9 +3928,6 @@ def edit_datacenter_to_tenant(mydb, nfvo_tenant, datacenter_id, vim_tenant_id=No
     return datacenter_id
 
 def deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter, vim_tenant_id=None):
-    #get datacenter info
-    datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
-
     #get nfvo_tenant info
     if not tenant_id or tenant_id=="any":
         tenant_uuid = None
@@ -3890,8 +3935,9 @@ def deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter, vim_tenant_id=
         tenant_dict = mydb.get_table_by_uuid_name('nfvo_tenants', tenant_id)
         tenant_uuid = tenant_dict['uuid']
 
+    datacenter_id = get_datacenter_uuid(mydb, tenant_uuid, datacenter)
     #check that this association exist before
-    tenants_datacenter_dict={"datacenter_id":datacenter_id }
+    tenants_datacenter_dict={"datacenter_id": datacenter_id }
     if tenant_uuid:
         tenants_datacenter_dict["nfvo_tenant_id"] = tenant_uuid
     tenant_datacenter_list = mydb.get_rows(FROM='tenants_datacenters', WHERE=tenants_datacenter_dict)
@@ -3911,6 +3957,7 @@ def deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter, vim_tenant_id=
             if vim_tenant_dict['created']=='true':
                 #delete tenant at VIM if created by NFVO
                 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:
                     warning = "Not possible to delete vim_tenant_id {} from VIM: {} ".format(vim_tenant_dict['vim_tenant_id'], str(e))