Feature 10937: Transport API (TAPI) WIM connector for RO
[osm/RO.git] / RO-SDN-tapi / osm_rosdn_tapi / services_composer.py
diff --git a/RO-SDN-tapi/osm_rosdn_tapi/services_composer.py b/RO-SDN-tapi/osm_rosdn_tapi/services_composer.py
new file mode 100644 (file)
index 0000000..720e423
--- /dev/null
@@ -0,0 +1,151 @@
+# -*- 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 ServiceComposer class used by the Transport API (TAPI) WIM
+connector to compose the services based on the service_endpoint_ids and their
+directionality."""
+
+from .exceptions import (
+    WimTapiIncongruentDirectionality,
+    WimTapiIncongruentEndPoints,
+    WimTapiMissingMappingField,
+    WimTapiSipNotFound,
+)
+from .message_composers import (
+    compose_endpoint,
+    compose_requested_capacity,
+    # compose_vlan_constraint,
+)
+
+
+class ServicesComposer:
+    def __init__(self, service_interface_points) -> None:
+        self.sips = service_interface_points
+
+        # if unidirectional
+        #   - a single service_endpoint item is created
+        #   - the service_endpoint item contains with the 2 bidirectional SIPs
+        # if bidirectional
+        #   - two service_endpoint items are created
+        #   - each service_endpoint item containing a list of 2 unidirectional SIPs (in, out)
+        self.services = list()
+
+        # TODO: populate dynamically capacity of the connection
+        self.requested_capacity = compose_requested_capacity(1, unit="GBPS")
+
+        self.vlan_constraint = None
+        # TODO: VLAN needs to be processed by connection point; by now deactivated
+        # if connection_point.get("service_endpoint_encapsulation_type") == "dot1q":
+        #    encap_info = connection_point.get("service_endpoint_encapsulation_info", {})
+        #    vlan_id = encap_info.get("vlan")
+        #    if vlan_id is not None:
+        #        vlan_constraint = compose_vlan_constraint(vlan_id)
+
+    def add_bidirectional(self, service_endpoint_id):
+        if len(self.services) == 0:
+            # assume bidirectional, SIP is service_endpoint_id
+            service_interface_point = self.sips[service_endpoint_id]
+            self.services.append([compose_endpoint(service_interface_point)])
+        elif len(self.services) == 1:
+            # is bidirectional, SIP is service_endpoint_id
+            if len(self.services[0]) > 1:
+                # too much endpoints per service
+                raise WimTapiIncongruentEndPoints(self.services, service_endpoint_id)
+            self.services[0].append(compose_endpoint(self.sips[service_endpoint_id]))
+        else:
+            raise WimTapiIncongruentDirectionality(self.services, service_endpoint_id)
+
+    def add_unidirectional(self, service_endpoint_id, sip_input, sip_output):
+        if len(self.services) == 0:
+            # assume unidirectional
+            self.services.append([compose_endpoint(self.sips[sip_output])])  # AZ
+            self.services.append([compose_endpoint(self.sips[sip_input])])  # ZA
+        elif len(self.services) == 2:
+            # is unidirectional
+
+            if len(self.services[0]) > 1:
+                # too much endpoints per service
+                raise WimTapiIncongruentEndPoints(self.services[0], service_endpoint_id)
+            self.services[0].append(compose_endpoint(self.sips[sip_input]))  # AZ
+
+            if len(self.services[1]) > 1:
+                # too much endpoints per service
+                raise WimTapiIncongruentEndPoints(self.services[1], service_endpoint_id)
+            self.services[1].insert(0, compose_endpoint(self.sips[sip_output]))  # ZA
+        else:
+            raise WimTapiIncongruentDirectionality(self.services, service_endpoint_id)
+
+    def add_service_endpoint(self, service_endpoint_id, mapping):
+        service_mapping_info = mapping.get("service_mapping_info", {})
+
+        if (
+            len(service_mapping_info) == 0
+            or "sip_input" not in service_mapping_info
+            or "sip_output" not in service_mapping_info
+        ):
+            # bidirectional (no mapping or no sip_input or no sip_output)
+            if service_endpoint_id not in self.sips:
+                raise WimTapiSipNotFound(service_endpoint_id, self.sips)
+            self.add_bidirectional(service_endpoint_id)
+
+        else:
+            # unidirectional, sip_input and sip_output provided in mapping
+
+            sip_input = service_mapping_info.get("sip_input")
+            if sip_input is None:
+                raise WimTapiMissingMappingField(
+                    mapping, "service_mapping_info.sip_input"
+                )
+
+            if sip_input not in self.sips:
+                raise WimTapiSipNotFound(sip_input, self.sips)
+
+            sip_output = service_mapping_info.get("sip_output")
+            if sip_output is None:
+                raise WimTapiMissingMappingField(
+                    mapping, "service_mapping_info.sip_output"
+                )
+
+            if sip_output not in self.sips:
+                raise WimTapiSipNotFound(sip_output, self.sips)
+
+            self.add_unidirectional(service_endpoint_id, sip_input, sip_output)
+
+    def is_bidirectional(self):
+        return len(self.services) == 1
+
+    def dump(self, logger):
+        str_data = "\n".join(
+            [
+                "services_composer {",
+                "  services={:s}".format(str(self.services)),
+                "  requested_capacity={:s}".format(str(self.requested_capacity)),
+                "  vlan_constraint={:s}".format(str(self.vlan_constraint)),
+                "}",
+            ]
+        )
+        logger.debug(str_data)