skip unknown parameters at VNFD,NSD pyangbind validation
[osm/RO.git] / osm_ro / nfvo.py
index b1c86f9..ff47fec 100644 (file)
@@ -151,14 +151,12 @@ 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
-    db = nfvo_db.nfvo_db()
+    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
 
-    if persistence:
-        persistence.lock = db_lock
-    else:
-        persistence = WimPersistence(db, lock=db_lock)
+    persistence = persistence or  WimPersistence(db)
 
     # Initialize openvim for SDN control
     # TODO: Avoid static configuration by adding new parameters to openmanod.cfg
@@ -344,7 +342,7 @@ def get_imagelist(mydb, vnf_id, nfvo_tenant=None):
     image_list = []
     vms = mydb.get_rows(SELECT=('image_id','image_list'), FROM='vms', WHERE={'vnf_id': vnf_id})
     for vm in vms:
-        if vm["image_id"] not in image_list:
+        if vm["image_id"] and vm["image_id"] not in image_list:
             image_list.append(vm["image_id"])
         if vm["image_list"]:
             vm_image_list = yaml.load(vm["image_list"])
@@ -890,7 +888,8 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
     try:
         myvnfd = vnfd_catalog.vnfd()
         try:
-            pybindJSONDecoder.load_ietf_json(vnf_descriptor, None, None, obj=myvnfd, path_helper=True)
+            pybindJSONDecoder.load_ietf_json(vnf_descriptor, None, None, obj=myvnfd, path_helper=True,
+                                             skip_unknown=True)
         except Exception as e:
             raise NfvoException("Error. Invalid VNF descriptor format " + str(e), httperrors.Bad_Request)
         db_vnfs = []
@@ -1070,6 +1069,11 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
 
                             devices.append(device)
 
+                if not db_vm.get("image_id"):
+                    if not db_vm["pdu_type"]:
+                        raise NfvoException("Not defined image for VDU")
+                    # create a fake image
+
                 # cloud-init
                 boot_data = {}
                 if vdu.get("cloud-init"):
@@ -1288,8 +1292,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                                             "'member-vdus':'{vdu}'. Reference to a non-existing vdu".format(
                                                 vnf=vnfd_id, pg=pg_name, vdu=vdu_id),
                                             httperrors.Bad_Request)
-                    if vdu_id2db_table_index[vdu_id]:
-                        db_vms[vdu_id2db_table_index[vdu_id]]["availability_zone"] = pg_name
+                    db_vms[vdu_id2db_table_index[vdu_id]]["availability_zone"] = pg_name
                     # TODO consider the case of isolation and not colocation
                     # if pg.get("strategy") == "ISOLATION":
 
@@ -2262,7 +2265,7 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
     try:
         mynsd = nsd_catalog.nsd()
         try:
-            pybindJSONDecoder.load_ietf_json(nsd_descriptor, None, None, obj=mynsd)
+            pybindJSONDecoder.load_ietf_json(nsd_descriptor, None, None, obj=mynsd, skip_unknown=True)
         except Exception as e:
             raise NfvoException("Error. Invalid NS descriptor format: " + str(e), httperrors.Bad_Request)
         db_scenarios = []
@@ -2405,7 +2408,7 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                                                 str(iface.get("vnfd-id-ref"))[:255]),
                                             httperrors.Bad_Request)
                     interface_uuid = existing_ifaces[0]["uuid"]
-                    if existing_ifaces[0]["iface_type"] == "data" and not db_sce_net["type"]:
+                    if existing_ifaces[0]["iface_type"] == "data":
                         db_sce_net["type"] = "data"
                     sce_interface_uuid = str(uuid4())
                     uuid_list.append(sce_net_uuid)
@@ -2437,7 +2440,6 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                 db_sce_vnffgs.append(db_sce_vnffg)
 
                 # deal with rsps
-                db_sce_rsps = []
                 for rsp in vnffg.get("rsp").itervalues():
                     sce_rsp_uuid = str(uuid4())
                     uuid_list.append(sce_rsp_uuid)
@@ -2448,7 +2450,6 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                         "id": get_str(rsp, "id", 255), # only useful to link with classifiers; will be removed later in the code
                     }
                     db_sce_rsps.append(db_sce_rsp)
-                    db_sce_rsp_hops = []
                     for iface in rsp.get("vnfd-connection-point-ref").itervalues():
                         vnf_index = str(iface['member-vnf-index-ref'])
                         if_order = int(iface['order'])
@@ -2460,32 +2461,47 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                                                     str(nsd["id"]), str(rsp["id"]), str(iface["member-vnf-index-ref"])),
                                                 httperrors.Bad_Request)
 
-                        existing_ifaces = mydb.get_rows(SELECT=('i.uuid as uuid',),
-                                                        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:
+                        ingress_existing_ifaces = mydb.get_rows(SELECT=('i.uuid as uuid',),
+                                                                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-ingress-connection-point-ref",
+                                                                                             255)})
+                        if not ingress_existing_ifaces:
                             raise NfvoException("Error. Invalid NS descriptor at 'nsd[{}]':'rsp[{}]':'vnfd-connection-point"
-                                                "-ref':'vnfd-connection-point-ref':'{}'. Reference to a non-existing "
+                                                "-ref':'vnfd-ingress-connection-point-ref':'{}'. Reference to a non-existing "
                                                 "connection-point name at VNFD '{}'".format(
-                                                    str(nsd["id"]), str(rsp["id"]), str(iface["vnfd-connection-point-ref"]),
-                                                    str(iface.get("vnfd-id-ref"))[:255]),
-                                                httperrors.Bad_Request)
-                        interface_uuid = existing_ifaces[0]["uuid"]
+                                str(nsd["id"]), str(rsp["id"]), str(iface["vnfd-ingress-connection-point-ref"]),
+                                str(iface.get("vnfd-id-ref"))[:255]), httperrors.Bad_Request)
+
+                        egress_existing_ifaces = mydb.get_rows(SELECT=('i.uuid as uuid',),
+                                                               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-egress-connection-point-ref",
+                                                                                            255)})
+                        if not egress_existing_ifaces:
+                            raise NfvoException("Error. Invalid NS descriptor at 'nsd[{}]':'rsp[{}]':'vnfd-connection-point"
+                                                "-ref':'vnfd-egress-connection-point-ref':'{}'. Reference to a non-existing "
+                                                "connection-point name at VNFD '{}'".format(
+                                str(nsd["id"]), str(rsp["id"]), str(iface["vnfd-egress-connection-point-ref"]),
+                                str(iface.get("vnfd-id-ref"))[:255]), HTTP_Bad_Request)
+
+                        ingress_interface_uuid = ingress_existing_ifaces[0]["uuid"]
+                        egress_interface_uuid = egress_existing_ifaces[0]["uuid"]
                         sce_rsp_hop_uuid = str(uuid4())
                         uuid_list.append(sce_rsp_hop_uuid)
                         db_sce_rsp_hop = {
                             "uuid": sce_rsp_hop_uuid,
                             "if_order": if_order,
-                            "interface_id": interface_uuid,
+                            "ingress_interface_id": ingress_interface_uuid,
+                            "egress_interface_id": egress_interface_uuid,
                             "sce_vnf_id": vnf_index2scevnf_uuid[vnf_index],
                             "sce_rsp_id": sce_rsp_uuid,
                         }
                         db_sce_rsp_hops.append(db_sce_rsp_hop)
 
                 # deal with classifiers
-                db_sce_classifiers = []
                 for classifier in vnffg.get("classifier").itervalues():
                     sce_classifier_uuid = str(uuid4())
                     uuid_list.append(sce_classifier_uuid)
@@ -2524,7 +2540,6 @@ def new_nsd_v3(mydb, tenant_id, nsd_descriptor):
                     db_sce_classifier["sce_rsp_id"] = rsp["uuid"]
                     db_sce_classifiers.append(db_sce_classifier)
 
-                    db_sce_classifier_matches = []
                     for match in classifier.get("match-attributes").itervalues():
                         sce_classifier_match_uuid = str(uuid4())
                         uuid_list.append(sce_classifier_match_uuid)
@@ -2615,7 +2630,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
             #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)
                 #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
@@ -2648,7 +2663,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
                 #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)
                 #print "VIM network id for scenario %s: %s" % (scenarioDict['name'],network_id)
                 net['vim_id'] = network_id
                 if sce_vnf['uuid'] not in auxNetDict:
@@ -2959,8 +2974,8 @@ def get_datacenter_by_name_uuid(mydb, tenant_id, datacenter_id_name=None, **extr
 
 
 def update(d, u):
-    '''Takes dict d and updates it with the values in dict u.'''
-    '''It merges all depth levels'''
+    """Takes dict d and updates it with the values in dict u.
+       It merges all depth levels"""
     for k, v in u.iteritems():
         if isinstance(v, collections.Mapping):
             r = update(d.get(k, {}), v)
@@ -2979,6 +2994,7 @@ def create_instance(mydb, tenant_id, instance_dict):
     myvims = {}
     myvim_threads_id = {}
     datacenter = instance_dict.get("datacenter")
+    default_wim_account = instance_dict.get("wim_account")
     default_datacenter_id, vim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
     myvims[default_datacenter_id] = vim
     myvim_threads_id[default_datacenter_id], _ = get_vim_thread(mydb, tenant_id, default_datacenter_id)
@@ -3110,6 +3126,8 @@ def create_instance(mydb, tenant_id, instance_dict):
                     raise NfvoException("Invalid net id or name '{}' at instance:vnfs:networks".format(net_id), httperrors.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("vim-network-id"):
+                    scenario_net["vim-network-id"] = net_instance_desc["vim-network-id"]
                 if net_instance_desc.get("name"):
                     scenario_net["name"] = net_instance_desc["name"]
                 if 'ip-profile' in net_instance_desc:
@@ -3142,7 +3160,9 @@ def create_instance(mydb, tenant_id, instance_dict):
         # However, this is not possible yet.
         for net_name, net_instance_desc in instance_dict.get("networks", {}).iteritems():
             for scenario_net in scenarioDict['nets']:
-                if net_name == scenario_net["name"]:
+                if net_name == scenario_net.get("name") or net_name == scenario_net.get("osm_id") or net_name == scenario_net.get("uuid"):
+                    if "wim_account" in net_instance_desc and net_instance_desc["wim_account"] is not None:
+                        scenario_net["wim_account"] = net_instance_desc["wim_account"]
                     if 'ip-profile' in net_instance_desc:
                         ipprofile_db = ip_profile_IM2RO(net_instance_desc['ip-profile'])
                         if 'ip_profile' not in scenario_net:
@@ -3179,17 +3199,28 @@ def create_instance(mydb, tenant_id, instance_dict):
                             break
             if not involved_datacenters:
                 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 = None
+            wim_account_id = wim_account_name = None
             if len(involved_datacenters) > 1 and 'uuid' in sce_net:
-                # 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_id = wim_engine.find_suitable_wim_account(
-                    involved_datacenters, tenant_id)
-                wim_usage[sce_net['uuid']] = wim_account_id
+                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 = {}
@@ -3250,7 +3281,7 @@ def create_instance(mydb, tenant_id, instance_dict):
                     lookfor_network = True
                     lookfor_filter["name"] = sce_net.get("vim_network_name")
                 elif sce_net["external"]:
-                    if sce_net['vim_id'] is not None:
+                    if sce_net.get('vim_id'):
                         # there is a netmap at datacenter_nets database   # TODO REVISE!!!!
                         create_network = False
                         lookfor_network = True
@@ -3280,7 +3311,7 @@ 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))
+                    task_extra["params"] = (net_vim_name, net_type, sce_net.get('ip_profile', None), wim_account_name)
                     if lookfor_network:
                         task_extra["find"] = (lookfor_filter,)
                 elif lookfor_network:
@@ -3367,8 +3398,9 @@ def create_instance(mydb, tenant_id, instance_dict):
                 sfs_created = []
                 for cp in rsp['connection_points']:
                     count = mydb.get_rows(
-                            SELECT=('vms.count'),
-                            FROM="vms join interfaces on vms.uuid=interfaces.vm_id join sce_rsp_hops as h on interfaces.uuid=h.interface_id",
+                            SELECT='vms.count',
+                            FROM="vms join interfaces on vms.uuid=interfaces.vm_id join sce_rsp_hops as h "
+                                 "on interfaces.uuid=h.ingress_interface_id",
                             WHERE={'h.uuid': cp['uuid']})[0]['count']
                     instance_vnf = next((item for item in db_instance_vnfs if item['sce_vnf_id'] == cp['sce_vnf_id']), None)
                     instance_vms = [item for item in db_instance_vms if item['instance_vnf_id'] == instance_vnf['uuid']]
@@ -3383,6 +3415,10 @@ def create_instance(mydb, tenant_id, instance_dict):
                     for i in range(count):
                         # create sfis
                         sfi_uuid = str(uuid4())
+                        extra_params = {
+                            "ingress_interface_id": cp["ingress_interface_id"],
+                            "egress_interface_id": cp["egress_interface_id"]
+                        }
                         uuid_list.append(sfi_uuid)
                         db_sfi = {
                             "uuid": sfi_uuid,
@@ -3401,7 +3437,7 @@ def create_instance(mydb, tenant_id, instance_dict):
                             "status": "SCHEDULED",
                             "item": "instance_sfis",
                             "item_id": sfi_uuid,
-                            "extra": yaml.safe_dump({"params": "", "depends_on": [dependencies[i]]},
+                            "extra": yaml.safe_dump({"params": extra_params, "depends_on": [dependencies[i]]},
                                                     default_flow_style=True, width=256)
                         }
                         sfis_created.append(task_index)
@@ -3552,12 +3588,14 @@ 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, db_base_Exception) as e:
+    except (NfvoException, vimconn.vimconnException, wimconn.WimConnectorError, 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):
             error_text = "VIM Exception"
+        elif isinstance(e, wimconn.WimConnectorError):
+            error_text = "WIM Exception"
         else:
             error_text = "Exception"
         error_text += " {} {}. {}".format(type(e).__name__, str(e), message)
@@ -3629,8 +3667,12 @@ def instantiate_vnf(mydb, sce_vnf, params, params_out, rollbackList):
         }
         db_instance_nets.append(db_net)
 
+        lookfor_filter = {}
         if net.get("vim-network-name"):
-            lookfor_filter = {"name": net["vim-network-name"]}
+            lookfor_filter["name"] = net["vim-network-name"]
+        if net.get("vim-network-id"):
+            lookfor_filter["id"] = net["vim-network-id"]
+        if lookfor_filter:
             task_action = "FIND"
             task_extra = {"params": (lookfor_filter,)}
         else:
@@ -4523,7 +4565,7 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
                 iface2iface = {}
                 where = {"item": "instance_vms", "item_id": target_vm["uuid"], "action": "CREATE"}
 
-                vim_action_to_clone = mydb.get_rows(FROM="vim_actions", WHERE=where)
+                vim_action_to_clone = mydb.get_rows(FROM="vim_wim_actions", WHERE=where)
                 if not vim_action_to_clone:
                     raise NfvoException("Cannot find the vim_action at database with {}".format(where), httperrors.Internal_Server_Error)
                 vim_action_to_clone = vim_action_to_clone[0]
@@ -4621,7 +4663,7 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
             # {"instance_sfs": db_instance_sfs},
             # {"instance_classifications": db_instance_classifications},
             # {"instance_sfps": db_instance_sfps},
-            {"vim_actions": db_vim_actions}
+            {"vim_wim_actions": db_vim_actions}
         ]
         logger.debug("create_vdu done DB tables: %s",
                      yaml.safe_dump(db_tables, indent=4, default_flow_style=False))
@@ -5418,7 +5460,7 @@ 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)
+            content, _ = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **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'):
@@ -5432,7 +5474,7 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor):
                     sdn_network['type'] = net_type
                     sdn_network['name'] = net_name
                     sdn_network['region'] = datacenter_tenant_id
-                    ovim_content = ovim.new_network(sdn_network)
+                    ovim_content  = ovim.new_network(sdn_network)
                 except ovimException as e:
                     logger.error("ovimException creating SDN network={} ".format(
                         sdn_network) + str(e), exc_info=True)
@@ -5518,9 +5560,8 @@ def datacenter_sdn_port_mapping_set(mydb, tenant_id, datacenter_id, sdn_port_map
             pci = port.get("pci")
             element["switch_port"] = port.get("switch_port")
             element["switch_mac"] = port.get("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'", httperrors.Bad_Request)
+            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))