summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/route/optimizers/inter_domain_route_opt.py370
-rwxr-xr-xconfig/osdf_config.yaml4
-rw-r--r--docs/sections/offeredapis.rst14
-rw-r--r--docs/sections/swaggerdoc/oof-optf-opteng-api.json584
-rwxr-xr-xosdfapp.py11
-rwxr-xr-xtest/functest/simulators/simulated-config/osdf_config.yaml4
-rw-r--r--test/inter_domain_route_opt/bandwidth_attributes.json176
-rw-r--r--test/inter_domain_route_opt/controllers_for_interfaces.json62
-rw-r--r--test/inter_domain_route_opt/controllers_list.json16
-rw-r--r--test/inter_domain_route_opt/get_links.json157
-rw-r--r--test/inter_domain_route_opt/request.json30
-rw-r--r--test/test_inter_domain_route_opt.py151
-rw-r--r--tox.ini14
13 files changed, 1591 insertions, 2 deletions
diff --git a/apps/route/optimizers/inter_domain_route_opt.py b/apps/route/optimizers/inter_domain_route_opt.py
new file mode 100644
index 0000000..253c7b2
--- /dev/null
+++ b/apps/route/optimizers/inter_domain_route_opt.py
@@ -0,0 +1,370 @@
+# -------------------------------------------------------------------------
+# Copyright (c) 2020 Fujitsu Limited Intellectual Property
+#
+# 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 os
+import itertools
+import json
+import requests
+from requests.auth import HTTPBasicAuth
+import urllib3
+
+from osdf.logging.osdf_logging import audit_log
+import pymzn
+from sklearn import preprocessing
+
+BASE_DIR = os.path.dirname(__file__)
+urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+
+class InterDomainRouteOpt:
+
+ """
+ This values will need to deleted..
+ only added for the debug purpose
+ """
+ aai_headers = {
+ "X-TransactionId": "9999",
+ "X-FromAppId": "OOF",
+ "Accept": "application/json",
+ "Content-Type": "application/json",
+ }
+
+
+ def get_route(self, request, osdf_config):
+ """
+ This method processes the mdons route request
+ and returns an optimised path for the given
+ two ports
+ """
+
+ try:
+ route_info = request["routeInfo"]["routeRequest"]
+ src_controller_id = route_info["srcDetails"]["controllerId"]
+ src_port_id = route_info["srcDetails"]["interfaceId"]
+ dst_controller_id = route_info["dstDetails"]["controllerId"]
+ dst_port_id = route_info["dstDetails"]["interfaceId"]
+ service_rate = route_info["serviceRate"]
+ dzn_data, mapping_table = self.build_dzn_data(osdf_config, src_controller_id,
+ dst_controller_id, service_rate)
+ audit_log.info("Dzn data")
+ audit_log.info(dzn_data)
+ mzn_model = os.path.join(BASE_DIR, 'route_opt.mzn')
+ links_list = self.find_suitable_path(mzn_model, dzn_data, mapping_table)
+ ordered_list = self.get_ordered_route_list(links_list,
+ src_controller_id, dst_controller_id)
+ solution = self.get_solution_object(ordered_list, src_port_id, dst_port_id)
+ return {
+ "requestId": request["requestInfo"]["requestId"],
+ "transactionId": request["requestInfo"]["transactionId"],
+ "statusMessage": "SUCCESS",
+ "requestStatus": "accepted",
+ "solutions": solution
+ }
+ except Exception as err:
+ audit_log.info(err)
+ raise err
+
+ def get_solution_object(self, ordered_list, src_port_id, dst_port_id):
+ """
+ :param ordered_list: service_route list
+ :param src_port_id: source port id of route
+ :param dst_port_id: destination port id of route
+ :return: solution object of the route respone
+ """
+ service_route_list = []
+ link_list = []
+ for value in ordered_list:
+ service_route_object = {}
+ service_route_object["srcInterfaceId"] = src_port_id
+ service_route_object["dstInterfaceId"] = value["srcPortId"]
+ service_route_object["controllerId"] = value["srcControllerId"]
+ service_route_list.append(service_route_object)
+ link_list.append(value["linkName"])
+ src_port_id = value["dstPortId"]
+ dst_controller_id = value["dstControllerId"]
+ service_route_object = {}
+ service_route_object["srcInterfaceId"] = src_port_id
+ service_route_object["dstInterfaceId"] = dst_port_id
+ service_route_object["controllerId"] = dst_controller_id
+ service_route_list.append(service_route_object)
+ route_info_object = {
+ "serviceRoute" : service_route_list,
+ "linkList" : link_list
+ }
+ solution = {
+ "routeInfo" : route_info_object
+ }
+ return solution
+
+
+ def get_ordered_route_list(self, link_list, src_controller_id, dst_controller_id):
+ """
+ :param link_list: link list from the minizinc response
+ :param src_controller_id: source port id of route
+ :param dst_controller_id: destination port id of route
+ :return: route list in order
+ """
+ ordered_link_list = []
+ flag = True
+ while flag:
+ for item in link_list:
+ if item["srcControllerId"] == src_controller_id:
+ ordered_link_list.append(item)
+ src_controller_id = item["dstControllerId"]
+ if src_controller_id == dst_controller_id:
+ flag = False
+ return ordered_link_list
+
+
+ def find_suitable_path(self, mzn_model, dzn_data, mapping_table):
+ """
+ :param mzn_model: minizinc model details
+ :param dzn_data: minizinc data
+ :param mapping_table: list that maintains AAI link details
+ :return: list of link from after running minizinc
+ """
+ minizinc_solution = self.solve(mzn_model, dzn_data)
+ audit_log.info("Minizinc Solution ==========>")
+ routes = list(minizinc_solution)
+ audit_log.info(routes)
+ try:
+ arr = routes[0]['x']
+ except Exception as err:
+ audit_log.info("No minizinc solutions found")
+ raise err
+ links_list = []
+ for i in range(0, len(routes[0]['x'])):
+ if arr[i] == 1:
+ links_list.append(mapping_table[i])
+ return links_list
+
+
+ def process_inter_domain_link(self, logical_link, osdf_config):
+ """
+ :param logical_link: logical links from AAI
+ :param osdf_config: OSDF config details
+ :return: list of link object with src and dst controller details
+ """
+ link_details = {}
+ link_details["linkName"] = logical_link["link-name"]
+ relationship = logical_link["relationship-list"]["relationship"]
+ flag = 1
+
+ for value in relationship:
+ if value["related-to"] == "p-interface" and flag == 1:
+ src_port_id = value["relationship-data"][1]["relationship-value"]
+ src_controller_id = self.get_controller_for_interface(osdf_config, src_port_id)
+ link_details["srcPortId"] = src_port_id
+ link_details["srcControllerId"] = src_controller_id
+ flag += 1
+ elif value["related-to"] == "p-interface" and flag == 2:
+ dest_port_id = value["relationship-data"][1]["relationship-value"]
+ dest_controller_id = self.get_controller_for_interface(osdf_config, dest_port_id)
+ link_details["dstPortId"] = dest_port_id
+ link_details["dstControllerId"] = dest_controller_id
+ return link_details
+
+
+ def prepare_map_table(self, osdf_config, logical_links):
+ """
+ :param logical_links: logical links from AAI
+ :param osdf_config: OSDF config details
+ :return: list of link object with src and dst controller details
+ """
+ results = map(self.process_inter_domain_link, logical_links,
+ itertools.repeat(osdf_config, len(logical_links)))
+ new_results = list(results)
+
+ new_list = []
+ new_list += new_results
+ for i in new_results:
+ link_details = {}
+ link_details["linkName"] = i["linkName"]
+ link_details["srcPortId"] = i["dstPortId"]
+ link_details["srcControllerId"] = i["dstControllerId"]
+ link_details["dstPortId"] = i["srcPortId"]
+ link_details["dstControllerId"] = i["srcControllerId"]
+ new_list.append(link_details)
+ return new_list
+
+
+ def solve(self, mzn_model, dzn_data):
+ """
+ :param mzn_model: minizinc template
+ :param dzn_data: minizinc data model
+ :return: minizinc response
+ """
+ return pymzn.minizinc(mzn=mzn_model, data=dzn_data)
+
+
+ def get_links_based_on_bandwidth_attributes(self, logical_links_list,
+ osdf_config, service_rate):
+ """
+ This method filters the logical links based on the
+ bandwidth attribute availability of the interfaces
+ from AAI
+ :return: filtered_list[]
+ """
+ filtered_list = []
+ for logical_link in logical_links_list:
+ relationship = logical_link["relationship-list"]["relationship"]
+ count = 0
+ for value in relationship:
+ if value["related-to"] == "p-interface":
+ interface_url = value["related-link"]
+ if self.get_available_bandwidth_aai(interface_url, osdf_config, service_rate):
+ count += 1
+ if count == 2:
+ filtered_list.append(logical_link)
+
+ return filtered_list
+
+
+ def build_dzn_data(self, osdf_config, src_controller_id, dst_controller_id, service_rate):
+ """
+ :param osdf_config: OSDF config details
+ :param src_controller_id: controller Id of the source port
+ :param dst_controller_id: controller id of the destination port
+ :param service_rate: service rate
+ :return: mapping atble which maintains link details from AAI
+ and minizinc data model to be used by template
+ """
+ logical_links = self.get_inter_domain_links(osdf_config)
+ logical_links_list = logical_links["logical-link"]
+ mapping_table = self.prepare_map_table(osdf_config,
+ self.get_links_based_on_bandwidth_attributes(logical_links_list, osdf_config, service_rate))
+
+ edge_start = []
+ edge_end = []
+ for item in mapping_table:
+ edge_start.append(item["srcControllerId"])
+ edge_end.append(item["dstControllerId"])
+ link_cost = []
+ for k in range(0, len(edge_start)):
+ link_cost.append(1)
+ list_controllers = self.get_controllers_from_aai(osdf_config)
+ le = preprocessing.LabelEncoder()
+ le.fit(list_controllers)
+
+ start_edge = le.transform(edge_start)
+ end_edge = le.transform(edge_end)
+ source = le.transform([src_controller_id])
+ destination = le.transform([dst_controller_id])
+
+ final_dzn_start_arr = []
+ for i in start_edge:
+ final_dzn_start_arr.append(i)
+
+ final_dzn_end_arr = []
+ for j in end_edge:
+ final_dzn_end_arr.append(j)
+
+ contollers_length = len(list_controllers)
+ no_of_edges = len(final_dzn_start_arr)
+ dzn_data = {
+ 'N': contollers_length,
+ 'M': no_of_edges,
+ 'Edge_Start': final_dzn_start_arr,
+ 'Edge_End': final_dzn_end_arr,
+ 'L': link_cost,
+ 'Start': source[0],
+ 'End' : destination[0]
+ }
+ return dzn_data, mapping_table
+
+
+ def get_inter_domain_links(self, osdf_config):
+ """
+ This method returns list of all cross ONAP links
+ from /aai/v19/network/logical-links?link-type=inter-domain&operational-status="Up"
+ :return: logical-links[]
+ """
+
+ config = osdf_config.deployment
+ aai_url = config["aaiUrl"]
+ aai_req_url = aai_url + config["aaiGetInterDomainLinksUrl"]
+ response = requests.get(aai_req_url, headers=self.aai_headers,
+ auth=HTTPBasicAuth("AAI", "AAI"), verify=False)
+ if response.status_code == 200:
+ return response.json()
+
+
+ def get_controller_for_interface(self, osdf_config, port_id):
+ """
+ This method returns returns the controller id
+ given a p-interface from the below query
+ :return: controller_id
+ """
+ data = {
+ "start": ["external-system"],
+ "query": "query/getDomainController?portid="
+ }
+ query = data.get("query") + port_id
+ data.update(query=query)
+ config = osdf_config.deployment
+ aai_url = config["aaiUrl"]
+ aai_req_url = aai_url + config["controllerQueryUrl"]
+ response = requests.put(aai_req_url, data=json.dumps(data),
+ headers=self.aai_headers,
+ auth=HTTPBasicAuth("AAI", "AAI"),
+ verify=False)
+ if response.status_code == 200:
+ response_body = response.json()
+ return response_body["results"][0]["esr-thirdparty-sdnc"]["thirdparty-sdnc-id"]
+
+
+ def get_controllers_from_aai(self, osdf_config):
+ """
+ This method returns returns the list of
+ controller names in AAI
+ :return: controllers_list[]
+ """
+ controllers_list = []
+ config = osdf_config.deployment
+ aai_url = config["aaiUrl"]
+ aai_req_url = aai_url + config["aaiGetControllersUrl"]
+ response = requests.get(aai_req_url,
+ headers=self.aai_headers,
+ auth=HTTPBasicAuth("AAI", "AAI"),
+ verify=False)
+ if response.status_code == 200:
+ response_body = response.json()
+ esr_thirdparty_list = response_body["esr-thirdparty-sdnc"]
+
+ for item in esr_thirdparty_list:
+ controllers_list.append(item["thirdparty-sdnc-id"])
+ return controllers_list
+
+
+ def get_available_bandwidth_aai(self, interface_url, osdf_config, service_rate):
+ """
+ Checks if the given interface has the required bandwidth
+ :return: boolean flag
+ """
+ config = osdf_config.deployment
+ aai_url = config["aaiUrl"]
+ aai_req_url = aai_url + interface_url + "?depth=all"
+ response = requests.get(aai_req_url,
+ headers=self.aai_headers,
+ auth=HTTPBasicAuth("AAI", "AAI"), verify=False)
+ if response.status_code == 200:
+ response_body = response.json()
+ available_bandwidth = response_body["bandwidth-attributes"]["bandwidth-attribute"][0]["available-bandwidth-map"]["available-bandwidth"]
+ for i in available_bandwidth:
+ if i["odu-type"] == service_rate and i["number"] > 0:
+ return True
diff --git a/config/osdf_config.yaml b/config/osdf_config.yaml
index 4802a67..19f5574 100755
--- a/config/osdf_config.yaml
+++ b/config/osdf_config.yaml
@@ -53,6 +53,10 @@ configDbGetNbrListUrl: 'SDNCConfigDBAPI/getNbrList'
#aai api
aaiUrl: "https://aai.url:30233"
aaiGetLinksUrl: "/aai/v16/network/logical-links"
+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
+
pciHMSUsername: test
pciHMSPassword: passwd
diff --git a/docs/sections/offeredapis.rst b/docs/sections/offeredapis.rst
index f50831f..8269dea 100644
--- a/docs/sections/offeredapis.rst
+++ b/docs/sections/offeredapis.rst
@@ -11,8 +11,20 @@ This document describes the OSDF HAS (Homing and Allocation Service) API
To view API documentation in the interactive swagger UI download the following and
paste into the swagger tool here: https://editor.swagger.io
-:download:`oof-osdf-has-api.json <./swaggerdoc/oof-osdf-has-api.json>`
+.. csv-table::
+ :header: "API name", "Swagger JSON"
+ :widths: 10,5
+ "OOF OSDF HAS API", ":download:`link <./swaggerdoc/oof-osdf-has-api.json>`"
+ "OOF OPTENG API", ":download:`link <./swaggerdoc/oof-optf-opteng-api.json>`"
+
+OOF OSDF HAS API
+................
.. swaggerv2doc:: ./swaggerdoc/oof-osdf-has-api.json
+OOF OPTENG API
+..............
+.. swaggerv2doc:: ./swaggerdoc/oof-optf-opteng-api.json
+
+
diff --git a/docs/sections/swaggerdoc/oof-optf-opteng-api.json b/docs/sections/swaggerdoc/oof-optf-opteng-api.json
new file mode 100644
index 0000000..4e77f76
--- /dev/null
+++ b/docs/sections/swaggerdoc/oof-optf-opteng-api.json
@@ -0,0 +1,584 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "This is the ONAP Optimization Engine (Generic Solver) API",
+ "version": "1.0.0",
+ "title": "ONAP Optimization ENGINE API",
+ "contact": {
+ "email": "vikas.varma@att.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "securityDefinitions": {
+ "basicAuth": {
+ "type": "basic",
+ "description": "HTTP Basic Auth"
+ }
+ },
+ "security": [
+ {
+ "basicAuth": []
+ }
+ ],
+ "paths": {
+ "/optengine/v1": {
+ "post": {
+ "tags": [
+ "Generic Solver Optimization"
+ ],
+ "summary": "Call the Generic Optimization engine",
+ "operationId": "optimizationRequest",
+ "description": "call optimization engine",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "optimizationRequest",
+ "description": "optimization request",
+ "schema": {
+ "$ref": "#/definitions/OptimizationRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/OptimizationResponse"
+ }
+ },
+ "202": {
+ "description": "An optimization request is accepted"
+ },
+ "400": {
+ "description": "bad request"
+ },
+ "401": {
+ "description": "Request body is not compliant with the API definition"
+ },
+ "404": {
+ "description": "The server cannot find the requested URI"
+ },
+ "405": {
+ "description": "The requested method is not supported by a server."
+ },
+ "500": {
+ "description": "The server encountered an internal server error or timed out"
+ }
+ }
+ }
+ },
+ "/optmodel/v1": {
+ "post": {
+ "tags": [
+ "Request to add the Optimizer model, metadata"
+ ],
+ "summary": "Add/Insert the optimization models in the database",
+ "operationId": "optimModelRequestAPI",
+ "description": "Request to add update the Optimizer model, metadata",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "optimModelRequest",
+ "description": "optimization model request",
+ "schema": {
+ "$ref": "#/definitions/OptimModelRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/OptimModelResponse"
+ }
+ },
+ "400": {
+ "description": "bad request"
+ },
+ "401": {
+ "description": "Request body is not compliant with the API definition"
+ },
+ "404": {
+ "description": "The server cannot find the requested URI"
+ },
+ "405": {
+ "description": "The requested method is not supported by a server."
+ },
+ "500": {
+ "description": "The server encountered an internal server error or timed out"
+ }
+ }
+ },
+ "put": {
+ "tags": [
+ "Request to update the Optimizer model, metadata"
+ ],
+ "summary": "Add/update the optimization models in the database",
+ "operationId": "updateModelRequestAPI",
+ "description": "Request to add update the Optimizer model, metadata",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "optimModelRequest",
+ "description": "optimization model request",
+ "schema": {
+ "$ref": "#/definitions/OptimModelRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/OptimModelResponse"
+ }
+ },
+ "400": {
+ "description": "bad request"
+ },
+ "401": {
+ "description": "Request body is not compliant with the API definition"
+ },
+ "404": {
+ "description": "The server cannot find the requested URI"
+ },
+ "405": {
+ "description": "The requested method is not supported by a server."
+ },
+ "500": {
+ "description": "The server encountered an internal server error or timed out"
+ }
+ }
+ },
+ "get": {
+ "tags": [
+ "Retrieve all models"
+ ],
+ "summary": "Gets all Optim Model data",
+ "description": "Retrieves all Optim Models",
+ "operationId": "getAllOptModelData",
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/ArrayOfOptimModelResponse"
+ }
+ },
+ "400": {
+ "description": "bad request"
+ },
+ "401": {
+ "description": "Request body is not compliant with the API definition"
+ },
+ "404": {
+ "description": "The server cannot find the requested URI"
+ },
+ "405": {
+ "description": "The requested method is not supported by a server."
+ },
+ "500": {
+ "description": "The server encountered an internal server error or timed out"
+ }
+ }
+ }
+ },
+ "/optmodel/v1/{model_id}": {
+ "get": {
+ "tags": [
+ "Retrieve Model Data"
+ ],
+ "summary": "Gets the Optim Model data",
+ "description": "Retrieves the Optim Model data given modelId",
+ "operationId": "getOptModelById",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "model_id",
+ "description": "Model ID",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/OptimModelResponse"
+ }
+ },
+ "400": {
+ "description": "bad request"
+ },
+ "401": {
+ "description": "Request body is not compliant with the API definition"
+ },
+ "404": {
+ "description": "The server cannot find the requested URI"
+ },
+ "405": {
+ "description": "The requested method is not supported by a server."
+ },
+ "500": {
+ "description": "The server encountered an internal server error or timed out"
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "Delete Model Data"
+ ],
+ "summary": "Delete the Optim Model data",
+ "description": "Deletes the Optim Model data given modelId",
+ "operationId": "deleteOptModelById",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "model_id",
+ "description": "Model ID",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/DeleteModelResponse"
+ }
+ },
+ "400": {
+ "description": "bad request"
+ },
+ "401": {
+ "description": "Request body is not compliant with the API definition"
+ },
+ "404": {
+ "description": "The server cannot find the requested URI"
+ },
+ "405": {
+ "description": "The requested method is not supported by a server."
+ },
+ "500": {
+ "description": "The server encountered an internal server error or timed out"
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "OptimizationResponse": {
+ "type": "object",
+ "required": [
+ "transactionId",
+ "requestID",
+ "requestStatus"
+ ],
+ "properties": {
+ "transactionId": {
+ "type": "string",
+ "format": "uuid",
+ "description": "unique ID to track an ONAP transaction",
+ "example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ },
+ "requestID": {
+ "type": "string",
+ "format": "uuid",
+ "description": "A unique ID to track multiple requests associated with a transaction",
+ "example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ },
+ "requestStatus": {
+ "type": "string",
+ "description": "request status (accepted, done, completed,failed)",
+ "example": "done"
+ },
+ "statusMessage": {
+ "type": "string",
+ "description": "Status message (incomplete, complete, unsatisfiable, unknown, unbounded, unsat_or_unbounded, error)",
+ "example": "complete"
+ },
+ "solutions": {
+ "additionalProperties": {
+ "type": "object"
+ },
+ "example": {
+ "SCHEDULED": [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 1
+ ]
+ ],
+ "OPTIMIZED": 2
+ }
+ }
+ }
+ },
+ "OptimizationRequest": {
+ "type": "object",
+ "required": [
+ "requestInfo",
+ "optimInfo"
+ ],
+ "properties": {
+ "requestInfo": {
+ "$ref": "#/definitions/RequestInfo"
+ },
+ "optimInfo": {
+ "$ref": "#/definitions/OptimInfo"
+ }
+ }
+ },
+ "RequestInfo": {
+ "type": "object",
+ "required": [
+ "transactionId",
+ "requestID",
+ "sourceId"
+ ],
+ "properties": {
+ "transactionId": {
+ "type": "string",
+ "format": "uuid",
+ "description": "unique ID to track an ONAP transaction",
+ "example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ },
+ "requestID": {
+ "type": "string",
+ "format": "uuid",
+ "description": "A unique ID to track multiple requests associated with a transaction",
+ "example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ },
+ "callbackUrl": {
+ "type": "string",
+ "format": "url",
+ "description": "The end point of a callback service where recommendations are posted.",
+ "example": "myDomain.com/myCallback"
+ },
+ "sourceId": {
+ "type": "string",
+ "description": "The unique ID of a client making an optimization call.",
+ "example": "son-handler"
+ },
+ "timeout": {
+ "type": "integer",
+ "description": "A tolerance window (in second) for expecting solutions",
+ "example": 5
+ }
+ }
+ },
+ "OptimInfo": {
+ "type": "object",
+ "properties": {
+ "modelId": {
+ "type": "string",
+ "description": "ModelId from the database, if its not populated, assume that solverModel will be populated",
+ "example": "pci_model1"
+ },
+ "solver": {
+ "type": "string",
+ "description": "type of solver (mzn, py, etc.)",
+ "example": "mzn"
+ },
+ "solverArgs": {
+ "type": "object",
+ "description": "Arguments for solver",
+ "additionalProperties": {
+ "type": "object"
+ },
+ "example": {
+ "solver": "cbc",
+ "timeout": 5
+ }
+ },
+ "modelContent": {
+ "type": "string",
+ "description": "a large blob string containing the model (which is not that problematic since models are fairly small)."
+ },
+ "optData": {
+ "$ref": "#/definitions/DataInfo"
+ }
+ }
+ },
+ "DataInfo": {
+ "type": "object",
+ "description": "Data Payload, input data for the solver, either text or json",
+ "properties": {
+ "text": {
+ "type": "string",
+ "description": "Solver data as a string",
+ "example": "flour = 8000; \r\nbanana = 11;\r\n "
+ },
+ "json": {
+ "type": "object",
+ "description": "Solver data as a json",
+ "additionalProperties": {
+ "type": "object"
+ },
+ "example": {
+ "flour": 8000,
+ "banana": 11
+ }
+ }
+ }
+ },
+ "OptimModelRequest": {
+ "type": "object",
+ "required": [
+ "requestInfo",
+ "modelInfo"
+ ],
+ "properties": {
+ "requestInfo": {
+ "$ref": "#/definitions/ModelRequestInfo"
+ },
+ "modelInfo": {
+ "$ref": "#/definitions/OptimModelInfo"
+ }
+ }
+ },
+ "ModelRequestInfo": {
+ "type": "object",
+ "required": [
+ "transactionId",
+ "requestID",
+ "sourceId"
+ ],
+ "properties": {
+ "transactionId": {
+ "type": "string",
+ "format": "uuid",
+ "description": "unique ID to track an ONAP transaction",
+ "example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ },
+ "requestID": {
+ "type": "string",
+ "format": "uuid",
+ "description": "A unique ID to track multiple requests associated with a transaction",
+ "example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
+ },
+ "sourceId": {
+ "type": "string",
+ "description": "The unique ID of a client making an optimization call.",
+ "example": "optf-osdf"
+ }
+ }
+ },
+ "OptimModelInfo": {
+ "type": "object",
+ "required": [
+ "modelId",
+ "solver",
+ "description",
+ "modelContent"
+ ],
+ "properties": {
+ "modelId": {
+ "type": "string",
+ "description": "ModelId from the database",
+ "example": "pci_anr_model1"
+ },
+ "solver": {
+ "type": "string",
+ "description": "type of solver (mzn, py, etc.)",
+ "example": "mzn"
+ },
+ "description": {
+ "type": "string",
+ "description": "Description of the model",
+ "example": "mzn model to optimize pci/anr models"
+ },
+ "modelContent": {
+ "type": "string",
+ "description": "a large blob string containing the model (which is not that problematic since models are fairly small).",
+ "example": "mzn content"
+ }
+ }
+ },
+ "ArrayOfOptimModelResponse": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/OptimModelResponse"
+ }
+ },
+ "OptimModelResponse": {
+ "type": "object",
+ "required": [
+ "modelId",
+ "solver",
+ "modelContent"
+ ],
+ "properties": {
+ "modelId": {
+ "type": "string",
+ "description": "ModelId from the database",
+ "example": "pci_anr_model1"
+ },
+ "solver": {
+ "type": "string",
+ "description": "type of solver (mzn, py, etc.)",
+ "example": "mzn"
+ },
+ "description": {
+ "type": "string",
+ "description": "Description of the model",
+ "example": "mzn model to optimize pci/anr models"
+ },
+ "modelContent": {
+ "type": "string",
+ "description": "a large blob string containing the model (which is not that problematic since models are fairly small).",
+ "example": "mzn content"
+ },
+ "statusMessage": {
+ "type": "string",
+ "description": "status message.",
+ "example": "mzn content"
+ }
+ }
+ },
+ "DeleteModelResponse": {
+ "type": "object",
+ "required": [
+ "statusMessage"
+ ],
+ "properties": {
+ "statusMessage": {
+ "type": "string",
+ "description": "status message.",
+ "example": "model data for modelId pci_anr_model1 deleted"
+ }
+ }
+ }
+ },
+ "schemes": [
+ "https"
+ ],
+ "host": "virtserver.swaggerhub.com",
+ "basePath": "/api/oof/"
+}
diff --git a/osdfapp.py b/osdfapp.py
index 5f45d9a..9b84bc9 100755
--- a/osdfapp.py
+++ b/osdfapp.py
@@ -33,6 +33,7 @@ from apps.nst.optimizers.nst_select_processor import process_nst_selection
from apps.pci.optimizers.pci_opt_processor import process_pci_optimation
from apps.placement.models.api.placementRequest import PlacementAPI
from apps.placement.optimizers.conductor.remote_opt_processor import process_placement_opt
+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
@@ -104,6 +105,16 @@ 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():
+ """
+ Perform the inter domain route calculation
+ """
+ request_json = request.get_json()
+ audit_log.info("Inter Domain Calculation Route request received!")
+ 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()
diff --git a/test/functest/simulators/simulated-config/osdf_config.yaml b/test/functest/simulators/simulated-config/osdf_config.yaml
index 414d7c7..d4d20c9 100755
--- a/test/functest/simulators/simulated-config/osdf_config.yaml
+++ b/test/functest/simulators/simulated-config/osdf_config.yaml
@@ -73,4 +73,8 @@ pciHMSPassword: "" # pcihandler password for call back.
aaiUrl: "https://api.url:30233"
aaiGetLinksUrl: "/aai/v16/network/logical-links"
+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/test/inter_domain_route_opt/bandwidth_attributes.json b/test/inter_domain_route_opt/bandwidth_attributes.json
new file mode 100644
index 0000000..0de7e51
--- /dev/null
+++ b/test/inter_domain_route_opt/bandwidth_attributes.json
@@ -0,0 +1,176 @@
+{
+ "int-1-bw":{
+ "interface-name":"int1",
+ "bandwidth-attributes":{
+ "bandwidth-attribute":[
+ {
+ "bwa-id":"bw6",
+ "resource-version":"1596387588545",
+ "available-bandwidth-map":{
+ "available-bandwidth":[
+ {
+ "ab-id":"ab226",
+ "odu-type":"ODU2",
+ "number":1,
+ "resource-version":"1596387588545"
+ },
+ {
+ "ab-id":"ab112",
+ "odu-type":"ODU4",
+ "number":8,
+ "resource-version":"1596387588545"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "resource-version":"1596387588545",
+ "in-maint":false
+ },
+ "int-3-bw":{
+ "interface-name":"int3",
+ "bandwidth-attributes":{
+ "bandwidth-attribute":[
+ {
+ "bwa-id":"bw6",
+ "resource-version":"1596387588545",
+ "available-bandwidth-map":{
+ "available-bandwidth":[
+ {
+ "ab-id":"ab226",
+ "odu-type":"ODU2",
+ "number":1,
+ "resource-version":"1596387588545"
+ },
+ {
+ "ab-id":"ab112",
+ "odu-type":"ODU4",
+ "number":8,
+ "resource-version":"1596387588545"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "resource-version":"1596387588545",
+ "in-maint":false
+ },
+ "int-4-bw":{
+ "interface-name":"int4",
+ "bandwidth-attributes":{
+ "bandwidth-attribute":[
+ {
+ "bwa-id":"bw6",
+ "resource-version":"1596387588545",
+ "available-bandwidth-map":{
+ "available-bandwidth":[
+ {
+ "ab-id":"ab226",
+ "odu-type":"ODU2",
+ "number":1,
+ "resource-version":"1596387588545"
+ },
+ {
+ "ab-id":"ab112",
+ "odu-type":"ODU4",
+ "number":8,
+ "resource-version":"1596387588545"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "resource-version":"1596387588545",
+ "in-maint":false
+ },
+ "int-5-bw":{
+ "interface-name":"int5",
+ "bandwidth-attributes":{
+ "bandwidth-attribute":[
+ {
+ "bwa-id":"bw6",
+ "resource-version":"1596387588545",
+ "available-bandwidth-map":{
+ "available-bandwidth":[
+ {
+ "ab-id":"ab226",
+ "odu-type":"ODU2",
+ "number":1,
+ "resource-version":"1596387588545"
+ },
+ {
+ "ab-id":"ab112",
+ "odu-type":"ODU4",
+ "number":8,
+ "resource-version":"1596387588545"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "resource-version":"1596387588545",
+ "in-maint":false
+ },
+ "int-6-bw":{
+ "interface-name":"int6",
+ "bandwidth-attributes":{
+ "bandwidth-attribute":[
+ {
+ "bwa-id":"bw6",
+ "resource-version":"1596387588545",
+ "available-bandwidth-map":{
+ "available-bandwidth":[
+ {
+ "ab-id":"ab226",
+ "odu-type":"ODU2",
+ "number":1,
+ "resource-version":"1596387588545"
+ },
+ {
+ "ab-id":"ab112",
+ "odu-type":"ODU4",
+ "number":8,
+ "resource-version":"1596387588545"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "resource-version":"1596387588545",
+ "in-maint":false
+ },
+ "int-7-bw":{
+ "interface-name":"int7",
+ "bandwidth-attributes":{
+ "bandwidth-attribute":[
+ {
+ "bwa-id":"bw6",
+ "resource-version":"1596387588545",
+ "available-bandwidth-map":{
+ "available-bandwidth":[
+ {
+ "ab-id":"ab226",
+ "odu-type":"ODU2",
+ "number":1,
+ "resource-version":"1596387588545"
+ },
+ {
+ "ab-id":"ab112",
+ "odu-type":"ODU4",
+ "number":8,
+ "resource-version":"1596387588545"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "resource-version":"1596387588545",
+ "in-maint":false
+ }
+}
diff --git a/test/inter_domain_route_opt/controllers_for_interfaces.json b/test/inter_domain_route_opt/controllers_for_interfaces.json
new file mode 100644
index 0000000..3de47d1
--- /dev/null
+++ b/test/inter_domain_route_opt/controllers_for_interfaces.json
@@ -0,0 +1,62 @@
+{
+ "int-1-cont":{
+ "results":[
+ {
+ "esr-thirdparty-sdnc":{
+ "thirdparty-sdnc-id":"Controller1",
+ "resource-version":"1593421890494"
+ }
+ }
+ ]
+ },
+ "int-3-cont":{
+ "results":[
+ {
+ "esr-thirdparty-sdnc":{
+ "thirdparty-sdnc-id":"Controller2",
+ "resource-version":"1593421890494"
+ }
+ }
+ ]
+ },
+ "int-4-cont":{
+ "results":[
+ {
+ "esr-thirdparty-sdnc":{
+ "thirdparty-sdnc-id":"Controller2",
+ "resource-version":"1593421890494"
+ }
+ }
+ ]
+ },
+ "int-5-cont":{
+ "results":[
+ {
+ "esr-thirdparty-sdnc":{
+ "thirdparty-sdnc-id":"Controller3",
+ "resource-version":"1593421890494"
+ }
+ }
+ ]
+ },
+ "int-6-cont":{
+ "results":[
+ {
+ "esr-thirdparty-sdnc":{
+ "thirdparty-sdnc-id":"Controller3",
+ "resource-version":"1593421890494"
+ }
+ }
+ ]
+ },
+ "int-7-cont":{
+ "results":[
+ {
+ "esr-thirdparty-sdnc":{
+ "thirdparty-sdnc-id":"Controller4",
+ "resource-version":"1593421890494"
+ }
+ }
+ ]
+ }
+}
diff --git a/test/inter_domain_route_opt/controllers_list.json b/test/inter_domain_route_opt/controllers_list.json
new file mode 100644
index 0000000..158f530
--- /dev/null
+++ b/test/inter_domain_route_opt/controllers_list.json
@@ -0,0 +1,16 @@
+{
+ "esr-thirdparty-sdnc":[
+ {
+ "thirdparty-sdnc-id":"Controller1"
+ },
+ {
+ "thirdparty-sdnc-id":"Controller2"
+ },
+ {
+ "thirdparty-sdnc-id":"Controller3"
+ },
+ {
+ "thirdparty-sdnc-id":"Controller4"
+ }
+ ]
+}
diff --git a/test/inter_domain_route_opt/get_links.json b/test/inter_domain_route_opt/get_links.json
new file mode 100644
index 0000000..0e70523
--- /dev/null
+++ b/test/inter_domain_route_opt/get_links.json
@@ -0,0 +1,157 @@
+{
+ "logical-link":[
+ {
+ "link-name":"link1",
+ "in-maint":false,
+ "link-type":"inter-domain",
+ "resource-version":"1588952379221",
+ "operational-status":"up",
+ "relationship-list":{
+ "relationship":[
+ {
+ "related-to":"p-interface",
+ "relationship-label":"tosca.relationships.network.LinksTo",
+ "related-link":"/aai/v19/network/pnfs/pnf/pnf1/p-interfaces/p-interface/int1",
+ "relationship-data":[
+ {
+ "relationship-key":"pnf.pnf-name",
+ "relationship-value":"pnf1"
+ },
+ {
+ "relationship-key":"p-interface.interface-name",
+ "relationship-value":"int1"
+ }
+ ],
+ "related-to-property":[
+ {
+ "property-key":"p-interface.prov-status"
+ }
+ ]
+ },
+ {
+ "related-to":"p-interface",
+ "relationship-label":"tosca.relationships.network.LinksTo",
+ "related-link":"/aai/v19/network/pnfs/pnf/pnf2/p-interfaces/p-interface/int3",
+ "relationship-data":[
+ {
+ "relationship-key":"pnf.pnf-name",
+ "relationship-value":"pnf2"
+ },
+ {
+ "relationship-key":"p-interface.interface-name",
+ "relationship-value":"int3"
+ }
+ ],
+ "related-to-property":[
+ {
+ "property-key":"p-interface.prov-status"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "link-name":"link2",
+ "in-maint":false,
+ "link-type":"inter-domain",
+ "resource-version":"1588952379221",
+ "operational-status":"up",
+ "relationship-list":{
+ "relationship":[
+ {
+ "related-to":"p-interface",
+ "relationship-label":"tosca.relationships.network.LinksTo",
+ "related-link":"/aai/v19/network/pnfs/pnf/pnf2/p-interfaces/p-interface/int4",
+ "relationship-data":[
+ {
+ "relationship-key":"pnf.pnf-name",
+ "relationship-value":"pnf2"
+ },
+ {
+ "relationship-key":"p-interface.interface-name",
+ "relationship-value":"int4"
+ }
+ ],
+ "related-to-property":[
+ {
+ "property-key":"p-interface.prov-status"
+ }
+ ]
+ },
+ {
+ "related-to":"p-interface",
+ "relationship-label":"tosca.relationships.network.LinksTo",
+ "related-link":"/aai/v19/network/pnfs/pnf/pnf3/p-interfaces/p-interface/int5",
+ "relationship-data":[
+ {
+ "relationship-key":"pnf.pnf-name",
+ "relationship-value":"pnf3"
+ },
+ {
+ "relationship-key":"p-interface.interface-name",
+ "relationship-value":"int5"
+ }
+ ],
+ "related-to-property":[
+ {
+ "property-key":"p-interface.prov-status"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "link-name":"link3",
+ "in-maint":false,
+ "link-type":"inter-domain",
+ "resource-version":"1588952379221",
+ "operational-status":"up",
+ "relationship-list":{
+ "relationship":[
+ {
+ "related-to":"p-interface",
+ "relationship-label":"tosca.relationships.network.LinksTo",
+ "related-link":"/aai/v19/network/pnfs/pnf/pnf3/p-interfaces/p-interface/int6",
+ "relationship-data":[
+ {
+ "relationship-key":"pnf.pnf-name",
+ "relationship-value":"pnf3"
+ },
+ {
+ "relationship-key":"p-interface.interface-name",
+ "relationship-value":"int6"
+ }
+ ],
+ "related-to-property":[
+ {
+ "property-key":"p-interface.prov-status"
+ }
+ ]
+ },
+ {
+ "related-to":"p-interface",
+ "relationship-label":"tosca.relationships.network.LinksTo",
+ "related-link":"/aai/v19/network/pnfs/pnf/pnf4/p-interfaces/p-interface/int7",
+ "relationship-data":[
+ {
+ "relationship-key":"pnf.pnf-name",
+ "relationship-value":"pnf4"
+ },
+ {
+ "relationship-key":"p-interface.interface-name",
+ "relationship-value":"int7"
+ }
+ ],
+ "related-to-property":[
+ {
+ "property-key":"p-interface.prov-status"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/test/inter_domain_route_opt/request.json b/test/inter_domain_route_opt/request.json
new file mode 100644
index 0000000..041a32f
--- /dev/null
+++ b/test/inter_domain_route_opt/request.json
@@ -0,0 +1,30 @@
+{
+ "requestInfo":{
+ "transactionId":"123456",
+ "requestId":"789456",
+ "callbackUrl":"",
+ "callbackHeader": "",
+ "sourceId":"SDNC",
+ "requestType":"create",
+ "numSolutions":1,
+ "optimizers":[
+ "route"
+ ],
+ "timeout":600
+ },
+ "routeInfo":{
+ "routeRequest":{
+ "srcDetails":{
+ "interfaceId":"int19",
+ "nodeId":"pnf1",
+ "controllerId":"Controller1"
+ },
+ "dstDetails":{
+ "interfaceId":"int20",
+ "nodeId":"pnf4",
+ "controllerId":"Controller3"
+ },
+ "serviceRate":"ODU2"
+ }
+ }
+}
diff --git a/test/test_inter_domain_route_opt.py b/test/test_inter_domain_route_opt.py
new file mode 100644
index 0000000..3d18abc
--- /dev/null
+++ b/test/test_inter_domain_route_opt.py
@@ -0,0 +1,151 @@
+# -------------------------------------------------------------------------
+# Copyright (c) 2020 Fujitsu Limited Intellectual Property
+#
+# 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
+from apps.route.optimizers.inter_domain_route_opt import InterDomainRouteOpt
+import osdf.config.loader as config_loader
+from osdf.utils.interfaces import json_from_file
+from osdf.utils.programming_utils import DotDict
+
+count = 1
+
+def mocked_requests_get(*args, **kwargs):
+ class MockResponse:
+ def __init__(self, json_data, status_code):
+ self.json_data = json_data
+ self.status_code = status_code
+
+ def json(self):
+ return self.json_data
+
+ main_dir = ""
+ response_data_file = main_dir + "test/inter_domain_route_opt/get_links.json"
+ bandwidth_attributes = main_dir + "test/inter_domain_route_opt/bandwidth_attributes.json"
+ bandwidth_attribute_values = json_from_file(bandwidth_attributes)
+
+ controllers_list = main_dir + "test/inter_domain_route_opt/controllers_list.json"
+
+ if args[0] == 'https://api.url:30233/aai/v19/network/logical-links?link-type=inter-domain&operational-status=up':
+ return MockResponse(json_from_file(response_data_file), 200)
+ elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf1/p-interfaces/p-interface/int1?depth=all':
+ return MockResponse(bandwidth_attribute_values["int-1-bw"], 200)
+ elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf2/p-interfaces/p-interface/int3?depth=all':
+ return MockResponse(bandwidth_attribute_values["int-3-bw"], 200)
+ elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf2/p-interfaces/p-interface/int4?depth=all':
+ return MockResponse(bandwidth_attribute_values["int-4-bw"], 200)
+ elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf3/p-interfaces/p-interface/int5?depth=all':
+ return MockResponse(bandwidth_attribute_values["int-5-bw"], 200)
+ elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf3/p-interfaces/p-interface/int6?depth=all':
+ return MockResponse(bandwidth_attribute_values["int-6-bw"], 200)
+ elif args[0] == 'https://api.url:30233/aai/v19/network/pnfs/pnf/pnf4/p-interfaces/p-interface/int7?depth=all':
+ return MockResponse(bandwidth_attribute_values["int-7-bw"], 200)
+ elif args[0] == 'https://api.url:30233/aai/v19/external-system/esr-thirdparty-sdnc-list':
+ return MockResponse(json_from_file(controllers_list), 200)
+ return MockResponse(None, 404)
+
+
+def mocked_requests_put(*args, **kwargs):
+ class MockResponse:
+ def __init__(self, json_data, status_code):
+ self.json_data = json_data
+ self.status_code = status_code
+
+ def json(self):
+ return self.json_data
+ main_dir = ""
+ controllers_for_interfaces = main_dir + "test/inter_domain_route_opt/controllers_for_interfaces.json"
+ controllers_for_interfaces_values = json_from_file(controllers_for_interfaces)
+
+ global count
+
+ if count == 1:
+ count += 1
+ return MockResponse(controllers_for_interfaces_values["int-1-cont"], 200)
+ elif count == 2:
+ count += 1
+ return MockResponse(controllers_for_interfaces_values["int-3-cont"], 200)
+ elif count == 3:
+ count += 1
+ return MockResponse(controllers_for_interfaces_values["int-4-cont"], 200)
+ elif count == 4:
+ count += 1
+ return MockResponse(controllers_for_interfaces_values["int-5-cont"], 200)
+ elif count == 5:
+ count += 1
+ return MockResponse(controllers_for_interfaces_values["int-6-cont"], 200)
+ elif count == 6:
+ count += 1
+ return MockResponse(controllers_for_interfaces_values["int-7-cont"], 200)
+
+ return MockResponse(None, 404)
+
+
+
+class TestInterDomainRouteOpt(unittest.TestCase):
+ @patch('apps.route.optimizers.inter_domain_route_opt.requests.get', side_effect=mocked_requests_get)
+ @patch('apps.route.optimizers.inter_domain_route_opt.requests.put', side_effect=mocked_requests_put)
+ @patch('apps.route.optimizers.simple_route_opt.pymzn.minizinc')
+ def test_process_get_route(self, mock_solve , mock_put, mock_get):
+ main_dir = ""
+ mock_solve.return_value = [{'x': [1, 1, 0, 0, 0, 0]}]
+ 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/inter_domain_route_opt/request.json"
+ request_json = json_from_file(parameter_data_file)
+ routopt = InterDomainRouteOpt()
+ actual_response = routopt.get_route(request_json,self.osdf_config)
+ mock_response = {
+ "requestId":"789456",
+ "transactionId":"123456",
+ "statusMessage":"SUCCESS",
+ "requestStatus":"accepted",
+ "solutions":{
+ "routeInfo":{
+ "serviceRoute":[
+ {
+ "srcInterfaceId":"int19",
+ "dstInterfaceId":"int1",
+ "controllerId":"Controller1"
+ },
+ {
+ "srcInterfaceId":"int3",
+ "dstInterfaceId":"int4",
+ "controllerId":"Controller2"
+ },
+ {
+ "srcInterfaceId":"int5",
+ "dstInterfaceId":"int20",
+ "controllerId":"Controller3"
+ }
+ ],
+ "linkList":[
+ "link1",
+ "link2"
+ ]
+ }
+ }
+ }
+ self.assertEqual(mock_response, actual_response)
+
+
+if __name__ == '__main__':
+ unittest.main()
+ \ No newline at end of file
diff --git a/tox.ini b/tox.ini
index 11c541a..1606b9b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,7 @@
[tox]
skipsdist=True
-envlist = py3, pylint
+envlist = py3, pylint, flake8diff
[testenv]
distribute = False
@@ -29,3 +29,15 @@ commands = bash -c "pylint --reports=y osdf apps runtime| tee pylint.out"
[testenv:py3]
basepython=python3.6
+
+[testenv:flake8diff]
+whitelist_externals=bash
+deps = hacking>=2.0.0
+commands =
+ bash -c "files=$(git diff HEAD^ HEAD --diff-filter=d --name-only | grep -E '(^apps\/|osdf\/|runtime\/)'| grep -E '*\.py$'); if [[ -z $files ]]; then exit 0; else flake8 $files; fi"
+
+[flake8]
+select = E,H,W,F
+max-line-length = 119
+ignore =
+per-file-ignores=