* YANG to TOSCA VNFFG Support
[osm/SO.git] / common / python / rift / mano / yang_translator / rwmano / yang / yang_nsd.py
index 491bd86..d28b3e1 100644 (file)
@@ -21,6 +21,7 @@ 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
 
 TARGET_CLASS_NAME = 'YangNsd'
 
@@ -49,7 +50,8 @@ class YangNsd(ToscaResource):
                  log,
                  name,
                  type_,
-                 yang):
+                 yang,
+                 vnfd_files):
         super(YangNsd, self).__init__(log,
                                       name,
                                       type_,
@@ -57,10 +59,20 @@ class YangNsd(ToscaResource):
         self.props = {}
         self.inputs = []
         self.vnfds = {}
-        self.vlds = []
+        self.vlds = {}
         self.conf_prims = []
         self.scale_grps = []
         self.initial_cfg = []
+        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
 
     def handle_yang(self, vnfds):
         self.log.debug(_("Process NSD desc {0}: {1}").
@@ -85,6 +97,7 @@ class YangNsd(ToscaResource):
             vnfd_id = cvnfd.pop(self.VNFD_ID_REF)
             for vnfd in vnfds:
                 if vnfd.type == self.VNFD and vnfd.id == vnfd_id:
+                    self.vnf_id_to_vnf_map[vnfd_id] = vnfd.name
                     self.vnfds[cvnfd.pop(self.MEM_VNF_INDEX)] = vnfd
                     if self.START_BY_DFLT in cvnfd:
                         vnfd.props[self.START_BY_DFLT] = \
@@ -160,7 +173,244 @@ class YangNsd(ToscaResource):
                 self.log.warn(_("{0}, Did not process all fields for {1}").
                               format(self, dic))
             self.log.debug(_("{0}, Initial config {1}").format(self, icp))
-            self.initial_cfg.append(icp)
+            self.initial_cfg.append({self.PROPERTIES : icp})
+
+        def process_vld(vld, dic):
+            vld_conf = {}
+            vld_prop = {}
+            ip_profile_vld = None
+            vld_name = None
+            if 'ip_profile_ref' in vld:
+                ip_profile_name  = vld['ip_profile_ref']
+                if 'ip_profiles' in dic:
+                    for ip_prof in dic['ip_profiles']:
+                        if ip_profile_name == ip_prof['name']:
+                            ip_profile_vld = ip_prof
+            if 'name' in vld:
+                vld_name = vld['name'].replace('-','_').replace(' ','')
+            if 'description' in vld:
+                vld_conf['description'] = vld['description']
+            if 'vendor' in vld:
+                vld_conf['vendor'] = vld['vendor']
+            if ip_profile_vld:
+                if 'ip_profile_params' in ip_profile_vld:
+                    ip_param = ip_profile_vld['ip_profile_params']
+                    if 'gateway_address' in ip_param:
+                        vld_conf['gateway_ip'] = ip_param['gateway_address']
+                    if 'subnet_address' in ip_param:
+                        vld_conf['cidr'] = ip_param['subnet_address']
+                    if 'ip_version' in ip_param:
+                        vld_conf['ip_version'] = ip_param['ip_version'].replace('ipv','')
+
+            if vld_name:
+                vld_prop = {vld_name :
+                {
+                 'type': self.T_ELAN,
+                 self.PROPERTIES : vld_conf
+                }}
+                self.vlds[vld_name] = { 'type': self.T_ELAN,
+                                         self.PROPERTIES : vld_conf
+                                        }
+
+                self.vld_to_vnf_map[vld_name] = []
+                if 'vnfd_connection_point_ref' in vld:
+                    for vnfd_ref in vld['vnfd_connection_point_ref']:
+                        vnf_name = self.vnf_id_to_vnf_map[vnfd_ref['vnfd_id_ref']]
+                        if vnf_name in self.vnf_to_vld_map:
+                            self.vnf_to_vld_map[vnf_name].append(vld_name)
+                            self._vnf_vld_conn_point_map[vnf_name].\
+                            append((vld_name ,vnfd_ref['vnfd_connection_point_ref']))
+                        else:
+                            self.vnf_to_vld_map[vnf_name] = []
+                            self._vnf_vld_conn_point_map[vnf_name] = []
+                            self.vnf_to_vld_map[vnf_name].append(vld_name)
+                            self._vnf_vld_conn_point_map[vnf_name].\
+                            append((vld_name ,vnfd_ref['vnfd_connection_point_ref']))
+
+        def process_placement_group(placement_groups):
+            for i in range(0, len(placement_groups)):
+                placement_group = placement_groups[i]
+                pg_name = "placement_{0}".format(i)
+                pg_config = {}
+                targets = []
+                if 'name' in placement_group:
+                    pg_config['name'] = placement_group['name']
+                if 'requirement' in placement_group:
+                    pg_config['requirement'] = placement_group['requirement']
+                if 'strategy' in placement_group:
+                    pg_config['strategy'] = placement_group['strategy']
+                if 'member_vnfd' in placement_group:
+                    for member_vnfd in placement_group['member_vnfd']:
+                        targets.append(self.vnf_id_to_vnf_map[member_vnfd['vnfd_id_ref']])
+                placement = { pg_name : {
+                                'type': self.T_PLACEMENT,
+                                self.PROPERTIES: pg_config,
+                                self.TARGETS   :  str(targets)
+                                }
+                            }
+                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:
@@ -177,15 +427,20 @@ class YangNsd(ToscaResource):
             # Process VLDs
             if self.VLD in dic:
                 for vld_dic in dic.pop(self.VLD):
-                    vld = YangVld(self.log, vld_dic.pop(self.NAME),
-                                  self.VLD, vld_dic)
-                    vld.process_vld(self.vnfds)
-                    self.vlds.append(vld)
+                    process_vld(vld_dic, dic)
+                    #self.vlds.append(vld)
+
+            #Process VNFFG
+            if self.VNFFGD in dic:
+                process_vnffgd(dic[self.VNFFGD], dic)
+
+
+            #if self.
 
             # 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)}
+                    conf_prim = {self.NAME: cprim.pop(self.NAME), self.DESC : 'TestDescription'}
                     if self.USER_DEF_SCRIPT in cprim:
                         conf_prim[self.USER_DEF_SCRIPT] = \
                                         cprim.pop(self.USER_DEF_SCRIPT)
@@ -212,6 +467,10 @@ class YangNsd(ToscaResource):
                 for param in dic.pop(self.INPUT_PARAM_XPATH):
                     process_input_param(param)
 
+            if 'placement_groups' in dic:
+                process_placement_group(dic['placement_groups'])
+
+
             self.remove_ignored_fields(dic)
             if len(dic):
                 self.log.warn(_("{0}, Did not process the following for "
@@ -226,13 +485,14 @@ class YangNsd(ToscaResource):
             raise ValidationError(message=err_msg)
 
     def generate_tosca_type(self):
+
         self.log.debug(_("{0} Generate tosa types").
                        format(self))
 
         tosca = {}
-        tosca[self.DATA_TYPES] = {}
-        tosca[self.NODE_TYPES] = {}
-
+        #tosca[self.DATA_TYPES] = {}
+        #tosca[self.NODE_TYPES] = {}
+        return tosca
         for idx, vnfd in self.vnfds.items():
             tosca = vnfd.generate_tosca_type(tosca)
 
@@ -287,20 +547,25 @@ class YangNsd(ToscaResource):
     def generate_tosca_template(self, tosca):
         self.log.debug(_("{0}, Generate tosca template").
                        format(self, tosca))
-
         # Add the standard entries
         tosca['tosca_definitions_version'] = \
-                                    'tosca_simple_profile_for_nfv_1_0_0'
+                                    'tosca_simple_profile_for_nfv_1_0'
         tosca[self.DESC] = self.props[self.DESC]
         tosca[self.METADATA] = {
             'ID': self.name,
             self.VENDOR: self.props[self.VENDOR],
             self.VERSION: self.props[self.VERSION],
         }
+        if len(self.vnfd_files) > 0:
+            tosca[self.IMPORT] = []
+            imports = []
+            for vnfd_file in self.vnfd_files:
+                tosca[self.IMPORT].append('"{0}.yaml"'.format(vnfd_file))
 
         tosca[self.TOPOLOGY_TMPL] = {}
 
         # Add input params
+        '''
         if len(self.inputs):
             if self.INPUTS not in tosca[self.TOPOLOGY_TMPL]:
                 tosca[self.TOPOLOGY_TMPL][self.INPUTS] = {}
@@ -309,15 +574,50 @@ class YangNsd(ToscaResource):
                                           self.DESC:
                                           'Translated from YANG'}}
                 tosca[self.TOPOLOGY_TMPL][self.INPUTS] = entry
-
+        '''
         tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL] = {}
 
         # Add the VNFDs and VLDs
         for idx, vnfd in self.vnfds.items():
-            vnfd.generate_vnf_template(tosca, idx)
+            #vnfd.generate_vnf_template(tosca, idx)
+            node = {
+              'type' : vnfd.vnf_type,
+              self.PROPERTIES : {
+                self.ID : idx,
+                self.VENDOR : self.props[self.VENDOR],
+                self.VERSION : self.props[self.VERSION]
+              }
+            }
+            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])
 
-        for vld in self.vlds:
-            vld.generate_tosca_template(tosca)
+            for sub_mapping in self.substitution_mapping_forwarder:
+                if sub_mapping[0] == vnfd.name:
+                    vnfd.generate_forwarder_sub_mapping(sub_mapping)
+
+            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
+
+        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):
@@ -337,8 +637,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
@@ -361,33 +661,45 @@ class YangNsd(ToscaResource):
                 tosca[self.TOPOLOGY_TMPL][self.POLICIES] = []
 
             for icp in self.initial_cfg:
-                icpt = {
-                    self.TYPE: self.T_INITIAL_CFG,
-                }
-                icpt.update(icp)
-                tosca[self.TOPOLOGY_TMPL][self.POLICIES].append({
-                    self.INITIAL_CFG: icpt
-                })
+                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_INITIAL_CFG,
+                        self.TARGETS : "[{0}]".format(node_name)
+                    }
+                    icpt.update(icp)
+                    tosca[self.TOPOLOGY_TMPL][self.POLICIES].append({
+                        self.INITIAL_CFG: icpt
+                    })
+
+        if len(self.placement_groups) > 0:
+            if self.POLICIES not in tosca[self.TOPOLOGY_TMPL]:
+                tosca[self.TOPOLOGY_TMPL][self.POLICIES] = []
+
+            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):
         files = []
-
-        for vnfd in self.vnfds.values():
-            f = vnfd.get_supporting_files()
-            if f and len(f):
-                files.extend(f)
-
         # Get the config files for initial config
         for icp in self.initial_cfg:
-            if self.USER_DEF_SCRIPT in icp:
-                script = os.path.basename(icp[self.USER_DEF_SCRIPT])
-                files.append({
-                    self.TYPE: 'script',
-                    self.NAME: script,
-                    self.DEST: "{}/{}".format(self.SCRIPT_DIR, script),
-                })
+            if 'properties' in icp:
+                if 'user_defined_script' in icp['properties']:
+                    script = os.path.basename(icp['properties']['user_defined_script'])
+                    files.append({
+                        self.TYPE: 'script',
+                        self.NAME: script,
+                        self.DEST: "{}/{}".format(self.SCRIPT_DIR, script),
+                    })
 
         # TODO (pjoseph): Add support for config scripts,
         # charms, etc