Add support to process NST Selection for HAS 36/118036/10
authorMD IRSHAD SHEIKH <md.irshad.sheikh@huawei.com>
Thu, 18 Feb 2021 13:01:25 +0000 (18:31 +0530)
committerkrishna moorthy <krishna.moorthy6@wipro.com>
Sun, 21 Feb 2021 07:19:31 +0000 (07:19 +0000)
Issue-ID: OPTFRA-911

Signed-off-by: MD IRSHAD SHEIKH <md.irshad.sheikh@huawei.com>
Change-Id: I809857348b00fe415d416954b1cfbedd404bdffc

apps/nst/optimizers/nst_select_processor.py
config/common_config.yaml
osdf/adapters/conductor/translation.py
osdfapp.py
test/policy-local-files/nst-selection-files/attribute_policy_nst.json [new file with mode: 0644]
test/policy-local-files/nst-selection-files/optimization_policy_nst.json [new file with mode: 0644]
test/policy-local-files/nst-selection-files/query_policy_nst.json [new file with mode: 0644]
test/policy-local-files/nst-selection-files/vnf_policy_nst.json [new file with mode: 0644]

index faab999..5b31f73 100644 (file)
 """
 This application generates NST SELECTION API calls using the information received from SO
 """
-import json
 import os
+from osdf.adapters.conductor import conductor
+from osdf.adapters.policy.interface import get_policies
+from osdf.logging.osdf_logging import debug_log
 from osdf.logging.osdf_logging import error_log
 from osdf.utils.interfaces import get_rest_client
 from requests import RequestException
@@ -38,6 +40,7 @@ class NstSelection(Thread):
         self.osdf_config = osdf_config
         self.request_json = request_json
         self.request_info = self.request_json['requestInfo']
+        self.request_info['numSolutions'] = 1
 
     def run(self):
         self.process_nst_selection()
@@ -52,7 +55,6 @@ class NstSelection(Thread):
         try:
             rest_client = get_rest_client(self.request_json, service='so')
             solution = self.get_nst_solution()
-            solution = self.get_nst_selection_response(solution)
         except Exception as err:
             error_log.error("Error for {} {}".format(self.request_info.get('requestId'),
                                                      traceback.format_exc()))
@@ -70,32 +72,12 @@ class NstSelection(Thread):
 
            integrated there...
         """
-
-        config_input_json = os.path.join(BASE_DIR, 'conf/configIinputs.json')
-        with open(config_input_json, 'r') as openfile:
-            service_profile = self.request_json["serviceProfile"]
-            nst_solution_list = []
-            resource_name = "NST"
-            nst_object = json.load(openfile)
-            for nst in nst_object[resource_name]:
-                [(nst_name, nst_list)] = nst.items()
-                individual_nst = dict()
-                matchall = False
-                for constraint_name in service_profile:
-                    constraint_value = nst_list.get(constraint_name)
-                    if not constraint_value:
-                        matchall = False
-                        break
-                    else:
-                        matchall = True
-                if matchall:
-                    individual_nst["NSTName"] = nst_list.get("name")
-                    individual_nst["UUID"] = nst_list.get("modeluuid")
-                    individual_nst["invariantUUID"] = nst_list.get("modelinvariantuuid")
-                    individual_nst["individual_nst"] = 1
-                    nst_solution_list.append(individual_nst)
-
-        return nst_solution_list
+        req_info = self.request_json['requestInfo']
+        requirements = self.request_json['serviceProfile']
+        model_name = "nst"
+        policies = self.get_app_policies(model_name, "nst_selection")
+        conductor_response = self.get_conductor(req_info, requirements, policies, model_name)
+        return conductor_response
 
     def get_nst_selection_response(self, solutions):
         """Get NST selection response from final solution
@@ -119,3 +101,55 @@ class NstSelection(Thread):
                 'transactionId': self.request_info['transactionId'],
                 'requestStatus': 'error',
                 'statusMessage': error_message}
+
+    def get_app_policies(self, model_name, app_name):
+        policy_request_json = self.request_json.copy()
+        policy_request_json['serviceInfo'] = {'serviceName': model_name}
+        debug_log.debug("policy_request_json {}".format(str(policy_request_json)))
+        return get_policies(policy_request_json, app_name)  # app_name: nst_selection
+
+    def get_conductor(self, req_info, request_parameters, policies, model_name):
+        demands = [
+            {
+                "resourceModuleName": model_name,
+                "resourceModelInfo": {}
+            }
+        ]
+
+        try:
+            template_fields = {
+                'location_enabled': False,
+                'version': '2020-08-13'
+            }
+            resp = conductor.request(req_info, demands, request_parameters, {}, template_fields,
+                                     self.osdf_config, policies)
+        except RequestException as e:
+            resp = e.response.json()
+            error = resp['plans'][0]['message']
+            if "Unable to find any" in error:
+                return self.get_nst_selection_response([])
+            error_log.error('Error from conductor {}'.format(error))
+            return self.error_response(error)
+        debug_log.debug("Response from conductor in get_conductor method {}".format(str(resp)))
+        recommendations = resp["plans"][0].get("recommendations")
+        return self.process_response(recommendations, model_name)
+
+    def process_response(self, recommendations, model_name):
+        """Process conductor response to form the response for the API request
+
+            :param recommendations: recommendations from conductor
+            :return: response json as a dictionary
+        """
+        if not recommendations:
+            return self.get_nst_selection_response([])
+        solutions = [self.get_solution_from_candidate(rec[model_name]['candidate'])
+                     for rec in recommendations]
+        return self.get_nst_selection_response(solutions)
+
+    def get_solution_from_candidate(self, candidate):
+        if candidate['inventory_type'] == 'nst':
+            return {
+                'UUID': candidate['model_version_id'],
+                'invariantUUID': candidate['model_invariant_id'],
+                'NSTName': candidate['name'],
+            }
index d720fb2..d4d205e 100644 (file)
@@ -12,6 +12,7 @@ osdf_temp:  # special configuration required for "workarounds" or testing
         global_disabled: True
         local_placement_policies_enabled: True
         local_slice_selection_policies_enabled: True
+        local_nst_selection_policies_enabled: True
         placement_policy_dir_vcpe: "./test/policy-local-files/"
         placement_policy_files_vcpe: # workaroud for policy platform glitches (or "work-arounds" for other components)
             - Affinity_vCPE_1.json
@@ -45,6 +46,17 @@ osdf_temp:  # special configuration required for "workarounds" or testing
             - query_policy_nsi.json
             - threshold_policy_nsi.json
             - vnf_policy_nsi_shared_case.json
+        nst_selection_policy_dir_embb-nst: "./test/policy-local-files/nst-selection-files/"
+        nst_selection_policy_files_embb-nst:
+            - query_policy_nst.json
+            - attribute_policy_nst.json
+            - vnf_policy_nst.json
+        nst_selection_policy_dir_nst: "./test/policy-local-files/nst-selection-files/"
+        nst_selection_policy_files_nst:
+            - query_policy_nst.json
+            - attribute_policy_nst.json
+            - vnf_policy_nst.json
+            - optimization_policy_nst.json
 
 service_info:
     vCPE:
@@ -89,6 +101,17 @@ policy_info:
                 resources:
                     - get_param: service_name
 
+    nst_selection:
+        policy_fetch: by_scope
+        policy_scope:
+            -
+                scope:
+                    - OSDF_GUILIN
+                services:
+                    - nst
+                resources:
+                    - nst
+
     subnet_selection:
         policy_fetch: by_scope
         policy_scope:
index 238428a..f44f27f 100644 (file)
@@ -320,7 +320,6 @@ def get_demand_properties(demand, policies):
         prop['filtering_attributes'].update({'equipment-role': policy_property['equipmentRole']}
                                             if 'equipmentRole' in policy_property and policy_property['equipmentRole']
                                             else {})
-
         prop.update(get_candidates_demands(demand))
         demand_properties.append(prop)
     return demand_properties
index df85343..28f9376 100755 (executable)
@@ -126,7 +126,9 @@ def do_mdons_route_calc():
 def do_nst_selection():
     request_json = request.get_json()
     req_id = request_json['requestInfo']['requestId']
+    audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json)))
     NSTSelectionAPI(request_json).validate()
+    audit_log.info(MH.new_worker_thread(req_id, "[for NST selection]"))
     nst_selection = NstSelection(osdf_config, request_json)
     nst_selection.start()
     return req_accept(request_id=req_id,
diff --git a/test/policy-local-files/nst-selection-files/attribute_policy_nst.json b/test/policy-local-files/nst-selection-files/attribute_policy_nst.json
new file mode 100644 (file)
index 0000000..0989927
--- /dev/null
@@ -0,0 +1,42 @@
+{
+    "Threshold_nst": {
+        "metadata": {
+            "policy-id": "Threshold_nst",
+            "policy-version": 1
+        },
+        "properties": {
+            "geography": [],
+            "identity": "nst_Threshold",
+            "resources": [
+                "nst"
+            ],
+            "scope": [
+                "OSDF_GUILIN"
+            ],
+            "services": [
+                "nst"
+            ],
+            "thresholdProperties": [
+                {
+                    "attribute": "latency",
+                    "operator": "lte",
+                    "threshold": {
+                        "get_param": "latency"
+                    },
+                    "unit": "ms"
+                },
+                {
+                    "attribute": "reliability",
+                    "operator": "gte",
+                    "threshold": {
+                        "get_param": "reliability"
+                    },
+                    "unit": ""
+                }
+            ]
+        },
+        "type": "onap.policies.optimization.resource.ThresholdPolicy",
+        "type_version": "1.0.0",
+        "version": "1.0.0"
+    }
+}
\ No newline at end of file
diff --git a/test/policy-local-files/nst-selection-files/optimization_policy_nst.json b/test/policy-local-files/nst-selection-files/optimization_policy_nst.json
new file mode 100644 (file)
index 0000000..27c1f7c
--- /dev/null
@@ -0,0 +1,37 @@
+{
+    "nst_minimize_latency": {
+        "metadata": {
+            "policy-id": "nst_minimize_latency",
+            "policy-version": 1
+        },
+        "properties": {
+            "geography": [],
+            "identity": "optimization",
+            "goal": "minimize",
+            "operation_function": {
+                "operator": "sum",
+                "operands": [
+                    {
+                        "function": "attribute",
+                        "params": {
+                            "attribute": "latency",
+                            "demand": "nst"
+                        }
+                    }
+                ]
+            },
+            "resources": [
+                "nst"
+            ],
+            "scope": [
+                "OSDF_GUILIN"
+            ],
+            "services": [
+                "nst"
+            ]
+        },
+        "type": "onap.policies.optimization.resource.OptimizationPolicy",
+        "type_version": "2.0.0",
+        "version": "1.0.0"
+    }
+}
\ No newline at end of file
diff --git a/test/policy-local-files/nst-selection-files/query_policy_nst.json b/test/policy-local-files/nst-selection-files/query_policy_nst.json
new file mode 100644 (file)
index 0000000..1955e7b
--- /dev/null
@@ -0,0 +1,31 @@
+{
+    "queryPolicy_nst": {
+        "type": "onap.policies.optimization.service.QueryPolicy",
+        "version": "1.0.0",
+        "type_version": "1.0.0",
+        "metadata": {
+            "policy-id": "queryPolicy_nst",
+            "policy-version": 1
+        },
+        "properties": {
+            "scope": [
+                "OSDF_GUILIN"
+            ],
+            "services": [
+                "nst"
+            ],
+            "geography": [],
+            "identity": "queryPolicy_nst",
+            "queryProperties": [
+                {
+                    "attribute": "latency",
+                    "attribute_location": "latency"
+                },
+                {
+                    "attribute": "reliability",
+                    "attribute_location": "reliability"
+                }
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/policy-local-files/nst-selection-files/vnf_policy_nst.json b/test/policy-local-files/nst-selection-files/vnf_policy_nst.json
new file mode 100644 (file)
index 0000000..14906df
--- /dev/null
@@ -0,0 +1,34 @@
+{
+    "vnfPolicy_nst": {
+        "type": "onap.policies.optimization.resource.VnfPolicy",
+        "version": "1.0.0",
+        "type_version": "1.0.0",
+        "metadata": {
+            "policy-id": "vnfPolicy_nst",
+            "policy-version": 1
+        },
+        "properties": {
+            "scope": [
+                "OSDF_GUILIN"
+            ],
+            "resources": [
+                "nst"
+            ],
+            "services": [
+                "nst"
+            ],
+            "identity": "vnf_nst",
+            "applicableResources": "any",
+            "vnfProperties": [
+                {
+                    "inventoryProvider": "aai",
+                    "inventoryType": "nst",
+                    "unique": "true",
+                    "attributes": {
+                        "model-role": "nst"
+                    }
+                }
+            ]
+        }
+    }
+}
\ No newline at end of file