update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / common / python / rift / mano / yang_translator / rwmano / yang / yang_nsd.py
index 4e421d1..63d8157 100644 (file)
@@ -21,6 +21,8 @@ from rift.mano.yang_translator.common.utils import _
 from rift.mano.yang_translator.rwmano.syntax.tosca_resource \
     import ToscaResource
 from rift.mano.yang_translator.rwmano.yang.yang_vld import YangVld
+from collections import OrderedDict
+import re
 
 TARGET_CLASS_NAME = 'YangNsd'
 
@@ -39,11 +41,11 @@ class YangNsd(ToscaResource):
                     INITIAL_CFG,) = \
                    ('scaling_group_descriptor', 'service_primitive',
                     'user_defined_script', 'scaling_config_action',
-                    'trigger', 'ns_config_primitive_name_ref',
+                    'trigger', 'ns_service_primitive_name_ref',
                     'constituent_vnfd', 'vnfd_member',
                     'min_instance_count', 'max_instance_count',
                     'input_parameter_xpath', 'config_actions',
-                    'initial_config_primitive',)
+                    'initial_config_primitive', )
 
     def __init__(self,
                  log,
@@ -62,12 +64,18 @@ class YangNsd(ToscaResource):
         self.conf_prims = []
         self.scale_grps = []
         self.initial_cfg = []
+        self.service_primitive = []
         self.placement_groups = []
         self.vnf_id_to_vnf_map = {}
         self.vnfd_files = vnfd_files
         self.vld_to_vnf_map = {}
         self.vnf_to_vld_map = {}
         self._vnf_vld_conn_point_map = {}
+        self.vnffgds = {}
+        self.forwarding_paths = {}
+        self.substitution_mapping_forwarder = []
+        self.vnfd_sfc_map = None
+        self.duplicate_vnfd_name_list = []
 
     def handle_yang(self, vnfds):
         self.log.debug(_("Process NSD desc {0}: {1}").
@@ -80,7 +88,7 @@ class YangNsd(ToscaResource):
                 self.inputs.append({
                     self.NAME:
                     self.map_yang_name_to_tosca(
-                        val.replace('/nsd:nsd-catalog/nsd:nsd/nsd:', ''))})
+                        val.replace('/rw-project:project/project-nsd:nsd-catalog/project-nsd:nsd/nsd:', ''))})
             if len(param):
                 self.log.warn(_("{0}, Did not process the following for "
                                 "input param {1}: {2}").
@@ -150,12 +158,12 @@ class YangNsd(ToscaResource):
                 if key in dic:
                     icp[key] = dic.pop(key)
 
-            params = {}
+            params = []
             if self.PARAM in dic:
                 for p in dic.pop(self.PARAM):
                     if (self.NAME in p and
                         self.VALUE in p):
-                        params[p[self.NAME]] = p[self.VALUE]
+                        params.append({self.NAME: p[self.NAME], self.VALUE:p[self.VALUE]})
                     else:
                         # TODO (pjoseph): Need to add support to read the
                         # config file and get the value from that
@@ -170,6 +178,31 @@ class YangNsd(ToscaResource):
             self.log.debug(_("{0}, Initial config {1}").format(self, icp))
             self.initial_cfg.append({self.PROPERTIES : icp})
 
+        def process_service_primitive(dic):
+            prop = {}
+            params = []
+            for key in [self.NAME, self.USER_DEF_SCRIPT]:
+                if key in dic:
+                    prop[key] = dic.pop(key)
+
+            if self.PARAM in dic:
+                for p in dic.pop(self.PARAM):
+                    p_entry = {}
+                    for name, value in p.items():
+                        p_entry[name] = value
+                    params.append(p_entry)
+
+            if len(params):
+                    prop[self.PARAM] = params
+
+            conf_prim = {self.NAME: prop[self.NAME], self.DESC : 'TestDescription'}
+            if self.USER_DEF_SCRIPT in prop:
+                conf_prim[self.USER_DEF_SCRIPT] = prop[self.USER_DEF_SCRIPT]
+                self.conf_prims.append(conf_prim)
+
+            self.service_primitive.append({self.PROPERTIES : prop})
+
+
         def process_vld(vld, dic):
             vld_conf = {}
             vld_prop = {}
@@ -182,7 +215,7 @@ class YangNsd(ToscaResource):
                         if ip_profile_name == ip_prof['name']:
                             ip_profile_vld = ip_prof
             if 'name' in vld:
-                vld_name = vld['name']
+                vld_name = vld['name'].replace('-','_').replace(' ','')
             if 'description' in vld:
                 vld_conf['description'] = vld['description']
             if 'vendor' in vld:
@@ -245,17 +278,187 @@ class YangNsd(ToscaResource):
                             }
                 self.placement_groups.append(placement)
 
+        def process_vnffgd(vnffgs, dic):
+            associated_cp_names = []
+            all_cp_names        = []
+            vnfd_sfc_map        = {}
+            
+            conn_point_to_conection_node = {}
+            conn_point_to_vnf_name_map = {}
+
+            unigue_id_forwarder_path_map = OrderedDict()
+            forwarder_name_to_constitent_vnf_map = OrderedDict()
+            unique_id_classifier_map = OrderedDict()
+            fp_path_count = 1
+            forwarder_count = 1
+
+            vnffg_to_unique_id_rsp_map = OrderedDict()
+            vnffg_to_unique_id_classifier_map = OrderedDict()
+            vnffg_to_associated_cp_names = OrderedDict()
+            rsp_associated_cp_names = OrderedDict()
+            vnffg_to_forwarder_map  = OrderedDict()
+            for vnffg in vnffgs:
+                unique_id_rsp_map = {}
+                for rs in vnffg['rsp']:
+                    unique_id_rsp_map[str(rs['id'])] = rs
+                for class_identifier in vnffg['classifier']:
+                    unique_id_classifier_map[str(class_identifier['rsp_id_ref'])] = class_identifier
+                    associated_cp_names.append(class_identifier['vnfd_connection_point_ref'])
+                    all_cp_names.append(class_identifier['vnfd_connection_point_ref'])
+                    conn_point_to_vnf_name_map[class_identifier['vnfd_connection_point_ref']] = self.vnf_id_to_vnf_map[class_identifier['vnfd_id_ref']]
+                    vnfd_sfc_map[self.vnf_id_to_vnf_map[class_identifier['vnfd_id_ref']]] = class_identifier['vnfd_connection_point_ref']
+
+                    rsp_associated_cp_names[str(class_identifier['rsp_id_ref'])] = class_identifier['vnfd_connection_point_ref']
+
+                vnffg_to_unique_id_rsp_map[vnffg['name']] = unique_id_rsp_map
+                vnffg_to_forwarder_map[vnffg['name']] = []
+    
+            for vnffg in vnffgs:
+                prop = {}
+                fp_members = []
+
+                
+                prop['type'] = self.T_VNFFG
+                prop[self.DESC] = "Test"
+                prop[self.PROPERTIES] = {}
+                if 'vendor' in vnffg:
+                    prop[self.PROPERTIES]['vendor'] = vnffg['vendor']
+                if 'name' in vnffg:
+                    self.vnffgds[vnffg['name']] = prop
+                
+                for rs_id, rs in vnffg_to_unique_id_rsp_map[vnffg['name']].items():
+                    associated_cp_node_names = []
+                    associated_vnf_names = []
+                    number_of_endpoints = 0
+                    if 'vnfd_connection_point_ref' in rs:
+                       number_of_endpoints = number_of_endpoints + len(rs['vnfd_connection_point_ref'])
+                       for vnf in rs['vnfd_connection_point_ref']:
+                            associated_vnf_names.append(str(self.vnf_id_to_vnf_map[vnf['vnfd_id_ref']]))
+                            associated_cp_names.append(vnf['vnfd_connection_point_ref'])
+                            all_cp_names.append(vnf['vnfd_connection_point_ref'])
+                            conn_point_to_vnf_name_map[vnf['vnfd_connection_point_ref']] = self.vnf_id_to_vnf_map[vnf['vnfd_id_ref']]
+                       if "forwarder{}".format(fp_path_count) not in  forwarder_name_to_constitent_vnf_map:
+                            forwarder_name_to_constitent_vnf_map["forwarder{}".format(fp_path_count)] = associated_vnf_names
+                            vnffg_to_forwarder_map[vnffg['name']].append("forwarder{}".format(fp_path_count))
+                    fp_path_count = fp_path_count + 1
+                    
+                    associated_cp_names = list(set(associated_cp_names))
+                    for cp_name in associated_cp_names:
+                            for idx, vnfd in self.vnfds.items():
+                                for vdu in vnfd.vdus:
+                                    if cp_name == rsp_associated_cp_names[rs_id]:
+                                        if cp_name in vdu.conn_point_to_conection_node:
+                                            associated_cp_node_names.append(vdu.conn_point_to_conection_node[cp_name])
+                                            #conn_point_to_conection_node[cp_name] = vdu.conn_point_to_conection_node[cp_name]
+
+                    for cp_name in all_cp_names:
+                        for idx, vnfd in self.vnfds.items():
+                            for vdu in vnfd.vdus:
+                                if cp_name in vdu.conn_point_to_conection_node:
+                                    conn_point_to_conection_node[cp_name] = vdu.conn_point_to_conection_node[cp_name]
+
+                    if len(associated_vnf_names) > 0:
+                        associated_vnf_names = list(set(associated_vnf_names))
+                        vnf_str = ", ".join(associated_vnf_names)
+                        prop[self.PROPERTIES]['constituent_vnfs'] = "[{}]".format(vnf_str)
+                    if len(associated_cp_node_names) > 0:
+                        associated_cp_node_names = list(set(associated_cp_node_names))
+                        connection_point_str = ", ".join(associated_cp_node_names)
+                        prop[self.PROPERTIES]['connection_point'] = "[{}]".format(", ".join(associated_cp_node_names))
+
+                    prop[self.PROPERTIES]['number_of_endpoints'] = number_of_endpoints
+                    fp_name = "Forwarding_path{}".format(forwarder_count)
+                    unigue_id_forwarder_path_map[fp_name] = rs_id
+                    fp_members.append(fp_name)
+                    forwarder_count = forwarder_count + 1
+
+                    if len(fp_members) > 0:
+                        prop['members'] = []
+                        for fp in fp_members:
+                            prop['members'].append(fp)
+
+            fp_count = 1
+            for fp, idx in unigue_id_forwarder_path_map.items():
+                for vnffg_name, unique_id_rsp_map in vnffg_to_unique_id_rsp_map.items():
+                    if idx in unique_id_rsp_map:
+                        prop = {}
+                        prop['type'] = self.T_FP
+                        prop[self.PROPERTIES] = {}
+                        prop[self.PROPERTIES][self.DESC] = "Forwarder"
+                        prop[self.PROPERTIES]['policy'] = {}
+                        prop[self.PROPERTIES]['policy']['type'] = 'ACL'
+                        prop[self.PROPERTIES]['policy']['criteria'] = []
+
+                        prop[self.PROPERTIES]['path'] = []
+
+                        rsp =  unique_id_rsp_map[idx]
+                        classifier = unique_id_classifier_map[idx]
+
+                        for match in classifier['match_attributes']:
+                            match_prop = {}
+                            if 'source_port' in match:
+                                port = "'{}'".format((match['source_port']))
+                                prop[self.PROPERTIES]['policy']['criteria'].append({'source_port_range': port})
+                            if 'destination_port' in match:
+                                port = "'f'{}''".format((match['destination_port']))
+                                prop[self.PROPERTIES]['policy']['criteria'].append({'destination_port_range': '5006'})
+                            if 'ip_proto' in match:
+                                port = match['ip_proto']
+                                prop[self.PROPERTIES]['policy']['criteria'].append({'ip_proto': port})
+                            if 'destination_ip_address' in match:
+                                port = "'{}'".format((match['destination_ip_address']))
+                                prop[self.PROPERTIES]['policy']['criteria'].append({'ip_dst_prefix': port})
+
+                        if 'vnfd_connection_point_ref' in classifier:
+                            if classifier['vnfd_connection_point_ref'] in conn_point_to_vnf_name_map:
+                                if 'cp' not in prop[self.PROPERTIES]:
+                                    prop[self.PROPERTIES]['cp'] = {}
+                                prop[self.PROPERTIES]['cp']['forwarder'] = conn_point_to_vnf_name_map[classifier['vnfd_connection_point_ref']]
+                                prop[self.PROPERTIES]['cp']['capability'] = conn_point_to_conection_node[classifier['vnfd_connection_point_ref']]
+
+                        for fp, vnf_list in forwarder_name_to_constitent_vnf_map.items():
+                            for vnf in vnf_list:
+                                for cp, vnf_name in conn_point_to_vnf_name_map.items():
+                                    if vnf == vnf_name:
+                                        self.substitution_mapping_forwarder.append((vnf, fp, conn_point_to_conection_node[cp]))
+
+                        visited_forwarder = []
+                        visited_path = None
+                        for path, vnfs in forwarder_name_to_constitent_vnf_map.items():
+                            for vnf in vnfs:
+                                if (vnf not in visited_forwarder) and (path in vnffg_to_forwarder_map[vnffg_name]):
+                                    path_prop = {}
+                                    path_prop['forwarder']  = vnf
+                                    path_prop['capability'] = path
+                                    prop[self.PROPERTIES]['path'].append(path_prop)
+                                    visited_forwarder.append(vnf)
+                                    visited_path = path
+                        forwarder_name_to_constitent_vnf_map.pop(visited_path)
+
+                        self.forwarding_paths["Forwarding_path{}".format(fp_count)] = prop
+                        fp_count = fp_count +1
+
+            self.vnfd_sfc_map = vnfd_sfc_map
+
         dic = deepcopy(self.yang)
         try:
             for key in self.REQUIRED_FIELDS:
-                self.props[key] = dic.pop(key)
+                if key in dic:
+                    self.props[key] = dic.pop(key)
 
             self.id = self.props[self.ID]
 
             # Process constituent VNFDs
+
+            vnfd_name_list = []
+            member_vnf_index_list = []
             if self.CONST_VNFD in dic:
                 for cvnfd in dic.pop(self.CONST_VNFD):
-                    process_const_vnfd(cvnfd)
+                    if cvnfd[self.VNFD_ID_REF] not in member_vnf_index_list:
+                        member_vnf_index_list.append(cvnfd[self.VNFD_ID_REF])
+                        process_const_vnfd(cvnfd)
+                    else:
+                        self.duplicate_vnfd_name_list.append(self.vnf_id_to_vnf_map[cvnfd[self.VNFD_ID_REF]])
 
             # Process VLDs
             if self.VLD in dic:
@@ -263,31 +466,28 @@ class YangNsd(ToscaResource):
                     process_vld(vld_dic, dic)
                     #self.vlds.append(vld)
 
-            # Process config primitives
-            if self.CONF_PRIM in dic:
-                for cprim in dic.pop(self.CONF_PRIM):
-                    conf_prim = {self.NAME: cprim.pop(self.NAME)}
-                    if self.USER_DEF_SCRIPT in cprim:
-                        conf_prim[self.USER_DEF_SCRIPT] = \
-                                        cprim.pop(self.USER_DEF_SCRIPT)
-                        self.conf_prims.append(conf_prim)
-                    else:
-                        err_msg = (_("{0}, Only user defined script supported "
-                                     "in config-primitive for now {}: {}").
-                                   format(self, conf_prim, cprim))
-                        self.log.error(err_msg)
-                        raise ValidationError(message=err_msg)
+            #Process VNFFG
+            if self.VNFFGD in dic:
+                process_vnffgd(dic[self.VNFFGD], dic)
 
-            # Process scaling group
-            if self.SCALE_GRP in dic:
-                for sg_dic in dic.pop(self.SCALE_GRP):
-                    process_scale_grp(sg_dic)
+
+            
 
             # Process initial config primitives
             if self.INITIAL_CFG in dic:
                 for icp_dic in dic.pop(self.INITIAL_CFG):
                     process_initial_config(icp_dic)
 
+            # NS service prmitive
+            if self.CONF_PRIM in dic:
+                for icp_dic in dic.pop(self.CONF_PRIM):
+                    process_service_primitive(icp_dic)
+
+            # Process scaling group
+            if self.SCALE_GRP in dic:
+                for sg_dic in dic.pop(self.SCALE_GRP):
+                    process_scale_grp(sg_dic)
+
             # Process the input params
             if self.INPUT_PARAM_XPATH in dic:
                 for param in dic.pop(self.INPUT_PARAM_XPATH):
@@ -382,10 +582,13 @@ class YangNsd(ToscaResource):
             self.VENDOR: self.props[self.VENDOR],
             self.VERSION: self.props[self.VERSION],
         }
+        if self.LOGO in self.props:
+            tosca[self.METADATA][self.LOGO] = self.props[self.LOGO]
+
         if len(self.vnfd_files) > 0:
             tosca[self.IMPORT] = []
             imports = []
-            for vnfd_file in self.vnfd_files:
+            for vnfd_file in set(self.vnfd_files):
                 tosca[self.IMPORT].append('"{0}.yaml"'.format(vnfd_file))
 
         tosca[self.TOPOLOGY_TMPL] = {}
@@ -404,6 +607,7 @@ class YangNsd(ToscaResource):
         tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL] = {}
 
         # Add the VNFDs and VLDs
+        vnf_type_vld_list = []
         for idx, vnfd in self.vnfds.items():
             #vnfd.generate_vnf_template(tosca, idx)
             node = {
@@ -417,22 +621,50 @@ class YangNsd(ToscaResource):
             if vnfd.name in self.vnf_to_vld_map:
                 vld_list = self.vnf_to_vld_map[vnfd.name]
                 node[self.REQUIREMENTS] = []
+
                 for vld_idx in range(0, len(vld_list)):
-                    vld_link_name = "{0}{1}".format("virtualLink", vld_idx + 1)
-                    vld_prop = {}
-                    vld_prop[vld_link_name] = vld_list[vld_idx]
-                    node[self.REQUIREMENTS].append(vld_prop)
-                    if vnfd.name in self._vnf_vld_conn_point_map:
-                        vnf_vld_list = self._vnf_vld_conn_point_map[vnfd.name]
-                        for vnf_vld in vnf_vld_list:
-                            vnfd.generate_vld_link(vld_link_name, vnf_vld[1])
+                    if  vnfd.vnf_type not in vnf_type_vld_list:
+                        vld_link_name = "{0}{1}".format("virtualLink", vld_idx + 1)
+                        vld_prop = {}
+                        vld_prop[vld_link_name] = vld_list[vld_idx]
+                        node[self.REQUIREMENTS].append(vld_prop)
+                        if  vnfd.vnf_type not in vnf_type_vld_list:
+                            vnf_type_vld_list.append(vnfd.vnf_type)
+                            if vnfd.name in self._vnf_vld_conn_point_map:
+                                vnf_vld_list = set(self._vnf_vld_conn_point_map[vnfd.name])
+                                for vnf_vld in vnf_vld_list:
+                                    vnfd.generate_vld_link(vld_link_name, vnf_vld[1])
+
+            for sub_mapping in self.substitution_mapping_forwarder:
+                if sub_mapping[0] == vnfd.name:
+                    vnfd.generate_forwarder_sub_mapping(sub_mapping)
+
+            if self.vnfd_sfc_map:
+                for vnfd_name, cp_name in self.vnfd_sfc_map.items():
+                    if vnfd.name == vnfd_name:
+                        vnfd.generate_sfc_link(cp_name)
+
 
 
             tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vnfd.name] = node
 
+        v_idx = len(self.vnfds) + 1 + len(self.duplicate_vnfd_name_list)
+        for vnfd_name in self.duplicate_vnfd_name_list:
+            node = tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vnfd_name]
+            new_node = deepcopy(node)
+            st = re.sub(r'\d+$', '', vnfd_name.rstrip('_vnfd'))
+
+            new_node[self.PROPERTIES][self.ID] = v_idx
+            node_name = "{}{}_vnfd".format(st, v_idx)
+            tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][node_name] = new_node
+            v_idx += 1
+
         for vld_node_name in self.vlds:
             tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vld_node_name] = self.vlds[vld_node_name]
 
+        for fp_name, fp in self.forwarding_paths.items():
+            tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][fp_name] = fp
+
         # add the config primitives
         if len(self.conf_prims):
             if self.GROUPS not in tosca[self.TOPOLOGY_TMPL]:
@@ -451,8 +683,8 @@ class YangNsd(ToscaResource):
             conf_prims[self.PROPERTIES] = {
                 self.PRIMITIVES: prims
             }
-
-            tosca[self.TOPOLOGY_TMPL][self.GROUPS][self.CONF_PRIM] = conf_prims
+            conf_prims[self.DESC] = 'Test'
+            #tosca[self.TOPOLOGY_TMPL][self.GROUPS][self.CONF_PRIM] = conf_prims
 
 
         # Add the scale group
@@ -486,6 +718,23 @@ class YangNsd(ToscaResource):
                         self.INITIAL_CFG: icpt
                     })
 
+        if len(self.service_primitive) > 0:
+            if self.POLICIES not in tosca[self.TOPOLOGY_TMPL]:
+                tosca[self.TOPOLOGY_TMPL][self.POLICIES] = []
+
+            for icp in self.service_primitive:
+                if len(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL]) > 0:
+                    node_name = list(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL].keys())[0]
+                    icpt = {
+                        self.TYPE: self.T_NS_PRIMITIVE,
+                        self.TARGETS : "[{0}]".format(node_name)
+                    }
+                    icpt.update(icp)
+                    tosca[self.TOPOLOGY_TMPL][self.POLICIES].append({
+                        'ns_service_primitives': icpt
+                    })
+
+
         if len(self.placement_groups) > 0:
             if self.POLICIES not in tosca[self.TOPOLOGY_TMPL]:
                 tosca[self.TOPOLOGY_TMPL][self.POLICIES] = []
@@ -493,6 +742,13 @@ class YangNsd(ToscaResource):
             for placment_group in self.placement_groups:
                 tosca[self.TOPOLOGY_TMPL][self.POLICIES].append(placment_group)
 
+        if len(self.vnffgds) > 0:
+            if self.GROUPS not in tosca[self.TOPOLOGY_TMPL]:
+                tosca[self.TOPOLOGY_TMPL][self.GROUPS] = {}
+            for vnffgd_name in self.vnffgds:
+                tosca[self.TOPOLOGY_TMPL][self.GROUPS][vnffgd_name] = self.vnffgds[vnffgd_name]
+
+
         return tosca
 
     def get_supporting_files(self):
@@ -508,6 +764,25 @@ class YangNsd(ToscaResource):
                         self.DEST: "{}/{}".format(self.SCRIPT_DIR, script),
                     })
 
+        for prim in self.service_primitive:
+            if 'properties' in prim:
+                if 'user_defined_script' in prim['properties']:
+                    script = os.path.basename(prim['properties']['user_defined_script'])
+                    files.append({
+                        self.TYPE: 'script',
+                        self.NAME: script,
+                        self.DEST: "{}/{}".format(self.SCRIPT_DIR, script),
+                    })
+
+        if 'logo' in self.props:
+            icon = os.path.basename(self.props['logo'])
+            files.append({
+                self.TYPE: 'icons',
+                self.NAME: icon,
+                self.DEST: "{}/{}".format(self.ICON_DIR, icon),
+            })
+
+
         # TODO (pjoseph): Add support for config scripts,
         # charms, etc