Adapts PLA to new SOL006 NSD descriptors format
[osm/PLA.git] / osm_pla / placement / mznplacement.py
index cf6236a..bbb3ddd 100755 (executable)
@@ -18,7 +18,7 @@ import itertools
 
 import pymzn
 from jinja2 import Environment
-from jinja2.loaders import FileSystemLoader
+from jinja2.loaders import FileSystemLoader, PackageLoader, ChoiceLoader
 
 
 class MznPlacementConductor(object):
@@ -88,7 +88,8 @@ class MznModelGenerator(object):
     NsPlacementData objects. Uses jinja2 as templating language for the model
     '''
     default_j2_template = "osm_pla_dynamic_template.j2"
-    template_search_path = ['osm_pla/placement', '../placement', '/pla/osm_pla/placement']
+    template_search_path = ['osm_pla/placement', '../placement', '/pla/osm_pla/placement',
+                            './', '/usr/lib/python3/dist-packages/osm_pla/placement']
 
     def __init__(self, log):
         '''
@@ -111,7 +112,9 @@ class MznModelGenerator(object):
 
     def _load_jinja_template(self, template_name=default_j2_template):
         """loads the jinja template used for model generation"""
-        env = Environment(loader=FileSystemLoader(MznModelGenerator.template_search_path))
+        loader1 = FileSystemLoader(MznModelGenerator.template_search_path)
+        loader2 = PackageLoader('osm_pla', '.')
+        env = Environment(loader=ChoiceLoader([loader1, loader2]))
         return env.get_template(template_name)
 
 
@@ -151,35 +154,52 @@ class NsPlacementDataFactory(object):
         trp_link_characteristics = [[0 if col == row else 0x7fff for col in range(num_vims)] for row in range(num_vims)]
         for pil in self._pil_info['pil']:
             if characteristics in pil.keys():
-                url1 = pil['pil_endpoints'][0]
-                url2 = pil['pil_endpoints'][1]
+                ep1 = pil['pil_endpoints'][0]
+                ep2 = pil['pil_endpoints'][1]
                 # only consider links between applicable vims
-                if url1 in self._vim_accounts_info and url2 in self._vim_accounts_info:
-                    idx1 = self._vim_accounts_info[url1]['idx']
-                    idx2 = self._vim_accounts_info[url2]['idx']
+                if ep1 in self._vim_accounts_info and ep2 in self._vim_accounts_info:
+                    idx1 = self._vim_accounts_info[ep1]['idx']
+                    idx2 = self._vim_accounts_info[ep2]['idx']
                     trp_link_characteristics[idx1][idx2] = pil[characteristics]
                     trp_link_characteristics[idx2][idx1] = pil[characteristics]
 
         return trp_link_characteristics
 
+    # TODO: Review if we should adapt this method with SOL006 /nsd/vnfd/int-virtual-link-desc/df/qos fields.
     def _produce_vld_desc(self):
         """
         Creates the expected vlds from the nsd. Includes constraints if part of nsd.
         Overrides constraints with any syntactically correct instantiation parameters
         :return:
         """
+
+        all_vld_member_vnf_index_refs = {}
+        # TODO: Change for multiple DF support
+        ns_df = self._nsd.get('df', [{}])[0]
+        for vnf_profile in ns_df.get('vnf-profile', []):
+            for vlc in vnf_profile.get('virtual-link-connectivity', []):
+                vld_id = vlc.get('virtual-link-profile-id')
+                vld_member_vnf_index_ref = vnf_profile.get('id')
+                if vld_id in all_vld_member_vnf_index_refs:
+                    all_vld_member_vnf_index_refs[vld_id].append(vld_member_vnf_index_ref)
+                else:
+                    all_vld_member_vnf_index_refs[vld_id] = [vld_member_vnf_index_ref]
+
         vld_desc = []
-        for vld in self._nsd['vld']:
-            if vld['mgmt-network'] is False:
-                vld_desc_entry = {}
-                cp_refs = [ep_ref['member-vnf-index-ref'] for ep_ref in vld['vnfd-connection-point-ref']]
+        for vld in self._nsd.get('virtual-link-desc', ()):
+            if vld.get('mgmt-network', False) is True:
+                continue
+            vld_desc_entry = {}
+            cp_refs = all_vld_member_vnf_index_refs[vld.get('id')]
+            if len(cp_refs) == 2:
                 vld_desc_entry['cp_refs'] = cp_refs
-                if 'link-constraint' in vld.keys():
-                    for constraint in vld['link-constraint']:
-                        if constraint['constraint-type'] == 'LATENCY':
-                            vld_desc_entry['latency'] = constraint['value']
-                        elif constraint['constraint-type'] == 'JITTER':
-                            vld_desc_entry['jitter'] = constraint['value']
+                # TODO: Change for multiple DF support
+                vld_df = vld.get('df', [{}])[0]
+                for constraint in vld_df.get('qos', {}):
+                    if constraint == 'latency':
+                        vld_desc_entry['latency'] = vld_df['qos'][constraint]
+                    elif constraint == 'packet-delay-variation':
+                        vld_desc_entry['jitter'] = vld_df['qos'][constraint]
                 vld_desc.append(vld_desc_entry)
 
         # create candidates from instantiate params
@@ -187,10 +207,10 @@ class NsPlacementDataFactory(object):
             candidate_vld_desc = []
             # use id to find the endpoints in the nsd
             for entry in self._order_constraints.get('vld-constraints'):
-                for vld in self._nsd['vld']:
-                    if entry['id'] == vld['id']:
+                for vld in self._nsd.get('virtual-link-desc', ()):
+                    if entry['id'] == vld.get('id'):
                         vld_desc_instantiate_entry = {}
-                        cp_refs = [ep_ref['member-vnf-index-ref'] for ep_ref in vld['vnfd-connection-point-ref']]
+                        cp_refs = all_vld_member_vnf_index_refs[vld.get('id')]
                         vld_desc_instantiate_entry['cp_refs'] = cp_refs
                         # add whatever constraints that are provided to the vld_desc_entry
                         # misspelled 'link-constraints' => empty dict
@@ -200,7 +220,7 @@ class NsPlacementDataFactory(object):
                                 vld_desc_instantiate_entry['latency'] = value
                             elif constraint == 'jitter':
                                 vld_desc_instantiate_entry['jitter'] = value
-                        if set(['latency', 'jitter']).intersection(vld_desc_instantiate_entry.keys()):
+                        if {'latency', 'jitter'}.intersection(vld_desc_instantiate_entry.keys()):
                             candidate_vld_desc.append(vld_desc_instantiate_entry)
             # merge with nsd originated, FIXME log any deviations?
             for vld_d in vld_desc:
@@ -219,10 +239,12 @@ class NsPlacementDataFactory(object):
         for the vim_accounts that are applicable, collect the vnf_price
         """
         ns_desc = []
-        for vnfd in self._nsd['constituent-vnfd']:
-            vnf_info = {'vnf_id': vnfd['member-vnf-index']}
+        # TODO: Change for multiple DF support
+        ns_df = self._nsd.get('df', [{}])[0]
+        for vnf_profile in ns_df.get('vnf-profile', []):
+            vnf_info = {'vnf_id': vnf_profile['id']}
             # prices
-            prices_for_vnfd = self._vnf_prices[vnfd['vnfd-id-ref']]
+            prices_for_vnfd = self._vnf_prices[vnf_profile['vnfd-id']]
             # the list of prices must be ordered according to the indexing of the vim_accounts
             price_list = [_ for _ in range(len(self._vim_accounts_info))]
             for k in prices_for_vnfd.keys():
@@ -233,7 +255,7 @@ class NsPlacementDataFactory(object):
             # pinning to dc
             if self._pinning is not None:
                 for pinned_vnf in self._pinning:
-                    if vnfd['member-vnf-index'] == pinned_vnf['member-vnf-index']:
+                    if vnf_profile['id'] == pinned_vnf['member-vnf-index']:
                         vnf_info['vim_account'] = 'vim' + pinned_vnf['vimAccountId'].replace('-', '_')
 
             ns_desc.append(vnf_info)
@@ -242,8 +264,9 @@ class NsPlacementDataFactory(object):
     def create_ns_placement_data(self):
         """populate NsPlacmentData object
         """
-        ns_placement_data = {'vim_accounts': [vim_data['id'] for
-                                              vim_data in self._vim_accounts_info.values()],
+        ns_placement_data = {'vim_accounts': [vim_data['id'] for _, vim_data in sorted(self._vim_accounts_info.items(),
+                                                                                       key=lambda item: item[1][
+                                                                                           'idx'])],
                              'trp_link_latency': self._produce_trp_link_characteristics_data('pil_latency'),
                              'trp_link_jitter': self._produce_trp_link_characteristics_data('pil_jitter'),
                              'trp_link_price_list': self._produce_trp_link_characteristics_data('pil_price'),