Add support to process NSI selection request 95/112195/9
authordhebeha <dhebeha.mj71@wipro.com>
Sat, 5 Sep 2020 14:46:48 +0000 (20:16 +0530)
committerVikas Varma <vikas.varma@att.com>
Fri, 18 Sep 2020 19:34:01 +0000 (19:34 +0000)
Issue-ID: OPTFRA-802
Signed-off-by: dhebeha <dhebeha.mj71@wipro.com>
Signed-off-by: krishnaa96 <krishna.moorthy6@wipro.com>
Change-Id: I85d951061abc697714425bd223b89102d4f2ede9

42 files changed:
apps/placement/optimizers/conductor/remote_opt_processor.py
apps/slice_selection/models/api/nsi_selection_request.py
apps/slice_selection/optimizers/conductor/remote_opt_processor.py
apps/slice_selection/optimizers/conductor/response_processor.py
config/common_config.yaml
config/has_config.yaml
config/slicing_config.yaml [new file with mode: 0644]
osdf/adapters/conductor/api_builder.py
osdf/adapters/conductor/conductor.py
osdf/adapters/conductor/templates/conductor_interface.json
osdf/adapters/conductor/translation.py
osdf/config/base.py
osdfapp.py
test/apps/slice_selection/new_solution_conductor_response.json
test/apps/slice_selection/new_solution_nsi_response.json
test/apps/slice_selection/no_recomm_nsi_response.json
test/apps/slice_selection/not_shared_nsi_request.json [deleted file]
test/apps/slice_selection/not_shared_nsi_response.json [deleted file]
test/apps/slice_selection/nsi_error_response.json
test/apps/slice_selection/nsi_selection_request.json
test/apps/slice_selection/nssi_conductor_response.json [new file with mode: 0644]
test/apps/slice_selection/nssi_error_response.json [new file with mode: 0644]
test/apps/slice_selection/nssi_selection_request.json
test/apps/slice_selection/shared_solution_conductor_response.json
test/apps/slice_selection/shared_solution_nsi_response.json
test/apps/slice_selection/shared_solution_nssi_response.json [new file with mode: 0644]
test/apps/slice_selection/slice_policies.txt
test/apps/slice_selection/subnet_policies.txt [new file with mode: 0644]
test/apps/slice_selection/test_remote_opt_processor.py
test/conductor/test_conductor_calls.py
test/conductor/test_conductor_translation.py
test/policy-local-files/slice-selection-files/opt_policy_nsi_reuse.json [new file with mode: 0644]
test/policy-local-files/slice-selection-files/opt_policy_nssi.json [new file with mode: 0644]
test/policy-local-files/slice-selection-files/query_policy_nsi.json [new file with mode: 0644]
test/policy-local-files/slice-selection-files/query_policy_nssi.json [new file with mode: 0644]
test/policy-local-files/slice-selection-files/threshold_policy_nsi.json [new file with mode: 0644]
test/policy-local-files/slice-selection-files/threshold_policy_nssi.json [new file with mode: 0644]
test/policy-local-files/slice-selection-files/vnf_policy_nsi_non_shared_case.json [new file with mode: 0644]
test/policy-local-files/slice-selection-files/vnf_policy_nsi_shared_case.json [new file with mode: 0644]
test/policy-local-files/slice-selection-files/vnf_policy_nssi_shared.json [new file with mode: 0644]
test/test_ConductorApiBuilder.py
tox.ini

index 3d7a287..2e681be 100644 (file)
 # -------------------------------------------------------------------------
 #
 
-import json
 from jinja2 import Template
+import json
 from requests import RequestException
-
 import traceback
-from osdf.operation.error_handling import build_json_error_body
-from osdf.logging.osdf_logging import metrics_log, MH, error_log, debug_log
-from osdf.adapters.conductor import conductor
+
 from apps.license.optimizers.simple_license_allocation import license_optim
+from osdf.adapters.conductor import conductor
+from osdf.logging.osdf_logging import debug_log
+from osdf.logging.osdf_logging import error_log
+from osdf.logging.osdf_logging import metrics_log
+from osdf.logging.osdf_logging import MH
+from osdf.operation.error_handling import build_json_error_body
 from osdf.utils.interfaces import get_rest_client
 from osdf.utils.mdc_utils import mdc_from_json
 
 
 def conductor_response_processor(conductor_response, req_id, transaction_id):
     """Build a response object to be sent to client's callback URL from Conductor's response
-    This includes Conductor's placement optimization response, and required ASDC license artifacts
 
+    This includes Conductor's placement optimization response, and required ASDC license artifacts
     :param conductor_response: JSON response from Conductor
     :param raw_response: Raw HTTP response corresponding to above
     :param req_id: Id of a request
@@ -60,13 +63,15 @@ def conductor_response_processor(conductor_response, req_id, transaction_id):
                     try:
                         solution['assignmentInfo'].append({"key": name_map.get(key, key), "value": value})
                     except KeyError:
-                        debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info".format(key))
+                        debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info"
+                                        .format(key))
 
             for key, value in reco[resource]['attributes'].items():
                 try:
                     solution['assignmentInfo'].append({"key": name_map.get(key, key), "value": value})
                 except KeyError:
-                    debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info".format(key))
+                    debug_log.debug("The key[{}] is not mapped and will not be returned in assignment info"
+                                    .format(key))
             composite_solutions.append(solution)
 
     request_status = "completed" if conductor_response['plans'][0]['status'] == "done" \
@@ -91,8 +96,8 @@ def conductor_response_processor(conductor_response, req_id, transaction_id):
 def conductor_no_solution_processor(conductor_response, request_id, transaction_id,
                                     template_placement_response="templates/plc_opt_response.jsont"):
     """Build a response object to be sent to client's callback URL from Conductor's response
-    This is for case where no solution is found
 
+    This is for case where no solution is found
     :param conductor_response: JSON response from Conductor
     :param raw_response: Raw HTTP response corresponding to above
     :param request_id: request Id associated with the client request (same as conductor response's "name")
@@ -108,6 +113,7 @@ def conductor_no_solution_processor(conductor_response, request_id, transaction_
 
 def process_placement_opt(request_json, policies, osdf_config):
     """Perform the work for placement optimization (e.g. call SDC artifact and make conductor request)
+
     NOTE: there is scope to make the requests to policy asynchronous to speed up overall performance
     :param request_json: json content from original request
     :param policies: flattened policies corresponding to this request
@@ -115,7 +121,7 @@ def process_placement_opt(request_json, policies, osdf_config):
     :param prov_status: provStatus retrieved from Subscriber policy
     :return: None, but make a POST to callback URL
     """
-    
+
     try:
         mdc_from_json(request_json)
         rc = get_rest_client(request_json, service="so")
@@ -134,7 +140,11 @@ def process_placement_opt(request_json, policies, osdf_config):
             demands = request_json['placementInfo']['placementDemands']
             request_parameters = request_json['placementInfo']['requestParameters']
             service_info = request_json['serviceInfo']
-            resp = conductor.request(req_info, demands, request_parameters, service_info, True,
+            template_fields = {
+                'location_enabled': True,
+                'version': '2017-10-10'
+            }
+            resp = conductor.request(req_info, demands, request_parameters, service_info, template_fields,
                                      osdf_config, policies)
             if resp["plans"][0].get("recommendations"):
                 placement_response = conductor_response_processor(resp, req_id, transaction_id)
@@ -162,8 +172,7 @@ def process_placement_opt(request_json, policies, osdf_config):
         return
 
     try:
-        metrics_log.info(MH.calling_back_with_body(req_id, rc.url,placement_response))
+        metrics_log.info(MH.calling_back_with_body(req_id, rc.url, placement_response))
         rc.request(json=placement_response, noresponse=True)
-    except RequestException :  # can't do much here but log it and move on
+    except RequestException:  # can't do much here but log it and move on
         error_log.error("Error sending asynchronous notification for {} {}".format(req_id, traceback.format_exc()))
-
index 943fa56..b395012 100644 (file)
 #
 
 from osdf.models.api.common import OSDFModel
-from schematics.types import BaseType, StringType, URLType, IntType, BooleanType
-from schematics.types.compound import ModelType, ListType, DictType
+from schematics.types import BaseType
+from schematics.types import BooleanType
+from schematics.types.compound import DictType
+from schematics.types.compound import ListType
+from schematics.types.compound import ModelType
+from schematics.types import IntType
+from schematics.types import StringType
+from schematics.types import URLType
 
 
 class RequestInfo(OSDFModel):
@@ -50,7 +56,7 @@ class NSISelectionAPI(OSDFModel):
     """Request for nsi selection (specific to optimization and additional metadata"""
     requestInfo = ModelType(RequestInfo, required=True)
     NSTInfo = ModelType(NxTInfo, required=True)
-    NSSTInfo = ListType(ModelType(NxTInfo), required=True)
+    NSSTInfo = ListType(ModelType(NxTInfo), required=False)
     serviceProfile = DictType(BaseType, required=True)
     subnetCapabilities = ListType(ModelType(SubnetCapability), required=True)
     preferReuse = BooleanType()
index 40638fc..c1c6980 100644 (file)
 Module for processing slice selection request
 """
 
-import json
-import traceback
 from requests import RequestException
+from threading import Thread
+import traceback
 
-from apps.slice_selection.optimizers.conductor.response_processor \
-    import conductor_response_processor, conductor_error_response_processor, solution_with_only_slice_profile, get_nsi_selection_response
+from apps.slice_selection.optimizers.conductor.response_processor import ResponseProcessor
 from osdf.adapters.conductor import conductor
 from osdf.adapters.policy.interface import get_policies
-from osdf.adapters.policy.utils import group_policies_gen
-from osdf.logging.osdf_logging import error_log, debug_log
+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 osdf.utils.mdc_utils import mdc_from_json
 
 
-def process_nsi_selection_opt(request_json, osdf_config):
-    """Process the nsi selection request from API layer
-        :param request_json: api request
-        :param policies: flattened policies corresponding to this request
-        :param osdf_config: configuration specific to OSDF app
-        :return: response as a dictionary
-        """
-    req_info = request_json['requestInfo']
-    try:
-        mdc_from_json(request_json)
-
-        overall_recommendations = dict()
-        nst_info_map = dict()
-        new_nsi_solutions = list()
-        for nst_info in request_json["NSTInfoList"]:
-            nst_name = nst_info["modelName"]
-            nst_info_map[nst_name] =  {"NSTName": nst_name,
-                                                    "UUID": nst_info["modelVersionId"],
-                                                    "invariantUUID": nst_info["modelInvariantId"]}
-
-            if request_json["serviceProfile"]["resourceSharingLevel"] == "non-shared":
-                new_nsi_solution = solution_with_only_slice_profile(request_json['serviceProfile'], nst_info_map.get(nst_name))
-                new_nsi_solutions.append(new_nsi_solution)
+class SliceSelectionOptimizer(Thread):
+    def __init__(self, osdf_config, slice_config, request_json, model_type):
+        self.osdf_config = osdf_config
+        self.slice_config = slice_config
+        self.request_json = request_json
+        self.model_type = model_type
+        self.response_processor = ResponseProcessor(request_json['requestInfo'], slice_config)
+
+    def run(self):
+        self.process_slice_selection_opt()
+
+    def process_slice_selection_opt(self):
+        """Process the slice selection request from the API layer"""
+        req_info = self.request_json['requestInfo']
+        rc = get_rest_client(self.request_json, service='so')
+
+        try:
+            if self.model_type == 'NSSI' \
+                    and self.request_json['sliceProfile'].get('resourceSharingLevel', "") == 'not-shared':
+                final_response = self.response_processor.get_slice_selection_response([])
+
             else:
-                policy_request_json = request_json.copy()
-                policy_request_json['serviceInfo']['serviceName'] = nst_name
-                policies = get_policies(policy_request_json, "slice_selection")
-
-                demands = get_slice_demands(nst_name, policies, osdf_config.core)
-
-                request_parameters = request_json.get('serviceProfile',{})
-                service_info = {}
-                req_info['numSolutions'] = 'all'
-                try:
-                    resp = conductor.request(req_info, demands, request_parameters, service_info, False,
-                                             osdf_config, policies)
-                except RequestException as e:
-                    resp = e.response.json()
-                    error = resp['plans'][0]['message']
-                    error_log.error('Error from conductor {}'.format(error))
-                debug_log.debug("Response from conductor {}".format(str(resp)))
-                overall_recommendations[nst_name] = resp["plans"][0].get("recommendations")
-
-        if request_json["serviceProfile"]["resourceSharingLevel"] == "non-shared":
-            solutions = dict()
-            solutions['newNSISolutions'] = new_nsi_solutions
-            solutions['sharedNSISolutions'] = []
-            return get_nsi_selection_response(req_info, solutions)
-        else:
-            return conductor_response_processor(overall_recommendations, nst_info_map, req_info, request_json["serviceProfile"])
-    except Exception as ex:
-        error_log.error("Error for {} {}".format(req_info.get('requestId'),
-                                                 traceback.format_exc()))
-        error_message = str(ex)
-        return conductor_error_response_processor(req_info, error_message)
-
-
-def get_slice_demands(model_name, policies, config):
-    """
-    :param model_name: model name of the slice
-    :param policies: flattened polcies corresponding to the request
-    :param config: configuration specific to OSDF app
-    :return: list of demands for the request
-    """
-    group_policies = group_policies_gen(policies, config)
-    subscriber_policy_list = group_policies["onap.policies.optimization.service.SubscriberPolicy"]
-    slice_demands = list()
-    for subscriber_policy in subscriber_policy_list:
-        policy_properties = subscriber_policy[list(subscriber_policy.keys())[0]]['properties']
-        if model_name in policy_properties["services"]:
-            for subnet in policy_properties["properties"]["subscriberName"]:
-                slice_demand = dict()
-                slice_demand["resourceModuleName"] = subnet
-                slice_demand['resourceModelInfo'] = {}
-                slice_demands.append(slice_demand)
-    return slice_demands
+                final_response = self.do_slice_selection()
+
+        except Exception as ex:
+            error_log.error("Error for {} {}".format(req_info.get('requestId'),
+                                                     traceback.format_exc()))
+            error_message = str(ex)
+            final_response = self.response_processor.process_error_response(error_message)
+
+        try:
+            rc.request(json=final_response, noresponse=True)
+        except RequestException:
+            error_log.error("Error sending asynchronous notification for {} {}".format(req_info['request_id'],
+                                                                                       traceback.format_exc()))
+
+    def do_slice_selection(self):
+        req_info = self.request_json['requestInfo']
+        app_info = self.slice_config['app_info'][self.model_type]
+        mdc_from_json(self.request_json)
+        requirements = self.request_json.get(app_info['requirements_field'], {})
+        model_info = self.request_json.get(app_info['model_info'])
+        model_name = model_info['name']
+        policies = self.get_app_policies(model_name, app_info['app_name'])
+        request_parameters = self.get_request_parameters(requirements)
+
+        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']
+            error_log.error('Error from conductor {}'.format(error))
+            return self.response_processor.process_error_response(error)
+
+        debug_log.debug("Response from conductor {}".format(str(resp)))
+        recommendations = resp["plans"][0].get("recommendations")
+        subnets = [subnet['domainType'] for subnet in self.request_json['subnetCapabilities']] \
+            if self.request_json.get('subnetCapabilities') else []
+        return self.response_processor.process_response(recommendations, model_info, subnets)
+
+    def get_request_parameters(self, requirements):
+        camel_to_snake = self.slice_config['attribute_mapping']['camel_to_snake']
+        request_params = {camel_to_snake[key]: value for key, value in requirements.items()}
+        subnet_capabilities = self.request_json.get('subnetCapabilities')
+        if subnet_capabilities:
+            for subnet_capability in subnet_capabilities:
+                domain_type = f"{subnet_capability['domainType'].lower().replace('-', '_')}_"
+                capability_details = subnet_capability['capabilityDetails']
+                for key, value in capability_details.items():
+                    request_params[f"{domain_type}{camel_to_snake[key]}"] = value
+        return request_params
+
+    def get_app_policies(self, model_name, app_name):
+        policy_request_json = self.request_json.copy()
+        policy_request_json['serviceInfo'] = {'serviceName': model_name}
+        if 'preferReuse' in self.request_json:
+            policy_request_json['preferReuse'] = "reuse" if self.request_json['preferReuse'] else "create_new"
+        return get_policies(policy_request_json, app_name)
index a9bdad0..71a350f 100644 (file)
 Module for processing response from conductor for slice selection
 """
 
-from osdf.logging.osdf_logging import debug_log
-
-
-SLICE_PROFILE_FIELDS = {"latency":"latency", "max_number_of_ues":"maxNumberOfUEs", "coverage_area_ta_list": "coverageAreaTAList",
-                        "ue_mobility_level":"uEMobilityLevel", "resource_sharing_level":"resourceSharingLevel", "exp_data_rate_ul": "expDataRateUL",
-                        "exp_data_rate_dl":"expDataRateDL", "area_traffic_cap_ul":"areaTrafficCapUL", "area_traffic_cap_dl": "areaTrafficCapDL",
-                        "activity_factor":"activityFactor", "e2e_latency":"e2eLatency", "jitter":"jitter", "survival_time": "survivalTime",
-                        "exp_data_rate":"expDataRate", "payload_size":"payloadSize", "traffic_density":"trafficDensity", "conn_density":"connDensity",
-                        "reliability":"reliability", "service_area_dimension":"serviceAreaDimension", "cs_availability": "csAvailability"}
-
-
-def conductor_response_processor(overall_recommendations, nst_info_map, request_info, service_profile):
-    """Process conductor response to form the response for the API request
-        :param overall_recommendations: recommendations from conductor
-        :param nst_info_map: NST info from the request
-        :param request_info: request info
-        :return: response json as a dictionary
-    """
-    shared_nsi_solutions = list()
-    new_nsi_solutions = list()
-    for nst_name, recommendations in overall_recommendations.items():
-        if  not (recommendations):
-            new_nsi_solution = solution_with_only_slice_profile(service_profile, nst_info_map.get(nst_name))
-            new_nsi_solutions.append(new_nsi_solution)
-            continue
-
-        for recommendation in recommendations:
-            nsi_set = set(values['candidate']['nsi_id'] for key, values in recommendation.items())
-            if len(nsi_set) == 1:
-                nsi_id = nsi_set.pop()
-                candidate = list(recommendation.values())[0]['candidate']
-                debug_log.debug("The NSSIs in the solution belongs to the same NSI {}"
-                                .format(nsi_id))
-                shared_nsi_solution = dict()
-                shared_nsi_solution["NSIId"] = nsi_id
-                shared_nsi_solution["NSIName"] = candidate.get('nsi_name')
-                shared_nsi_solution["UUID"] = candidate.get('nsi_model_version_id')
-                shared_nsi_solution["invariantUUID"] = candidate.get('nsi_model_invariant_id')
-
-                nssi_info_list = get_nssi_solutions(recommendation)
-                nssis = list()
-                for nssi_info in nssi_info_list:
-                    nssi = dict()
-                    nssi["NSSIId"] = nssi_info.get("NSSISolution").get("NSSIId")
-                    nssi["NSSIName"] = nssi_info.get("NSSISolution").get("NSSIName")
-                    nssi["UUID"] = ""
-                    nssi["invariantUUID"] = ""
-                    nssi_info.get("sliceProfile").update({"domainType":"cn"})
-                    nssi["sliceProfile"] = [nssi_info.get("sliceProfile")]
-                    nssis.append(nssi)
-
-                shared_nsi_solution["NSSIs"] = nssis
-                shared_nsi_solutions.append(shared_nsi_solution)
-            else:
-                nssi_solutions = get_nssi_solutions(recommendation)
-                new_nsi_solution = dict()
-                new_nsi_solution['matchLevel'] = ""
-                new_nsi_solution['NSTInfo'] = nst_info_map.get(nst_name)
-                new_nsi_solution['NSSISolutions'] = nssi_solutions
-                new_nsi_solutions.append(new_nsi_solution)
-
-    solutions = dict()
-    solutions['sharedNSISolutions'] = shared_nsi_solutions
-    solutions['newNSISolutions'] = new_nsi_solutions
-    return get_nsi_selection_response(request_info, solutions)
-
-
-def solution_with_only_slice_profile(service_profile, nst_info):
-    nssi_solutions = get_slice_profile_from_service_profile(service_profile)
-    new_nsi_solution = dict()
-    new_nsi_solution['matchLevel'] = ""
-    new_nsi_solution['NSTInfo'] = nst_info
-    new_nsi_solution['NSSISolutions'] = nssi_solutions
-    return new_nsi_solution
-
-def conductor_error_response_processor(request_info, error_message):
-    """Form response message from the error message
-        :param request_info: request info
-        :param error_message: error message while processing the request
-        :return: response json as dictionary
-    """
-    return {'requestId': request_info['requestId'],
-            'transactionId': request_info['transactionId'],
-            'requestStatus': 'error',
-            'statusMessage': error_message}
-
-
-def get_slice_profile_from_service_profile(service_profile):
-    nssi_solutions = list()
-    service_profile["domainType"] = "cn"
-    nssi_solution = {"sliceProfile": service_profile}
-    nssi_solutions.append(nssi_solution)
-    return nssi_solutions
-
-
-def get_nssi_solutions(recommendation):
-    """Get nssi solutions from recommendation
-        :param recommendation: recommendation from conductor
-        :return: new nssi solutions list
-    """
-    nssi_solutions = list()
-
-    for nsst_name, nsst_rec in recommendation.items():
-        candidate = nsst_rec['candidate']
-        nssi_info, slice_profile = get_solution_from_candidate(candidate)
-        nsst_info = {"NSSTName": nsst_name}
-        nssi_solution = {"sliceProfile": slice_profile,
-                         "NSSTInfo": nsst_info,
-                         "NSSISolution": nssi_info}
-        nssi_solutions.append(nssi_solution)
-    return nssi_solutions
-
-
-def get_solution_from_candidate(candidate):
-    """Get nssi info from candidate
-        :param candidate: Candidate from the recommendation
-        :return: nssi_info and slice profile derived from candidate
-    """
-    slice_profile = dict()
-    nssi_info = {"NSSIName": candidate['instance_name'],
-                 "NSSIId": candidate['candidate_id']}
-
-    for field in SLICE_PROFILE_FIELDS:
-        if candidate[field]:
-            slice_profile[SLICE_PROFILE_FIELDS[field]] = candidate[field]
-
-    return nssi_info, slice_profile
-
-
-def get_nsi_selection_response(request_info, solutions):
-    """Get NSI selection response from final solution
-        :param request_info: request info
-        :param solutions: final solutions
-        :return: NSI selection response to send back as dictionary
-    """
-    return {'requestId': request_info['requestId'],
-            'transactionId': request_info['transactionId'],
-            'requestStatus': 'completed',
-            'statusMessage': '',
-            'solutions': solutions}
-
+import re
+
+
+class ResponseProcessor(object):
+    def __init__(self, request_info, slice_config):
+        self.request_info = request_info
+        self.slice_config = slice_config
+
+    def process_response(self, recommendations, model_info, subnets):
+        """Process conductor response to form the response for the API request
+
+            :param recommendations: recommendations from conductor
+            :param model_info: model info from the request
+            :param subnets: list of subnets
+            :return: response json as a dictionary
+        """
+        if not recommendations:
+            return self.get_slice_selection_response([])
+        model_name = model_info['name']
+        solutions = [self.get_solution_from_candidate(rec[model_name]['candidate'], model_info, subnets)
+                     for rec in recommendations]
+        return self.get_slice_selection_response(solutions)
+
+    def get_solution_from_candidate(self, candidate, model_info, subnets):
+        if candidate['inventory_type'] == 'nssi':
+            return {
+                'UUID': model_info['UUID'],
+                'invariantUUID': model_info['invariantUUID'],
+                'NSSIName': candidate['instance_name'],
+                'NSSIId': candidate['instance_id']
+            }
+
+        elif candidate['inventory_type'] == 'nsi':
+            return {
+                'existingNSI': True,
+                'sharedNSISolution': {
+                    'UUID': model_info['UUID'],
+                    'invariantUUID': model_info['invariantUUID'],
+                    'NSIName': candidate['instance_name'],
+                    'NSIId': candidate['instance_id']
+                }
+            }
+
+        elif candidate['inventory_type'] == 'slice_profiles':
+            return {
+                'existingNSI': False,
+                'newNSISolution': {
+                    'slice_profiles': self.get_slice_profiles_from_candidate(candidate, subnets)
+                }
+            }
+
+    def get_slice_profiles_from_candidate(self, candidate, subnets):
+        slice_profiles = []
+        for subnet in subnets:
+            slice_profile = {self.get_profile_attribute(k, subnet): v for k, v in candidate.items()
+                             if k.startswith(subnet)}
+            slice_profile['domainType'] = subnet
+            slice_profiles.append(slice_profile)
+        return slice_profiles
+
+    def get_profile_attribute(self, attribute, subnet):
+        snake_to_camel = self.slice_config['attribute_mapping']['snake_to_camel']
+        return snake_to_camel[re.sub(f'^{subnet}_', '', attribute)]
+
+    def process_error_response(self, error_message):
+        """Form response message from the error message
+
+            :param error_message: error message while processing the request
+            :return: response json as dictionary
+        """
+        return {'requestId': self.request_info['requestId'],
+                'transactionId': self.request_info['transactionId'],
+                'requestStatus': 'error',
+                'statusMessage': error_message}
+
+    def get_slice_selection_response(self, solutions):
+        """Get NSI selection response from final solution
+
+            :param solutions: final solutions
+            :return: NSI selection response to send back as dictionary
+        """
+        return {'requestId': self.request_info['requestId'],
+                'transactionId': self.request_info['transactionId'],
+                'requestStatus': 'completed',
+                'statusMessage': '',
+                'solutions': solutions}
index f010e44..905aa0b 100644 (file)
@@ -40,13 +40,11 @@ osdf_temp:  # special configuration required for "workarounds" or testing
             - vnfPolicy_vPGN_TD.json
             - Affinity_vFW_TD.json
             - QueryPolicy_vFW_TD.json
-        slice_selection_policy_dir_urllc_1: "./test/policy-local-files/"
-        slice_selection_policy_files_urllc_1:
-            - vnfPolicy_URLLC_Core_1.json
-            - subscriber_policy_URLLC_1.json
-            - thresholdPolicy_URLLC_Core_1.json
-            - aggregationPolicy_URLLC_1.json
-            - queryPolicy_URLLC_.json
+        slice_selection_policy_dir_embb-nst: "./test/policy-local-files/slice-selection-files/"
+        slice_selection_policy_files_embb-nst:
+            - query_policy_nsi.json
+            - threshold_policy_nsi.json
+            - vnf_policy_nsi_shared_case.json
 
 service_info:
     vCPE:
@@ -66,6 +64,12 @@ references:
     subscriber_role:
         source: onap.policies.optimization.SubscriberPolicy
         value: properties.properties.subscriberRole
+    resource_sharing_level:
+        source: request
+        value: serviceProfile.resourceSharingLevel
+    reuse_preference:
+        source: request
+        value: preferReuse
 
 policy_info:
     prioritization_attributes:
@@ -82,10 +86,20 @@ policy_info:
         policy_scope:
             -
                 scope:
-                  - OSDF_FRANKFURT
+                  - OSDF_GUILIN
+                  - get_param: resource_sharing_level
+                  - get_param: reuse_preference
                 services:
                     - get_param: service_name
 
+    subnet_selection:
+        policy_fetch: by_scope
+        policy_scope:
+            - scope:
+                  - OSDF_GUILIN
+              services:
+                  - get_param: service_name
+
     placement:
         policy_fetch: by_scope
         policy_scope:
index 0b94391..2f4a1cd 100644 (file)
@@ -20,10 +20,11 @@ policy_config_mapping:
         orchestrationStatus: orchestration-status
         provStatus: prov-status
         cloudRegion: cloud-region
-    cloud_region_attributes: 
+    cloud_region_attributes:
         serviceRequests: service-requests
         cloudRequests: cloud-requests
     passthrough_attributes: {}
+    default_attributes: {}
     candidates:
         # for (k1, v1), if k1 is in demand, set prop[k2] = _get_candidates(demand[k1])
         excludedCandidates: excluded_candidates
diff --git a/config/slicing_config.yaml b/config/slicing_config.yaml
new file mode 100644 (file)
index 0000000..a7e3a48
--- /dev/null
@@ -0,0 +1,82 @@
+app_info:
+  NSI:
+    app_name: slice_selection
+    requirements_field: serviceProfile
+    model_info: NSTInfo
+  NSSI:
+    app_name: subnet_selection
+    requirements_field: sliceProfile
+    model_info: NSSTInfo
+
+attribute_mapping:
+  camel_to_snake:
+    maxBandwidth: max_bandwidth
+    jitter: jitter
+    sST: sst
+    latency: latency
+    resourceSharingLevel: resource_sharing_level
+    uEMobilityLevel: ue_mobility_level
+    maxNumberOfUEs: max_number_of_ues
+    dLThptPerUE: dl_thpt_per_ue
+    uLThptPerUE: ul_thpt_per_ue
+    sNSSAI: s_nssai
+    pLMNIdList: plmn_id_list
+    activityFactor: activity_factor
+    coverageAreaTAList: coverage_area_ta_List
+    availability: availability
+    cSAvailabilityTarget: cs_availability_target
+    reliability: reliability
+    cSReliabilityMeanTime: cs_reliability_mean_time
+    dLThptPerSlice: dl_thpt_per_slice
+    expDataRateDL: exp_data_rate_dl
+    uLThptPerSlice: ul_thpt_per_slice
+    expDataRateUL: exp_data_rate_ul
+    MaxPktSize: max_pkt_size
+    msgSizeByte: msg_size_byte
+    maxNumberOfConns: max_number_of_conns
+    maxNumberOfPDUSessions: max_number_of_pdu_sessions
+    terminalDensity: terminal_density
+    survivalTime: survival_time
+    areaTrafficCapDL: area_traffic_cap_dl
+    areaTrafficCapUL: area_traffic_cap_ul
+    overallUserDensity: overall_user_density
+    transferIntervalTarget: transfer_interval_target
+    expDataRate: exp_data_rate
+    security: security
+    maxThroughput: max_throughput
+
+  snake_to_camel:
+    max_bandwidth: maxBandwidth
+    jitter: jitter
+    sst: sST
+    latency: latency
+    resource_sharing_level: resourceSharingLevel
+    ue_mobility_level: uEMobilityLevel
+    max_number_of_ues: maxNumberOfUEs
+    dl_thpt_per_ue: dLThptPerUE
+    ul_thpt_per_ue: uLThptPerUE
+    s_nssai: sNSSAI
+    plmn_id_list: pLMNIdList
+    activity_factor: activityFactor
+    coverage_area_ta_List: coverageAreaTAList
+    availability: availability
+    cs_availability_target: cSAvailabilityTarget
+    reliability: reliability
+    cs_reliability_mean_time: cSReliabilityMeanTime
+    dl_thpt_per_slice: dLThptPerSlice
+    exp_data_rate_dl: expDataRateDL
+    ul_thpt_per_slice: uLThptPerSlice
+    exp_data_rate_ul: expDataRateUL
+    max_pkt_size: MaxPktSize
+    msg_size_byte: msgSizeByte
+    max_number_of_conns: maxNumberOfConns
+    max_number_of_pdu_sessions: maxNumberOfPDUSessions
+    terminal_density: terminalDensity
+    survival_time: survivalTime
+    area_traffic_cap_dl: areaTrafficCapDL
+    area_traffic_cap_ul: areaTrafficCapUL
+    overall_user_density: overallUserDensity
+    transfer_interval_target: transferIntervalTarget
+    exp_data_rate: expDataRate
+    security: security
+    max_throughput: maxThroughput
index daf8e54..f3b0798 100644 (file)
 from jinja2 import Template
 import json
 
-from osdf.adapters.policy.utils import group_policies_gen
 import osdf.adapters.conductor.translation as tr
+from osdf.adapters.policy.utils import group_policies_gen
 from osdf.utils.programming_utils import list_flatten
 
 
 def _build_parameters(group_policies, service_info, request_parameters):
-    """
-        Function prepares parameters section for has request
+    """Function prepares parameters section for has request
+
         :param group_policies: filtered policies
         :param service_info: service info
         :param request_parameters: request parameters
@@ -50,14 +50,15 @@ def _build_parameters(group_policies, service_info, request_parameters):
 
 
 def conductor_api_builder(req_info, demands, request_parameters, service_info,
-                          location_enabled, flat_policies: list, local_config,
+                          template_fields, flat_policies: list, local_config,
                           template="osdf/adapters/conductor/templates/conductor_interface.json"):
     """Build an OSDF southbound API call for HAS-Conductor/Placement optimization
+
         :param req_info: parameter data received from a client
         :param demands: list of demands
         :param request_parameters: request parameters
         :param service_info: service info object
-        :param location_enabled: boolean to check location to be sent in the request
+        :param template_fields: Fields that has to be passed to the template to render
         :param flat_policies: policy data received from the policy platform (flat policies)
         :param template: template to generate southbound API call to conductor
         :param local_config: local configuration file with pointers for
@@ -73,6 +74,7 @@ def conductor_api_builder(req_info, demands, request_parameters, service_info,
     demand_list = tr.gen_demands(demands, gp['onap.policies.optimization.resource.VnfPolicy'])
     attribute_policy_list = tr.gen_attribute_policy(
         demand_name_list, gp['onap.policies.optimization.resource.AttributePolicy'])
+
     distance_to_location_policy_list = tr.gen_distance_to_location_policy(
         demand_name_list, gp['onap.policies.optimization.resource.DistancePolicy'])
     inventory_policy_list = tr.gen_inventory_group_policy(
@@ -95,8 +97,8 @@ def conductor_api_builder(req_info, demands, request_parameters, service_info,
                                                     gp['onap.policies.optimization.resource.'
                                                        'ThresholdPolicy'])
     aggregation_policy_list = tr.gen_aggregation_policy(demand_name_list,
-                                                      gp['onap.policies.optimization.resource.'
-                                                         'AggregationPolicy'])
+                                                        gp['onap.policies.optimization.resource.'
+                                                           'AggregationPolicy'])
     req_params_dict = _build_parameters(gp, service_info, request_parameters)
     conductor_policies = [attribute_policy_list, distance_to_location_policy_list,
                           inventory_policy_list, resource_instance_policy_list,
@@ -114,7 +116,8 @@ def conductor_api_builder(req_info, demands, request_parameters, service_info,
         timeout=req_info['timeout'],
         limit=req_info['numSolutions'],
         request_params=req_params_dict,
-        location_enabled=location_enabled,
+        location_enabled=template_fields.get('location_enabled'),
+        version=template_fields.get('version'),
         json=json)
     json_payload = json.dumps(json.loads(rendered_req))  # need this because template's JSON is ugly!
     return json_payload
index 155d4d5..6749c2c 100644 (file)
 #
 
 import json
-
 from requests import RequestException
 import time
 
 from osdf.adapters.conductor.api_builder import conductor_api_builder
 from osdf.logging.osdf_logging import debug_log
-from osdf.utils.interfaces import RestClient
 from osdf.operation.exceptions import BusinessException
+from osdf.utils.interfaces import RestClient
 
 
-def request(req_info, demands, request_parameters, service_info, location_enabled,
+def request(req_info, demands, request_parameters, service_info, template_fields,
             osdf_config, flat_policies):
     config = osdf_config.deployment
     local_config = osdf_config.core
@@ -53,7 +52,7 @@ def request(req_info, demands, request_parameters, service_info, location_enable
     rc = RestClient(userid=uid, passwd=passwd, method="GET", log_func=debug_log.debug,
                     headers=headers)
     conductor_req_json_str = conductor_api_builder(req_info, demands, request_parameters,
-                                                   service_info, location_enabled, flat_policies,
+                                                   service_info, template_fields, flat_policies,
                                                    local_config)
     conductor_req_json = json.loads(conductor_req_json_str)
 
@@ -74,7 +73,7 @@ def request(req_info, demands, request_parameters, service_info, location_enable
 
             if resp["plans"][0].get("status") in ["done", "not found"]:
                 return resp
-            new_url = resp['plans'][0]['links'][0][0]['href']  # TODO: check why a list of lists
+            new_url = resp['plans'][0]['links'][0][0]['href']  # TODO(krishna): check why a list of lists
 
         if total_time >= max_timeout:
             raise BusinessException("Conductor could not provide a solution within {} seconds,"
@@ -95,6 +94,7 @@ def request(req_info, demands, request_parameters, service_info, location_enable
 
 def initial_request_to_conductor(rc, conductor_url, conductor_req_json):
     """First steps in the request-redirect chain in making a call to Conductor
+
     :param rc: REST client object for calling conductor
     :param conductor_url: conductor's base URL to submit a placement request
     :param conductor_req_json: request json object to send to Conductor
@@ -112,7 +112,7 @@ def initial_request_to_conductor(rc, conductor_url, conductor_req_json):
     debug_log.debug("Attempting to read the plan from "
                     "the conductor provided url {}".format(plan_url))
     raw_resp = rc.request(raw_response=True,
-                          url=plan_url)  # TODO: check why a list of lists for links
+                          url=plan_url)
     resp = raw_resp.json()
 
     if resp["plans"][0]["status"] in ["error"]:
index d4a9a0e..e8d47b3 100755 (executable)
@@ -4,7 +4,7 @@
   "timeout": {{ timeout }},
   "num_solution": "{{ limit }}",
   "template": {
-    "homing_template_version": "2017-10-10",
+    "homing_template_version": "{{ version }}",
     "parameters": {
       {% set comma=joiner(",") %}
       {% for key, value in request_params.items() %} {{ comma() }}
index 4b012f5..238428a 100644 (file)
@@ -19,8 +19,8 @@
 import copy
 import json
 import re
-
 import yaml
+
 from osdf.utils.programming_utils import dot_notation
 
 policy_config_mapping = yaml.safe_load(open('config/has_config.yaml')).get('policy_config_mapping')
@@ -41,8 +41,8 @@ CONSTRAINT_TYPE_MAP = {"onap.policies.optimization.resource.AttributePolicy": "a
 
 
 def get_opt_query_data(request_parameters, policies):
-    """
-        Fetch service and order specific details from the requestParameters field of a request.
+    """Fetch service and order specific details from the requestParameters field of a request.
+
         :param request_parameters: A list of request parameters
         :param policies: A set of policies
         :return: A dictionary with service and order-specific attributes.
@@ -60,10 +60,20 @@ def get_opt_query_data(request_parameters, policies):
 
 def gen_optimization_policy(vnf_list, optimization_policy):
     """Generate optimization policy details to pass to Conductor
+
     :param vnf_list: List of vnf's to used in placement request
     :param optimization_policy: optimization objective policy information provided in the incoming request
     :return: List of optimization objective policies in a format required by Conductor
     """
+    if len(optimization_policy) == 1:
+        policy = optimization_policy[0]
+        policy_content = policy[list(policy.keys())[0]]
+        if policy_content['type_version'] == '2.0.0':
+            properties = policy_content['properties']
+            objective = {'goal': properties['goal'],
+                         'operation_function': properties['operation_function']}
+            return [objective]
+
     optimization_policy_list = []
     for policy in optimization_policy:
         content = policy[list(policy.keys())[0]]['properties']
@@ -71,7 +81,7 @@ def gen_optimization_policy(vnf_list, optimization_policy):
         parameters = ["cloud_version", "hpa_score"]
 
         for attr in content['objectiveParameter']['parameterAttributes']:
-            parameter = attr['parameter'] if attr['parameter'] in parameters else attr['parameter']+"_between"
+            parameter = attr['parameter'] if attr['parameter'] in parameters else attr['parameter'] + "_between"
             default, vnfs = get_matching_vnfs(attr['resources'], vnf_list)
             for vnf in vnfs:
                 value = [vnf] if attr['parameter'] in parameters else [attr['customerLocationInfo'], vnf]
@@ -80,13 +90,14 @@ def gen_optimization_policy(vnf_list, optimization_policy):
                 })
 
         optimization_policy_list.append({
-                content['objective']: {content['objectiveParameter']['operator']: parameter_list }
+            content['objective']: {content['objectiveParameter']['operator']: parameter_list}
         })
     return optimization_policy_list
 
 
 def get_matching_vnfs(resources, vnf_list, match_type="intersection"):
     """Get a list of matching VNFs from the list of resources
+
     :param resources:
     :param vnf_list: List of vnfs to used in placement request
     :param match_type: "intersection" or "all" or "any" (any => send all_vnfs if there is any intersection)
@@ -106,6 +117,7 @@ def get_matching_vnfs(resources, vnf_list, match_type="intersection"):
 
 def gen_policy_instance(vnf_list, resource_policy, match_type="intersection", rtype=None):
     """Generate a list of policies
+
     :param vnf_list: List of vnf's to used in placement request
     :param resource_policy: policy for this specific resource
     :param match_type: How to match the vnf_names with the vnf_list (intersection or "any")
@@ -129,13 +141,14 @@ def gen_policy_instance(vnf_list, resource_policy, match_type="intersection", rt
             if default:
                 for d in demands:
                     resource_repeated = True \
-                        if {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']), 'demands': d}} \
-                           in resource_policy_list else False
+                        if {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']),
+                                                           'demands': d}} in resource_policy_list else False
                     if resource_repeated:
                         continue
                     else:
                         resource_policy_list.append(
-                            {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']), 'demands': d }})
+                            {pc['properties']['identity']: {'type': CONSTRAINT_TYPE_MAP.get(pc['type']),
+                                                            'demands': d}})
                         policy[list(policy.keys())[0]]['properties']['resources'] = d
                         related_policies.append(policy)
             # Need to override the default policies, here delete the outdated policy stored in the db
@@ -194,9 +207,9 @@ def gen_attribute_policy(vnf_list, attribute_policy):
         properties = p_main[list(p_main.keys())[0]]['properties']['attributeProperties']
         attribute_mapping = policy_config_mapping['filtering_attributes']  # wanted attributes and mapping
         p_new[p_main[list(p_main.keys())[0]]['properties']['identity']]['properties'] = {
-            'evaluate': dict((attribute_mapping[k], properties.get(k) 
-                              if k != "cloudRegion" else gen_cloud_region(properties)) 
-                              for k in attribute_mapping.keys()) 
+            'evaluate': dict((attribute_mapping[k], properties.get(k)
+                              if k != "cloudRegion" else gen_cloud_region(properties))
+                             for k in attribute_mapping.keys())
         }
     return cur_policies  # cur_policies gets updated in place...
 
@@ -271,7 +284,7 @@ def get_policy_properties(demand, policies):
         policy_demands = set([x.lower() for x in policy[list(policy.keys())[0]]['properties']['resources']])
         if policy_demands and demand['resourceModuleName'].lower() not in policy_demands:
             continue  # no match for this policy
-        elif policy_demands == set(): # Append resource name for default policy
+        elif policy_demands == set():  # Append resource name for default policy
             policy[list(policy.keys())[0]]['properties'].update(resources=list(demand.get('resourceModuleName')))
         for policy_property in policy[list(policy.keys())[0]]['properties']['vnfProperties']:
             yield policy_property
@@ -285,35 +298,44 @@ def get_demand_properties(demand, policies):
                     inventory_type=policy_property['inventoryType'],
                     service_type=demand.get('serviceResourceId', ''),
                     service_resource_id=demand.get('serviceResourceId', ''))
+        policy_property_mapping = {'filtering_attributes': 'attributes',
+                                   'passthrough_attributes': 'passthroughAttributes',
+                                   'default_attributes': 'defaultAttributes'}
 
         prop.update({'unique': policy_property['unique']} if 'unique' in policy_property and
                                                              policy_property['unique'] else {})
         prop['filtering_attributes'] = dict()
-        if policy_property.get('attributes'):
-            for attr_key, attr_val in policy_property['attributes'].items():
-                update_converted_attribute(attr_key, attr_val, prop, 'filtering_attributes')
-        if policy_property.get('passthroughAttributes'):
-            prop['passthrough_attributes'] = dict()
-            for attr_key, attr_val in policy_property['passthroughAttributes'].items():
-                update_converted_attribute(attr_key, attr_val, prop, 'passthrough_attributes')
+        for key, value in policy_property_mapping.items():
+            get_demand_attributes(prop, policy_property, key, value)
 
         prop['filtering_attributes'].update({'global-customer-id': policy_property['customerId']}
-                                            if 'customerId' in policy_property and policy_property['customerId'] else {})
+                                            if 'customerId' in policy_property and policy_property['customerId']
+                                            else {})
         prop['filtering_attributes'].update({'model-invariant-id': demand['resourceModelInfo']['modelInvariantId']}
-                                            if 'modelInvariantId' in demand['resourceModelInfo'] and demand['resourceModelInfo']['modelInvariantId'] else {})
+                                            if 'modelInvariantId' in demand['resourceModelInfo']
+                                               and demand['resourceModelInfo']['modelInvariantId'] else {})
         prop['filtering_attributes'].update({'model-version-id': demand['resourceModelInfo']['modelVersionId']}
-                                            if 'modelVersionId' in demand['resourceModelInfo'] and demand['resourceModelInfo']['modelVersionId'] else {})
+                                            if 'modelVersionId' in demand['resourceModelInfo']
+                                               and demand['resourceModelInfo']['modelVersionId'] else {})
         prop['filtering_attributes'].update({'equipment-role': policy_property['equipmentRole']}
-                                            if 'equipmentRole' in policy_property and policy_property['equipmentRole'] else {})
+                                            if 'equipmentRole' in policy_property and policy_property['equipmentRole']
+                                            else {})
 
         prop.update(get_candidates_demands(demand))
         demand_properties.append(prop)
     return demand_properties
 
 
+def get_demand_attributes(prop, policy_property, attribute_type, key):
+    if policy_property.get(key):
+        prop[attribute_type] = dict()
+        for attr_key, attr_val in policy_property[key].items():
+            update_converted_attribute(attr_key, attr_val, prop, attribute_type)
+
+
 def update_converted_attribute(attr_key, attr_val, properties, attribute_type):
-    """
-    Updates dictonary of attributes with one specified in the arguments.
+    """Updates dictonary of attributes with one specified in the arguments.
+
     Automatically translates key namr from camelCase to hyphens
     :param attribute_type: attribute section name
     :param attr_key: key of the attribute
@@ -333,6 +355,7 @@ def update_converted_attribute(attr_key, attr_val, properties, attribute_type):
 
 def gen_demands(demands, vnf_policies):
     """Generate list of demands based on request and VNF policies
+
     :param demands: A List of demands
     :param vnf_policies: Policies associated with demand resources
            (e.g. from grouped_policies['vnfPolicy'])
@@ -349,6 +372,6 @@ def gen_demands(demands, vnf_policies):
 def gen_cloud_region(property):
     prop = {"cloud_region_attributes": dict()}
     if 'cloudRegion' in property:
-        for k,v in property['cloudRegion'].items():
+        for k, v in property['cloudRegion'].items():
             update_converted_attribute(k, v, prop, 'cloud_region_attributes')
     return prop["cloud_region_attributes"]
index fbe9315..2393642 100644 (file)
@@ -1,5 +1,6 @@
 # -------------------------------------------------------------------------
 #   Copyright (c) 2015-2017 AT&T Intellectual Property
+#   Copyright (C) 2020 Wipro Limited.
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
@@ -27,6 +28,10 @@ config_spec = {
     "core": "config/common_config.yaml"
 }
 
+slicing_spec = "config/slicing_config.yaml"
+
+slice_config = config_loader.load_config_file(slicing_spec)
+
 osdf_config = DotDict(config_loader.all_configs(**config_spec))
 
 http_basic_auth_credentials = creds.load_credentials(osdf_config)
index 9234d78..8d40273 100755 (executable)
@@ -37,16 +37,18 @@ from apps.placement.optimizers.conductor.remote_opt_processor import process_pla
 from apps.route.optimizers.inter_domain_route_opt import InterDomainRouteOpt
 from apps.route.optimizers.simple_route_opt import RouteOpt
 from apps.slice_selection.models.api.nsi_selection_request import NSISelectionAPI
-from apps.slice_selection.optimizers.conductor.remote_opt_processor import process_nsi_selection_opt
+from apps.slice_selection.models.api.nssi_selection_request import NSSISelectionAPI
+from apps.slice_selection.optimizers.conductor.remote_opt_processor import SliceSelectionOptimizer
 from osdf.adapters.policy.interface import get_policies
 from osdf.adapters.policy.interface import upload_policy_models
 from osdf.config.base import osdf_config
+from osdf.config.base import slice_config
 from osdf.logging.osdf_logging import MH, audit_log
 from osdf.operation.responses import osdf_response_for_request_accept as req_accept
 from osdf.utils import api_data_utils
 from osdf.webapp.appcontroller import auth_basic
 from apps.nxi_termination.optimizers.remote_opt_processor import process_nxi_termination_opt
-from apps.nxi_termination.models.api.nxi_termination_request import  NxiTerminationApi
+from apps.nxi_termination.models.api.nxi_termination_request import NxiTerminationApi
 
 
 @app.route("/api/oof/v1/healthcheck", methods=["GET"])
@@ -128,6 +130,7 @@ def do_nst_selection():
     response = process_nst_selection(request_json, osdf_config)
     return response
 
+
 @app.route("/api/oof/v1/pci", methods=["POST"])
 @app.route("/api/oof/pci/v1", methods=["POST"])
 @auth_basic.login_required
@@ -163,8 +166,8 @@ def do_nsi_selection():
     audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json)))
     NSISelectionAPI(request_json).validate()
     audit_log.info(MH.new_worker_thread(req_id, "[for NSI selection]"))
-    t = Thread(target=process_nsi_selection_opt, args=(request_json, osdf_config))
-    t.start()
+    slice_opt = SliceSelectionOptimizer(osdf_config, slice_config, request_json, 'NSI')
+    slice_opt.start()
     return req_accept(request_id=req_id,
                       transaction_id=request_json['requestInfo']['transactionId'],
                       request_status="accepted", status_message="")
@@ -178,12 +181,13 @@ def do_nssi_selection():
     audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json)))
     NSSISelectionAPI(request_json).validate()
     audit_log.info(MH.new_worker_thread(req_id, "[for NSSI selection]"))
-    t = Thread(target=process_nsi_selection_opt, args=(request_json, osdf_config))
-    t.start()
+    slice_opt = SliceSelectionOptimizer(osdf_config, slice_config, request_json, 'NSSI')
+    slice_opt.start()
     return req_accept(request_id=req_id,
                       transaction_id=request_json['requestInfo']['transactionId'],
                       request_status="accepted", status_message="")
 
+
 @app.route("/api/oof/terminate/nxi/v1",methods=["POST"])
 def do_nxi_terminaton():
     request_json = request.get_json()
@@ -193,5 +197,6 @@ def do_nxi_terminaton():
     NxiTerminationApi(request_json).validate()
     return process_nxi_termination_opt(request_json,osdf_config)
 
+
 if __name__ == "__main__":
     run_app()
index fea3544..897aa2b 100644 (file)
          ],
          "recommendations":[
             {
-               "URLLC_Core_1":{
-                  "inventory_provider":"aai",
-                  "candidate":{
-                     "exp_data_rate":0,
-                     "conn_density":0,
-                     "coverage_area_ta_list":"[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
-                     "activity_factor":0,
-                     "cs_availability":null,
+               "embb-nst":{
+                  "inventory_provider":"generator",
+                  "candidate": {
                      "candidate_id":"1a636c4d-5e76-427e-bfd6-241a947224b0",
-                     "area_traffic_cap_dl":null,
                      "latency":20,
-                     "service_area_dimension":null,
-                     "domain":"cn",
-                     "e2e_latency":0,
-                     "area_traffic_cap_ul":null,
-                     "inventory_provider":"aai",
-                     "exp_data_rate_ul":100,
-                     "max_number_of_ues":0,
+                     "inventory_provider":"generator",
+                     "max_number_of_ues":100,
                      "ue_mobility_level":"stationary",
-                     "candidate_type":"nssi",
-                     "traffic_density":0,
-                     "payload_size":0,
-                     "exp_data_rate_dl":100,
-                     "jitter":0,
-                     "survival_time":0,
-                     "resource_sharing_level":"0",
-                     "inventory_type":"nssi",
-                     "reliability":null,
-                     "cost":1.0,
-                     "nsi_name":"nsi_test_0211",
-                     "nsi_id": "7ecdfb7a-bc38-4abd-9cb3-6677d71e892f",
-                     "nsi_model_version_id": "c3d8a690-f138-4554-89af-9349aeb9b19a",
-                     "nsi_model_invariant_id": "590b9fcf-6927-495e-a898-a1418dd4820c",
-                     "instance_name":"nssi_test_0211"
+                     "candidate_type":"slice_profiles",
+                     "resource_sharing_level":"shared",
+                     "inventory_type":"slice_profiles",
+                     "reliability": 99.99,
+                     "AN_latency": 10,
+                     "AN_ue_mobility_level": "stationary",
+                     "AN_max_number_of_ues": 100,
+                     "AN_reliability": 99.99,
+                     "AN_resource_sharing_level":"shared",
+                     "CN_latency": 5,
+                     "CN_reliability": 99.99,
+                     "CN_resource_sharing_level":"shared",
+                     "TN-BH_reliability": 99.99,
+                     "TN-BH_latency": 5,
+                     "TN-BH_resource_sharing_level":"shared",
+                     "cost":1.0
                   }
-               },
-              "URLLC_Ran_1":{
-                "inventory_provider":"aai",
-                  "candidate":{
-                     "exp_data_rate":0,
-                     "conn_density":0,
-                     "coverage_area_ta_list":"[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
-                     "activity_factor":0,
-                     "cs_availability":null,
-                     "candidate_id":"490c68b0-639c-11ea-bc55-0242ac130003",
-                     "area_traffic_cap_dl":null,
-                     "latency":15,
-                     "service_area_dimension":null,
-                     "domain":"cn",
-                     "e2e_latency":0,
-                     "area_traffic_cap_ul":null,
-                     "inventory_provider":"aai",
-                     "exp_data_rate_ul":100,
-                     "max_number_of_ues":0,
-                     "ue_mobility_level":"stationary",
-                     "candidate_type":"nssi",
-                     "traffic_density":0,
-                     "payload_size":0,
-                     "exp_data_rate_dl":100,
-                     "jitter":0,
-                     "survival_time":0,
-                     "resource_sharing_level":"0",
-                     "inventory_type":"nssi",
-                     "reliability":null,
-                     "cost":1.0,
-                     "nsi_name":"nsi_test_0212",
-                     "nsi_id": "0cf2caed-c0e2-4a9d-8590-6a871e86f178",
-                     "nsi_model_version_id": "a841af61-3c77-4310-ae63-61e1cbf7fe9b",
-                     "nsi_model_invariant_id": "4e5351fe-0877-4c2e-bf75-ccb149f34a77",
-                     "instance_name":"nssi_test_ran_0211"
-                  }
-              }
+               }
             }
          ]
       }
index 02023ce..2ecb543 100644 (file)
@@ -1,56 +1,35 @@
 {
-   "requestId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
-   "transactionId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
-   "requestStatus":"completed",
-   "statusMessage":"",
-   "solutions":{
-      "sharedNSISolutions":[
-
-      ],
-      "newNSISolutions":[
-         {
-            "matchLevel":"",
-            "NSTInfo":{"invariantUUID": "fda3c1e8-7653-4acd-80ef-f5755c1d3859",
-      "UUID": "a6906768-1cae-4e78-acd1-d753ac61f3e8",
-      "NSTName": "URLLC_1"
-                },
-            "NSSISolutions":[
+   "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+   "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+   "requestStatus": "completed",
+   "statusMessage": "",
+   "solutions": [
+      {
+         "existingNSI": false,
+         "newNSISolution": {
+            "slice_profiles": [
                {
-                  "sliceProfile":{
-                     "latency":20,
-                     "coverageAreaTAList":"[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
-                     "uEMobilityLevel":"stationary",
-                     "resourceSharingLevel":"0",
-                     "expDataRateUL":100,
-                     "expDataRateDL":100
-                  },
-                  "NSSTInfo":{
-                     "NSSTName":"URLLC_Core_1"
-                  },
-                  "NSSISolution":{
-                     "NSSIName":"nssi_test_0211",
-                     "NSSIId":"1a636c4d-5e76-427e-bfd6-241a947224b0"
-                  }
+                  "domainType": "AN",
+                  "resourceSharingLevel": "shared",
+                  "latency": 10,
+                  "reliability": 99.99,
+                  "uEMobilityLevel": "stationary",
+                  "maxNumberOfUEs": 100
                },
                {
-                  "sliceProfile":{
-                     "latency":15,
-                     "coverageAreaTAList":"[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
-                     "uEMobilityLevel":"stationary",
-                     "resourceSharingLevel":"0",
-                     "expDataRateUL":100,
-                     "expDataRateDL":100
-                  },
-                  "NSSTInfo":{
-                     "NSSTName":"URLLC_Ran_1"
-                  },
-                  "NSSISolution":{
-                     "NSSIName":"nssi_test_ran_0211",
-                     "NSSIId":"490c68b0-639c-11ea-bc55-0242ac130003"
-                  }
+                  "domainType": "CN",
+                  "resourceSharingLevel": "shared",
+                  "latency": 5,
+                  "reliability": 99.99
+               },
+               {
+                  "domainType": "TN-BH",
+                  "resourceSharingLevel": "shared",
+                  "latency": 5,
+                  "reliability": 99.99
                }
             ]
          }
-      ]
-   }
-}
+      }
+   ]
+}
\ No newline at end of file
index daf151d..e36a243 100644 (file)
@@ -3,35 +3,5 @@
    "transactionId":"d290f1ee-6c54-4b01-90e6-d701748f0851",\r
    "requestStatus":"completed",\r
    "statusMessage":"",\r
-   "solutions":{\r
-      "sharedNSISolutions":[\r
-\r
-      ],\r
-      "newNSISolutions":[\r
-         {\r
-            "matchLevel":"",\r
-            "NSTInfo":{"invariantUUID": "fda3c1e8-7653-4acd-80ef-f5755c1d3859",\r
-                       "UUID": "a6906768-1cae-4e78-acd1-d753ac61f3e8",\r
-                       "NSTName": "URLLC_1"\r
-                },\r
-\r
-            "NSSISolutions":[\r
-               {\r
-                  "sliceProfile": {\r
-                  "latency": 2,\r
-                  "security": "High",\r
-                  "reliability": 99.9999,\r
-                  "trafficDensity": 1,\r
-                  "connDensity": 100000,\r
-                  "expDataRate": 50,\r
-                  "jitter": 1,\r
-                  "survivalTime": 0,\r
-                  "domainType":"cn",\r
-                  "resourceSharingLevel":"shared"\r
-                }\r
-               }\r
-            ]\r
-         }\r
-      ]\r
-   }\r
+   "solutions": []\r
 }\r
diff --git a/test/apps/slice_selection/not_shared_nsi_request.json b/test/apps/slice_selection/not_shared_nsi_request.json
deleted file mode 100644 (file)
index 1e22f41..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-{\r
-  "serviceProfile": {\r
-    "latency": 2,\r
-    "security": "High",\r
-    "reliability": 99.9999,\r
-    "trafficDensity": 1,\r
-    "connDensity": 100000,\r
-    "expDataRate": 50,\r
-    "jitter": 1,\r
-    "survivalTime": 0,\r
-    "resourceSharingLevel": "non-shared"\r
-  },\r
-  "serviceInfo":{\r
-      "serviceInstanceId": "209fb01e-60ca-4325-b074-c5ad4e0499f8",\r
-      "serviceName": ""\r
-   },\r
-  "requestInfo": {\r
-    "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851",\r
-    "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",\r
-    "callbackUrl": "http://0.0.0.0:9000/osdfCallback/",\r
-    "sourceId": "SO",\r
-    "timeout": 5\r
-  },\r
-  "NSTInfoList": [\r
-    {\r
-      "modelInvariantId": "fda3c1e8-7653-4acd-80ef-f5755c1d3859",\r
-      "modelVersionId": "a6906768-1cae-4e78-acd1-d753ac61f3e8",\r
-      "modelName": "URLLC_1"\r
-    }\r
-  ]\r
-}\r
diff --git a/test/apps/slice_selection/not_shared_nsi_response.json b/test/apps/slice_selection/not_shared_nsi_response.json
deleted file mode 100644 (file)
index 873ad2e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-{\r
-   "requestId":"d290f1ee-6c54-4b01-90e6-d701748f0851",\r
-   "transactionId":"d290f1ee-6c54-4b01-90e6-d701748f0851",\r
-   "requestStatus":"completed",\r
-   "statusMessage":"",\r
-   "solutions":{\r
-      "sharedNSISolutions":[\r
-\r
-      ],\r
-      "newNSISolutions":[\r
-         {\r
-            "matchLevel":"",\r
-            "NSTInfo":{"invariantUUID": "fda3c1e8-7653-4acd-80ef-f5755c1d3859",\r
-                       "UUID": "a6906768-1cae-4e78-acd1-d753ac61f3e8",\r
-                       "NSTName": "URLLC_1"\r
-                },\r
-\r
-            "NSSISolutions":[\r
-               {\r
-                  "sliceProfile": {\r
-                  "latency": 2,\r
-                  "security": "High",\r
-                  "reliability": 99.9999,\r
-                  "trafficDensity": 1,\r
-                  "connDensity": 100000,\r
-                  "expDataRate": 50,\r
-                  "jitter": 1,\r
-                  "survivalTime": 0,\r
-                  "domainType": "cn",\r
-                  "resourceSharingLevel": "non-shared"\r
-                }\r
-               }\r
-            ]\r
-         }\r
-      ]\r
-   }\r
-}\r
index f022779..9dc5300 100644 (file)
@@ -1,37 +1,6 @@
 {
    "requestId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
    "transactionId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
-   "requestStatus":"completed",
-   "statusMessage":"",
-   "solutions":{
-      "sharedNSISolutions":[
-
-      ],
-      "newNSISolutions":[
-         {
-            "matchLevel":"",
-            "NSTInfo":{"invariantUUID": "fda3c1e8-7653-4acd-80ef-f5755c1d3859",
-                       "UUID": "a6906768-1cae-4e78-acd1-d753ac61f3e8",
-                       "NSTName": "URLLC_1"
-                },
-
-            "NSSISolutions":[
-               {
-                  "sliceProfile": {
-                  "latency": 2,
-                  "security": "High",
-                  "reliability": 99.9999,
-                  "trafficDensity": 1,
-                  "connDensity": 100000,
-                  "expDataRate": 50,
-                  "jitter": 1,
-                  "survivalTime": 0,
-                  "domainType":"cn",
-                  "resourceSharingLevel":"shared"
-                }
-               }
-            ]
-         }
-      ]
-   }
+   "requestStatus":"error",
+   "statusMessage":"Some error message"
 }
index 9c4fcda..acd23a2 100644 (file)
@@ -1,23 +1,22 @@
 {
-  "serviceProfile": {
-    "latency": 2,
-    "security": "High",
-    "reliability": 99.9999,
-    "trafficDensity": 1,
-    "connDensity": 100000,
-    "expDataRate": 50,
-    "jitter": 1,
-    "survivalTime": 0,
-    "resourceSharingLevel":"shared"
-  },
-  "requestInfo": {
-    "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
-    "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
-    "callbackUrl": "http://0.0.0.0:9000/osdfCallback/",
-    "sourceId": "SO",
-    "timeout": 5
-  },
-  "NSTInfo":{
+   "serviceProfile":{
+      "latency":5,
+      "security":"High",
+      "reliability":99.999,
+      "resourceSharingLevel":"shared"
+   },
+   "requestInfo":{
+      "transactionId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+      "requestId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+      "callbackUrl": "http://0.0.0.0:9000/osdfCallback/",
+      "callbackHeader":{
+         "blob":"content"
+      },
+      "sourceId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+      "timeout":5,
+      "numSolutions":1
+   },
+   "NSTInfo":{
       "UUID":"3fa85f64-5717-4562-b3fc-2c963f66afa1",
       "invariantUUID":"7ua85f64-5717-4562-b3fc-2c963f66afa6",
       "name":"embb-nst"
          "name":"embb-tn-bh"
       }
    ],
-   "preferReuse":true,
+   "preferReuse":false,
    "subnetCapabilities":[
       {
-         "domainType":"AN-NF",
+         "domainType":"AN",
          "capabilityDetails":{
-            "blob":"content"
+             "latency": "4",
+             "reliability": "99.9",
+             "maxNumberOfUEs": "10",
+             "maxThroughput": "50",
+             "terminalDensity": "60"
          }
       },
       {
          "domainType":"CN",
          "capabilityDetails":{
-            "blob":"content"
-         }
-      },
-      {
-         "domainType":"TN-FH",
-         "capabilityDetails":{
-            "blob":"content"
-         }
-      },
-      {
-         "domainType":"TN-MH",
-         "capabilityDetails":{
-            "blob":"content"
+             "latency": "3",
+             "reliability": "99.9",
+             "maxNumberOfUEs": "10",
+             "maxThroughput": "50",
+             "terminalDensity": "60"
          }
       },
       {
          "domainType":"TN-BH",
          "capabilityDetails":{
-            "blob":"content"
+             "latency": "2",
+             "reliability": "99.9",
+             "maxNumberOfUEs": "10",
+             "maxThroughput": "50",
+             "terminalDensity": "60"
          }
       }
    ]
diff --git a/test/apps/slice_selection/nssi_conductor_response.json b/test/apps/slice_selection/nssi_conductor_response.json
new file mode 100644 (file)
index 0000000..d2edcf5
--- /dev/null
@@ -0,0 +1,53 @@
+{
+   "plans":[
+      {
+         "status":"done",
+         "id":"plan_id",
+         "name":"Plan Name 1",
+         "links":[
+            [
+               {
+                  "href":"http://conductor:8091/v1/plans/plan_id",
+                  "rel":"self"
+               }
+            ]
+         ],
+         "recommendations":[
+            {
+               "embb-cn": {
+                  "inventory_provider": "aai",
+                  "candidate": {
+                     "exp_data_rate": 0,
+                     "conn_density": 0,
+                     "coverage_area_ta_list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+                     "activity_factor": 0,
+                     "cs_availability": null,
+                     "candidate_id": "1a636c4d-5e76-427e-bfd6-241a947224b0",
+                     "area_traffic_cap_dl": null,
+                     "latency": 20,
+                     "service_area_dimension": null,
+                     "e2e_latency": 0,
+                     "area_traffic_cap_ul": null,
+                     "inventory_provider": "aai",
+                     "exp_data_rate_ul": 100,
+                     "max_number_of_ues": 0,
+                     "ue_mobility_level": "stationary",
+                     "candidate_type": "nssi",
+                     "traffic_density": 0,
+                     "payload_size": 0,
+                     "exp_data_rate_dl": 100,
+                     "jitter": 0,
+                     "survival_time": 0,
+                     "resource_sharing_level": "shared",
+                     "inventory_type": "nssi",
+                     "reliability": null,
+                     "cost": 1.0,
+                     "instance_id": "e1041cdc-12da-4f36-b84e-68c380e9cd47",
+                     "instance_name": "nssi_test_0211"
+                  }
+               }
+            }
+         ]
+      }
+   ]
+}
diff --git a/test/apps/slice_selection/nssi_error_response.json b/test/apps/slice_selection/nssi_error_response.json
new file mode 100644 (file)
index 0000000..70e0596
--- /dev/null
@@ -0,0 +1,7 @@
+{
+   "requestId":"r450f1ee-6c54-4b01-90e6-d701748f0851",
+   "transactionId":"t670f1ee-6c54-4b01-90e6-d701748f0851",
+   "requestStatus":"completed",
+   "statusMessage":"",
+   "solutions": []
+}
index 1a49a8b..61ee563 100644 (file)
@@ -1,6 +1,9 @@
 {
   "sliceProfile": {
-    "blob": "content"
+    "latency":5,
+      "security":"High",
+      "reliability":99.999,
+      "resourceSharingLevel":"shared"
   },
   "requestInfo": {
     "transactionId": "t670f1ee-6c54-4b01-90e6-d701748f0851",
@@ -17,7 +20,7 @@
     }
   },
   "NSSTInfo": {
-    "UUID": "y7785f64-5717-4562-b3fc-2c963f66afa6",
+    "UUID": "a7785f64-5717-4562-b3fc-2c963f66afa6",
     "invariantUUID": "9fh85f64-5717-4562-b3fc-2c963f66afa6",
     "name": "embb-cn"
   }
index 13ea29c..a187fef 100644 (file)
          ],
          "recommendations":[
             {
-               "URLLC_Core_1":{
-                  "inventory_provider":"aai",
-                  "candidate":{
-                     "exp_data_rate":0,
-                     "conn_density":0,
-                     "coverage_area_ta_list":"[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
-                     "activity_factor":0,
-                     "cs_availability":null,
-                     "candidate_id":"1a636c4d-5e76-427e-bfd6-241a947224b0",
-                     "area_traffic_cap_dl":null,
-                     "latency":20,
-                     "service_area_dimension":null,
-                     "domain":"cn",
-                     "e2e_latency":0,
-                     "area_traffic_cap_ul":null,
-                     "inventory_provider":"aai",
-                     "exp_data_rate_ul":100,
-                     "max_number_of_ues":0,
-                     "ue_mobility_level":"stationary",
-                     "candidate_type":"nssi",
-                     "traffic_density":0,
-                     "payload_size":0,
-                     "exp_data_rate_dl":100,
-                     "jitter":0,
-                     "survival_time":0,
-                     "resource_sharing_level":"0",
-                     "inventory_type":"nssi",
-                     "reliability":null,
-                     "cost":1.0,
-                     "nsi_name":"nsi_test_0212",
-                     "nsi_id": "7ecdfb7a-bc38-4abd-9cb3-6677d71e892f",
-                     "nsi_model_version_id": "c3d8a690-f138-4554-89af-9349aeb9b19a",
-                     "nsi_model_invariant_id": "590b9fcf-6927-495e-a898-a1418dd4820c",
-                     "instance_name":"nssi_test_0211"
+               "embb-nst": {
+                  "inventory_provider": "aai",
+                  "candidate": {
+                     "exp_data_rate": 0,
+                     "conn_density": 0,
+                     "coverage_area_ta_list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+                     "activity_factor": 0,
+                     "cs_availability": null,
+                     "candidate_id": "1a636c4d-5e76-427e-bfd6-241a947224b0",
+                     "area_traffic_cap_dl": null,
+                     "latency": 20,
+                     "service_area_dimension": null,
+                     "e2e_latency": 0,
+                     "area_traffic_cap_ul": null,
+                     "inventory_provider": "aai",
+                     "exp_data_rate_ul": 100,
+                     "max_number_of_ues": 0,
+                     "ue_mobility_level": "stationary",
+                     "candidate_type": "nsi",
+                     "traffic_density": 0,
+                     "payload_size": 0,
+                     "exp_data_rate_dl": 100,
+                     "jitter": 0,
+                     "survival_time": 0,
+                     "resource_sharing_level": "shared",
+                     "inventory_type": "nsi",
+                     "reliability": null,
+                     "cost": 1.0,
+                     "instance_id": "f1041cdc-12da-4f36-b84e-68c380e9cd47",
+                     "instance_name": "nsi_test_0211"
                   }
-               },
-              "URLLC_Ran_1":{
-                "inventory_provider":"aai",
-                  "candidate":{
-                     "exp_data_rate":0,
-                     "conn_density":0,
-                     "coverage_area_ta_list":"[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
-                     "activity_factor":0,
-                     "cs_availability":null,
-                     "candidate_id":"490c68b0-639c-11ea-bc55-0242ac130003",
-                     "area_traffic_cap_dl":null,
-                     "latency":15,
-                     "service_area_dimension":null,
-                     "domain":"cn",
-                     "e2e_latency":0,
-                     "area_traffic_cap_ul":null,
-                     "inventory_provider":"aai",
-                     "exp_data_rate_ul":100,
-                     "max_number_of_ues":0,
-                     "ue_mobility_level":"stationary",
-                     "candidate_type":"nssi",
-                     "traffic_density":0,
-                     "payload_size":0,
-                     "exp_data_rate_dl":100,
-                     "jitter":0,
-                     "survival_time":0,
-                     "resource_sharing_level":"0",
-                     "inventory_type":"nssi",
-                     "reliability":null,
-                     "cost":1.0,
-                     "nsi_name":"nsi_test_0212",
-                     "nsi_id": "7ecdfb7a-bc38-4abd-9cb3-6677d71e892f",
-                     "nsi_model_version_id": "c3d8a690-f138-4554-89af-9349aeb9b19a",
-                     "nsi_model_invariant_id": "590b9fcf-6927-495e-a898-a1418dd4820c",
-                     "instance_name":"nssi_test_ran_0211"
-                  }
-              }
+               }
             }
          ]
       }
index 3e1ad9f..ff83dfc 100644 (file)
@@ -1,54 +1,17 @@
 {
     "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
     "requestStatus": "completed",
-    "solutions": {
-        "newNSISolutions": [],
-        "sharedNSISolutions": [
-            {
-                "NSIId": "7ecdfb7a-bc38-4abd-9cb3-6677d71e892f",
-                "NSIName": "nsi_test_0212",
-                "NSSIs": [
-                    {
-                        "NSSIId": "1a636c4d-5e76-427e-bfd6-241a947224b0",
-                        "NSSIName": "nssi_test_0211",
-                        "UUID": "",
-                        "invariantUUID": "",
-                        "sliceProfile": [
-                            {
-                                "coverageAreaTAList": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
-                                "expDataRateDL": 100,
-                                "expDataRateUL": 100,
-                                "latency": 20,
-                                "resourceSharingLevel": "0",
-                                "uEMobilityLevel": "stationary",
-                                "domainType" : "cn"
-                            }
-                        ]
-                    },
-                    {
-                        "NSSIId": "490c68b0-639c-11ea-bc55-0242ac130003",
-                        "NSSIName": "nssi_test_ran_0211",
-                        "UUID": "",
-                        "invariantUUID": "",
-                        "sliceProfile": [
-                            {
-                                "coverageAreaTAList": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
-                                "expDataRateDL": 100,
-                                "expDataRateUL": 100,
-                                "latency": 15,
-                                "resourceSharingLevel": "0",
-                                "uEMobilityLevel": "stationary",
-                                 "domainType" : "cn"
-
-                            }
-                        ]
-                    }
-                ],
-                "UUID": "c3d8a690-f138-4554-89af-9349aeb9b19a",
-                "invariantUUID": "590b9fcf-6927-495e-a898-a1418dd4820c"
+    "solutions": [
+        {
+            "existingNSI": true,
+            "sharedNSISolution": {
+                "UUID": "3fa85f64-5717-4562-b3fc-2c963f66afa1",
+                "invariantUUID": "7ua85f64-5717-4562-b3fc-2c963f66afa6",
+                "NSIName": "nsi_test_0211",
+                "NSIId": "f1041cdc-12da-4f36-b84e-68c380e9cd47"
             }
-        ]
-    },
+        }
+    ],
     "statusMessage": "",
     "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851"
 }
diff --git a/test/apps/slice_selection/shared_solution_nssi_response.json b/test/apps/slice_selection/shared_solution_nssi_response.json
new file mode 100644 (file)
index 0000000..f3468a4
--- /dev/null
@@ -0,0 +1,15 @@
+{
+    "requestId": "r450f1ee-6c54-4b01-90e6-d701748f0851",
+    "requestStatus": "completed",
+    "solutions": [
+        {
+                "UUID": "a7785f64-5717-4562-b3fc-2c963f66afa6",
+                "invariantUUID": "9fh85f64-5717-4562-b3fc-2c963f66afa6",
+                "NSSIName": "nssi_test_0211",
+                "NSSIId": "e1041cdc-12da-4f36-b84e-68c380e9cd47"
+
+        }
+    ],
+    "statusMessage": "",
+    "transactionId": "t670f1ee-6c54-4b01-90e6-d701748f0851"
+}
index 55c934b..7eb55de 100644 (file)
@@ -1,4 +1,5 @@
-subscriber_policy_URLLC_1.json
-thresholdPolicy_URLLC_Core_1.json
-vnfPolicy_URLLC_Core_1.json
-aggregationPolicy_URLLC_1.json
+query_policy_nsi.json
+threshold_policy_nsi.json
+vnf_policy_nsi_shared_case.json
+opt_policy_nsi_reuse.json
+
diff --git a/test/apps/slice_selection/subnet_policies.txt b/test/apps/slice_selection/subnet_policies.txt
new file mode 100644 (file)
index 0000000..649672d
--- /dev/null
@@ -0,0 +1,5 @@
+query_policy_nsi.json
+threshold_policy_nsi.json
+vnf_policy_nssi_shared.json
+opt_policy_nssi.json
+
index 5321880..7c2d191 100644 (file)
@@ -20,7 +20,7 @@ import json
 import unittest\r
 from requests import RequestException, Response\r
 \r
-from apps.slice_selection.optimizers.conductor.remote_opt_processor import process_nsi_selection_opt\r
+from apps.slice_selection.optimizers.conductor.remote_opt_processor import SliceSelectionOptimizer\r
 from osdf.adapters.local_data import local_policies\r
 from osdf.utils.interfaces import json_from_file, yaml_from_file\r
 from osdf.utils.programming_utils import DotDict\r
@@ -37,31 +37,33 @@ class TestRemoteOptProcessor(unittest.TestCase):
             "deployment": "config/osdf_config.yaml",\r
             "core": "config/common_config.yaml"\r
         }\r
+        slice_spec = "config/slicing_config.yaml"\r
+        self.slice_config = config_loader.load_config_file(slice_spec)\r
         self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec))\r
+        self.patcher_RestClient = patch(\r
+            'osdf.utils.interfaces.RestClient.request', return_value=MagicMock())\r
+        self.mock_rc = self.patcher_RestClient.start()\r
 \r
     def tearDown(self):\r
         patch.stopall()\r
 \r
     def test_process_nsi_selection_opt(self):\r
         main_dir = ""\r
-        request_file = main_dir + 'test/apps/slice_selection/nsi_request.json'\r
+        request_file = main_dir + 'test/apps/slice_selection/nsi_selection_request.json'\r
         not_shared_request_file = main_dir + 'test/apps/slice_selection/not_shared_nsi_request.json'\r
         #response files\r
         new_solution_response_file = main_dir + 'test/apps/slice_selection/new_solution_nsi_response.json'\r
         shared_solution_response_file = main_dir + 'test/apps/slice_selection/shared_solution_nsi_response.json'\r
         no_solution_response_file = main_dir + 'test/apps/slice_selection/no_recomm_nsi_response.json'\r
-        not_shared_response_file = main_dir + 'test/apps/slice_selection/not_shared_nsi_response.json'\r
         error_response_file = main_dir + 'test/apps/slice_selection/nsi_error_response.json'\r
 \r
-        not_shared_request_json = json_from_file(not_shared_request_file)\r
-        not_shared_response_json = json_from_file(not_shared_response_file)\r
         request_json = json_from_file(request_file)\r
         new_solution_response_json = json_from_file(new_solution_response_file)\r
         shared_solution_response_json = json_from_file(shared_solution_response_file)\r
         no_solution_response_json = json_from_file(no_solution_response_file)\r
         error_response_json = json_from_file(error_response_file)\r
 \r
-        policies_path = main_dir + 'test/policy-local-files'\r
+        policies_path = main_dir + 'test/policy-local-files/slice-selection-files'\r
         slice_policies_file = main_dir + 'test/apps/slice_selection/slice_policies.txt'\r
 \r
         valid_policies_files = local_policies.get_policy_names_from_file(slice_policies_file)\r
@@ -69,36 +71,41 @@ class TestRemoteOptProcessor(unittest.TestCase):
         self.patcher_get_policies = patch('osdf.adapters.policy.interface.remote_api',\r
                                           return_value=policies)\r
         self.Mock_get_policies = self.patcher_get_policies.start()\r
+\r
         # new solution\r
         new_solution_conductor_response_file = 'test/apps/slice_selection/new_solution_conductor_response.json'\r
         new_solution_conductor_response = json_from_file(new_solution_conductor_response_file)\r
         self.patcher_req = patch('osdf.adapters.conductor.conductor.request',\r
                                  return_value=new_solution_conductor_response)\r
         self.Mock_req = self.patcher_req.start()\r
-        self.assertEquals(new_solution_response_json, process_nsi_selection_opt(request_json, self.osdf_config))\r
+        slice_select_opt = SliceSelectionOptimizer(self.osdf_config, self.slice_config, request_json, 'NSI')\r
+        slice_select_opt.process_slice_selection_opt()\r
+        self.mock_rc.assert_called_with(json=new_solution_response_json, noresponse=True)\r
         self.patcher_req.stop()\r
+\r
         # shared solution\r
+        request_json['preferReuse'] = True\r
         shared_solution_conductor_response_file = 'test/apps/slice_selection/shared_solution_conductor_response.json'\r
         shared_solution_conductor_response = json_from_file(shared_solution_conductor_response_file)\r
         self.patcher_req = patch('osdf.adapters.conductor.conductor.request',\r
                                  return_value=shared_solution_conductor_response)\r
         self.Mock_req = self.patcher_req.start()\r
-        self.assertEquals(shared_solution_response_json,\r
-                          process_nsi_selection_opt(request_json, self.osdf_config))\r
+        slice_select_opt = SliceSelectionOptimizer(self.osdf_config, self.slice_config, request_json, 'NSI')\r
+        slice_select_opt.process_slice_selection_opt()\r
+        self.mock_rc.assert_called_with(json=shared_solution_response_json, noresponse=True)\r
         self.patcher_req.stop()\r
-        # not-shared solution\r
-        self.assertEquals(not_shared_response_json,\r
-                          process_nsi_selection_opt(not_shared_request_json, self.osdf_config))\r
+\r
         # no recommendation\r
         no_solution_conductor_response_file = 'test/apps/slice_selection/no_rec.json'\r
         no_solution_conductor_response = json_from_file(no_solution_conductor_response_file)\r
         self.patcher_req = patch('osdf.adapters.conductor.conductor.request',\r
                                  return_value=no_solution_conductor_response)\r
         self.Mock_req = self.patcher_req.start()\r
-        self.assertEquals(no_solution_response_json,\r
-                          process_nsi_selection_opt(request_json, self.osdf_config))\r
+        slice_select_opt.process_slice_selection_opt()\r
+        self.mock_rc.assert_called_with(json=no_solution_response_json, noresponse=True)\r
         self.patcher_req.stop()\r
 \r
+        # Exception\r
         conductor_error_response_file = 'test/apps/slice_selection/conductor_error_response.json'\r
         conductor_error_response = json_from_file(conductor_error_response_file)\r
 \r
@@ -107,17 +114,52 @@ class TestRemoteOptProcessor(unittest.TestCase):
         self.patcher_req = patch('osdf.adapters.conductor.conductor.request',\r
                                  side_effect=RequestException(response=response))\r
         self.Mock_req = self.patcher_req.start()\r
-        self.assertEquals(error_response_json, process_nsi_selection_opt(request_json, self.osdf_config))\r
+        slice_select_opt.process_slice_selection_opt()\r
+        self.mock_rc.assert_called_with(json=error_response_json, noresponse=True)\r
+        self.patcher_req.stop()\r
+\r
+        self.patcher_req = patch('osdf.adapters.conductor.conductor.request',\r
+                                 side_effect=Exception("Some error message"))\r
+        self.Mock_req = self.patcher_req.start()\r
+        slice_select_opt.process_slice_selection_opt()\r
+        self.mock_rc.assert_called_with(json=error_response_json, noresponse=True)\r
         self.patcher_req.stop()\r
 \r
+    def test_process_nssi_selection_opt(self):\r
+        main_dir = ""\r
+        request_file = main_dir + 'test/apps/slice_selection/nssi_selection_request.json'\r
+        # response files\r
+        shared_solution_response_file = main_dir + 'test/apps/slice_selection/shared_solution_nssi_response.json'\r
+        error_response_file = main_dir + 'test/apps/slice_selection/nssi_error_response.json'\r
+\r
+        request_json = json_from_file(request_file)\r
+        shared_solution_response_json = json_from_file(shared_solution_response_file)\r
+        error_response_json = json_from_file(error_response_file)\r
+\r
+        policies_path = main_dir + 'test/policy-local-files/slice-selection-files'\r
+        slice_policies_file = main_dir + 'test/apps/slice_selection/subnet_policies.txt'\r
+\r
+        valid_policies_files = local_policies.get_policy_names_from_file(slice_policies_file)\r
+        policies = [json_from_file(policies_path + '/' + name) for name in valid_policies_files]\r
+        self.patcher_get_policies = patch('osdf.adapters.policy.interface.remote_api',\r
+                                          return_value=policies)\r
+        self.Mock_get_policies = self.patcher_get_policies.start()\r
+\r
+        shared_solution_conductor_response_file = 'test/apps/slice_selection/nssi_conductor_response.json'\r
+        shared_solution_conductor_response = json_from_file(shared_solution_conductor_response_file)\r
         self.patcher_req = patch('osdf.adapters.conductor.conductor.request',\r
-                                 side_effect=Exception("test_exception"))\r
+                                 return_value=shared_solution_conductor_response)\r
         self.Mock_req = self.patcher_req.start()\r
-        self.assertEquals('test_exception',\r
-                          process_nsi_selection_opt(request_json, self.osdf_config).get('statusMessage'))\r
+        slice_select_opt = SliceSelectionOptimizer(self.osdf_config, self.slice_config, request_json, 'NSSI')\r
+        slice_select_opt.process_slice_selection_opt()\r
+        self.mock_rc.assert_called_with(json=shared_solution_response_json, noresponse=True)\r
         self.patcher_req.stop()\r
 \r
+        request_json['sliceProfile']['resourceSharingLevel'] = "not-shared"\r
+        slice_select_opt = SliceSelectionOptimizer(self.osdf_config, self.slice_config, request_json, 'NSSI')\r
+        slice_select_opt.process_slice_selection_opt()\r
+        self.mock_rc.assert_called_with(json=error_response_json, noresponse=True)\r
+\r
 \r
 if __name__ == "__main__":\r
     unittest.main()\r
-\r
index d342fa5..8b4411d 100644 (file)
@@ -35,6 +35,10 @@ class TestConductorCalls(unittest.TestCase):
         self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec))
         self.lp = self.osdf_config.core.get('osdf_temp', {}).get('local_policies', {}
                                                                  ).get('placement_policy_files_vcpe')
+        self.template_fields = {
+            'location_enabled': True,
+            'version': '2017-10-10'
+        }
 
     def tearDown(self):
         pass
@@ -46,7 +50,8 @@ class TestConductorCalls(unittest.TestCase):
         demands = req_json['placementInfo']['placementDemands']
         request_parameters = req_json['placementInfo']['requestParameters']
         service_info = req_json['serviceInfo']
-        conductor.request(req_info, demands, request_parameters, service_info, True, self.osdf_config, policies)
+        conductor.request(req_info, demands, request_parameters, service_info, self.template_fields,
+                          self.osdf_config, policies)
 
     def test_request_vfmod(self):
         req_json = json_from_file("./test/placement-tests/request_vfmod.json")
@@ -55,7 +60,8 @@ class TestConductorCalls(unittest.TestCase):
         demands = req_json['placementInfo']['placementDemands']
         request_parameters = req_json['placementInfo']['requestParameters']
         service_info = req_json['serviceInfo']
-        conductor.request(req_info, demands, request_parameters, service_info, True, self.osdf_config, policies)
+        conductor.request(req_info, demands, request_parameters, service_info, self.template_fields,
+                          self.osdf_config, policies)
 
 
 if __name__ == "__main__":
index 8b6c0a1..2a600e4 100644 (file)
@@ -40,6 +40,9 @@ class TestConductorTranslation(unittest.TestCase):
         self.request_vfmod_json = json_from_file(parameter_data_file)
         self.policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files]
 
+        self.optimization_policies = [json_from_file(policy_data_path + '/'
+                                                     + "slice-selection-files/opt_policy_nsi_reuse.json")]
+
     def tearDown(self):
         pass
 
@@ -58,6 +61,26 @@ class TestConductorTranslation(unittest.TestCase):
         res = tr.gen_demands(self.request_vfmod_json['placementInfo']['placementDemands'], vnf_policies)
         assert res is not None
 
+    def test_gen_optimization_policy(self):
+        expected = [{
+            "goal": "minimize",
+            "operation_function": {
+                "operator": "sum",
+                "operands": [
+                    {
+                        "function": "attribute",
+                        "params": {
+                            "attribute": "creation_cost",
+                            "demand": "embb-nst"
+                        }
+                    }
+                ]
+            }
+        }]
+        self.assertEqual(expected,
+                         tr.gen_optimization_policy(self.request_vfmod_json['placementInfo']['placementDemands'],
+                                                    self.optimization_policies))
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/policy-local-files/slice-selection-files/opt_policy_nsi_reuse.json b/test/policy-local-files/slice-selection-files/opt_policy_nsi_reuse.json
new file mode 100644 (file)
index 0000000..33dbfee
--- /dev/null
@@ -0,0 +1,38 @@
+{
+    "OSDF_FRANKFURT.minimizeCost_URLLC": {
+        "metadata": {
+            "policy-id": "OSDF_FRANKFURT.minimizeCost_URLLC",
+            "policy-version": 1
+        },
+        "properties": {
+            "geography": [],
+            "identity": "optimization",
+            "goal": "minimize",
+            "operation_function": {
+                "operator": "sum",
+                "operands": [
+                    {
+                        "function": "attribute",
+                        "params": {
+                            "attribute": "creation_cost",
+                            "demand": "embb-nst"
+                        }
+                    }
+                ]
+            },
+            "resources": [
+                "embb-nst"
+            ],
+            "scope": [
+                "REUSE",
+                "SHARED"
+            ],
+            "services": [
+                "embb-nst"
+            ]
+        },
+        "type": "onap.policies.optimization.resource.OptimizationPolicy",
+        "type_version": "2.0.0",
+        "version": "1.0.0"
+    }
+}
diff --git a/test/policy-local-files/slice-selection-files/opt_policy_nssi.json b/test/policy-local-files/slice-selection-files/opt_policy_nssi.json
new file mode 100644 (file)
index 0000000..2b7cbf8
--- /dev/null
@@ -0,0 +1,37 @@
+{
+    "OSDF_FRANKFURT.minimizeCost_URLLC": {
+        "metadata": {
+            "policy-id": "OSDF_FRANKFURT.minimizeCost_URLLC",
+            "policy-version": 1
+        },
+        "properties": {
+            "geography": [],
+            "identity": "optimization",
+            "goal": "minimize",
+            "operation_function": {
+                "operator": "sum",
+                "operands": [
+                    {
+                        "function": "attribute",
+                        "params": {
+                            "attribute": "latency",
+                            "demand": "embb-cn"
+                        }
+                    }
+                ]
+            },
+            "resources": [
+                "embb-cn"
+            ],
+            "scope": [
+                "SHARED"
+            ],
+            "services": [
+                "embb-cn"
+            ]
+        },
+        "type": "onap.policies.optimization.resource.OptimizationPolicy",
+        "type_version": "2.0.0",
+        "version": "1.0.0"
+    }
+}
diff --git a/test/policy-local-files/slice-selection-files/query_policy_nsi.json b/test/policy-local-files/slice-selection-files/query_policy_nsi.json
new file mode 100644 (file)
index 0000000..49b1ca6
--- /dev/null
@@ -0,0 +1,44 @@
+
+         {
+            "OSDF_GUILIN.queryPolicy_URLLC":{
+               "type":"onap.policies.optimization.service.QueryPolicy",
+               "version":"1.0.0",
+               "type_version":"1.0.0",
+               "metadata":{
+                  "policy-id":"OSDF_GUILIN.queryPolicy_URLLC",
+                  "policy-version":1
+               },
+               "properties":{
+                  "scope":[
+                     "OSDF_GUILIN"
+                  ],
+                  "services":[
+                     "embb-nst"
+                  ],
+                  "geography":[],
+                  "identity":"queryPolicy_URLLC",
+                  "queryProperties":[
+                     {
+                        "attribute":"latency",
+                        "attribute_location":"latency"
+                     },
+                     {
+                        "attribute":"reliability",
+                        "attribute_location":"reliability"
+                     },
+                     {
+                        "attribute":"an_latency",
+                        "attribute_location":"an_latency"
+                     },
+                     {
+                        "attribute":"cn_latency",
+                        "attribute_location":"cn_latency"
+                     },
+                     {
+                        "attribute":"tn_bh_latency",
+                        "attribute_location":"tn_bh_latency"
+                     }
+                  ]
+               }
+            }
+         }
diff --git a/test/policy-local-files/slice-selection-files/query_policy_nssi.json b/test/policy-local-files/slice-selection-files/query_policy_nssi.json
new file mode 100644 (file)
index 0000000..5e5893b
--- /dev/null
@@ -0,0 +1,32 @@
+
+         {
+            "OSDF_GUILIN.queryPolicy_URLLC":{
+               "type":"onap.policies.optimization.service.QueryPolicy",
+               "version":"1.0.0",
+               "type_version":"1.0.0",
+               "metadata":{
+                  "policy-id":"OSDF_GUILIN.queryPolicy_URLLC",
+                  "policy-version":1
+               },
+               "properties":{
+                  "scope":[
+                     "SHARED"
+                  ],
+                  "services":[
+                     "embb-cn"
+                  ],
+                  "geography":[],
+                  "identity":"queryPolicy_URLLC",
+                  "queryProperties":[
+                     {
+                        "attribute":"latency",
+                        "attribute_location":"latency"
+                     },
+                     {
+                        "attribute":"reliability",
+                        "attribute_location":"reliability"
+                     }
+                  ]
+               }
+            }
+         }
diff --git a/test/policy-local-files/slice-selection-files/threshold_policy_nsi.json b/test/policy-local-files/slice-selection-files/threshold_policy_nsi.json
new file mode 100644 (file)
index 0000000..6e518dd
--- /dev/null
@@ -0,0 +1,46 @@
+
+         {
+            "OSDF_GUILIN.Threshold_URLLC":{
+               "metadata":{
+                  "policy-id":"OSDF_GUILIN.Threshold_URLLC",
+                  "policy-version":1
+               },
+               "properties":{
+                  "geography":[
+
+                  ],
+                  "identity":"Threshold_URLLC",
+                  "resources":[
+                     "embb-nst"
+                  ],
+                  "scope":[
+                     "OSDF_GUILIN"
+                  ],
+                  "services":[
+                     "embb-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"
+            }
+         }
+
diff --git a/test/policy-local-files/slice-selection-files/threshold_policy_nssi.json b/test/policy-local-files/slice-selection-files/threshold_policy_nssi.json
new file mode 100644 (file)
index 0000000..72a24da
--- /dev/null
@@ -0,0 +1,46 @@
+
+         {
+            "OSDF_GUILIN.Threshold_URLLC":{
+               "metadata":{
+                  "policy-id":"OSDF_GUILIN.Threshold_URLLC",
+                  "policy-version":1
+               },
+               "properties":{
+                  "geography":[
+
+                  ],
+                  "identity":"Threshold_URLLC",
+                  "resources":[
+                     "embb-cn"
+                  ],
+                  "scope":[
+                     "SHARED"
+                  ],
+                  "services":[
+                     "embb-cn"
+                  ],
+                  "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"
+            }
+         }
+
diff --git a/test/policy-local-files/slice-selection-files/vnf_policy_nsi_non_shared_case.json b/test/policy-local-files/slice-selection-files/vnf_policy_nsi_non_shared_case.json
new file mode 100644 (file)
index 0000000..1774780
--- /dev/null
@@ -0,0 +1,76 @@
+
+         {
+            "OSDF_GUILIN.vnfPolicy_URLLC":{
+               "metadata":{
+                  "policy-id":"OSDF_GUILIN.vnfPolicy_URLLC",
+                  "policy-version":1
+               },
+               "properties":{
+                  "identity":"vnf_URLLC",
+                  "resources":["embb-nst"],
+                  "scope":[
+                     "OSDF_GUILIN",
+                     "non-shared"
+                  ],
+                  "services":[
+                     "embb-nst"
+                  ],
+                  "geography":[],
+                  "vnfProperties":[
+                        {
+                        "attributes":{
+                           "core":{
+                              "latency":{
+                                 "max":{"get_param":"latency"},
+                                 "min":{"get_param":"cn_latency"},
+                                 "steps":1
+                              },
+                              "reliability":{
+                                 "values":[
+                                    99.9,
+                                    99.999
+                                 ]
+                              }
+                           },
+                           "ran":{
+                              "latency":{
+                                 "max":{"get_param":"latency"},
+                                 "min":{"get_param":"an_latency"},
+                                 "steps":1
+                              },
+                              "reliability":{
+                                 "values":[
+                                    99.9,
+                                    99.9
+                                 ]
+                              }
+                           },
+                           "transport":{
+                              "latency":{
+                                 "max":{"get_param":"latency"},
+                                 "min":{"get_param":"tn_bh_latency"},
+                                 "steps":1
+                              },
+                              "reliability":{
+                                 "values":[
+                                    99.9,
+                                    99.99
+                                 ]
+                              }
+                           }
+                        },
+                        "inventoryProvider":"generator",
+                        "inventoryType":"slice_profiles",
+                        "unique":"true",
+                        "defaultAttributes":{
+                           "creation-cost" : 0.9
+                        }
+                     }
+                  ]
+               },
+               "type":"onap.policies.optimization.resource.VnfPolicy",
+               "type_version":"1.0.0",
+               "version":"1.0.0"
+            }
+         }
+
diff --git a/test/policy-local-files/slice-selection-files/vnf_policy_nsi_shared_case.json b/test/policy-local-files/slice-selection-files/vnf_policy_nsi_shared_case.json
new file mode 100644 (file)
index 0000000..9932cc1
--- /dev/null
@@ -0,0 +1,90 @@
+
+         {
+            "OSDF_GUILIN.vnfPolicy_URLLC":{
+               "metadata":{
+                  "policy-id":"OSDF_GUILIN.vnfPolicy_URLLC",
+                  "policy-version":1
+               },
+               "properties":{
+                  "identity":"vnf_URLLC",
+                  "resources":["embb-nst"],
+                  "scope":[
+                     "OSDF_GUILIN",
+                     "shared"
+                  ],
+                  "services":[
+                     "embb-nst"
+                  ],
+                  "geography":[],
+                  "vnfProperties":[
+                     {
+                        "attributes":{
+                           "modelInvariantId":"bfbg3636-e39c-iidd-0987-27c28f4oo3",
+                           "modelVersionId":"bfbg3636-e39c-iidd-0987-27c28f4d33",
+                           "environment-context":"shared",
+                           "service-role":"nsi"
+                        },
+                        "inventoryProvider":"aai",
+                        "inventoryType":"nsi",
+                        "unique":"true",
+                        "defaultAttributes":{
+                           "creation-cost" : 0.1
+                        }
+                     },
+                     {
+                        "attributes":{
+                           "core":{
+                              "latency":{
+                                 "max":{"get_param":"latency"},
+                                 "min":{"get_param":"cn_latency"},
+                                 "steps":1
+                              },
+                              "reliability":{
+                                 "values":[
+                                    99.9,
+                                    99.999
+                                 ]
+                              }
+                           },
+                           "ran":{
+                              "latency":{
+                                 "max":{"get_param":"latency"},
+                                 "min":{"get_param":"an_latency"},
+                                 "steps":1
+                              },
+                              "reliability":{
+                                 "values":[
+                                    99.9,
+                                    99.9
+                                 ]
+                              }
+                           },
+                           "transport":{
+                              "latency":{
+                                 "max":{"get_param":"latency"},
+                                 "min":{"get_param":"tn_bh_latency"},
+                                 "steps":1
+                              },
+                              "reliability":{
+                                 "values":[
+                                    99.9,
+                                    99.99
+                                 ]
+                              }
+                           }
+                        },
+                        "inventoryProvider":"generator",
+                        "inventoryType":"slice_profiles",
+                        "unique":"true",
+                        "defaultAttributes":{
+                           "creation-cost" : 0.9
+                        }
+                     }
+                  ]
+               },
+               "type":"onap.policies.optimization.resource.VnfPolicy",
+               "type_version":"1.0.0",
+               "version":"1.0.0"
+            }
+         }
+
diff --git a/test/policy-local-files/slice-selection-files/vnf_policy_nssi_shared.json b/test/policy-local-files/slice-selection-files/vnf_policy_nssi_shared.json
new file mode 100644 (file)
index 0000000..bd12f84
--- /dev/null
@@ -0,0 +1,37 @@
+
+         {
+            "OSDF_GUILIN.vnfPolicy_URLLC":{
+               "metadata":{
+                  "policy-id":"OSDF_GUILIN.vnfPolicy_URLLC",
+                  "policy-version":1
+               },
+               "properties":{
+                  "identity":"vnf_URLLC",
+                  "resources":["embb-cn"],
+                  "scope":[
+                     "SHARED"
+                  ],
+                  "services":[
+                     "embb-cn"
+                  ],
+                  "geography":[],
+                  "vnfProperties":[
+                     {
+                        "attributes":{
+                           "modelInvariantId":"bfbg3636-e39c-iidd-0987-27c28f4oo3",
+                           "modelVersionId":"bfbg3636-e39c-iidd-0987-27c28f4d33",
+                           "environment-context":"shared",
+                           "service-role":"nssi"
+                        },
+                        "inventoryProvider":"aai",
+                        "inventoryType":"nssi",
+                        "unique":"true"
+                     }
+                  ]
+               },
+               "type":"onap.policies.optimization.resource.VnfPolicy",
+               "type_version":"1.0.0",
+               "version":"1.0.0"
+            }
+         }
+
index b3dd97c..34f6989 100644 (file)
@@ -43,6 +43,10 @@ class TestConductorApiBuilder(unittest.TestCase):
         parameter_data_file = self.main_dir + "test/placement-tests/request_placement_vfmod.json"
         self.request_placement_vfmod_json = json_from_file(parameter_data_file)
         self.policies = [json_from_file(policy_data_path + '/' + name) for name in valid_policies_files]
+        self.template_fields = {
+            'location_enabled': True,
+            'version': '2017-10-10'
+        }
 
     def test_conductor_api_call_builder(self):
         main_dir = self.main_dir
@@ -53,8 +57,8 @@ class TestConductorApiBuilder(unittest.TestCase):
         demands = request_json['placementInfo']['placementDemands']
         request_parameters = request_json['placementInfo']['requestParameters']
         service_info = request_json['serviceInfo']
-        templ_string = conductor_api_builder(req_info, demands, request_parameters, service_info, True, policies,
-                                             local_config, self.conductor_api_template)
+        templ_string = conductor_api_builder(req_info, demands, request_parameters, service_info, self.template_fields,
+                                             policies, local_config, self.conductor_api_template)
         templ_json = json.loads(templ_string)
         self.assertEqual(templ_json["name"], "yyy-yyy-yyyy")
 
@@ -66,8 +70,8 @@ class TestConductorApiBuilder(unittest.TestCase):
         demands = request_json['placementInfo']['placementDemands']
         request_parameters = request_json['placementInfo']['requestParameters']
         service_info = request_json['serviceInfo']
-        templ_string = conductor_api_builder(req_info, demands, request_parameters, service_info, True, policies,
-                                             local_config, self.conductor_api_template)
+        templ_string = conductor_api_builder(req_info, demands, request_parameters, service_info, self.template_fields,
+                                             policies, local_config, self.conductor_api_template)
         templ_json = json.loads(templ_string)
         self.assertEqual(templ_json, self.request_placement_vfmod_json)
 
diff --git a/tox.ini b/tox.ini
index 1606b9b..3b15855 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -9,11 +9,15 @@ basepython=python3
 setenv   =
     OSDF_CONFIG_FILE={toxinidir}/test/config/osdf_config.yaml
 commands =
+    /bin/cp config/slicing_config.yaml test/config/
+    /bin/cp config/slicing_config.yaml test/functest/simulators/simulated-config/
     /bin/bash test/functest/scripts/start-simulators.sh
     coverage run --module pytest --junitxml xunit-results.xml
     coverage xml --omit=".tox/py3/*","test/*"
     coverage report -m --omit=".tox/py3/*","test/*"
     /bin/bash test/functest/scripts/stop-simulators.sh
+    /bin/rm test/config/slicing_config.yaml
+    /bin/rm test/functest/simulators/simulated-config/slicing_config.yaml
     # TODO: need to update the above "omit" when we package osdf as pip-installable
 deps = -r{toxinidir}/requirements.txt
     -r{toxinidir}/test/test-requirements.txt
@@ -31,6 +35,7 @@ commands = bash -c "pylint --reports=y osdf apps runtime| tee pylint.out"
 basepython=python3.6
 
 [testenv:flake8diff]
+basepython=python3.6
 whitelist_externals=bash
 deps = hacking>=2.0.0
 commands =
@@ -39,5 +44,5 @@ commands =
 [flake8]
 select = E,H,W,F
 max-line-length = 119
-ignore =
+ignore = W503   #conflict with W504
 per-file-ignores=