Add Nxi-Termination feature 84/111784/3
authorhariharan97 <rh20085046@wipro.com>
Wed, 26 Aug 2020 04:12:29 +0000 (09:42 +0530)
committerhariharan97 <rh20085046@wipro.com>
Wed, 2 Sep 2020 12:08:41 +0000 (17:38 +0530)
Issue-ID: OPTFRA-825
Signed-off-by: hariharan97 <rh20085046@wipro.com>
Change-Id: I0d768e36708e9f26e5bcdf661b2bcb5772ed48c2

31 files changed:
apps/nxi_termination/__init__.py [new file with mode: 0644]
apps/nxi_termination/models/api/_init_.py [new file with mode: 0644]
apps/nxi_termination/models/api/nxi_termination_request.py [new file with mode: 0644]
apps/nxi_termination/optimizers/__init__.py [new file with mode: 0644]
apps/nxi_termination/optimizers/remote_opt_processor.py [new file with mode: 0644]
apps/nxi_termination/optimizers/response_processor.py [new file with mode: 0644]
config/osdf_config.yaml
osdf/adapters/aai/_init_.py [new file with mode: 0644]
osdf/adapters/aai/fetch_aai_data.py [new file with mode: 0644]
osdfapp.py
test/apps/nxi_termination/_init_.py [new file with mode: 0644]
test/apps/nxi_termination/aai_exception_response.json [new file with mode: 0644]
test/apps/nxi_termination/aai_response.json [new file with mode: 0644]
test/apps/nxi_termination/exception_response1.json [new file with mode: 0644]
test/apps/nxi_termination/failure_relationship_list.json [new file with mode: 0644]
test/apps/nxi_termination/failure_relationship_list2.json [new file with mode: 0644]
test/apps/nxi_termination/failure_service_profiles.json [new file with mode: 0644]
test/apps/nxi_termination/failure_service_profiles2.json [new file with mode: 0644]
test/apps/nxi_termination/invalid_request.json [new file with mode: 0644]
test/apps/nxi_termination/nsi_success_output.json [new file with mode: 0644]
test/apps/nxi_termination/nssi_failure_output.json [new file with mode: 0644]
test/apps/nxi_termination/nssi_termination.json [new file with mode: 0644]
test/apps/nxi_termination/nxi_failure_output1.json [new file with mode: 0644]
test/apps/nxi_termination/nxi_failure_output2.json [new file with mode: 0644]
test/apps/nxi_termination/nxi_termination.json [new file with mode: 0644]
test/apps/nxi_termination/service_profiles.json [new file with mode: 0644]
test/apps/nxi_termination/success_relationship_list.json [new file with mode: 0644]
test/apps/nxi_termination/test_fetch_aai_data.py [new file with mode: 0644]
test/apps/nxi_termination/test_remote_opt_processor_termination.py [new file with mode: 0644]
test/config/osdf_config.yaml
test/test_api_validation.py

diff --git a/apps/nxi_termination/__init__.py b/apps/nxi_termination/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/nxi_termination/models/api/_init_.py b/apps/nxi_termination/models/api/_init_.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/nxi_termination/models/api/nxi_termination_request.py b/apps/nxi_termination/models/api/nxi_termination_request.py
new file mode 100644 (file)
index 0000000..45456cf
--- /dev/null
@@ -0,0 +1,45 @@
+# -------------------------------------------------------------------------
+#   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.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+from osdf.models.api.common import OSDFModel
+from schematics.types import BaseType
+from schematics.types.compound import DictType
+from schematics.types.compound import ModelType
+from schematics.types import IntType
+from schematics.types import StringType
+from schematics.types import URLType
+
+
+class RequestInfo(OSDFModel):
+    """Info for northbound request from client such as SO"""
+    transactionId = StringType(required=True)
+    requestId = StringType(required=True)
+    callbackUrl = URLType(required=True)
+    callbackHeader = DictType(BaseType)
+    sourceId = StringType(required=True)
+    timeout = IntType()
+    addtnlArgs = DictType(BaseType)
+
+
+class NxiTerminationApi(OSDFModel):
+    """Request for nxi termination (specific to optimization and additional metadata"""
+    requestInfo = ModelType(RequestInfo, required=True)
+    type = StringType(required=True, choices=['NSI', 'NSSI'])
+    NxIId = StringType(required=True)
+    UUID = StringType()
+    invariantUUID = StringType()
diff --git a/apps/nxi_termination/optimizers/__init__.py b/apps/nxi_termination/optimizers/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/nxi_termination/optimizers/remote_opt_processor.py b/apps/nxi_termination/optimizers/remote_opt_processor.py
new file mode 100644 (file)
index 0000000..8867c97
--- /dev/null
@@ -0,0 +1,123 @@
+# -------------------------------------------------------------------------
+#   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.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+from apps.nxi_termination.optimizers.response_processor import get_nxi_termination_failure_response
+from apps.nxi_termination.optimizers.response_processor import get_nxi_termination_response
+from osdf.adapters.aai.fetch_aai_data import AAIException
+from osdf.adapters.aai.fetch_aai_data import get_aai_data
+
+
+def process_nxi_termination_opt(request_json, osdf_config):
+
+    """Process the nxi Termination request from API layer
+
+       :param request_json: api request
+       :param osdf_config: configuration specific to OSDF app
+       :return: response as a success,failure
+    """
+
+    type = request_json["type"]
+    request_info = request_json.get("requestInfo", {})
+    addtnl_args = request_info.get("addtnlArgs", {})
+    if type == "NSI":
+        arg_val = addtnl_args.get("serviceProfileId", "")
+        return check_nxi_termination(request_json, osdf_config, addtnl_args, arg_val,
+                                     get_service_profiles, get_service_profile_id)
+
+    else:
+        arg_val = addtnl_args.get("serviceInstanceId", "")
+        return check_nxi_termination(request_json, osdf_config, addtnl_args, arg_val, get_relationshiplist, get_nsi_id)
+
+
+def check_nxi_termination(request_json, osdf_config, addtnl_args, arg_val, get_response_object, get_response_id):
+    request_info = request_json.get("requestInfo", {})
+
+    try:
+        response_object = get_response_object(request_json, osdf_config)
+        if addtnl_args and arg_val and len(response_object) == 1:
+            response_id = get_response_id(response_object)
+            if arg_val == response_id:
+                reason = ''
+                return set_success_response(reason, request_info, terminate_response=True)
+
+            else:
+                reason = "{} is not available in AAI".format(arg_val)
+                return set_success_response(reason, request_info, terminate_response=False)
+
+        elif len(response_object) == 0:
+            reason = ''
+            return set_success_response(reason, request_info, terminate_response=True)
+
+        else:
+            reason = "Associated to more than one"
+            return set_success_response(reason, request_info, terminate_response=False)
+
+    except AAIException as e:
+        reason = str(e)
+        return set_failure_response(reason, request_info)
+
+    except Exception as e:
+        reason = "{} Exception Occurred while processing".format(str(e))
+        return set_failure_response(reason, request_info)
+
+
+def get_service_profiles(request_json, osdf_config):
+    try:
+        json_response = get_aai_data(request_json, osdf_config)
+        service_profiles = json_response.get("service-profiles", {})
+        service_profile = service_profiles.get("service-profile", [])
+        return service_profile
+    except AAIException as e:
+        raise AAIException(e)
+
+
+def get_relationshiplist(request_json, osdf_config):
+    try:
+        json_response = get_aai_data(request_json, osdf_config)
+        rel_list = json_response.get("relationship-list", {})
+        relationship = rel_list.get("relationship", [])
+        return relationship
+    except AAIException as e:
+        raise AAIException(e)
+
+
+def get_service_profile_id(service_profile):
+    profile_obj = service_profile[0]
+    return profile_obj.get("profile-id", "")
+
+
+def get_nsi_id(relationship):
+    rel_obj = relationship[0]
+    rel_data = rel_obj.get("relationship-data", [])
+    for data in rel_data:
+        if data["relationship-key"] == "service-instance.service-instance-id":
+            return data["relationship-value"]
+
+
+def set_success_response(reason, request_info, terminate_response):
+    res = dict()
+    res["requestStatus"] = "success"
+    res["terminateResponse"] = terminate_response
+    res["reason"] = reason
+    return get_nxi_termination_response(request_info, res)
+
+
+def set_failure_response(reason, request_info,):
+    res = dict()
+    res["requestStatus"] = "failure"
+    res["reason"] = reason
+    return get_nxi_termination_failure_response(request_info, res)
diff --git a/apps/nxi_termination/optimizers/response_processor.py b/apps/nxi_termination/optimizers/response_processor.py
new file mode 100644 (file)
index 0000000..e7f1041
--- /dev/null
@@ -0,0 +1,45 @@
+# -------------------------------------------------------------------------
+#   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.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+def get_nxi_termination_response(request_info, response):
+
+    """Get NXI termination response from final solution
+
+       :param request_info: request info
+       :param response: response to be send
+       :return: NxI Termination response to send back as dictionary
+    """
+    return {'requestId': request_info['requestId'],
+            'transactionId': request_info['transactionId'],
+            'requestStatus': response["requestStatus"],
+            'terminateResponse': response["terminateResponse"],
+            'reason': response['reason']}
+
+
+def get_nxi_termination_failure_response(request_info, response):
+
+    """Get NXI termination response from final solution
+
+       :param request_info: request info
+       :param response: response to be send
+       :return: NxI Termination response to send back as dictionary
+    """
+    return {'requestId': request_info['requestId'],
+            'transactionId': request_info['transactionId'],
+            'requestStatus': response["requestStatus"],
+            'reason': response['reason']}
index 19f5574..4207b34 100755 (executable)
@@ -53,6 +53,7 @@ configDbGetNbrListUrl: 'SDNCConfigDBAPI/getNbrList'
 #aai api
 aaiUrl: "https://aai.url:30233"
 aaiGetLinksUrl: "/aai/v16/network/logical-links"
+aaiServiceInstanceUrl : "/aai/v20/nodes/service-instances/service-instance/"
 aaiGetControllersUrl: /aai/v19/external-system/esr-thirdparty-sdnc-list
 controllerQueryUrl: /aai/v19/query?format=resource
 aaiGetInterDomainLinksUrl: /aai/v19/network/logical-links?link-type=inter-domain&operational-status=up
diff --git a/osdf/adapters/aai/_init_.py b/osdf/adapters/aai/_init_.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/osdf/adapters/aai/fetch_aai_data.py b/osdf/adapters/aai/fetch_aai_data.py
new file mode 100644 (file)
index 0000000..634aa04
--- /dev/null
@@ -0,0 +1,55 @@
+# -------------------------------------------------------------------------
+#   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.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+import requests
+from requests.auth import HTTPBasicAuth
+from requests import RequestException
+
+
+class AAIException(Exception):
+    pass
+
+
+def get_aai_data(request_json, osdf_config):
+
+    """Get the response from AAI
+
+       :param request_json: requestjson
+       :param osdf_config: configuration specific to OSDF app
+       :return:response body from AAI
+    """
+    aai_headers = {
+        "X-TransactionId": "9999",
+        "X-FromAppId": "OOF",
+        "Accept": "application/json",
+        "Content-Type": "application/json",
+    }
+    nxi_id = request_json["NxIId"]
+    config = osdf_config.deployment
+    aai_url = config["aaiUrl"]
+    aai_req_url = aai_url + config["aaiServiceInstanceUrl"] + nxi_id + "?depth=2"
+
+    try:
+        response = requests.get(aai_req_url, aai_headers, auth=HTTPBasicAuth("AAI", "AAI"), verify=False)
+    except RequestException as e:
+        raise AAIException("Request exception was encountered {}".format(e))
+
+    if response.status_code == 200:
+        return response.json()
+    else:
+        raise AAIException("Error response recieved from AAI for the request {}".format(aai_req_url))
index eb99fac..9234d78 100755 (executable)
@@ -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.
@@ -44,6 +45,8 @@ 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
 
 
 @app.route("/api/oof/v1/healthcheck", methods=["GET"])
@@ -105,6 +108,7 @@ def do_route_calc():
     response = RouteOpt().get_route(request_json, osdf_config)
     return response
 
+
 @app.route("/api/oof/mdons/route/v1", methods=["POST"])
 def do_mdons_route_calc():
     """
@@ -115,6 +119,7 @@ def do_mdons_route_calc():
     response = InterDomainRouteOpt().get_route(request_json, osdf_config)
     return response
 
+
 @app.route("/api/oof/v1/selection/nst", methods=["POST"])
 def do_nst_selection():
     request_json = request.get_json()
@@ -179,6 +184,14 @@ def do_nssi_selection():
                       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()
+    req_id = request_json['requestInfo']['requestId']
+    g.request_id = req_id
+    audit_log.info(MH.received_request(request.url, request.remote_addr, json.dumps(request_json)))
+    NxiTerminationApi(request_json).validate()
+    return process_nxi_termination_opt(request_json,osdf_config)
 
 if __name__ == "__main__":
     run_app()
diff --git a/test/apps/nxi_termination/_init_.py b/test/apps/nxi_termination/_init_.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/apps/nxi_termination/aai_exception_response.json b/test/apps/nxi_termination/aai_exception_response.json
new file mode 100644 (file)
index 0000000..56f61df
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "status-code": 404,
+   "status-response": "NOT FOUND"
+}
\ No newline at end of file
diff --git a/test/apps/nxi_termination/aai_response.json b/test/apps/nxi_termination/aai_response.json
new file mode 100644 (file)
index 0000000..b7ef43b
--- /dev/null
@@ -0,0 +1,64 @@
+{"service-instance": [{
+    "service-instance-id": "1a636c4d-5e76-427e-bfd6-241a947224b0",
+    "service-instance-name": "nssi_test_0211",
+    "service-type": "embb",
+    "service-role": "nssi",
+    "environment-context": "cn",
+    "model-invariant-id": "21d57d4b-52ad-4d3c-a798-248b5bb9124a",
+    "model-version-id": "bfba363e-e39c-4bd9-a9d5-1371c28f4d22",
+    "resource-version": "1581418601616",
+    "orchestration-status": "active",
+    "relationship-list": {
+        "relationship": [
+            {
+                "related-to": "service-instance",
+                "relationship-label": "org.onap.relationships.inventory.ComposedOf",
+                "related-link": "/aai/v16/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/4115d3c8-dd59-45d6-b09d-e756dee9b518",
+                "relationship-data": [
+                    {
+                        "relationship-key": "customer.global-customer-id",
+                        "relationship-value": "5GCustomer"
+                    },
+                    {
+                        "relationship-key": "service-subscription.service-type",
+                        "relationship-value": "5G"
+                    },
+                    {
+                        "relationship-key": "service-instance.service-instance-id",
+                        "relationship-value": "4115d3c8-dd59-45d6-b09d-e756dee9b518"
+                    }
+                ],
+                "related-to-property": [
+                    {
+                        "property-key": "service-instance.service-instance-name",
+                        "property-value": "nsi_test_0211"
+                    }
+                ]
+            }
+        ]
+    },
+    "slice-profiles": {
+        "slice-profile": [
+            {
+                "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c299",
+                "latency": 20,
+                "max-number-of-UEs": 0,
+                "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+                "ue-mobility-level": "stationary",
+                "resource-sharing-level": "0",
+                "exp-data-rate-UL": 100,
+                "exp-data-rate-DL": 100,
+                "activity-factor": 0,
+                "e2e-latency": 0,
+                "jitter": 0,
+                "survival-time": 0,
+                "exp-data-rate": 0,
+                "payload-size": 0,
+                "traffic-density": 0,
+                "conn-density": 0,
+                "reliability": 99.999,
+                "resource-version": "1581418602494"
+            }
+        ]
+    }
+}]}
diff --git a/test/apps/nxi_termination/exception_response1.json b/test/apps/nxi_termination/exception_response1.json
new file mode 100644 (file)
index 0000000..cde603f
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "requestStatus": "failure",
+  "reason": "Error response recieved from AAI for the request"
+
+}
\ No newline at end of file
diff --git a/test/apps/nxi_termination/failure_relationship_list.json b/test/apps/nxi_termination/failure_relationship_list.json
new file mode 100644 (file)
index 0000000..537f283
--- /dev/null
@@ -0,0 +1,27 @@
+[
+            {
+                "related-to": "service-instance",
+                "relationship-label": "org.onap.relationships.inventory.ComposedOf",
+                "related-link": "/aai/v16/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/4115d3c8-dd59-45d6-b09d-e756dee9b518",
+                "relationship-data": [
+                    {
+                        "relationship-key": "customer.global-customer-id",
+                        "relationship-value": "5GCustomer"
+                    },
+                    {
+                        "relationship-key": "service-subscription.service-type",
+                        "relationship-value": "5G"
+                    },
+                    {
+                        "relationship-key": "service-instance.service-instance-id",
+                        "relationship-value": "4115d3c8-dd59-45d6-b09d-e756dee9b567"
+                    }
+                ],
+                "related-to-property": [
+                    {
+                        "property-key": "service-instance.service-instance-name",
+                        "property-value": "nsi_test_0211"
+                    }
+                ]
+            }
+        ]
\ No newline at end of file
diff --git a/test/apps/nxi_termination/failure_relationship_list2.json b/test/apps/nxi_termination/failure_relationship_list2.json
new file mode 100644 (file)
index 0000000..624448a
--- /dev/null
@@ -0,0 +1,52 @@
+[
+            {
+                "related-to": "service-instance",
+                "relationship-label": "org.onap.relationships.inventory.ComposedOf",
+                "related-link": "/aai/v16/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/4115d3c8-dd59-45d6-b09d-e756dee9b518",
+                "relationship-data": [
+                    {
+                        "relationship-key": "customer.global-customer-id",
+                        "relationship-value": "5GCustomer"
+                    },
+                    {
+                        "relationship-key": "service-subscription.service-type",
+                        "relationship-value": "5G"
+                    },
+                    {
+                        "relationship-key": "service-instance.service-instance-id",
+                        "relationship-value": "4115d3c8-dd59-45d6-b09d-e756dee9b567"
+                    }
+                ],
+                "related-to-property": [
+                    {
+                        "property-key": "service-instance.service-instance-name",
+                        "property-value": "nsi_test_0211"
+                    }
+                ]
+            },
+   {
+                "related-to": "service-instance",
+                "relationship-label": "org.onap.relationships.inventory.ComposedOf",
+                "related-link": "/aai/v16/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/4115d3c8-dd59-45d6-b09d-e756dee9b518",
+                "relationship-data": [
+                    {
+                        "relationship-key": "customer.global-customer-id",
+                        "relationship-value": "5GCustomer"
+                    },
+                    {
+                        "relationship-key": "service-subscription.service-type",
+                        "relationship-value": "5G"
+                    },
+                    {
+                        "relationship-key": "service-instance.service-instance-id",
+                        "relationship-value": "4115d3c8-dd59-45d6-b09d-e756dee9b567"
+                    }
+                ],
+                "related-to-property": [
+                    {
+                        "property-key": "service-instance.service-instance-name",
+                        "property-value": "nsi_test_0211"
+                    }
+                ]
+            }
+        ]
\ No newline at end of file
diff --git a/test/apps/nxi_termination/failure_service_profiles.json b/test/apps/nxi_termination/failure_service_profiles.json
new file mode 100644 (file)
index 0000000..d10a818
--- /dev/null
@@ -0,0 +1,24 @@
+[
+        {
+            "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c29",
+            "latency": 20,
+            "max-number-of-UEs": 0,
+            "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+            "ue-mobility-level": "stationary",
+            "resource-sharing-level": "0",
+            "exp-data-rate-UL": 100,
+            "exp-data-rate-DL": 100,
+            "activity-factor": 0,
+            "e2e-latency": 0,
+            "jitter": 0,
+            "survival-time": 0,
+            "exp-data-rate": 0,
+            "payload-size": 0,
+            "traffic-density": 0,
+            "conn-density": 0,
+            "reliability": 99.999,
+            "resource-version": "1581418602494"
+        }
+]
+
+
diff --git a/test/apps/nxi_termination/failure_service_profiles2.json b/test/apps/nxi_termination/failure_service_profiles2.json
new file mode 100644 (file)
index 0000000..1740758
--- /dev/null
@@ -0,0 +1,42 @@
+[
+        {
+            "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c299",
+            "latency": 20,
+            "max-number-of-UEs": 0,
+            "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+            "ue-mobility-level": "stationary",
+            "resource-sharing-level": "0",
+            "exp-data-rate-UL": 100,
+            "exp-data-rate-DL": 100,
+            "activity-factor": 0,
+            "e2e-latency": 0,
+            "jitter": 0,
+            "survival-time": 0,
+            "exp-data-rate": 0,
+            "payload-size": 0,
+            "traffic-density": 0,
+            "conn-density": 0,
+            "reliability": 99.999,
+            "resource-version": "1581418602494"
+        },
+  {
+            "profile-id": "abcd9f49-4201-4e3a-aac1-b0f27902c299",
+            "latency": 20,
+            "max-number-of-UEs": 0,
+            "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+            "ue-mobility-level": "stationary",
+            "resource-sharing-level": "0",
+            "exp-data-rate-UL": 100,
+            "exp-data-rate-DL": 100,
+            "activity-factor": 0,
+            "e2e-latency": 0,
+            "jitter": 0,
+            "survival-time": 0,
+            "exp-data-rate": 0,
+            "payload-size": 0,
+            "traffic-density": 0,
+            "conn-density": 0,
+            "reliability": 99.999,
+            "resource-version": "1581418602494"
+        }
+]
diff --git a/test/apps/nxi_termination/invalid_request.json b/test/apps/nxi_termination/invalid_request.json
new file mode 100644 (file)
index 0000000..793776d
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "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,
+    "addtnlArgs": {
+      "serviceProfileId":"cdad9f49-4201-4e3a-aac1-b0f27902c299"
+    }
+  },
+  "type":"NST",
+  "NxIId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "UUID":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "invariantUUID":"d290f1ee-6c54-4b01-90e6-d701748f0851"
+
+}
\ No newline at end of file
diff --git a/test/apps/nxi_termination/nsi_success_output.json b/test/apps/nxi_termination/nsi_success_output.json
new file mode 100644 (file)
index 0000000..e25a272
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "requestStatus": "success",
+  "terminateResponse": true,
+  "reason": ""
+}
\ No newline at end of file
diff --git a/test/apps/nxi_termination/nssi_failure_output.json b/test/apps/nxi_termination/nssi_failure_output.json
new file mode 100644 (file)
index 0000000..5ada892
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "requestStatus": "success",
+  "terminateResponse": false,
+  "reason": "4115d3c8-dd59-45d6-b09d-e756dee9b518 is not available in AAI"
+
+}
\ No newline at end of file
diff --git a/test/apps/nxi_termination/nssi_termination.json b/test/apps/nxi_termination/nssi_termination.json
new file mode 100644 (file)
index 0000000..b4e3711
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "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,
+    "addtnlArgs": {
+      "serviceInstanceId":"4115d3c8-dd59-45d6-b09d-e756dee9b518"
+    }
+  },
+  "type":"NSSI",
+  "NxIId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "UUID":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "invariantUUID":"d290f1ee-6c54-4b01-90e6-d701748f0851"
+
+}
\ No newline at end of file
diff --git a/test/apps/nxi_termination/nxi_failure_output1.json b/test/apps/nxi_termination/nxi_failure_output1.json
new file mode 100644 (file)
index 0000000..b363e86
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "requestStatus": "success",
+  "terminateResponse": false,
+  "reason": "cdad9f49-4201-4e3a-aac1-b0f27902c299 is not available in AAI"
+
+}
+
+
diff --git a/test/apps/nxi_termination/nxi_failure_output2.json b/test/apps/nxi_termination/nxi_failure_output2.json
new file mode 100644 (file)
index 0000000..5d430bc
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "requestId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "transactionId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "requestStatus": "success",
+  "terminateResponse": false,
+  "reason": "Associated to more than one"
+}
\ No newline at end of file
diff --git a/test/apps/nxi_termination/nxi_termination.json b/test/apps/nxi_termination/nxi_termination.json
new file mode 100644 (file)
index 0000000..ac53c4d
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "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,
+    "addtnlArgs": {
+      "serviceProfileId":"cdad9f49-4201-4e3a-aac1-b0f27902c299"
+    }
+  },
+  "type":"NSI",
+  "NxIId":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "UUID":"d290f1ee-6c54-4b01-90e6-d701748f0851",
+  "invariantUUID":"d290f1ee-6c54-4b01-90e6-d701748f0851"
+
+}
\ No newline at end of file
diff --git a/test/apps/nxi_termination/service_profiles.json b/test/apps/nxi_termination/service_profiles.json
new file mode 100644 (file)
index 0000000..899acb4
--- /dev/null
@@ -0,0 +1,23 @@
+[
+        {
+            "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c299",
+            "latency": 20,
+            "max-number-of-UEs": 0,
+            "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+            "ue-mobility-level": "stationary",
+            "resource-sharing-level": "0",
+            "exp-data-rate-UL": 100,
+            "exp-data-rate-DL": 100,
+            "activity-factor": 0,
+            "e2e-latency": 0,
+            "jitter": 0,
+            "survival-time": 0,
+            "exp-data-rate": 0,
+            "payload-size": 0,
+            "traffic-density": 0,
+            "conn-density": 0,
+            "reliability": 99.999,
+            "resource-version": "1581418602494"
+        }
+]
+
diff --git a/test/apps/nxi_termination/success_relationship_list.json b/test/apps/nxi_termination/success_relationship_list.json
new file mode 100644 (file)
index 0000000..4f2a8bb
--- /dev/null
@@ -0,0 +1,27 @@
+[
+            {
+                "related-to": "service-instance",
+                "relationship-label": "org.onap.relationships.inventory.ComposedOf",
+                "related-link": "/aai/v16/business/customers/customer/5GCustomer/service-subscriptions/service-subscription/5G/service-instances/service-instance/4115d3c8-dd59-45d6-b09d-e756dee9b518",
+                "relationship-data": [
+                    {
+                        "relationship-key": "customer.global-customer-id",
+                        "relationship-value": "5GCustomer"
+                    },
+                    {
+                        "relationship-key": "service-subscription.service-type",
+                        "relationship-value": "5G"
+                    },
+                    {
+                        "relationship-key": "service-instance.service-instance-id",
+                        "relationship-value": "4115d3c8-dd59-45d6-b09d-e756dee9b518"
+                    }
+                ],
+                "related-to-property": [
+                    {
+                        "property-key": "service-instance.service-instance-name",
+                        "property-value": "nsi_test_0211"
+                    }
+                ]
+            }
+        ]
\ No newline at end of file
diff --git a/test/apps/nxi_termination/test_fetch_aai_data.py b/test/apps/nxi_termination/test_fetch_aai_data.py
new file mode 100644 (file)
index 0000000..241b24b
--- /dev/null
@@ -0,0 +1,70 @@
+# -------------------------------------------------------------------------
+#   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.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+import unittest
+import mock
+from unittest.mock import patch
+from osdf.config.base import osdf_config
+import osdf.config.loader as config_loader
+from osdf.utils.programming_utils import DotDict
+from osdf.utils.interfaces import json_from_file
+from osdf.adapters.aai.fetch_aai_data import get_aai_data,AAIException
+
+class TestRemoteOptProcessor(unittest.TestCase):
+    def setUp(self):
+        self.config_spec = {
+            "deployment": "config/osdf_config.yaml",
+            "core": "config/common_config.yaml"
+        }
+        self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec))
+
+    def tearDown(self):
+
+        patch.stopall()
+
+
+    def test_get_aai_data(self):
+        main_dir = ""
+        response_file = main_dir + 'test/apps/nxi_termination/aai_response.json'
+        exception_response_file = main_dir + 'test/apps/nxi_termination/aai_exception_response.json'
+        request_file = main_dir + 'test/apps/nxi_termination/nxi_termination.json'
+        response_json = json_from_file(response_file)
+        request_json = json_from_file(request_file)
+        exception_json = json_from_file(exception_response_file)
+        response = mock.MagicMock()
+        response.status_code = 200
+        response.ok = True
+        response.json.return_value = response_json
+        self.patcher_req = patch('requests.get',
+                                         return_value = response)
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(response_json, get_aai_data(request_json,osdf_config))
+        self.patcher_req.stop()
+
+        responsenew=mock.MagicMock()
+        responsenew.status_code=404
+        responsenew.json.return_value = exception_json
+        self.patcher_req = patch('requests.get',
+                                 return_value=responsenew)
+        self.Mock_req = self.patcher_req.start()
+        self.assertRaises( AAIException,get_aai_data,request_json,osdf_config)
+        self.patcher_req.stop()
+
+
+if __name__ == "__main__":
+    unittest.main()
\ No newline at end of file
diff --git a/test/apps/nxi_termination/test_remote_opt_processor_termination.py b/test/apps/nxi_termination/test_remote_opt_processor_termination.py
new file mode 100644 (file)
index 0000000..4fa8170
--- /dev/null
@@ -0,0 +1,141 @@
+# -------------------------------------------------------------------------
+#   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.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+import unittest
+from unittest.mock import patch
+import osdf.config.loader as config_loader
+import pytest
+from apps.nxi_termination.optimizers.remote_opt_processor import process_nxi_termination_opt
+from osdf.adapters.aai.fetch_aai_data import AAIException
+
+from osdf.config.base import osdf_config
+from osdf.utils.programming_utils import DotDict
+from osdf.utils.interfaces import json_from_file
+
+class TestRemoteOptProcessor(unittest.TestCase):
+    def setUp(self):
+        self.config_spec = {
+            "deployment": "config/osdf_config.yaml",
+            "core": "config/common_config.yaml"
+        }
+        self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec))
+
+    def tearDown(self):
+
+        patch.stopall()
+
+    def test_process_nxi_termination_opt(self):
+        main_dir = ""
+        request_file = main_dir + 'test/apps/nxi_termination/nxi_termination.json'
+        nssi_request_file=main_dir + 'test/apps/nxi_termination/nssi_termination.json'
+        service_file = main_dir + 'test/apps/nxi_termination/service_profiles.json'
+        failure_service_file = main_dir + 'test/apps/nxi_termination/failure_service_profiles.json'
+        failure_service_file2 = main_dir + 'test/apps/nxi_termination/failure_service_profiles2.json'
+        nsi_success=main_dir + 'test/apps/nxi_termination/nsi_success_output.json'
+        nxi_failure1 = main_dir + 'test/apps/nxi_termination/nxi_failure_output1.json'
+        nxi_failure2 = main_dir + 'test/apps/nxi_termination/nxi_failure_output2.json'
+        nssi_failure = main_dir + 'test/apps/nxi_termination/nssi_failure_output.json'
+        success_rel_file = main_dir + 'test/apps/nxi_termination/success_relationship_list.json'
+        failure_rel_file1 = main_dir + 'test/apps/nxi_termination/failure_relationship_list.json'
+        failure_rel_file2 = main_dir + 'test/apps/nxi_termination/failure_relationship_list2.json'
+        exception_response_file1 = main_dir + 'test/apps/nxi_termination/exception_response1.json'
+        request_json=json_from_file(request_file)
+        nssi_request_json = json_from_file(nssi_request_file)
+        service_profile_json = json_from_file(service_file)
+        failure_service_profile_json = json_from_file(failure_service_file)
+        failure_service_profile_json2 = json_from_file(failure_service_file2)
+        success_rel_json=json_from_file(success_rel_file)
+        failure_rel_json = json_from_file(failure_rel_file1)
+        failure_rel_json2 = json_from_file(failure_rel_file2)
+        success_output_json=json_from_file(nsi_success)
+        nxi_failure_output_json1 = json_from_file(nxi_failure1)
+        nxi_failure_output_json2 = json_from_file(nxi_failure2)
+        nssi_failure_output_json = json_from_file(nssi_failure)
+        exception_response_json1 = json_from_file(exception_response_file1)
+
+        #nsi success scenario
+        self.patcher_req=patch('apps.nxi_termination.optimizers.remote_opt_processor.get_service_profiles',return_value=service_profile_json)
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(success_output_json, process_nxi_termination_opt(request_json,osdf_config))
+        self.patcher_req.stop()
+
+        #nsi failure scenario
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_service_profiles', return_value=failure_service_profile_json)
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(nxi_failure_output_json1, process_nxi_termination_opt(request_json, osdf_config))
+        self.patcher_req.stop()
+
+        #nsi success scenario
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_service_profiles',
+                                 return_value=[])
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(success_output_json, process_nxi_termination_opt(request_json, osdf_config))
+        self.patcher_req.stop()
+
+        # nsi failure scenario
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_service_profiles',
+                                 return_value=failure_service_profile_json2)
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(nxi_failure_output_json2, process_nxi_termination_opt(request_json, osdf_config))
+        self.patcher_req.stop()
+        # #
+        # nssi success scenario
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_relationshiplist', return_value=success_rel_json)
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(success_output_json, process_nxi_termination_opt(nssi_request_json, osdf_config))
+        self.patcher_req.stop()
+
+        # nssi success scenario
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_relationshiplist',
+                                 return_value=[])
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(success_output_json, process_nxi_termination_opt(nssi_request_json, osdf_config))
+        self.patcher_req.stop()
+
+        # nssi failure scenario
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_relationshiplist',
+                                 return_value=failure_rel_json)
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(nssi_failure_output_json, process_nxi_termination_opt(nssi_request_json, osdf_config))
+        self.patcher_req.stop()
+
+        # nssi failure scenario
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_relationshiplist',
+                                 return_value=failure_rel_json2)
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(nxi_failure_output_json2, process_nxi_termination_opt(nssi_request_json, osdf_config))
+        self.patcher_req.stop()
+
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_relationshiplist',
+                                 side_effect=AAIException("Error response recieved from AAI for the request"))
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals(exception_response_json1, process_nxi_termination_opt(nssi_request_json, osdf_config))
+        self.patcher_req.stop()
+
+
+        self.patcher_req = patch('apps.nxi_termination.optimizers.remote_opt_processor.get_relationshiplist',
+                                 side_effect=AAIException("Request exception was encountered"))
+        self.Mock_req = self.patcher_req.start()
+        self.assertEquals("failure", process_nxi_termination_opt(nssi_request_json, osdf_config).get('requestStatus'))
+        self.patcher_req.stop()
+
+
+
+
+if __name__ == "__main__":
+    unittest.main()
\ No newline at end of file
index eaa31ff..2ed8b88 100755 (executable)
@@ -67,3 +67,6 @@ pciHMSPassword: ""   # pcihandler password for call back.
 # Credentials for the OOF PCI Opt service
 osdfPCIOptUsername: PCI-OSDF-USER
 osdfPCIOptPassword: PCI-OSDF-PASSWD
+
+aaiUrl: "https://aai.url:30233"
+aaiServiceInstanceUrl : "/aai/v20/nodes/service-instances/service-instance/"
index 37f1321..3a20262 100644 (file)
@@ -24,6 +24,7 @@ from apps.placement.models.api.placementRequest import PlacementAPI
 from apps.placement.models.api.placementResponse import PlacementResponse
 from apps.slice_selection.models.api.nsi_selection_request import NSISelectionAPI
 from apps.slice_selection.models.api.nssi_selection_request import NSSISelectionAPI
+from apps.nxi_termination.models.api.nxi_termination_request import NxiTerminationApi
 
 
 class TestReqValidation(unittest.TestCase):
@@ -62,6 +63,16 @@ class TestReqValidation(unittest.TestCase):
         req_json = {}
         self.assertRaises(DataError, lambda: PlacementAPI(req_json).validate())
 
+    def test_req_nxi_validation(self):
+        req_file = "./test/apps/nxi_termination/nxi_termination.json"
+        req_json = json.loads(open(req_file).read())
+        self.assertEqual(NxiTerminationApi(req_json).validate(), None)
+
+    def test_req_invalid_nxi(self):
+        req_file = "./test/apps/nxi_termination/invalid_request.json"
+        req_json = json.loads(open(req_file).read())
+        self.assertRaises(DataError, lambda: NxiTerminationApi(req_json).validate())
+
 
 class TestResponseValidation(unittest.TestCase):