Route optimization -take 2 91/106291/2
authorSithara Nambiar <sitharav.aredath@huawei.com>
Mon, 20 Apr 2020 16:09:33 +0000 (21:39 +0530)
committerSithara Nambiar <sitharav.aredath@huawei.com>
Tue, 21 Apr 2020 08:23:52 +0000 (13:53 +0530)
Issue-ID: OPTFRA-420

Signed-off-by: Sithara Nambiar <sitharav.aredath@huawei.com>
Change-Id: I06624f5adee060ce220a82f2a05a17c0986ca904

apps/route/optimizers/simple_route_opt.py
config/osdf_config.yaml
osdfapp.py
test/functest/simulators/simulated-config/osdf_config.yaml
test/simple_route_opt/AAI.json [new file with mode: 0644]
test/simple_route_opt/routeOpt.json [new file with mode: 0644]
test/test_simple_route_opt.py [new file with mode: 0644]

index 27c1141..9113516 100644 (file)
@@ -17,6 +17,7 @@
 #
 
 import requests
+import json
 from requests.auth import HTTPBasicAuth
 
 from osdf.utils.mdc_utils import mdc_from_json
@@ -35,18 +36,14 @@ class RouteOpt:
     """
     # DNS server and standard port of AAI.. 
     # TODO: read the port from the configuration and add to DNS
-    aai_host = "https://aai.api.simpledemo.onap.org:8443"
-    audit_log.info("base directory")
-    audit_log.info(BASE_DIR)
     aai_headers = {
         "X-TransactionId": "9999",
         "X-FromAppId": "OOF",
         "Accept": "application/json",
         "Content-Type": "application/json",
-        "Real-Time": "true"
     }
 
-    def isCrossONAPLink(self, logical_link):
+    def is_cross_onap_link(self, logical_link):
         """
         This method checks if cross link is cross onap
         :param logical_link:
@@ -57,57 +54,54 @@ class RouteOpt:
                 return True
         return False
 
-    def getLinksName(self, routes,initial_start_edge,initial_end_edge, mappingTable):
+    def get_links_name(self, routes,initial_start_edge,initial_end_edge, mappingTable):
         routes=list(routes)
-        arr=routes[0]['x']
+        try:
+            arr=routes[0]['x']
+        except Exception as err:
+            audit_log.info("No satisfiable solutions found")
+            raise err
         listOfLinks=[]
         for i in range(0, len(routes[0]['x'])):
+            individual_link = {}
             if arr[i] == 1 :
                 # listOfLinks.append(self.fetchLogicalLinks(initial_start_edge[i], initial_end_edge[i], mappingTable))
-                listOfLinks.append(mappingTable[initial_start_edge[i] + ":" + initial_end_edge[i]])
+                individual_link["link"] = mappingTable[initial_start_edge[i] + ":" + initial_end_edge[i]]
+                individual_link["start_node"] = initial_start_edge[i]
+                individual_link["end_node"] = initial_end_edge[i]
+                listOfLinks.append(individual_link)
 
         return listOfLinks
 
-    # def search(self, ip1, ip2, dic):
-    #     if ip1 == "" or ip2 == "":
-    #         return ""
-    #     else:
-    #         string = ip1 + ":" + ip2
-    #         return dic[string]
-    #
-    # def fetchLogicalLinks(self, initial_start_edge, initial_end_edge, mappingTable):
-    #     link_name=self.search(initial_start_edge, initial_end_edge, mappingTable)
-    #     return link_name
-
-
-    # def fetchLogicalLinks(self, initial_start_edge, initial_end_edge, mappingTable):
-    #     return mappingTable[initial_start_edge + ":" + initial_end_edge]
-
     def solve(self, mzn_model, dzn_data):
         return pymzn.minizinc(mzn=mzn_model, data=dzn_data)
 
-    def getLinks(self, mzn_model, dzn_data, initial_start_edge,initial_end_edge, mappingTable):
+    def get_links(self, mzn_model, dzn_data, initial_start_edge,initial_end_edge, mappingTable):
         routes = self.solve(mzn_model, dzn_data)
         audit_log.info("mocked minizinc solution====>")
         audit_log.info(routes)
 
-        converted_links=self.getLinksName(routes, initial_start_edge,initial_end_edge, mappingTable)
+        converted_links=self.get_links_name(routes, initial_start_edge,initial_end_edge, mappingTable)
         audit_log.info("converted links===>")
         audit_log.info(converted_links)
         return converted_links
 
     def addition(self, data):
-        relationship = data["relationship-list"]["relationship"]
         res = ""
-        for index, eachItem in enumerate(relationship):
-            if index == len(relationship) - 1:
-                res += eachItem["accessNodeId"]
-            else:
-                res += eachItem["accessNodeId"] + ":"
-
-        return data["link-name"], res
-
-    def createMapTable(self, logical_links):
+        if 'relationship-list' in data.keys():
+            relationship = data["relationship-list"]["relationship"]
+            for index, eachItem in enumerate(relationship):
+                temp = eachItem["relationship-data"][0]
+                if index == len(relationship) - 1:
+                    res += temp['relationship-value']
+                else:
+                    res += temp['relationship-value'] + ":"
+
+            return data["link-name"], res
+        else:
+            return data["link-name"], res
+
+    def create_map_table(self, logical_links):
         result = map(self.addition, logical_links)
 
         parseTemplate = {}
@@ -118,30 +112,47 @@ class RouteOpt:
         audit_log.info(parseTemplate)
         return parseTemplate
 
-    def build_dzn_data(self, src_access_node_id, dst_access_node_id):
+    def build_dzn_data(self, src_access_node_id, dst_access_node_id, osdf_config):
         Edge_Start = []
         Edge_End = []
-        logical_links = self.get_logical_links()
+        logical_links = self.get_logical_links(osdf_config)
+
+
+        logical_links = logical_links['logical-link']
         audit_log.info("mocked response of AAI received (logical links) successful===>")
         audit_log.info(logical_links)
         # prepare map table
-        mappingTable = self.createMapTable(logical_links)
+        mappingTable = self.create_map_table(logical_links)
+        audit_log.info("mapping table created successfully====>")
+        audit_log.info(mappingTable)
         # take the logical link where both the p-interface in same onap
         if logical_links is not None:
+            audit_log.info('logical links not empty=====>')
             for logical_link in logical_links:
-                if not self.isCrossONAPLink(logical_link):
-                    # link is in local ONAP
-                    relationship = logical_link["relationship-list"]["relationship"]
-
-                    relationshipStartNode = relationship[0]
-                    relationshipStartNodeID = relationshipStartNode["related-link"].split("/")[-1]
-                    start_accessNodeId = relationshipStartNodeID.split("-")[-3]
-                    Edge_Start.append(start_accessNodeId)
-
-                    relationshipEndtNode = relationship[1]
-                    relationshipEndNodeID = relationshipEndtNode["related-link"].split("/")[-1]
-                    end_accessNodeId = relationshipEndNodeID.split("-")[-3]
-                    Edge_End.append(end_accessNodeId)
+                audit_log.info('logical_link')
+                audit_log.info(logical_link)
+
+                if 'relationship-list' in logical_link.keys():
+                    if not self.is_cross_onap_link(logical_link):
+                        # link is in local ONAP
+                        audit_log.info('link is inside onap===>')
+                        relationship = logical_link["relationship-list"]["relationship"]
+
+                        relationshipStartNode = relationship[0]
+                        audit_log.info('relationshipStartNode')
+                        audit_log.info(relationshipStartNode)
+                        relationshipStartNodeID = relationshipStartNode["related-link"].split("/")[-4]
+                        audit_log.info('relationshipStartNodeID')
+                        audit_log.info(relationshipStartNodeID)
+                        Edge_Start.append(relationshipStartNodeID)
+
+                        relationshipEndtNode = relationship[1]
+                        relationshipEndNodeID = relationshipEndtNode["related-link"].split("/")[-4]
+                        audit_log.info('relationshipEndNodeID')
+                        audit_log.info(relationshipEndNodeID)
+                        Edge_End.append(relationshipEndNodeID)
+                else:
+                    continue
 
         audit_log.info("edge start and end array of i/p address are===>")
         audit_log.info(Edge_Start)
@@ -149,7 +160,6 @@ class RouteOpt:
         # labeling ip to number for mapping
         le = preprocessing.LabelEncoder()
         le.fit(Edge_Start + Edge_End)
-        # print(le.classes_)
         dzn_start_edge = le.transform(Edge_Start)
 
         final_dzn_start_arr = []
@@ -211,39 +221,46 @@ class RouteOpt:
         total_node = len(nodeSet)
         return total_node
 
-    def getRoute(self, request):
+    def get_route(self, request, osdf_config):
         """
         This method checks
         :param logical_link:
         :return:
         """
-        routeInfo = request["routeInfo"]["routeRequests"]
-        routeRequest = routeInfo[0]
-        src_access_node_id = routeRequest["srcPort"]["accessNodeId"]
-        dst_access_node_id = routeRequest["dstPort"]["accessNodeId"]
+        try:
+            routeInfo = request["routeInfo"]["routeRequests"]
+            routeRequest = routeInfo[0]
+            src_access_node_id = routeRequest["srcPort"]["accessNodeId"]
+            dst_access_node_id = routeRequest["dstPort"]["accessNodeId"]
 
-        dzn_data, initial_start_edge, initial_end_edge, mappingTable = self.build_dzn_data(src_access_node_id, dst_access_node_id )
-        #mzn_model = "/home/root1/Videos/projects/osdf/test/functest/simulators/osdf/optimizers/routeopt/route_opt.mzn"
-        mzn_model = os.path.join(BASE_DIR, 'route_opt.mzn')
+            dzn_data, initial_start_edge, initial_end_edge, mappingTable = self.build_dzn_data(src_access_node_id, dst_access_node_id, osdf_config)
+            #mzn_model = "/home/root1/Videos/projects/osdf/test/functest/simulators/osdf/optimizers/routeopt/route_opt.mzn"
+            mzn_model = os.path.join(BASE_DIR, 'route_opt.mzn')
 
-        routeSolutions = self.getLinks(mzn_model, dzn_data, initial_start_edge,initial_end_edge, mappingTable)
+            routeSolutions = self.get_links(mzn_model, dzn_data, initial_start_edge,initial_end_edge, mappingTable)
 
-        return {
+            return {
             "requestId": request["requestInfo"]["requestId"],
             "transactionId": request["requestInfo"]["transactionId"],
             "statusMessage": " ",
             "requestStatus": "accepted",
             "solutions": routeSolutions
-        }
+            }
+        except Exception as err:
+            audit_log.info(err)
+            raise err
 
-    def get_logical_links(self):
+    def get_logical_links(self, osdf_config):
         """
         This method returns list of all cross ONAP links
         from /aai/v14/network/logical-links?operation-status="Up"
         :return: logical-links[]
         """
-        logical_link_url = "/aai/v13/network/logical-links?operational-status=up"
-        aai_req_url = self.aai_host + logical_link_url
+
+        config = osdf_config.deployment
+        aai_url = config["aaiUrl"]
+        aai_req_url = aai_url + config["aaiGetLinksUrl"]
+
         response = requests.get(aai_req_url,headers=self.aai_headers,auth=HTTPBasicAuth("AAI", "AAI"),verify=False)
         if response.status_code == 200:
             return response.json()
\ No newline at end of file
index c7c5898..4802a67 100755 (executable)
@@ -50,6 +50,10 @@ configDbUrl: http://config.db.url:8080
 configDbGetCellListUrl: 'SDNCConfigDBAPI/getCellList'
 configDbGetNbrListUrl: 'SDNCConfigDBAPI/getNbrList'
 
+#aai api
+aaiUrl: "https://aai.url:30233"
+aaiGetLinksUrl: "/aai/v16/network/logical-links"
+
 pciHMSUsername: test
 pciHMSPassword: passwd
 
index fdc2c1d..1099e55 100755 (executable)
@@ -101,7 +101,7 @@ def do_route_calc():
     """
     request_json = request.get_json()
     audit_log.info("Calculate Route request received!")
-    response = RouteOpt().getRoute(request_json)
+    response = RouteOpt().get_route(request_json, osdf_config)
     return response
 
 @app.route("/api/oof/v1/selection/nst", methods=["POST"])
index eccad14..414d7c7 100755 (executable)
@@ -71,3 +71,6 @@ configDbGetNbrListUrl: 'getNbrList'
 pciHMSUsername: ""   # pcihandler username for call back.
 pciHMSPassword: ""   # pcihandler password for call back.
 
+aaiUrl: "https://api.url:30233"
+aaiGetLinksUrl: "/aai/v16/network/logical-links"
+
diff --git a/test/simple_route_opt/AAI.json b/test/simple_route_opt/AAI.json
new file mode 100644 (file)
index 0000000..6ef264b
--- /dev/null
@@ -0,0 +1,164 @@
+{
+  "logical-link": [
+    {
+      "link-name": "link-id-1",
+      "in-maint": true,
+      "link-type": "example-link-type-val-16287",
+      "resource-version": "1585009311719",
+      "operational-status": "UP",
+      "relationship-list": {
+        "relationship": [
+          {
+            "related-to": "p-interface",
+            "relationship-label": "tosca.relationships.network.LinksTo",
+            "related-link": "/aai/v16/network/pnfs/pnf/20.20.20.20/p-interfaces/p-interface/p-interface-3",
+            "relationship-data": [
+              {
+                "relationship-key": "pnf.pnf-name",
+                "relationship-value": "20.20.20.20"
+              },
+              {
+                "relationship-key": "p-interface.interface-name",
+                "relationship-value": "p-interface-3"
+              }
+            ],
+            "related-to-property": [
+              {
+                "property-key": "p-interface.prov-status"
+              }
+            ]
+          },
+          {
+            "related-to": "p-interface",
+            "relationship-label": "tosca.relationships.network.LinksTo",
+            "related-link": "/aai/v16/network/pnfs/pnf/10.10.10.10/p-interfaces/p-interface/p-interface-2",
+            "relationship-data": [
+              {
+                "relationship-key": "pnf.pnf-name",
+                "relationship-value": "10.10.10.10"
+              },
+              {
+                "relationship-key": "p-interface.interface-name",
+                "relationship-value": "p-interface-2"
+              }
+            ],
+            "related-to-property": [
+              {
+                "property-key": "p-interface.prov-status"
+              }
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "link-name": "link-id-2",
+      "in-maint": true,
+      "link-type": "example-link-type-val-16287",
+      "resource-version": "1584943281792",
+      "operational-status": "UP",
+      "relationship-list": {
+        "relationship": [
+          {
+            "related-to": "p-interface",
+            "relationship-label": "tosca.relationships.network.LinksTo",
+            "related-link": "/aai/v16/network/pnfs/pnf/22.22.22.22/p-interfaces/p-interface/p-interface-7",
+            "relationship-data": [
+              {
+                "relationship-key": "pnf.pnf-name",
+                "relationship-value": "22.22.22.22"
+              },
+              {
+                "relationship-key": "p-interface.interface-name",
+                "relationship-value": "p-interface-7"
+              }
+            ],
+            "related-to-property": [
+              {
+                "property-key": "p-interface.prov-status"
+              }
+            ]
+          },
+          {
+            "related-to": "p-interface",
+            "relationship-label": "tosca.relationships.network.LinksTo",
+            "related-link": "/aai/v16/network/pnfs/pnf/11.11.11.11/p-interfaces/p-interface/p-interface-6",
+            "relationship-data": [
+              {
+                "relationship-key": "pnf.pnf-name",
+                "relationship-value": "11.11.11.11"
+              },
+              {
+                "relationship-key": "p-interface.interface-name",
+                "relationship-value": "p-interface-6"
+              }
+            ],
+            "related-to-property": [
+              {
+                "property-key": "p-interface.prov-status"
+              }
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "link-name": "link-id-3",
+      "in-maint": true,
+      "link-type": "example-link-type-val-16287",
+      "resource-version": "1584943345290",
+      "operational-status": "UP",
+      "relationship-list": {
+        "relationship": [
+          {
+            "related-to": "p-interface",
+            "relationship-label": "tosca.relationships.network.LinksTo",
+            "related-link": "/aai/v16/network/pnfs/pnf/11.11.11.11/p-interfaces/p-interface/p-interface-5",
+            "relationship-data": [
+              {
+                "relationship-key": "pnf.pnf-name",
+                "relationship-value": "11.11.11.11"
+              },
+              {
+                "relationship-key": "p-interface.interface-name",
+                "relationship-value": "p-interface-5"
+              }
+            ],
+            "related-to-property": [
+              {
+                "property-key": "p-interface.prov-status"
+              }
+            ]
+          },
+          {
+            "related-to": "p-interface",
+            "relationship-label": "tosca.relationships.network.LinksTo",
+            "related-link": "/aai/v16/network/pnfs/pnf/20.20.20.20/p-interfaces/p-interface/p-interface-4",
+            "relationship-data": [
+              {
+                "relationship-key": "pnf.pnf-name",
+                "relationship-value": "20.20.20.20"
+              },
+              {
+                "relationship-key": "p-interface.interface-name",
+                "relationship-value": "p-interface-4"
+              }
+            ],
+            "related-to-property": [
+              {
+                "property-key": "p-interface.prov-status"
+              }
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "link-name": "rahul",
+      "in-maint": true,
+      "link-type": "example-link-type-val-rahul",
+      "resource-version": "1585023629505",
+      "operational-status": "UP"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/test/simple_route_opt/routeOpt.json b/test/simple_route_opt/routeOpt.json
new file mode 100644 (file)
index 0000000..887b85b
--- /dev/null
@@ -0,0 +1,34 @@
+{
+  "requestInfo": {
+    "transactionId": "xxx-xxx-xxxx",
+    "requestId": "yyy-yyy-yyyy",
+    "callbackUrl": "https://wiki.onap.org:5000/callbackUrl",
+    "sourceId": "",
+    "requestType": "create",
+    "numSolutions": 1,
+    "optimizers": [
+      "route"
+    ],
+    "timeout": 600
+  },
+  "routeInfo": {
+    "routeRequests": [
+      {
+        "srcPort": {
+          "accessTopologyId": "Topo113",
+          "accessClientId": "clientU12",
+          "accessProviderId": "VDF1234",
+          "accessNodeId": "22.22.22.22",
+          "accessLtpId": "1345"
+        },
+        "dstPort": {
+          "accessTopologyId": "Topo3421",
+          "accessClientId": "clientD123",
+          "accessProviderId": "VDF3214",
+          "accessNodeId": "10.10.10.10",
+          "accessLtpId": "3452"
+        }
+      }
+    ]
+  }
+}
diff --git a/test/test_simple_route_opt.py b/test/test_simple_route_opt.py
new file mode 100644 (file)
index 0000000..3b4facc
--- /dev/null
@@ -0,0 +1,57 @@
+from apps.route.optimizers.simple_route_opt import RouteOpt
+from osdf.utils.interfaces import json_from_file
+from unittest.mock import patch
+import osdf.config.loader as config_loader
+from osdf.utils.programming_utils import DotDict
+import unittest
+
+
+class TestSimpleRouteOptimization(unittest.TestCase):
+    @patch('apps.route.optimizers.simple_route_opt.requests.get')
+    @patch('apps.route.optimizers.simple_route_opt.pymzn.minizinc')
+    def test_process_nst_selection_solutions( self, mock_solve, mock_get):
+
+        main_dir = ""
+        response_data_file = main_dir + "test/simple_route_opt/AAI.json"
+        mock_get.return_value.json.return_value = json_from_file(response_data_file)
+        mock_get.return_value.status_code = 200
+        mock_solve.return_value = [{'x': [1, 1, 1]}]
+        self.config_spec = {
+            "deployment": "test/functest/simulators/simulated-config/osdf_config.yaml",
+            "core": "test/functest/simulators/simulated-config/common_config.yaml"
+        }
+        self.osdf_config = DotDict(config_loader.all_configs(**self.config_spec))
+        parameter_data_file = main_dir + "test/simple_route_opt/routeOpt.json"
+        request_json = json_from_file(parameter_data_file)
+        mock_response = {
+            "requestId": "yyy-yyy-yyyy",
+            "requestStatus": "accepted",
+            "solutions": [
+                {
+                    "end_node": "10.10.10.10",
+                    "link": "link-id-1",
+                    "start_node": "20.20.20.20"
+                },
+                {
+                    "end_node": "11.11.11.11",
+                    "link": "link-id-2",
+                    "start_node": "22.22.22.22"
+                },
+                {
+                    "end_node": "20.20.20.20",
+                    "link": "link-id-3",
+                    "start_node": "11.11.11.11"
+                }
+            ],
+            "statusMessage": " ",
+            "transactionId": "xxx-xxx-xxxx"
+        }
+        routopt = RouteOpt()
+        actual_response = routopt.get_route(request_json,self.osdf_config)
+        self.assertEqual(mock_response, actual_response)
+
+
+
+if __name__ == '__main__':
+    unittest.main()
+