Feature 10937: Transport API (TAPI) WIM connector for RO
Change-Id: If0dac9f8ba2d00945eb86a89fb0b2f174c672794
Signed-off-by: gifrerenom <lluis.gifre@cttc.es>
diff --git a/RO-SDN-tapi/osm_rosdn_tapi/tests/__init__.py b/RO-SDN-tapi/osm_rosdn_tapi/tests/__init__.py
new file mode 100644
index 0000000..ab3006a
--- /dev/null
+++ b/RO-SDN-tapi/osm_rosdn_tapi/tests/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+#######################################################################################
+# This file is part of OSM RO module
+#
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+# This work has been performed in the context of the TeraFlow Project -
+# funded by the European Commission under Grant number 101015857 through the
+# Horizon 2020 program.
+#######################################################################################
diff --git a/RO-SDN-tapi/osm_rosdn_tapi/tests/constants.py b/RO-SDN-tapi/osm_rosdn_tapi/tests/constants.py
new file mode 100644
index 0000000..a9808ba
--- /dev/null
+++ b/RO-SDN-tapi/osm_rosdn_tapi/tests/constants.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+
+#######################################################################################
+# This file is part of OSM RO module
+#
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+# This work has been performed in the context of the TeraFlow Project -
+# funded by the European Commission under Grant number 101015857 through the
+# Horizon 2020 program.
+# Contributors:
+# - Lluis Gifre <lluis.gifre@cttc.es>
+# - Ricard Vilalta <ricard.vilalta@cttc.es>
+#######################################################################################
+
+"""This file contains the WIM settings for the unit test used to validate the
+Transport API (TAPI) WIM connector."""
+
+
+from osm_rosdn_tapi.tests.tools import wim_port_mapping
+
+
+WIM_HOST_PORT = ("127.0.0.127", 49000)
+
+# WIM_URL should be populated with the WIM url provided for the WIM connector during its instantiation
+WIM_URL = "http://{:s}:{:d}".format(*WIM_HOST_PORT)
+
+# WIM_ACCOUNT should be populated with the WIM credentials provided for the WIM connector during its instantiation
+WIM_ACCOUNT = {"user": "admin", "password": "admin"}
+
+# WIM_PORT_MAPPING should be populated with the port mapping provided for the WIM connector during its instantiation
+# In this example, SIPs are taken from mock_tapi_handler.py file.
+WIM_PORT_MAPPING = [
+ wim_port_mapping(
+ "dc1",
+ "dc1r1",
+ "eth0",
+ "R1-eth0",
+ service_mapping_info={},
+ ),
+ wim_port_mapping(
+ "dc2",
+ "dc2r2",
+ "eth0",
+ "R2-eth0",
+ service_mapping_info={},
+ ),
+ wim_port_mapping(
+ "dc3",
+ "dc3r3",
+ "eth0",
+ "R3-opt1",
+ service_mapping_info={
+ "sip_input": "R3-opt1-rx",
+ "sip_output": "R3-opt1-tx",
+ },
+ ),
+ wim_port_mapping(
+ "dc4",
+ "dc4r4",
+ "eth0",
+ "R4-opt1",
+ service_mapping_info={
+ "sip_input": "R4-opt1-rx",
+ "sip_output": "R4-opt1-tx",
+ },
+ ),
+]
diff --git a/RO-SDN-tapi/osm_rosdn_tapi/tests/exceptions.py b/RO-SDN-tapi/osm_rosdn_tapi/tests/exceptions.py
new file mode 100644
index 0000000..f52a020
--- /dev/null
+++ b/RO-SDN-tapi/osm_rosdn_tapi/tests/exceptions.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+
+#######################################################################################
+# This file is part of OSM RO module
+#
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+# This work has been performed in the context of the TeraFlow Project -
+# funded by the European Commission under Grant number 101015857 through the
+# Horizon 2020 program.
+# Contributors:
+# - Lluis Gifre <lluis.gifre@cttc.es>
+# - Ricard Vilalta <ricard.vilalta@cttc.es>
+#######################################################################################
+
+"""This file contains the exception classes the Mock OSM RO module can raise."""
+
+
+_PREFIX = "Mock OSM RO: "
+
+
+class MockOsmRoError(Exception):
+ """Base Exception for all Mock OSM RO related errors."""
+
+ def __init__(self, message):
+ super().__init__(_PREFIX + message)
+
+
+class MockOsmRoServiceNotFound(MockOsmRoError):
+ def __init__(self, service_id):
+ MESSAGE = "ServiceId({:s}) not found"
+ message = MESSAGE.format(str(service_id))
+ super().__init__(message)
diff --git a/RO-SDN-tapi/osm_rosdn_tapi/tests/mock_osm_ro.py b/RO-SDN-tapi/osm_rosdn_tapi/tests/mock_osm_ro.py
new file mode 100644
index 0000000..71c2148
--- /dev/null
+++ b/RO-SDN-tapi/osm_rosdn_tapi/tests/mock_osm_ro.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+
+#######################################################################################
+# This file is part of OSM RO module
+#
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+# This work has been performed in the context of the TeraFlow Project -
+# funded by the European Commission under Grant number 101015857 through the
+# Horizon 2020 program.
+# Contributors:
+# - Lluis Gifre <lluis.gifre@cttc.es>
+# - Ricard Vilalta <ricard.vilalta@cttc.es>
+#######################################################################################
+
+"""This file contains a Mock OSM RO component that can be used for rapid unit testing.
+
+This code is based on code taken with permission from ETSI TeraFlowSDN project at:
+ https://labs.etsi.org/rep/tfs/controller
+"""
+
+
+from typing import Dict, List
+
+from osm_ro_plugin.sdnconn import SdnConnectorBase
+
+from .exceptions import MockOsmRoServiceNotFound
+
+
+class MockOsmRo:
+ def __init__(
+ self,
+ klass: SdnConnectorBase,
+ url: str,
+ wim_account: Dict,
+ wim_port_mapping: Dict,
+ ) -> None:
+ wim = {"wim_url": url}
+ config = {
+ "mapping_not_needed": False,
+ "service_endpoint_mapping": wim_port_mapping,
+ }
+
+ # Instantiate WIM connector
+ self.wim_connector = klass(wim, wim_account, config=config)
+
+ # Internal DB emulating OSM RO storage provided to WIM Connectors
+ self.conn_info = {}
+
+ def create_connectivity_service(
+ self, service_type: str, connection_points: List[Dict]
+ ) -> str:
+ self.wim_connector.check_credentials()
+ service_uuid, conn_info = self.wim_connector.create_connectivity_service(
+ service_type, connection_points
+ )
+ self.conn_info[service_uuid] = conn_info
+ return service_uuid
+
+ def get_connectivity_service_status(self, service_uuid: str) -> Dict:
+ conn_info = self.conn_info.get(service_uuid)
+ if conn_info is None:
+ raise MockOsmRoServiceNotFound(service_uuid)
+ self.wim_connector.check_credentials()
+ return self.wim_connector.get_connectivity_service_status(
+ service_uuid, conn_info=conn_info
+ )
+
+ def edit_connectivity_service(
+ self, service_uuid: str, connection_points: List[Dict]
+ ) -> None:
+ conn_info = self.conn_info.get(service_uuid)
+ if conn_info is None:
+ raise MockOsmRoServiceNotFound(service_uuid)
+ self.wim_connector.check_credentials()
+ self.wim_connector.edit_connectivity_service(
+ service_uuid, conn_info=conn_info, connection_points=connection_points
+ )
+
+ def delete_connectivity_service(self, service_uuid: str) -> None:
+ conn_info = self.conn_info.get(service_uuid)
+ if conn_info is None:
+ raise MockOsmRoServiceNotFound(service_uuid)
+ self.wim_connector.check_credentials()
+ self.wim_connector.delete_connectivity_service(
+ service_uuid, conn_info=conn_info
+ )
diff --git a/RO-SDN-tapi/osm_rosdn_tapi/tests/mock_tapi_handler.py b/RO-SDN-tapi/osm_rosdn_tapi/tests/mock_tapi_handler.py
new file mode 100644
index 0000000..5f38210
--- /dev/null
+++ b/RO-SDN-tapi/osm_rosdn_tapi/tests/mock_tapi_handler.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+
+#######################################################################################
+# This file is part of OSM RO module
+#
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+# This work has been performed in the context of the TeraFlow Project -
+# funded by the European Commission under Grant number 101015857 through the
+# Horizon 2020 program.
+# Contributors:
+# - Lluis Gifre <lluis.gifre@cttc.es>
+# - Ricard Vilalta <ricard.vilalta@cttc.es>
+#######################################################################################
+
+"""This file contains a minimalistic Mock Transport API (TAPI) WIM server."""
+
+import http.server
+import json
+import uuid
+
+
+PHOTONIC_PROTOCOL_QUALIFIER = "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC"
+DSR_PROTOCOL_QUALIFIER = "tapi-dsr:DIGITAL_SIGNAL_TYPE"
+
+
+def compose_sip(
+ uuid, layer_protocol_name, supported_layer_protocol_qualifier, direction
+):
+ return {
+ "uuid": uuid,
+ "layer-protocol-name": layer_protocol_name,
+ "supported-layer-protocol-qualifier": [supported_layer_protocol_qualifier],
+ "administrative-state": "UNLOCKED",
+ "operational-state": "ENABLED",
+ "direction": direction,
+ }
+
+
+def compose_sip_dsr(uuid):
+ return compose_sip(uuid, "DSR", DSR_PROTOCOL_QUALIFIER, "BIDIRECTIONAL")
+
+
+def compose_sip_photonic_input(uuid):
+ return compose_sip(uuid, "PHOTONIC_MEDIA", PHOTONIC_PROTOCOL_QUALIFIER, "INPUT")
+
+
+def compose_sip_photonic_output(uuid):
+ return compose_sip(uuid, "PHOTONIC_MEDIA", PHOTONIC_PROTOCOL_QUALIFIER, "OUTPUT")
+
+
+CONTEXT = {
+ "uuid": str(uuid.uuid4()),
+ "service-interface-point": [
+ compose_sip_dsr("R1-eth0"),
+ compose_sip_dsr("R2-eth0"),
+ compose_sip_photonic_input("R3-opt1-rx"),
+ compose_sip_photonic_output("R3-opt1-tx"),
+ compose_sip_photonic_input("R4-opt1-rx"),
+ compose_sip_photonic_output("R4-opt1-tx"),
+ ],
+ # topology details not used by the WIM connector
+ "topology-context": {},
+ "connectivity-context": {"connectivity-service": [], "connection": []},
+}
+
+
+class MockTapiRequestHandler(http.server.BaseHTTPRequestHandler):
+ """Mock TAPI Request Handler for the unit tests"""
+
+ def do_GET(self): # pylint: disable=invalid-name
+ """Handle GET requests"""
+ path = self.path.replace("tapi-common:", "").replace("tapi-connectivity:", "")
+
+ if path == "/restconf/data/context":
+ status = 200 # ok
+ headers = {"Content-Type": "application/json"}
+ data = CONTEXT
+ elif path == "/restconf/data/context/service-interface-point":
+ status = 200 # ok
+ headers = {"Content-Type": "application/json"}
+ data = CONTEXT["service-interface-point"]
+ data = {"tapi-common:service-interface-point": data}
+ elif path == "/restconf/data/context/connectivity-context/connectivity-service":
+ status = 200 # ok
+ headers = {"Content-Type": "application/json"}
+ data = CONTEXT["connectivity-context"]["connectivity-service"]
+ data = {"tapi-connectivity:connectivity-service": data}
+ else:
+ status = 404 # not found
+ headers = {}
+ data = {"error": "Not found"}
+
+ self.send_response(status)
+ for header_name, header_value in headers.items():
+ self.send_header(header_name, header_value)
+ self.end_headers()
+ data = json.dumps(data)
+ self.wfile.write(data.encode("UTF-8"))
+
+ def do_POST(self): # pylint: disable=invalid-name
+ """Handle POST requests"""
+ path = self.path.replace("tapi-common:", "").replace("tapi-connectivity:", "")
+ length = int(self.headers["content-length"])
+ data = json.loads(self.rfile.read(length))
+
+ if path == "/restconf/data/context/connectivity-context":
+ if "tapi-connectivity:connectivity-service" in data:
+ data["connectivity-service"] = data.pop(
+ "tapi-connectivity:connectivity-service"
+ )
+
+ if (
+ isinstance(data["connectivity-service"], list)
+ and len(data["connectivity-service"]) > 0
+ ):
+ data["connectivity-service"] = data["connectivity-service"][0]
+
+ conn_svc = data["connectivity-service"]
+ if "connectivity-constraint" in conn_svc:
+ conn_constr = conn_svc.pop("connectivity-constraint")
+ if "requested-capacity" in conn_constr:
+ req_cap = conn_constr.pop("requested-capacity")
+ conn_svc["requested-capacity"] = req_cap
+ if "connectivity-direction" in conn_constr:
+ conn_dir = conn_constr.pop("connectivity-direction")
+ conn_svc["connectivity-direction"] = conn_dir
+
+ connection = {"uuid": conn_svc["uuid"], "connection-end-point": []}
+ conn_svc["connection"] = [{"connection_uuid": conn_svc["uuid"]}]
+
+ CONTEXT["connectivity-context"]["connection"].append(connection)
+ CONTEXT["connectivity-context"]["connectivity-service"].append(conn_svc)
+
+ status = 201 # created
+ headers = {}
+ elif path == "/restconf/operations/delete-connectivity-service":
+ if "tapi-connectivity:input" in data:
+ data["input"] = data.pop("tapi-connectivity:input")
+ conn_svc_uuid = data["input"]["uuid"]
+ conn_ctx = CONTEXT["connectivity-context"]
+
+ # keep connectivity services and connections with different uuid
+ conn_ctx["connection"] = [
+ conn for conn in conn_ctx["connection"] if conn["uuid"] != conn_svc_uuid
+ ]
+ conn_ctx["connectivity-service"] = [
+ conn_svc
+ for conn_svc in conn_ctx["connectivity-service"]
+ if conn_svc["uuid"] != conn_svc_uuid
+ ]
+
+ status = 204 # ok, no content
+ headers = {}
+ else:
+ status = 404 # not found
+ headers = {}
+
+ self.send_response(status)
+ for header_name, header_value in headers.items():
+ self.send_header(header_name, header_value)
+ self.end_headers()
diff --git a/RO-SDN-tapi/osm_rosdn_tapi/tests/test_wim_tapi.py b/RO-SDN-tapi/osm_rosdn_tapi/tests/test_wim_tapi.py
new file mode 100644
index 0000000..8c18b86
--- /dev/null
+++ b/RO-SDN-tapi/osm_rosdn_tapi/tests/test_wim_tapi.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+
+#######################################################################################
+# This file is part of OSM RO module
+#
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+# This work has been performed in the context of the TeraFlow Project -
+# funded by the European Commission under Grant number 101015857 through the
+# Horizon 2020 program.
+# Contributors:
+# - Lluis Gifre <lluis.gifre@cttc.es>
+# - Ricard Vilalta <ricard.vilalta@cttc.es>
+#######################################################################################
+
+"""This file contains the unit tests for the Transport API (TAPI) WIM connector."""
+
+import http.server
+import threading
+import unittest
+
+from osm_rosdn_tapi.exceptions import (
+ WimTapiConnectionPointsBadFormat,
+ WimTapiMissingConnPointField,
+ WimTapiUnsupportedServiceType,
+)
+from osm_rosdn_tapi.tests.constants import (
+ WIM_ACCOUNT,
+ WIM_HOST_PORT,
+ WIM_PORT_MAPPING,
+ WIM_URL,
+)
+from osm_rosdn_tapi.tests.mock_osm_ro import MockOsmRo
+from osm_rosdn_tapi.tests.mock_tapi_handler import MockTapiRequestHandler
+from osm_rosdn_tapi.wimconn_tapi import WimconnectorTAPI
+
+
+SERVICE_TYPE = "ELINE"
+SERVICE_CONNECTION_POINTS_BIDIRECTIONAL = [
+ # SIPs taken from mock_tapi_handler.py
+ {"service_endpoint_id": "R1-eth0"},
+ {"service_endpoint_id": "R2-eth0"},
+]
+SERVICE_CONNECTION_POINTS_UNIDIRECTIONAL = [
+ # SIPs taken from mock_tapi_handler.py
+ {"service_endpoint_id": "R3-opt1"},
+ {"service_endpoint_id": "R4-opt1"},
+]
+
+
+class UnitTests(unittest.TestCase):
+ """Unit tests for Transport API WIM connector"""
+
+ def setUp(self) -> None:
+ self.wim_server = http.server.ThreadingHTTPServer(
+ WIM_HOST_PORT, MockTapiRequestHandler
+ )
+
+ def test_wrong_cases(self):
+ with self.wim_server:
+ wim_server_thread = threading.Thread(target=self.wim_server.serve_forever)
+ wim_server_thread.daemon = True
+ wim_server_thread.start()
+
+ mock_osm_ro_tapi = MockOsmRo(
+ WimconnectorTAPI, WIM_URL, WIM_ACCOUNT, WIM_PORT_MAPPING
+ )
+
+ # Unsupported service type
+ with self.assertRaises(WimTapiUnsupportedServiceType) as test_context:
+ mock_osm_ro_tapi.create_connectivity_service(
+ "ELAN", SERVICE_CONNECTION_POINTS_BIDIRECTIONAL
+ )
+ self.assertEqual(
+ str(test_context.exception.args[0]),
+ "Unsupported ServiceType(ELAN). Supported ServiceTypes({'ELINE'})",
+ )
+
+ # Wrong number of connection_points
+ with self.assertRaises(WimTapiConnectionPointsBadFormat) as test_context:
+ mock_osm_ro_tapi.create_connectivity_service(SERVICE_TYPE, [])
+ self.assertEqual(
+ str(test_context.exception.args[0]),
+ "ConnectionPoints([]) must be a list or tuple of length 2",
+ )
+
+ # Wrong type of connection_points
+ with self.assertRaises(WimTapiConnectionPointsBadFormat) as test_context:
+ mock_osm_ro_tapi.create_connectivity_service(
+ SERVICE_TYPE, {"a": "b", "c": "d"}
+ )
+ self.assertEqual(
+ str(test_context.exception.args[0]),
+ "ConnectionPoints({'a': 'b', 'c': 'd'}) must be a list or tuple of length 2",
+ )
+
+ with self.assertRaises(WimTapiMissingConnPointField) as test_context:
+ mock_osm_ro_tapi.create_connectivity_service(
+ SERVICE_TYPE,
+ [
+ {"wrong_service_endpoint_id": "value"},
+ {"service_endpoint_id": "value"},
+ ],
+ )
+ self.assertEqual(
+ str(test_context.exception.args[0]),
+ "WIM TAPI Connector: ConnectionPoint({'wrong_service_endpoint_id': 'value'}) has no field 'service_endpoint_id'",
+ )
+
+ self.wim_server.shutdown()
+ wim_server_thread.join()
+
+ def test_correct_bidirectional(self):
+ with self.wim_server:
+ wim_server_thread = threading.Thread(target=self.wim_server.serve_forever)
+ wim_server_thread.daemon = True
+ wim_server_thread.start()
+
+ mock_osm_ro_tapi = MockOsmRo(
+ WimconnectorTAPI, WIM_URL, WIM_ACCOUNT, WIM_PORT_MAPPING
+ )
+
+ # Create bidirectional TAPI service
+ service_uuid = mock_osm_ro_tapi.create_connectivity_service(
+ SERVICE_TYPE, SERVICE_CONNECTION_POINTS_BIDIRECTIONAL
+ )
+ self.assertIsInstance(service_uuid, str)
+
+ # Check status of bidirectional TAPI service
+ status = mock_osm_ro_tapi.get_connectivity_service_status(service_uuid)
+ self.assertIsInstance(status, dict)
+ self.assertIn("sdn_status", status)
+ self.assertEqual(status["sdn_status"], "ACTIVE")
+
+ # Delete bidirectional TAPI service
+ mock_osm_ro_tapi.delete_connectivity_service(service_uuid)
+
+ self.wim_server.shutdown()
+ wim_server_thread.join()
+
+ def test_correct_unidirectional(self):
+ with self.wim_server:
+ wim_server_thread = threading.Thread(target=self.wim_server.serve_forever)
+ wim_server_thread.daemon = True
+ wim_server_thread.start()
+
+ mock_osm_ro_tapi = MockOsmRo(
+ WimconnectorTAPI, WIM_URL, WIM_ACCOUNT, WIM_PORT_MAPPING
+ )
+
+ # Create unidirectional TAPI service
+ service_uuid = mock_osm_ro_tapi.create_connectivity_service(
+ SERVICE_TYPE, SERVICE_CONNECTION_POINTS_UNIDIRECTIONAL
+ )
+ self.assertIsInstance(service_uuid, str)
+
+ # Check status of unidirectional TAPI service
+ status = mock_osm_ro_tapi.get_connectivity_service_status(service_uuid)
+ self.assertIsInstance(status, dict)
+ self.assertIn("sdn_status", status)
+ self.assertEqual(status["sdn_status"], "ACTIVE")
+
+ # Delete unidirectional TAPI service
+ mock_osm_ro_tapi.delete_connectivity_service(service_uuid)
+
+ self.wim_server.shutdown()
+ wim_server_thread.join()
diff --git a/RO-SDN-tapi/osm_rosdn_tapi/tests/tools.py b/RO-SDN-tapi/osm_rosdn_tapi/tests/tools.py
new file mode 100644
index 0000000..b75543f
--- /dev/null
+++ b/RO-SDN-tapi/osm_rosdn_tapi/tests/tools.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+#######################################################################################
+# This file is part of OSM RO module
+#
+# Copyright ETSI Contributors and Others.
+#
+# 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.
+#######################################################################################
+# This work has been performed in the context of the TeraFlow Project -
+# funded by the European Commission under Grant number 101015857 through the
+# Horizon 2020 program.
+# Contributors:
+# - Lluis Gifre <lluis.gifre@cttc.es>
+# - Ricard Vilalta <ricard.vilalta@cttc.es>
+#######################################################################################
+
+"""This file contains a helper methods for the Mock OSM RO component that can be used
+for rapid unit testing.
+
+This code is based on code taken with permission from ETSI TeraFlowSDN project at:
+ https://labs.etsi.org/rep/tfs/controller
+"""
+
+from typing import Dict, Optional
+
+
+# Ref: https://osm.etsi.org/wikipub/index.php/WIM
+# Fields defined according to from osm_ro_plugin.sdnconn import SdnConnectorBase
+def wim_port_mapping(
+ datacenter_id: str,
+ device_id: str,
+ device_interface_id: str,
+ service_endpoint_id: str,
+ switch_dpid: Optional[str] = None,
+ switch_port: Optional[str] = None,
+ service_mapping_info: Dict = {},
+):
+ mapping = {
+ "datacenter_id": datacenter_id,
+ "device_id": device_id,
+ "device_interface_id": device_interface_id,
+ "service_endpoint_id": service_endpoint_id,
+ "service_mapping_info": service_mapping_info,
+ }
+ if switch_dpid is not None:
+ mapping["switch_dpid"] = switch_dpid
+ if switch_port is not None:
+ mapping["switch_port"] = switch_port
+ return mapping