Route optimization -take 2
[optf/osdf.git] / apps / route / optimizers / simple_route_opt.py
1 # -------------------------------------------------------------------------
2 #   Copyright (c) 2020 Huawei Intellectual Property
3 #
4 #   Licensed under the Apache License, Version 2.0 (the "License");
5 #   you may not use this file except in compliance with the License.
6 #   You may obtain a copy of the License at
7 #
8 #       http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #   Unless required by applicable law or agreed to in writing, software
11 #   distributed under the License is distributed on an "AS IS" BASIS,
12 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 #   See the License for the specific language governing permissions and
14 #   limitations under the License.
15 #
16 # -------------------------------------------------------------------------
17 #
18
19 import requests
20 import json
21 from requests.auth import HTTPBasicAuth
22
23 from osdf.utils.mdc_utils import mdc_from_json
24 from osdf.logging.osdf_logging import MH, audit_log, error_log, debug_log
25 import pymzn
26 from sklearn import preprocessing
27
28 import os
29 BASE_DIR = os.path.dirname(__file__)
30
31 class RouteOpt:
32
33     """
34     This values will need to deleted.. 
35     only added for the debug purpose 
36     """
37     # DNS server and standard port of AAI.. 
38     # TODO: read the port from the configuration and add to DNS
39     aai_headers = {
40         "X-TransactionId": "9999",
41         "X-FromAppId": "OOF",
42         "Accept": "application/json",
43         "Content-Type": "application/json",
44     }
45
46     def is_cross_onap_link(self, logical_link):
47         """
48         This method checks if cross link is cross onap
49         :param logical_link:
50         :return:
51         """
52         for relationship in logical_link["relationship-list"]["relationship"]:
53             if relationship["related-to"] == "ext-aai-network":
54                 return True
55         return False
56
57     def get_links_name(self, routes,initial_start_edge,initial_end_edge, mappingTable):
58         routes=list(routes)
59         try:
60             arr=routes[0]['x']
61         except Exception as err:
62             audit_log.info("No satisfiable solutions found")
63             raise err
64         listOfLinks=[]
65         for i in range(0, len(routes[0]['x'])):
66             individual_link = {}
67             if arr[i] == 1 :
68                 # listOfLinks.append(self.fetchLogicalLinks(initial_start_edge[i], initial_end_edge[i], mappingTable))
69                 individual_link["link"] = mappingTable[initial_start_edge[i] + ":" + initial_end_edge[i]]
70                 individual_link["start_node"] = initial_start_edge[i]
71                 individual_link["end_node"] = initial_end_edge[i]
72                 listOfLinks.append(individual_link)
73
74         return listOfLinks
75
76     def solve(self, mzn_model, dzn_data):
77         return pymzn.minizinc(mzn=mzn_model, data=dzn_data)
78
79     def get_links(self, mzn_model, dzn_data, initial_start_edge,initial_end_edge, mappingTable):
80         routes = self.solve(mzn_model, dzn_data)
81         audit_log.info("mocked minizinc solution====>")
82         audit_log.info(routes)
83
84         converted_links=self.get_links_name(routes, initial_start_edge,initial_end_edge, mappingTable)
85         audit_log.info("converted links===>")
86         audit_log.info(converted_links)
87         return converted_links
88
89     def addition(self, data):
90         res = ""
91         if 'relationship-list' in data.keys():
92             relationship = data["relationship-list"]["relationship"]
93             for index, eachItem in enumerate(relationship):
94                 temp = eachItem["relationship-data"][0]
95                 if index == len(relationship) - 1:
96                     res += temp['relationship-value']
97                 else:
98                     res += temp['relationship-value'] + ":"
99
100             return data["link-name"], res
101         else:
102             return data["link-name"], res
103
104     def create_map_table(self, logical_links):
105         result = map(self.addition, logical_links)
106
107         parseTemplate = {}
108
109         for eachItem in result:
110             parseTemplate[eachItem[1]] = eachItem[0]
111         audit_log.info("mapping table")
112         audit_log.info(parseTemplate)
113         return parseTemplate
114
115     def build_dzn_data(self, src_access_node_id, dst_access_node_id, osdf_config):
116         Edge_Start = []
117         Edge_End = []
118         logical_links = self.get_logical_links(osdf_config)
119
120
121         logical_links = logical_links['logical-link']
122         audit_log.info("mocked response of AAI received (logical links) successful===>")
123         audit_log.info(logical_links)
124         # prepare map table
125         mappingTable = self.create_map_table(logical_links)
126         audit_log.info("mapping table created successfully====>")
127         audit_log.info(mappingTable)
128         # take the logical link where both the p-interface in same onap
129         if logical_links is not None:
130             audit_log.info('logical links not empty=====>')
131             for logical_link in logical_links:
132                 audit_log.info('logical_link')
133                 audit_log.info(logical_link)
134
135                 if 'relationship-list' in logical_link.keys():
136                     if not self.is_cross_onap_link(logical_link):
137                         # link is in local ONAP
138                         audit_log.info('link is inside onap===>')
139                         relationship = logical_link["relationship-list"]["relationship"]
140
141                         relationshipStartNode = relationship[0]
142                         audit_log.info('relationshipStartNode')
143                         audit_log.info(relationshipStartNode)
144                         relationshipStartNodeID = relationshipStartNode["related-link"].split("/")[-4]
145                         audit_log.info('relationshipStartNodeID')
146                         audit_log.info(relationshipStartNodeID)
147                         Edge_Start.append(relationshipStartNodeID)
148
149                         relationshipEndtNode = relationship[1]
150                         relationshipEndNodeID = relationshipEndtNode["related-link"].split("/")[-4]
151                         audit_log.info('relationshipEndNodeID')
152                         audit_log.info(relationshipEndNodeID)
153                         Edge_End.append(relationshipEndNodeID)
154                 else:
155                     continue
156
157         audit_log.info("edge start and end array of i/p address are===>")
158         audit_log.info(Edge_Start)
159         audit_log.info(Edge_End)
160         # labeling ip to number for mapping
161         le = preprocessing.LabelEncoder()
162         le.fit(Edge_Start + Edge_End)
163         dzn_start_edge = le.transform(Edge_Start)
164
165         final_dzn_start_arr = []
166         for i in range(0, len(dzn_start_edge)):
167             final_dzn_start_arr.append(dzn_start_edge[i])
168
169         final_dzn_end_arr = []
170         dzn_end_edge = le.transform(Edge_End)
171         for j in range(0, len(dzn_end_edge)):
172             final_dzn_end_arr.append(dzn_end_edge[j])
173
174         audit_log.info("start and end array that passed in dzn_data===>")
175         audit_log.info(final_dzn_start_arr)
176         audit_log.info(final_dzn_end_arr)
177
178         link_cost  = []
179         for k in range(0, len(final_dzn_start_arr)):
180             link_cost.append(1)
181
182         audit_log.info("src_access_node_id")
183         audit_log.info(src_access_node_id)
184         source= le.transform([src_access_node_id])
185         audit_log.info("vallue of source===>")
186         audit_log.info(source)
187         if source in final_dzn_start_arr :
188             start = source[0]
189             audit_log.info("source node")
190             audit_log.info(start)
191
192         audit_log.info("dst_access_node_id")
193         audit_log.info(dst_access_node_id)
194         destination= le.transform([dst_access_node_id])
195         if destination in final_dzn_end_arr :
196             end = destination[0]
197             audit_log.info("destination node")
198             audit_log.info(end)
199         # data to be prepared in the below format:
200         dzn_data = {
201             'N': self.total_node(final_dzn_start_arr + final_dzn_end_arr),
202             'M': len(final_dzn_start_arr),
203             'Edge_Start': final_dzn_start_arr,
204             'Edge_End': final_dzn_end_arr,
205             'L': link_cost,
206             'Start': start,
207             'End': end,
208         }
209         # can not do reverse mapping outside of this scope, so doing here
210         audit_log.info("reverse mapping after prepared dzn_data")
211         initial_start_edge=le.inverse_transform(final_dzn_start_arr)
212         initial_end_edge=le.inverse_transform(final_dzn_end_arr)
213         audit_log.info(initial_start_edge)
214         audit_log.info(initial_end_edge)
215         return dzn_data, initial_start_edge,initial_end_edge, mappingTable
216
217     def total_node(self, node):
218         nodeSet = set()
219         for i in range(0, len(node)):
220             nodeSet.add(node[i])
221         total_node = len(nodeSet)
222         return total_node
223
224     def get_route(self, request, osdf_config):
225         """
226         This method checks
227         :param logical_link:
228         :return:
229         """
230         try:
231             routeInfo = request["routeInfo"]["routeRequests"]
232             routeRequest = routeInfo[0]
233             src_access_node_id = routeRequest["srcPort"]["accessNodeId"]
234             dst_access_node_id = routeRequest["dstPort"]["accessNodeId"]
235
236             dzn_data, initial_start_edge, initial_end_edge, mappingTable = self.build_dzn_data(src_access_node_id, dst_access_node_id, osdf_config)
237             #mzn_model = "/home/root1/Videos/projects/osdf/test/functest/simulators/osdf/optimizers/routeopt/route_opt.mzn"
238             mzn_model = os.path.join(BASE_DIR, 'route_opt.mzn')
239
240             routeSolutions = self.get_links(mzn_model, dzn_data, initial_start_edge,initial_end_edge, mappingTable)
241
242             return {
243             "requestId": request["requestInfo"]["requestId"],
244             "transactionId": request["requestInfo"]["transactionId"],
245             "statusMessage": " ",
246             "requestStatus": "accepted",
247             "solutions": routeSolutions
248             }
249         except Exception as err:
250             audit_log.info(err)
251             raise err
252
253     def get_logical_links(self, osdf_config):
254         """
255         This method returns list of all cross ONAP links
256         from /aai/v14/network/logical-links?operation-status="Up"
257         :return: logical-links[]
258         """
259
260         config = osdf_config.deployment
261         aai_url = config["aaiUrl"]
262         aai_req_url = aai_url + config["aaiGetLinksUrl"]
263
264         response = requests.get(aai_req_url,headers=self.aai_headers,auth=HTTPBasicAuth("AAI", "AAI"),verify=False)
265         if response.status_code == 200:
266             return response.json()