Added more instantitaion parameters: volume_id.
[osm/RO.git] / osm_ro / nfvo.py
index 68037b9..8cde0d6 100644 (file)
@@ -52,7 +52,8 @@ from Crypto.PublicKey import RSA
 import osm_im.vnfd as vnfd_catalog
 import osm_im.nsd as nsd_catalog
 from pyangbind.lib.serialise import pybindJSONDecoder
-from itertools import chain
+from copy import deepcopy
+
 
 global global_config
 global vimconn_imported
@@ -306,7 +307,7 @@ def get_imagelist(mydb, vnf_id, nfvo_tenant=None):
 
 
 def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, datacenter_tenant_id=None,
-            vim_tenant=None, vim_tenant_name=None, vim_user=None, vim_passwd=None):
+            vim_tenant=None, vim_tenant_name=None, vim_user=None, vim_passwd=None, ignore_errors=False):
     '''Obtain a dictionary of VIM (datacenter) classes with some of the input parameters
     return dictionary with {datacenter_id: vim_class, ... }. vim_class contain:
             'nfvo_tenant_id','datacenter_id','vim_tenant_id','vim_url','vim_url_admin','datacenter_name','type','user','passwd'
@@ -350,6 +351,10 @@ def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, da
                 except (IOError, ImportError) as e:
                     # if module_info and module_info[0]:
                     #     file.close(module_info[0])
+                    if ignore_errors:
+                        logger.error("Unknown vim type '{}'. Can not open file '{}.py'; {}: {}".format(
+                                            vim["type"], module, type(e).__name__, str(e)))
+                        continue
                     raise NfvoException("Unknown vim type '{}'. Can not open file '{}.py'; {}: {}".format(
                                             vim["type"], module, type(e).__name__, str(e)), HTTP_Bad_Request)
 
@@ -372,7 +377,13 @@ def get_vim(mydb, nfvo_tenant=None, datacenter_id=None, datacenter_name=None, da
                                 config=extra, persistent_info=persistent_info
                         )
             except Exception as e:
-                raise NfvoException("Error at VIM  {}; {}: {}".format(vim["type"], type(e).__name__, str(e)), HTTP_Internal_Server_Error)
+                if ignore_errors:
+                    logger.error("Error at VIM  {}; {}: {}".format(vim["type"], type(e).__name__, str(e)))
+                    continue
+                http_code = HTTP_Internal_Server_Error
+                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
     except db_base_Exception as e:
         raise NfvoException(str(e) + " at nfvo.get_vim", e.http_code)
@@ -711,7 +722,7 @@ def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_
                 device=devices_original[index]
                 if "image" not in device and "image name" not in device:
                     if 'size' in device:
-                        disk_list.append({'size': device.get('size', default_volume_size)})
+                        disk_list.append({'size': device.get('size', default_volume_size), 'name': device.get('name')})
                     continue
                 image_dict={}
                 image_dict['name']=device.get('image name',flavor_dict['name']+str(dev_nb)+"-img")
@@ -905,6 +916,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                     "vnf_id": vnf_uuid,
                     "uuid": net_uuid,
                     "description": get_str(vld, "description", 255),
+                    "osm_id": get_str(vld, "id", 255),
                     "type": "bridge",   # TODO adjust depending on connection point type
                 }
                 net_id2uuid[vld.get("id")] = net_uuid
@@ -984,7 +996,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                 # volumes
                 devices = []
                 if vdu.get("volumes"):
-                    for volume_key in sorted(vdu["volumes"]):
+                    for volume_key in vdu["volumes"]:
                         volume = vdu["volumes"][volume_key]
                         if not image_present:
                             # Convert the first volume to vnfc.image
@@ -997,7 +1009,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                             db_vm["image_id"] = image_uuid
                         else:
                             # Add Openmano devices
-                            device = {}
+                            device = {"name": str(volume.get("name"))}
                             device["type"] = str(volume.get("device-type"))
                             if volume.get("size"):
                                 device["size"] = int(volume["size"])
@@ -1005,6 +1017,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                                 device["image name"] = str(volume["image"])
                                 if volume.get("image-checksum"):
                                     device["image checksum"] = str(volume["image-checksum"])
+
                             devices.append(device)
 
                 # cloud-init
@@ -1138,8 +1151,8 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                                                     vnf=vnfd_id, vdu=vdu_id, iface=iface["name"],
                                                     cp=iface.get("internal-connection-point-ref"), msg=str(e)),
                                                 HTTP_Bad_Request)
-                    if iface.get("position") is not None:
-                        db_interface["created_at"] = int(iface.get("position")) - 1000
+                    if iface.get("position"):
+                        db_interface["created_at"] = int(iface.get("position")) * 50
                     if iface.get("mac-address"):
                         db_interface["mac"] = str(iface.get("mac-address"))
                     db_interfaces.append(db_interface)
@@ -1306,7 +1319,7 @@ def new_vnf(mydb, tenant_id, vnf_descriptor):
             vnf_descriptor['vnf']['tenant_id'] = tenant_id
         # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter
         if global_config["auto_push_VNF_to_VIMs"]:
-            vims = get_vim(mydb, tenant_id)
+            vims = get_vim(mydb, tenant_id, ignore_errors=True)
 
     # Step 4. Review the descriptor and add missing  fields
     #print vnf_descriptor
@@ -1443,7 +1456,7 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor):
             vnf_descriptor['vnf']['tenant_id'] = tenant_id
         # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter
         if global_config["auto_push_VNF_to_VIMs"]:
-            vims = get_vim(mydb, tenant_id)
+            vims = get_vim(mydb, tenant_id, ignore_errors=True)
 
     # Step 4. Review the descriptor and add missing  fields
     #print vnf_descriptor
@@ -1627,7 +1640,7 @@ def delete_vnf(mydb,tenant_id,vnf_id,datacenter=None,vim_tenant=None):
     if tenant_id != "any":
         check_tenant(mydb, tenant_id)
         # Get the URL of the VIM from the nfvo_tenant and the datacenter
-        vims = get_vim(mydb, tenant_id)
+        vims = get_vim(mydb, tenant_id, ignore_errors=True)
     else:
         vims={}
 
@@ -2281,6 +2294,7 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                     "scenario_id": scenario_uuid,
                     # "type": #TODO
                     "multipoint": not vld.get("type") == "ELINE",
+                    "osm_id":  get_str(vld, "id", 255),
                     # "external": #TODO
                     "description": get_str(vld, "description", 255),
                 }
@@ -2957,18 +2971,35 @@ def create_instance(mydb, tenant_id, instance_dict):
     sce_net2instance = {}
     net2task_id = {'scenario': {}}
 
+    def ip_profile_IM2RO(ip_profile_im):
+        # translate from input format to database format
+        ip_profile_ro = {}
+        if 'subnet-address' in ip_profile_im:
+            ip_profile_ro['subnet_address'] = ip_profile_im['subnet-address']
+        if 'ip-version' in ip_profile_im:
+            ip_profile_ro['ip_version'] = ip_profile_im['ip-version']
+        if 'gateway-address' in ip_profile_im:
+            ip_profile_ro['gateway_address'] = ip_profile_im['gateway-address']
+        if 'dns-address' in ip_profile_im:
+            ip_profile_ro['dns_address'] = ip_profile_im['dns-address']
+            if isinstance(ip_profile_ro['dns_address'], (list, tuple)):
+                ip_profile_ro['dns_address'] = ";".join(ip_profile_ro['dns_address'])
+        if 'dhcp' in ip_profile_im:
+            ip_profile_ro['dhcp_start_address'] = ip_profile_im['dhcp'].get('start-address')
+            ip_profile_ro['dhcp_enabled'] = ip_profile_im['dhcp'].get('enabled', True)
+            ip_profile_ro['dhcp_count'] = ip_profile_im['dhcp'].get('count')
+        return ip_profile_ro
+
     # logger.debug("Creating instance from scenario-dict:\n%s",
     #               yaml.safe_dump(scenarioDict, indent=4, default_flow_style=False))
     try:
         # 0 check correct parameters
         for net_name, net_instance_desc in instance_dict.get("networks", {}).iteritems():
-            found = False
             for scenario_net in scenarioDict['nets']:
-                if net_name == scenario_net["name"]:
-                    found = True
+                if net_name == scenario_net.get("name") or net_name == scenario_net.get("osm_id") or net_name == scenario_net.get("uuid"):
                     break
-            if not found:
-                raise NfvoException("Invalid scenario network name '{}' at instance:networks".format(net_name),
+            else:
+                raise NfvoException("Invalid scenario network name or id '{}' at instance:networks".format(net_name),
                                     HTTP_Bad_Request)
             if "sites" not in net_instance_desc:
                 net_instance_desc["sites"] = [ {} ]
@@ -2990,12 +3021,10 @@ def create_instance(mydb, tenant_id, instance_dict):
                     site["datacenter"] = default_datacenter_id   # change name to id
 
         for vnf_name, vnf_instance_desc in instance_dict.get("vnfs",{}).iteritems():
-            found = False
             for scenario_vnf in scenarioDict['vnfs']:
-                if vnf_name == scenario_vnf['name'] or vnf_name == scenario_vnf['member_vnf_index']:
-                    found = True
+                if vnf_name == scenario_vnf['member_vnf_index'] or vnf_name == scenario_vnf['uuid'] or vnf_name == scenario_vnf['name']:
                     break
-            if not found:
+            else:
                 raise NfvoException("Invalid vnf name '{}' at instance:vnfs".format(vnf_name), HTTP_Bad_Request)
             if "datacenter" in vnf_instance_desc:
                 # Add this datacenter to myvims
@@ -3006,6 +3035,38 @@ def create_instance(mydb, tenant_id, instance_dict):
                     myvim_threads_id[d], _ = get_vim_thread(mydb, tenant_id, vnf_instance_desc["datacenter"])
                 scenario_vnf["datacenter"] = vnf_instance_desc["datacenter"]
 
+            for net_id, net_instance_desc in vnf_instance_desc.get("networks", {}).iteritems():
+                for scenario_net in scenario_vnf['nets']:
+                    if net_id == scenario_net['osm_id'] or net_id == scenario_net['uuid'] or net_id == scenario_net["name"]:
+                        break
+                else:
+                    raise NfvoException("Invalid net id or name '{}' at instance:vnfs:networks".format(net_id), HTTP_Bad_Request)
+                if net_instance_desc.get("vim-network-name"):
+                    scenario_net["vim-network-name"] = net_instance_desc["vim-network-name"]
+                if net_instance_desc.get("name"):
+                    scenario_net["name"] = net_instance_desc["name"]
+                if 'ip-profile' in net_instance_desc:
+                    ipprofile_db = ip_profile_IM2RO(net_instance_desc['ip-profile'])
+                    if 'ip_profile' not in scenario_net:
+                        scenario_net['ip_profile'] = ipprofile_db
+                    else:
+                        update(scenario_net['ip_profile'], ipprofile_db)
+
+            for vdu_id, vdu_instance_desc in vnf_instance_desc.get("vdus", {}).iteritems():
+                for scenario_vm in scenario_vnf['vms']:
+                    if vdu_id == scenario_vm['osm_id'] or vdu_id == scenario_vm["name"]:
+                        break
+                else:
+                    raise NfvoException("Invalid vdu id or name '{}' at instance:vnfs:vdus".format(vdu_id), HTTP_Bad_Request)
+                scenario_vm["instance_parameters"] = vdu_instance_desc
+                for iface_id, iface_instance_desc in vdu_instance_desc.get("interfaces", {}).iteritems():
+                    for scenario_interface in scenario_vm['interfaces']:
+                        if iface_id == scenario_interface['internal_name'] or iface_id == scenario_interface["external_name"]:
+                            scenario_interface.update(iface_instance_desc)
+                            break
+                    else:
+                        raise NfvoException("Invalid vdu id or name '{}' at instance:vnfs:vdus".format(vdu_id), HTTP_Bad_Request)
+
         # 0.1 parse cloud-config parameters
         cloud_config = unify_cloud_config(instance_dict.get("cloud-config"), scenarioDict.get("cloud-config"))
 
@@ -3016,19 +3077,7 @@ def create_instance(mydb, tenant_id, instance_dict):
             for scenario_net in scenarioDict['nets']:
                 if net_name == scenario_net["name"]:
                     if 'ip-profile' in net_instance_desc:
-                        # translate from input format to database format
-                        ipprofile_in = net_instance_desc['ip-profile']
-                        ipprofile_db = {}
-                        ipprofile_db['subnet_address'] = ipprofile_in.get('subnet-address')
-                        ipprofile_db['ip_version'] = ipprofile_in.get('ip-version', 'IPv4')
-                        ipprofile_db['gateway_address'] = ipprofile_in.get('gateway-address')
-                        ipprofile_db['dns_address'] = ipprofile_in.get('dns-address')
-                        if isinstance(ipprofile_db['dns_address'], (list, tuple)):
-                            ipprofile_db['dns_address'] = ";".join(ipprofile_db['dns_address'])
-                        if 'dhcp' in ipprofile_in:
-                            ipprofile_db['dhcp_start_address'] = ipprofile_in['dhcp'].get('start-address')
-                            ipprofile_db['dhcp_enabled'] = ipprofile_in['dhcp'].get('enabled', True)
-                            ipprofile_db['dhcp_count'] = ipprofile_in['dhcp'].get('count' )
+                        ipprofile_db = ip_profile_IM2RO(net_instance_desc['ip-profile'])
                         if 'ip_profile' not in scenario_net:
                             scenario_net['ip_profile'] = ipprofile_db
                         else:
@@ -3049,21 +3098,41 @@ def create_instance(mydb, tenant_id, instance_dict):
         number_mgmt_networks = 0
         db_instance_nets = []
         for sce_net in scenarioDict['nets']:
-            descriptor_net = instance_dict.get("networks", {}).get(sce_net["name"], {})
+            # get involved datacenters where this network need to be created
+            involved_datacenters = []
+            for sce_vnf in scenarioDict.get("vnfs"):
+                vnf_datacenter = sce_vnf.get("datacenter", default_datacenter_id)
+                if vnf_datacenter in involved_datacenters:
+                    continue
+                if sce_vnf.get("interfaces"):
+                    for sce_vnf_ifaces in sce_vnf["interfaces"]:
+                        if sce_vnf_ifaces.get("sce_net_id") == sce_net["uuid"]:
+                            involved_datacenters.append(vnf_datacenter)
+                            break
+
+            descriptor_net = {}
+            if instance_dict.get("networks") and instance_dict["networks"].get(sce_net["name"]):
+                descriptor_net = instance_dict["networks"][sce_net["name"]]
             net_name = descriptor_net.get("vim-network-name")
             sce_net2instance[sce_net['uuid']] = {}
             net2task_id['scenario'][sce_net['uuid']] = {}
 
-            sites = descriptor_net.get("sites", [ {} ])
-            for site in sites:
-                if site.get("datacenter"):
-                    vim = myvims[ site["datacenter"] ]
-                    datacenter_id = site["datacenter"]
-                    myvim_thread_id = myvim_threads_id[ site["datacenter"] ]
-                else:
-                    vim = myvims[ default_datacenter_id ]
-                    datacenter_id = default_datacenter_id
-                    myvim_thread_id = myvim_threads_id[default_datacenter_id]
+            if sce_net["external"]:
+                number_mgmt_networks += 1
+
+            for datacenter_id in involved_datacenters:
+                netmap_use = None
+                netmap_create = None
+                if descriptor_net.get("sites"):
+                    for site in descriptor_net["sites"]:
+                        if site.get("datacenter") == datacenter_id:
+                            netmap_use = site.get("netmap-use")
+                            netmap_create = site.get("netmap-create")
+                            break
+
+                vim = myvims[datacenter_id]
+                myvim_thread_id = myvim_threads_id[datacenter_id]
+
                 net_type = sce_net['type']
                 lookfor_filter = {'admin_state_up': True, 'status': 'ACTIVE'}  # 'shared': True
 
@@ -3071,31 +3140,29 @@ def create_instance(mydb, tenant_id, instance_dict):
                     if sce_net["external"]:
                         net_name = sce_net["name"]
                     else:
-                        net_name = "{}.{}".format(instance_name, sce_net["name"])
+                        net_name = "{}-{}".format(instance_name, sce_net["name"])
                         net_name = net_name[:255]     # limit length
 
-                if sce_net["external"]:
-                    number_mgmt_networks += 1
-                if "netmap-use" in site or "netmap-create" in site:
+                if netmap_use or netmap_create:
                     create_network = False
                     lookfor_network = False
-                    if "netmap-use" in site:
+                    if netmap_use:
                         lookfor_network = True
-                        if utils.check_valid_uuid(site["netmap-use"]):
-                            lookfor_filter["id"] = site["netmap-use"]
+                        if utils.check_valid_uuid(netmap_use):
+                            lookfor_filter["id"] = netmap_use
                         else:
-                            lookfor_filter["name"] = site["netmap-use"]
-                    if "netmap-create" in site:
+                            lookfor_filter["name"] = netmap_use
+                    if netmap_create:
                         create_network = True
                         net_vim_name = net_name
-                        if site["netmap-create"]:
-                            net_vim_name = site["netmap-create"]
+                        if isinstance(netmap_create, str):
+                            net_vim_name = netmap_create
                 elif sce_net.get("vim_network_name"):
                     create_network = False
                     lookfor_network = True
                     lookfor_filter["name"] = sce_net.get("vim_network_name")
                 elif sce_net["external"]:
-                    if sce_net['vim_id'] != None:
+                    if sce_net['vim_id'] is not None:
                         # there is a netmap at datacenter_nets database   # TODO REVISE!!!!
                         create_network = False
                         lookfor_network = True
@@ -3144,7 +3211,7 @@ 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"
                 }
                 db_instance_nets.append(db_net)
                 db_vim_action = {
@@ -3197,8 +3264,8 @@ def create_instance(mydb, tenant_id, instance_dict):
             "net2task_id": net2task_id,
             "sce_net2instance": sce_net2instance,
         }
-        sce_vnf_list = sorted(scenarioDict['vnfs'], key=lambda k: k['name'])
-        for sce_vnf in sce_vnf_list:
+        sce_vnf_list = sorted(scenarioDict['vnfs'], key=lambda k: k['name'])
+        for sce_vnf in scenarioDict['vnfs']:  # sce_vnf_list:
             instantiate_vnf(mydb, sce_vnf, vnf_params, vnf_params_out, rollbackList)
         task_index = vnf_params_out["task_index"]
         uuid_list = vnf_params_out["uuid_list"]
@@ -3434,7 +3501,7 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
         # net_name = descriptor_net.get("name")
         net_name = None
         if not net_name:
-            net_name = "{}.{}".format(instance_name, net["name"])
+            net_name = "{}-{}".format(instance_name, net["name"])
             net_name = net_name[:255]  # limit length
         net_type = net['type']
 
@@ -3459,16 +3526,23 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
         }
         db_instance_nets.append(db_net)
 
+        if net.get("vim-network-name"):
+            lookfor_filter = {"name": net["vim-network-name"]}
+            task_action = "FIND"
+            task_extra = {"params": (lookfor_filter,)}
+        else:
+            task_action = "CREATE"
+            task_extra = {"params": (net_name, net_type, net.get('ip_profile', None))}
+
         db_vim_action = {
             "instance_action_id": instance_action_id,
             "task_index": task_index,
             "datacenter_vim_id": myvim_thread_id,
             "status": "SCHEDULED",
-            "action": "CREATE",
+            "action": task_action,
             "item": "instance_nets",
             "item_id": net_uuid,
-            "extra": yaml.safe_dump({"params": (net_name, net_type, net.get('ip_profile', None))},
-                                    default_flow_style=True, width=256)
+            "extra": yaml.safe_dump(task_extra, default_flow_style=True, width=256)
         }
         task_index += 1
         db_vim_actions.append(db_vim_action)
@@ -3530,10 +3604,13 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
 
     for vm in sce_vnf['vms']:
         myVMDict = {}
-        myVMDict['name'] = "{}.{}.{}".format(instance_name[:64], sce_vnf['name'][:64], vm["name"][:64])
+        sce_vnf_name = sce_vnf['member_vnf_index'] if sce_vnf['member_vnf_index'] else sce_vnf['name']
+        myVMDict['name'] = "{}-{}-{}".format(instance_name[:64], sce_vnf_name[:64], vm["name"][:64])
         myVMDict['description'] = myVMDict['name'][0:99]
         #                if not startvms:
         #                    myVMDict['start'] = "no"
+        if vm.get("instance_parameters") and vm["instance_parameters"].get("name"):
+            myVMDict['name'] = vm["instance_parameters"].get("name")
         myVMDict['name'] = myVMDict['name'][0:255]  # limit name length
         # create image at vim in case it not exist
         image_uuid = vm['image_id']
@@ -3565,6 +3642,10 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
             extended_flavor_dict_yaml = yaml.load(extended_info)
             if 'disks' in extended_flavor_dict_yaml:
                 myVMDict['disks'] = extended_flavor_dict_yaml['disks']
+                if vm.get("instance_parameters") and vm["instance_parameters"].get("devices"):
+                    for disk in myVMDict['disks']:
+                        if disk.get("name") in vm["instance_parameters"]["devices"]:
+                            disk.update(vm["instance_parameters"]["devices"][disk.get("name")])
 
         vm['vim_flavor_id'] = flavor_id
         myVMDict['imageRef'] = vm['vim_image_id']
@@ -3668,10 +3749,8 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
         else:
             av_index = None
         for vm_index in range(0, vm.get('count', 1)):
-            vm_index_name = ""
-            if vm.get('count', 1) > 1:
-                vm_index_name += "." + chr(97 + vm_index)
-            task_params = (myVMDict['name'] + vm_index_name, myVMDict['description'], myVMDict.get('start', None),
+            vm_name = myVMDict['name'] + "-" + str(vm_index+1)
+            task_params = (vm_name, myVMDict['description'], myVMDict.get('start', None),
                            myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'], cloud_config_vm,
                            myVMDict['disks'], av_index, vnf_availability_zones)
             # put interface uuid back to scenario[vnfs][vms[[interfaces]
@@ -3688,6 +3767,7 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
                 'instance_vnf_id': vnf_uuid,
                 # TODO delete "vim_vm_id": vm_id,
                 "vm_id": vm["uuid"],
+                "vim_name": vm_name,
                 # "status":
             }
             db_instance_vms.append(db_vm)
@@ -4014,6 +4094,26 @@ def delete_instance(mydb, tenant_id, instance_id):
     else:
         return "action_id={} instance {} deleted".format(instance_action_id, message)
 
+def get_instance_id(mydb, tenant_id, instance_id):
+    global ovim
+    #check valid tenant_id
+    check_tenant(mydb, tenant_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"),
+            }
+    return instance_dict
 
 def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenant=None):
     '''Refreshes a scenario instance. It modifies instanceDict'''
@@ -4189,21 +4289,196 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
     if len(vims) == 0:
         raise NfvoException("datacenter '{}' not found".format(str(instanceDict['datacenter_id'])), HTTP_Not_Found)
     myvim = vims.values()[0]
+    vm_result = {}
+    vm_error = 0
+    vm_ok = 0
 
-    if action_dict.get("create-vdu"):
-        for vdu in action_dict["create-vdu"]:
+    myvim_threads_id = {}
+    if action_dict.get("vdu-scaling"):
+        db_instance_vms = []
+        db_vim_actions = []
+        db_instance_interfaces = []
+        instance_action_id = get_task_id()
+        db_instance_action = {
+            "uuid": instance_action_id,   # same uuid for the instance and the action on create
+            "tenant_id": nfvo_tenant,
+            "instance_id": instance_id,
+            "description": "SCALE",
+        }
+        vm_result["instance_action_id"] = instance_action_id
+        task_index = 0
+        for vdu in action_dict["vdu-scaling"]:
             vdu_id = vdu.get("vdu-id")
+            osm_vdu_id = vdu.get("osm_vdu_id")
+            member_vnf_index = vdu.get("member-vnf-index")
             vdu_count = vdu.get("count", 1)
-            # get from database TODO
-            # insert tasks TODO
-            pass
+            if vdu_id:
+                target_vm = mydb.get_rows(
+                    FROM="instance_vms as vms join instance_vnfs as vnfs on vms.instance_vnf_id=vnfs.uuid",
+                    WHERE={"vms.uuid": vdu_id},
+                    ORDER_BY="vms.created_at"
+                )
+                if not target_vm:
+                    raise NfvoException("Cannot find the vdu with id {}".format(vdu_id), HTTP_Not_Found)
+            else:
+                if not osm_vdu_id and not member_vnf_index:
+                    raise NfvoException("Invalid imput vdu parameters. Must supply either 'vdu-id' of 'osm_vdu_id','member-vnf-index'")
+                target_vm = mydb.get_rows(
+                    # SELECT=("ivms.uuid", "ivnfs.datacenter_id", "ivnfs.datacenter_tenant_id"),
+                    FROM="instance_vms as ivms join instance_vnfs as ivnfs on ivms.instance_vnf_id=ivnfs.uuid"\
+                         " join sce_vnfs as svnfs on ivnfs.sce_vnf_id=svnfs.uuid"\
+                         " join vms on ivms.vm_id=vms.uuid",
+                    WHERE={"vms.osm_id": osm_vdu_id, "svnfs.member_vnf_index": member_vnf_index},
+                    ORDER_BY="ivms.created_at"
+                )
+                if not target_vm:
+                    raise NfvoException("Cannot find the vdu with osm_vdu_id {} and member-vnf-index {}".format(osm_vdu_id, member_vnf_index), HTTP_Not_Found)
+                vdu_id = target_vm[-1]["uuid"]
+            vm_result[vdu_id] = {"created": [], "deleted": [], "description": "scheduled"}
+            target_vm = target_vm[-1]
+            datacenter = target_vm["datacenter_id"]
+            myvim_threads_id[datacenter], _ = get_vim_thread(mydb, nfvo_tenant, datacenter)
+            if vdu["type"] == "delete":
+                # look for nm
+                vm_interfaces = None
+                for sce_vnf in instanceDict['vnfs']:
+                    for vm in sce_vnf['vms']:
+                        if vm["uuid"] == vdu_id:
+                            vm_interfaces = vm["interfaces"]
+                            break
+
+                db_vim_action = {
+                    "instance_action_id": instance_action_id,
+                    "task_index": task_index,
+                    "datacenter_vim_id": target_vm["datacenter_tenant_id"],
+                    "action": "DELETE",
+                    "status": "SCHEDULED",
+                    "item": "instance_vms",
+                    "item_id": target_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)
+                vm_result[vdu_id]["deleted"].append(vdu_id)
+                # delete from database
+                db_instance_vms.append({"TO-DELETE": vdu_id})
+
+            else:  # vdu["type"] == "create":
+                iface2iface = {}
+                where = {"item": "instance_vms", "item_id": target_vm["uuid"], "action": "CREATE"}
+
+                vim_action_to_clone = mydb.get_rows(FROM="vim_actions", WHERE=where)
+                if not vim_action_to_clone:
+                    raise NfvoException("Cannot find the vim_action at database with {}".format(where), HTTP_Internal_Server_Error)
+                vim_action_to_clone = vim_action_to_clone[0]
+                extra = yaml.safe_load(vim_action_to_clone["extra"])
+
+                # generate a new depends_on. Convert format TASK-Y into new format TASK-ACTION-XXXX.XXXX.Y
+                # 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"]:
+                            task_depends_on.append("{}.{}".format(vim_action_to_clone["instance_action_id"],
+                                                             iface["net_id"][5:]))
+                            iface["net_id"] = "TASK-{}.{}".format(vim_action_to_clone["instance_action_id"],
+                                                                  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):
+                    vm_uuid = str(uuid4())
+                    vm_name = target_vm.get('vim_name')
+                    try:
+                        suffix = vm_name.rfind("-")
+                        vm_name = vm_name[:suffix+1] + str(1 + int(vm_name[suffix+1:]))
+                    except Exception:
+                        pass
+                    db_instance_vm = {
+                        "uuid": vm_uuid,
+                        'instance_vnf_id': target_vm['instance_vnf_id'],
+                        'vm_id': target_vm['vm_id'],
+                        'vim_name': vm_name
+                    }
+                    db_instance_vms.append(db_instance_vm)
+
+                    for vm_iface in vm_ifaces_to_clone:
+                        iface_uuid = str(uuid4())
+                        iface2iface[vm_iface["uuid"]] = iface_uuid
+                        db_vm_iface = {
+                            "uuid": iface_uuid,
+                            'instance_vm_id': vm_uuid,
+                            "instance_net_id": vm_iface["instance_net_id"],
+                            'interface_id': vm_iface['interface_id'],
+                            'type': vm_iface['type'],
+                            'floating_ip': vm_iface['floating_ip'],
+                            'port_security': vm_iface['port_security']
+                        }
+                        db_instance_interfaces.append(db_vm_iface)
+                    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 vm_name:
+                        task_params_copy[0] = vm_name
+                    db_vim_action = {
+                        "instance_action_id": instance_action_id,
+                        "task_index": task_index,
+                        "datacenter_vim_id": vim_action_to_clone["datacenter_vim_id"],
+                        "action": "CREATE",
+                        "status": "SCHEDULED",
+                        "item": "instance_vms",
+                        "item_id": vm_uuid,
+                        # ALF
+                        # ALF
+                        # TODO examinar parametros, quitar MAC o incrementar. Incrementar IP y colocar las dependencias con ACTION-asdfasd.
+                        # ALF
+                        # ALF
+                        "extra": yaml.safe_dump({"params": task_params_copy, "depends_on": task_depends_on}, default_flow_style=True, width=256)
+                    }
+                    task_index += 1
+                    db_vim_actions.append(db_vim_action)
+                    vm_result[vdu_id]["created"].append(vm_uuid)
+
+        db_instance_action["number_tasks"] = task_index
+        db_tables = [
+            {"instance_vms": db_instance_vms},
+            {"instance_interfaces": db_instance_interfaces},
+            {"instance_actions": db_instance_action},
+            # TODO revise sfps
+            # {"instance_sfis": db_instance_sfis},
+            # {"instance_sfs": db_instance_sfs},
+            # {"instance_classifications": db_instance_classifications},
+            # {"instance_sfps": db_instance_sfps},
+            {"vim_actions": db_vim_actions}
+        ]
+        logger.debug("create_vdu done DB tables: %s",
+                     yaml.safe_dump(db_tables, indent=4, default_flow_style=False))
+        mydb.new_rows(db_tables, [])
+        for myvim_thread in myvim_threads_id.values():
+            vim_threads["running"][myvim_thread].insert_task(db_vim_actions)
+
+        return vm_result
 
     input_vnfs = action_dict.pop("vnfs", [])
     input_vms = action_dict.pop("vms", [])
     action_over_all = True if not input_vnfs and not input_vms else False
-    vm_result = {}
-    vm_error = 0
-    vm_ok = 0
     for sce_vnf in instanceDict['vnfs']:
         for vm in sce_vnf['vms']:
             if not action_over_all and sce_vnf['uuid'] not in input_vnfs and sce_vnf['vnf_name'] not in input_vnfs and \
@@ -4297,7 +4572,7 @@ def instance_action_get(mydb, nfvo_tenant, instance_id, action_id):
             raise NfvoException("Not found any action with this criteria", HTTP_Not_Found)
         vim_actions = mydb.get_rows(FROM="vim_actions", WHERE={"instance_action_id": action_id})
         rows[0]["vim_actions"] = vim_actions
-    return {"ations": rows}
+    return {"actions": rows}
 
 
 def create_or_use_console_proxy_thread(console_server, console_port):
@@ -4354,22 +4629,33 @@ def delete_tenant(mydb, tenant):
 
 
 def new_datacenter(mydb, datacenter_descriptor):
+    sdn_port_mapping = None
     if "config" in datacenter_descriptor:
-        datacenter_descriptor["config"]=yaml.safe_dump(datacenter_descriptor["config"],default_flow_style=True,width=256)
-    #Check that datacenter-type is correct
+        sdn_port_mapping = datacenter_descriptor["config"].pop("sdn-port-mapping", None)
+        datacenter_descriptor["config"] = yaml.safe_dump(datacenter_descriptor["config"], default_flow_style=True,
+                                                         width=256)
+    # Check that datacenter-type is correct
     datacenter_type = datacenter_descriptor.get("type", "openvim");
-    module_info = None
+    module_info = None
     try:
         module = "vimconn_" + datacenter_type
         pkg = __import__("osm_ro." + module)
-        vim_conn = getattr(pkg, module)
+        vim_conn = getattr(pkg, module)
         # module_info = imp.find_module(module, [__file__[:__file__.rfind("/")]])
     except (IOError, ImportError):
         # if module_info and module_info[0]:
         #    file.close(module_info[0])
-        raise NfvoException("Incorrect datacenter type '{}'. Plugin '{}.py' not installed".format(datacenter_type, module), HTTP_Bad_Request)
+        raise NfvoException("Incorrect datacenter type '{}'. Plugin '{}.py' not installed".format(datacenter_type,
+                                                                                                  module),
+                            HTTP_Bad_Request)
 
     datacenter_id = mydb.new_row("datacenters", datacenter_descriptor, add_uuid=True, confidential_data=True)
+    if sdn_port_mapping:
+        try:
+            datacenter_sdn_port_mapping_set(mydb, None, datacenter_id, sdn_port_mapping)
+        except Exception as e:
+            mydb.delete_row_by_id("datacenters", datacenter_id)   # Rollback
+            raise e
     return datacenter_id
 
 
@@ -4381,10 +4667,14 @@ def edit_datacenter(mydb, datacenter_id_name, datacenter_descriptor):
     datacenter_id = datacenter['uuid']
     where={'uuid': datacenter['uuid']}
     remove_port_mapping = False
+    new_sdn_port_mapping = None
     if "config" in datacenter_descriptor:
         if datacenter_descriptor['config'] != None:
             try:
                 new_config_dict = datacenter_descriptor["config"]
+                if "sdn-port-mapping" in new_config_dict:
+                    remove_port_mapping = True
+                    new_sdn_port_mapping = new_config_dict.pop("sdn-port-mapping")
                 #delete null fields
                 to_delete=[]
                 for k in new_config_dict:
@@ -4414,6 +4704,11 @@ def edit_datacenter(mydb, datacenter_id_name, datacenter_descriptor):
                 logger.error("Error deleting datacenter-port-mapping " + str(e))
 
     mydb.update_rows('datacenters', datacenter_descriptor, where)
+    if new_sdn_port_mapping:
+        try:
+            datacenter_sdn_port_mapping_set(mydb, None, datacenter_id, new_sdn_port_mapping)
+        except ovimException as e:
+            logger.error("Error adding datacenter-port-mapping " + str(e))
     return datacenter_id
 
 
@@ -4581,9 +4876,10 @@ def deassociate_datacenter_to_tenant(mydb, tenant_id, datacenter, vim_tenant_id=
             logger.error("Cannot delete datacenter_tenants " + str(e))
             pass  # the error will be caused because dependencies, vim_tenant can not be deleted
         thread_id = tenant_datacenter_item["datacenter_tenant_id"]
-        thread = vim_threads["running"][thread_id]
-        thread.insert_task("exit")
-        vim_threads["deleting"][thread_id] = thread
+        thread = vim_threads["running"].get(thread_id)
+        if thread:
+            thread.insert_task("exit")
+            vim_threads["deleting"][thread_id] = thread
     return "datacenter {} detached. {}".format(datacenter_id, warning)
 
 
@@ -5047,13 +5343,15 @@ def datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, sdn_port_map
         element = dict()
         element["compute_node"] = compute_node["compute_node"]
         for port in compute_node["ports"]:
-            element["pci"] = port.get("pci")
+            pci = port.get("pci")
             element["switch_port"] = port.get("switch_port")
             element["switch_mac"] = port.get("switch_mac")
-            if not element["pci"] or not (element["switch_port"] or element["switch_mac"]):
+            if not pci or not (element["switch_port"] or element["switch_mac"]):
                 raise NfvoException ("The mapping must contain the 'pci' and at least one of the elements 'switch_port'"
                                      " or 'switch_mac'", HTTP_Bad_Request)
-            maps.append(dict(element))
+            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)