DEVICEDRIVER_XR = 6;
*/
DEVICEDRIVER_XR(6),
+ /**
+ * DEVICEDRIVER_IETF_L2VPN = 7;
+ */
+ DEVICEDRIVER_IETF_L2VPN(7),
UNRECOGNIZED(-1),
;
@@ -212,6 +216,10 @@ public final class ContextOuterClass {
* DEVICEDRIVER_XR = 6;
*/
public static final int DEVICEDRIVER_XR_VALUE = 6;
+ /**
+ * DEVICEDRIVER_IETF_L2VPN = 7;
+ */
+ public static final int DEVICEDRIVER_IETF_L2VPN_VALUE = 7;
public final int getNumber() {
@@ -245,6 +253,7 @@ public final class ContextOuterClass {
case 4: return DEVICEDRIVER_IETF_NETWORK_TOPOLOGY;
case 5: return DEVICEDRIVER_ONF_TR_352;
case 6: return DEVICEDRIVER_XR;
+ case 7: return DEVICEDRIVER_IETF_L2VPN;
default: return null;
}
}
diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py
index 99255defdb6b5ee155607536a2e13d23b97b2d3a..bb8948585f163aeb84ee758b8581bc6509d29799 100644
--- a/src/common/DeviceTypes.py
+++ b/src/common/DeviceTypes.py
@@ -25,9 +25,12 @@ class DeviceTypeEnum(Enum):
EMULATED_OPEN_LINE_SYSTEM = 'emu-open-line-system'
EMULATED_OPTICAL_ROADM = 'emu-optical-roadm'
EMULATED_OPTICAL_TRANSPONDER = 'emu-optical-transponder'
+ EMULATED_OPTICAL_SPLITTER = 'emu-optical-splitter' # passive component required for XR Constellation
EMULATED_P4_SWITCH = 'emu-p4-switch'
+ EMULATED_PACKET_RADIO_ROUTER = 'emu-packet-radio-router'
EMULATED_PACKET_ROUTER = 'emu-packet-router'
EMULATED_PACKET_SWITCH = 'emu-packet-switch'
+ EMULATED_XR_CONSTELLATION = 'emu-xr-constellation'
# Real device types
DATACENTER = 'datacenter'
@@ -36,6 +39,10 @@ class DeviceTypeEnum(Enum):
OPTICAL_ROADM = 'optical-roadm'
OPTICAL_TRANSPONDER = 'optical-transponder'
P4_SWITCH = 'p4-switch'
+ PACKET_RADIO_ROUTER = 'packet-radio-router'
PACKET_ROUTER = 'packet-router'
PACKET_SWITCH = 'packet-switch'
- XR_CONSTELLATION = 'xr-constellation'
\ No newline at end of file
+ XR_CONSTELLATION = 'xr-constellation'
+
+ # ETSI TeraFlowSDN controller
+ TERAFLOWSDN_CONTROLLER = 'teraflowsdn'
diff --git a/src/common/tools/object_factory/Device.py b/src/common/tools/object_factory/Device.py
index 0cc4555d455bf28ac2143a5d58b87e084a8360c7..66c87b14dd866d44b5d48addf93d172aea962f8e 100644
--- a/src/common/tools/object_factory/Device.py
+++ b/src/common/tools/object_factory/Device.py
@@ -43,6 +43,9 @@ DEVICE_MICROWAVE_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY]
DEVICE_P4_TYPE = DeviceTypeEnum.P4_SWITCH.value
DEVICE_P4_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_P4]
+DEVICE_TFS_TYPE = DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value
+DEVICE_TFS_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN]
+
def json_device_id(device_uuid : str):
return {'device_uuid': {'uuid': device_uuid}}
@@ -120,6 +123,13 @@ def json_device_p4_disabled(
return json_device(
device_uuid, DEVICE_P4_TYPE, DEVICE_DISABLED, endpoints=endpoints, config_rules=config_rules, drivers=drivers)
+def json_device_tfs_disabled(
+ device_uuid : str, endpoints : List[Dict] = [], config_rules : List[Dict] = [],
+ drivers : List[Dict] = DEVICE_TFS_DRIVERS
+ ):
+ return json_device(
+ device_uuid, DEVICE_TFS_TYPE, DEVICE_DISABLED, endpoints=endpoints, config_rules=config_rules, drivers=drivers)
+
def json_device_connect_rules(address : str, port : int, settings : Dict = {}):
return [
json_config_rule_set('_connect/address', address),
diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py
index c0442d8770c682ac1eea032980b58e7028be90c4..ba82e535ec958104bd14abf625eb6cd38c2a08ee 100644
--- a/src/common/type_checkers/Assertions.py
+++ b/src/common/type_checkers/Assertions.py
@@ -33,6 +33,7 @@ def validate_device_driver_enum(message):
'DEVICEDRIVER_IETF_NETWORK_TOPOLOGY',
'DEVICEDRIVER_ONF_TR_352',
'DEVICEDRIVER_XR',
+ 'DEVICEDRIVER_IETF_L2VPN',
]
def validate_device_operational_status_enum(message):
diff --git a/src/compute/service/__main__.py b/src/compute/service/__main__.py
index 9705e3187ffff633a4d127855c1c57afcf397e39..19a04c4c8ef4f77b3b7fb6949a1b567ef5cbec00 100644
--- a/src/compute/service/__main__.py
+++ b/src/compute/service/__main__.py
@@ -22,6 +22,7 @@ from .ComputeService import ComputeService
from .rest_server.RestServer import RestServer
from .rest_server.nbi_plugins.debug_api import register_debug_api
from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn
+from .rest_server.nbi_plugins.ietf_network_slice import register_ietf_nss
terminate = threading.Event()
LOGGER = None
@@ -58,8 +59,9 @@ def main():
grpc_service.start()
rest_server = RestServer()
+ register_ietf_l2vpn(rest_server) # Registering L2VPN entrypoint
+ register_ietf_nss(rest_server) # Registering NSS entrypoint
register_debug_api(rest_server)
- register_ietf_l2vpn(rest_server)
rest_server.start()
# Wait for Ctrl+C or termination signal
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py
index f95b532af4ba01968d17bc3958e1cffbf84a5e7f..ed25dbab3cd6b07ef73d64c5d37ad64e85353c02 100644
--- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py
@@ -74,4 +74,18 @@ BEARER_MAPPINGS = {
'R3:1/3': ('R3', '1/3', '5.3.1.3', None, 0, None, None, None, None),
'R4:1/2': ('R4', '1/2', '5.4.1.2', None, 0, None, None, None, None),
'R4:1/3': ('R4', '1/3', '5.4.1.3', None, 0, None, None, None, None),
+
+ # OFC'23
+ 'PE1:1/1': ('PE1', '1/1', '10.1.1.1', None, 0, None, None, None, None),
+ 'PE1:1/2': ('PE1', '1/2', '10.1.1.2', None, 0, None, None, None, None),
+ 'PE2:1/1': ('PE2', '1/1', '10.2.1.1', None, 0, None, None, None, None),
+ 'PE2:1/2': ('PE2', '1/2', '10.2.1.2', None, 0, None, None, None, None),
+ 'PE3:1/1': ('PE3', '1/1', '10.3.1.1', None, 0, None, None, None, None),
+ 'PE3:1/2': ('PE3', '1/2', '10.3.1.2', None, 0, None, None, None, None),
+ 'PE4:1/1': ('PE4', '1/1', '10.4.1.1', None, 0, None, None, None, None),
+ 'PE4:1/2': ('PE4', '1/2', '10.4.1.2', None, 0, None, None, None, None),
+
+ 'R149:eth-1/0/22': ('R149', 'eth-1/0/22', '5.5.5.5', None, 0, None, None, '5.5.5.1', '100'),
+ 'R155:eth-1/0/22': ('R155', 'eth-1/0/22', '5.5.5.1', None, 0, None, None, '5.5.5.5', '100'),
+ 'R199:eth-1/0/21': ('R199', 'eth-1/0/21', '5.5.5.6', None, 0, None, None, '5.5.5.5', '100'),
}
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py
index 2db231221faad1af2c7f0651dee2b95427e97acd..f12c4526aec27a60e579540ecae90720c707a117 100644
--- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py
@@ -20,8 +20,8 @@ from common.proto.context_pb2 import SliceStatusEnum
from common.tools.context_queries.Slice import get_slice
from context.client.ContextClient import ContextClient
from slice.client.SliceClient import SliceClient
-from .tools.Authentication import HTTP_AUTH
-from .tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR
+from ..tools.Authentication import HTTP_AUTH
+from ..tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR
LOGGER = logging.getLogger(__name__)
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py
index dcdecb70ffff4059e557d1dadbbb958a4e264fab..69676bd0d0165160da4e64c515d04b5a3252102c 100644
--- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py
@@ -22,9 +22,9 @@ from common.Constants import DEFAULT_CONTEXT_NAME
from common.proto.context_pb2 import SliceStatusEnum, Slice
from slice.client.SliceClient import SliceClient
from .schemas.vpn_service import SCHEMA_VPN_SERVICE
-from .tools.Authentication import HTTP_AUTH
-from .tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR
-from .tools.Validator import validate_message
+from compute.service.rest_server.nbi_plugins.tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR
+from compute.service.rest_server.nbi_plugins.tools.Validator import validate_message
+from compute.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH
LOGGER = logging.getLogger(__name__)
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py
index b89fa2207d1cd69e30612e8cecc8aa0f325e9dd3..ff7ad3c1481d3c0f3cdf7a6b6004f62677948ecc 100644
--- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py
@@ -30,11 +30,10 @@ from common.tools.grpc.Tools import grpc_message_to_json_string
from context.client.ContextClient import ContextClient
from slice.client.SliceClient import SliceClient
from .schemas.site_network_access import SCHEMA_SITE_NETWORK_ACCESS
-from .tools.Authentication import HTTP_AUTH
-from .tools.HttpStatusCodes import HTTP_NOCONTENT, HTTP_SERVERERROR
-from .tools.Validator import validate_message
-from .Constants import (
- BEARER_MAPPINGS, DEFAULT_ADDRESS_FAMILIES, DEFAULT_BGP_AS, DEFAULT_BGP_ROUTE_TARGET, DEFAULT_MTU)
+from compute.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH
+from compute.service.rest_server.nbi_plugins.tools.HttpStatusCodes import HTTP_NOCONTENT, HTTP_SERVERERROR
+from compute.service.rest_server.nbi_plugins.tools.Validator import validate_message
+from .Constants import BEARER_MAPPINGS, DEFAULT_ADDRESS_FAMILIES, DEFAULT_BGP_AS, DEFAULT_BGP_ROUTE_TARGET, DEFAULT_MTU
LOGGER = logging.getLogger(__name__)
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py b/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9b17c8b144ad6cf477b978ceb50b3497c9c074b
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py
@@ -0,0 +1,78 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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 logging
+from flask.json import jsonify
+from flask_restful import Resource
+from common.proto.context_pb2 import SliceStatusEnum
+from common.tools.context_queries.Slice import get_slice
+from common.tools.grpc.Tools import grpc_message_to_json
+from context.client.ContextClient import ContextClient
+from slice.client.SliceClient import SliceClient
+from ..tools.Authentication import HTTP_AUTH
+from ..tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR
+
+LOGGER = logging.getLogger(__name__)
+
+class NSS_Service(Resource):
+ @HTTP_AUTH.login_required
+ def get(self, slice_id : str):
+ LOGGER.debug('GET Slice ID: {:s}'.format(str(slice_id)))
+ try:
+ context_client = ContextClient()
+
+ target = get_slice(context_client, slice_id, rw_copy=True)
+ if target is None:
+ raise Exception('Slice({:s}) not found in database'.format(str(slice_id)))
+
+ if target.slice_id.slice_uuid.uuid != slice_id: # pylint: disable=no-member
+ raise Exception('Slice retrieval failed. Wrong Slice Id was returned')
+
+ slice_ready_status = SliceStatusEnum.SLICESTATUS_ACTIVE
+ slice_status = target.slice_status.slice_status # pylint: disable=no-member
+ response = jsonify(grpc_message_to_json(target))
+ response.status_code = HTTP_OK if slice_status == slice_ready_status else HTTP_GATEWAYTIMEOUT
+
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Something went wrong Retrieving Slice({:s})'.format(str(slice_id)))
+ response = jsonify({'error': str(e)})
+ response.status_code = HTTP_SERVERERROR
+ return response
+
+
+ @HTTP_AUTH.login_required
+ def delete(self, slice_id : str):
+ LOGGER.debug('DELETE Slice ID: {:s}'.format(str(slice_id)))
+ try:
+ context_client = ContextClient()
+ target = get_slice(context_client, slice_id)
+
+ response = jsonify({})
+ response.status_code = HTTP_OK
+
+ if target is None:
+ LOGGER.warning('Slice({:s}) not found in database. Nothing done.'.format(str(slice_id)))
+ response.status_code = HTTP_NOCONTENT
+ else:
+ if target.slice_id.slice_uuid.uuid != slice_id: # pylint: disable=no-member
+ raise Exception('Slice retrieval failed. Wrong Slice Id was returned')
+ slice_client = SliceClient()
+ slice_client.DeleteSlice(target.slice_id)
+ LOGGER.debug(f"Slice({slice_id}) successfully deleted")
+
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Something went wrong Deleting Slice({:s})'.format(str(slice_id)))
+ response = jsonify({'error': str(e)})
+ response.status_code = HTTP_SERVERERROR
+ return response
\ No newline at end of file
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py b/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py
new file mode 100644
index 0000000000000000000000000000000000000000..72b09f2b7d1422da2554a3dfe873a6bcff87413d
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py
@@ -0,0 +1,122 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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 json
+import logging
+import ssl
+import uuid
+from typing import Dict
+from flask.json import jsonify
+from flask_restful import Resource
+from flask import request
+
+from common.Constants import DEFAULT_CONTEXT_NAME
+from common.proto.context_pb2 import Slice, SliceStatusEnum, EndPointId, Constraint
+from common.tools.grpc.Tools import grpc_message_to_json
+from ..tools.Authentication import HTTP_AUTH
+from ..tools.HttpStatusCodes import HTTP_BADREQUEST, HTTP_OK, HTTP_CREATED, HTTP_SERVERERROR
+from werkzeug.exceptions import UnsupportedMediaType
+
+from slice.client.SliceClient import SliceClient
+from .bindings import load_json_data
+from .bindings.network_slice_services import NetworkSliceServices
+
+LOGGER = logging.getLogger(__name__)
+
+class NSS_Services(Resource):
+ @HTTP_AUTH.login_required
+ def get(self):
+ response = jsonify({"message": "All went well!"})
+ # TODO Return list of current network-slice-services
+ return response
+
+ @HTTP_AUTH.login_required
+ def post(self):
+ if not request.is_json:
+ raise UnsupportedMediaType('JSON payload is required')
+ request_data = json.dumps(request.json)
+ response = jsonify({})
+ response.status_code = HTTP_CREATED
+
+ slices: NetworkSliceServices = load_json_data(request_data, NetworkSliceServices)[0]
+ for ietf_slice in slices.slice_service:
+ slice_request: Slice = Slice()
+ # Meta information
+ # TODO implement name and owner based on "tags"
+ slice_request.slice_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME
+ slice_request.slice_id.slice_uuid.uuid = ietf_slice.service_id()
+ # TODO map with admin status of IETF Slice
+ slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED
+
+ list_endpoints = []
+ for sdp in ietf_slice.sdps().sdp:
+ endpoint = EndPointId()
+ endpoint.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME
+ endpoint.device_id.device_uuid.uuid = sdp.node_id()
+ endpoint.endpoint_uuid.uuid = sdp.sdp_id()
+ list_endpoints.append(endpoint)
+ slice_request.slice_endpoint_ids.extend(list_endpoints)
+
+ # TODO Map connectivity_groups and connectivity constructs to real connections
+ LOGGER.debug(f"Connection groups detected: {len(ietf_slice.connection_groups().connection_group())}")
+ list_constraints = []
+ for cg in ietf_slice.connection_groups().connection_group:
+ for cc in cg.connectivity_construct:
+ if cc.slo_sle_policy.custom:
+ with cc.slo_sle_policy.custom as slo:
+ for metric_bound in slo.service_slo_sle_policy().metric_bounds().metric_bound:
+ metric_type = str(metric_bound.metric_type()).casefold()
+ if metric_type == "service-slo-two-way-bandwidth": # TODO fix to two way!
+ constraint = Constraint()
+ metric_unit = metric_bound.metric_unit().casefold()
+ capacity = float(metric_bound.bound()) # Assuming capacity already in Gbps
+ if metric_unit == "mbps":
+ capacity /= 1E3
+ elif metric_unit != "gbps":
+ LOGGER.warning(f"Invalided metric unit ({metric_bound.metric_unit()}), must be Mbps or Gbps")
+ response.status_code = HTTP_SERVERERROR
+ return response
+ constraint.sla_capacity.capacity_gbps = capacity
+ list_constraints.append(constraint)
+
+ elif metric_type == "service-slo-one-way-delay":
+ if metric_bound.metric_unit().casefold() == "ms":
+ latency = int(metric_bound.bound())
+ else:
+ LOGGER.warning(f"Invalided metric unit ({metric_bound.metric_unit()}), must be \"ms\" ")
+ response.status_code = HTTP_SERVERERROR
+ return response
+ constraint = Constraint()
+ constraint.sla_latency.e2e_latency_ms = latency
+ list_constraints.append(constraint)
+
+ elif metric_type == "service-slo-availability":
+ availability = float(metric_bound.bound())
+ if availability > 100.0 or availability < 0.0:
+ raise Exception(f'Slice SLO availability ({availability}) must be constrained [0,100]')
+ constraint = Constraint()
+ constraint.sla_availability.availability = availability
+ # TODO not really necessary, remove after OFC2023
+ constraint.sla_availability.num_disjoint_paths = 0
+ constraint.sla_availability.all_active = False
+ list_constraints.append(constraint)
+
+ slice_request.slice_constraints.extend(list_constraints)
+ LOGGER.debug(grpc_message_to_json(slice_request)) # TODO remove
+ # TODO adding owner, needs to be recoded after updating the bindings
+ owner = request.json["data"]["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["service-tags"][0]["value"]
+ slice_request.slice_owner.owner_string = owner
+ slice_request.slice_owner.owner_uuid.uuid = str(uuid.uuid5(uuid.NAMESPACE_DNS, owner))
+ slice_client = SliceClient()
+ slice_client.CreateSlice(slice_request)
+ return response
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/__init__.py b/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c8aca124acfd15b2003b4272cf0b3103dbca3bdb
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_network_slice/__init__.py
@@ -0,0 +1,31 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+# IETF draft-ietf-teas-ietf-network-slice-nbi-yang-02 - IETF Network Slice Service YANG Model
+# Ref: https://datatracker.ietf.org/doc/draft-ietf-teas-ietf-network-slice-nbi-yang/
+
+from flask_restful import Resource
+from compute.service.rest_server.RestServer import RestServer
+from .NSS_Services import NSS_Services
+from .NSS_Service import NSS_Service
+
+URL_PREFIX = '/data/ietf-network-slice-service:ietf-nss'
+
+def _add_resource(rest_server : RestServer, resource : Resource, *urls, **kwargs):
+ urls = [(URL_PREFIX + url) for url in urls]
+ rest_server.add_resource(resource, *urls, **kwargs)
+
+def register_ietf_nss(rest_server : RestServer):
+ _add_resource(rest_server, NSS_Services, '/network-slice-services')
+ _add_resource(rest_server, NSS_Service, '/network-slice-services/slice-service=DEVICEDRIVER_XR = 6;
*/
DEVICEDRIVER_XR(6),
+ /**
+ * DEVICEDRIVER_IETF_L2VPN = 7;
+ */
+ DEVICEDRIVER_IETF_L2VPN(7),
UNRECOGNIZED(-1),
;
@@ -212,6 +216,10 @@ public final class ContextOuterClass {
* DEVICEDRIVER_XR = 6;
*/
public static final int DEVICEDRIVER_XR_VALUE = 6;
+ /**
+ * DEVICEDRIVER_IETF_L2VPN = 7;
+ */
+ public static final int DEVICEDRIVER_IETF_L2VPN_VALUE = 7;
public final int getNumber() {
@@ -245,6 +253,7 @@ public final class ContextOuterClass {
case 4: return DEVICEDRIVER_IETF_NETWORK_TOPOLOGY;
case 5: return DEVICEDRIVER_ONF_TR_352;
case 6: return DEVICEDRIVER_XR;
+ case 7: return DEVICEDRIVER_IETF_L2VPN;
default: return null;
}
}
diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py
index a73ec53f37d68e0414eeb1df146373c6906273c5..3ec71dc64536e28457c4f1adbf3679186285786d 100644
--- a/src/service/service/service_handler_api/FilterFields.py
+++ b/src/service/service/service_handler_api/FilterFields.py
@@ -33,7 +33,8 @@ DEVICE_DRIVER_VALUES = {
DeviceDriverEnum.DEVICEDRIVER_P4,
DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY,
DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352,
- DeviceDriverEnum.DEVICEDRIVER_XR
+ DeviceDriverEnum.DEVICEDRIVER_XR,
+ DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN,
}
# Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified
diff --git a/src/service/service/service_handler_api/ServiceHandlerFactory.py b/src/service/service/service_handler_api/ServiceHandlerFactory.py
index 6aa21b49920254383fad5f28aa234b6ec0cad5a3..64ea204a2600a71b08c8c373a15640f5e2134787 100644
--- a/src/service/service/service_handler_api/ServiceHandlerFactory.py
+++ b/src/service/service/service_handler_api/ServiceHandlerFactory.py
@@ -73,6 +73,9 @@ class ServiceHandlerFactory:
if field_indice is None: continue
if not isinstance(field_values, Iterable) or isinstance(field_values, str):
field_values = [field_values]
+ if len(field_values) == 0:
+ # do not allow empty fields; might cause wrong selection
+ raise UnsatisfiedFilterException(filter_fields)
field_enum_values = FILTER_FIELD_ALLOWED_VALUES.get(field_name)
diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py
index 4c9059779d6b7031685e1de76b0a7ed651af6c5f..257bc138fe932e7e5abee00981848248039d0b3f 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -15,12 +15,14 @@
from common.proto.context_pb2 import DeviceDriverEnum, ServiceTypeEnum
from ..service_handler_api.FilterFields import FilterFieldEnum
from .l2nm_emulated.L2NMEmulatedServiceHandler import L2NMEmulatedServiceHandler
+from .l2nm_ietfl2vpn.L2NM_IETFL2VPN_ServiceHandler import L2NM_IETFL2VPN_ServiceHandler
from .l2nm_openconfig.L2NMOpenConfigServiceHandler import L2NMOpenConfigServiceHandler
from .l3nm_emulated.L3NMEmulatedServiceHandler import L3NMEmulatedServiceHandler
from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceHandler
+from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler
from .p4.p4_service_handler import P4ServiceHandler
from .tapi_tapi.TapiServiceHandler import TapiServiceHandler
-from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler
+from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler
SERVICE_HANDLERS = [
(L2NMEmulatedServiceHandler, [
@@ -50,13 +52,19 @@ SERVICE_HANDLERS = [
(TapiServiceHandler, [
{
FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
- FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API, DeviceDriverEnum.DEVICEDRIVER_XR],
+ FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API],
+ }
+ ]),
+ (TapiXrServiceHandler, [
+ {
+ FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
+ FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_XR],
}
]),
(MicrowaveServiceHandler, [
{
FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L2NM,
- FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY,
+ FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY, DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352],
}
]),
(P4ServiceHandler, [
@@ -65,4 +73,10 @@ SERVICE_HANDLERS = [
FilterFieldEnum.DEVICE_DRIVER: DeviceDriverEnum.DEVICEDRIVER_P4,
}
]),
+ (L2NM_IETFL2VPN_ServiceHandler, [
+ {
+ FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L2NM,
+ FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN],
+ }
+ ]),
]
diff --git a/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py b/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py
index c2ea6e213ee8d18b4507089fb2762c913e03039a..363983b8653e1cfa553279d2df74d6ac893a4fec 100644
--- a/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py
+++ b/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py
@@ -21,15 +21,18 @@ def setup_config_rules(
service_settings : TreeNode, endpoint_settings : TreeNode
) -> List[Dict]:
- json_settings : Dict = {} if service_settings is None else service_settings.value
- json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value
+ if service_settings is None: return []
+ if endpoint_settings is None: return []
- mtu = json_settings.get('mtu', 1450 ) # 1512
+ #json_settings : Dict = service_settings.value
+ json_endpoint_settings : Dict = endpoint_settings.value
+
+ #mtu = json_settings.get('mtu', 1450 ) # 1512
#address_families = json_settings.get('address_families', [] ) # ['IPV4']
#bgp_as = json_settings.get('bgp_as', 0 ) # 65000
#bgp_route_target = json_settings.get('bgp_route_target', '0:0') # 65000:333
- router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10'
+ #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10'
#route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0' ) # '60001:801'
sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1
vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400
@@ -43,17 +46,17 @@ def setup_config_rules(
connection_point_id = 'VC-1'
json_config_rules = [
- json_config_rule_set(
- '/network_instance[default]',
- {'name': 'default', 'type': 'DEFAULT_INSTANCE', 'router_id': router_id}),
+ #json_config_rule_set(
+ # '/network_instance[default]',
+ # {'name': 'default', 'type': 'DEFAULT_INSTANCE', 'router_id': router_id}),
- json_config_rule_set(
- '/network_instance[default]/protocols[OSPF]',
- {'name': 'default', 'identifier': 'OSPF', 'protocol_name': 'OSPF'}),
+ #json_config_rule_set(
+ # '/network_instance[default]/protocols[OSPF]',
+ # {'name': 'default', 'identifier': 'OSPF', 'protocol_name': 'OSPF'}),
- json_config_rule_set(
- '/network_instance[default]/protocols[STATIC]',
- {'name': 'default', 'identifier': 'STATIC', 'protocol_name': 'STATIC'}),
+ #json_config_rule_set(
+ # '/network_instance[default]/protocols[STATIC]',
+ # {'name': 'default', 'identifier': 'STATIC', 'protocol_name': 'STATIC'}),
json_config_rule_set(
'/network_instance[{:s}]'.format(network_instance_name),
@@ -66,7 +69,7 @@ def setup_config_rules(
json_config_rule_set(
'/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_cirid_name),
{'name': network_instance_name, 'id': if_cirid_name, 'interface': if_cirid_name,
- 'subinterface': sub_interface_index}),
+ 'subinterface': sub_interface_index}),
json_config_rule_set(
'/network_instance[{:s}]/connection_point[{:s}]'.format(network_instance_name, connection_point_id),
@@ -80,15 +83,18 @@ def teardown_config_rules(
service_settings : TreeNode, endpoint_settings : TreeNode
) -> List[Dict]:
- #json_settings : Dict = {} if service_settings is None else service_settings.value
- json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value
+ if service_settings is None: return []
+ if endpoint_settings is None: return []
+
+ #json_settings : Dict = service_settings.value
+ json_endpoint_settings : Dict = endpoint_settings.value
#mtu = json_settings.get('mtu', 1450 ) # 1512
#address_families = json_settings.get('address_families', [] ) # ['IPV4']
#bgp_as = json_settings.get('bgp_as', 0 ) # 65000
#bgp_route_target = json_settings.get('bgp_route_target', '0:0') # 65000:333
- router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10'
+ #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10'
#route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0' ) # '60001:801'
sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1
#vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400
@@ -99,36 +105,36 @@ def teardown_config_rules(
if_cirid_name = '{:s}.{:s}'.format(endpoint_name, str(circuit_id))
network_instance_name = 'ELAN-AC:{:s}'.format(str(circuit_id))
- connection_point_id = 'VC-1'
+ #connection_point_id = 'VC-1'
json_config_rules = [
- json_config_rule_delete(
- '/network_instance[{:s}]/connection_point[{:s}]'.format(network_instance_name, connection_point_id),
- {'name': network_instance_name, 'connection_point': connection_point_id}),
+ #json_config_rule_delete(
+ # '/network_instance[{:s}]/connection_point[{:s}]'.format(network_instance_name, connection_point_id),
+ # {'name': network_instance_name, 'connection_point': connection_point_id}),
- json_config_rule_delete(
- '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_cirid_name),
- {'name': network_instance_name, 'id': if_cirid_name, 'interface': if_cirid_name,
- 'subinterface': sub_interface_index}),
-
- json_config_rule_delete(
- '/interface[{:s}]/subinterface[{:d}]'.format(if_cirid_name, sub_interface_index),
- {'name': if_cirid_name, 'index': sub_interface_index}),
+ #json_config_rule_delete(
+ # '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_cirid_name),
+ # {'name': network_instance_name, 'id': if_cirid_name, 'interface': if_cirid_name,
+ # 'subinterface': sub_interface_index}),
json_config_rule_delete(
'/network_instance[{:s}]'.format(network_instance_name),
{'name': network_instance_name}),
json_config_rule_delete(
- '/network_instance[default]/protocols[STATIC]',
- {'name': 'default', 'identifier': 'STATIC', 'protocol_name': 'STATIC'}),
+ '/interface[{:s}]/subinterface[{:d}]'.format(if_cirid_name, sub_interface_index),
+ {'name': if_cirid_name, 'index': sub_interface_index}),
- json_config_rule_delete(
- '/network_instance[default]/protocols[OSPF]',
- {'name': 'default', 'identifier': 'OSPF', 'protocol_name': 'OSPF'}),
+ #json_config_rule_delete(
+ # '/network_instance[default]/protocols[STATIC]',
+ # {'name': 'default', 'identifier': 'STATIC', 'protocol_name': 'STATIC'}),
- json_config_rule_delete(
- '/network_instance[default]',
- {'name': 'default', 'type': 'DEFAULT_INSTANCE', 'router_id': router_id}),
+ #json_config_rule_delete(
+ # '/network_instance[default]/protocols[OSPF]',
+ # {'name': 'default', 'identifier': 'OSPF', 'protocol_name': 'OSPF'}),
+
+ #json_config_rule_delete(
+ # '/network_instance[default]',
+ # {'name': 'default', 'type': 'DEFAULT_INSTANCE', 'router_id': router_id}),
]
return json_config_rules
diff --git a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py
index 9de6c607b1336a4b3fb43867efc16d30048177e0..0a2261ceb4e1a63c984cf833121e3a87e13c0e9f 100644
--- a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py
+++ b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py
@@ -75,10 +75,12 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name,
settings, endpoint_settings)
- del device_obj.device_config.config_rules[:]
- for json_config_rule in json_config_rules:
- device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
- self.__task_executor.configure_device(device_obj)
+ if len(json_config_rules) > 0:
+ del device_obj.device_config.config_rules[:]
+ for json_config_rule in json_config_rules:
+ device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(device_obj)
+
results.append(True)
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
@@ -110,10 +112,12 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name,
settings, endpoint_settings)
- del device_obj.device_config.config_rules[:]
- for json_config_rule in json_config_rules:
- device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
- self.__task_executor.configure_device(device_obj)
+ if len(json_config_rules) > 0:
+ del device_obj.device_config.config_rules[:]
+ for json_config_rule in json_config_rules:
+ device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(device_obj)
+
results.append(True)
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
diff --git a/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..880a6c5a20d618c9d9f21701b7ef3886dbb9fc21
--- /dev/null
+++ b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py
@@ -0,0 +1,173 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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 json, logging
+from typing import Any, Dict, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+from common.tools.object_factory.Device import json_device_id
+from common.type_checkers.Checkers import chk_type
+from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching
+from service.service.service_handler_api._ServiceHandler import _ServiceHandler
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
+from service.service.task_scheduler.TaskExecutor import TaskExecutor
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'tapi_tapi'})
+
+class L2NM_IETFL2VPN_ServiceHandler(_ServiceHandler):
+ def __init__( # pylint: disable=super-init-not-called
+ self, service : Service, task_executor : TaskExecutor, **settings
+ ) -> None:
+ self.__service = service
+ self.__task_executor = task_executor
+ self.__settings_handler = SettingsHandler(service.service_config, **settings)
+
+ @metered_subclass_method(METRICS_POOL)
+ def SetEndpoint(
+ self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+ ) -> List[Union[bool, Exception]]:
+
+ chk_type('endpoints', endpoints, list)
+ if len(endpoints) < 2: return []
+
+ service_uuid = self.__service.service_id.service_uuid.uuid
+ settings = self.__settings_handler.get('/settings')
+ json_settings : Dict = {} if settings is None else settings.value
+ encap_type = json_settings.get('encapsulation_type', '')
+ vlan_id = json_settings.get('vlan_id', 100)
+
+ results = []
+ try:
+ src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0])
+ src_device = self.__task_executor.get_device(DeviceId(**json_device_id(src_device_uuid)))
+ src_endpoint = get_endpoint_matching(src_device, src_endpoint_uuid)
+ src_controller = self.__task_executor.get_device_controller(src_device)
+
+ dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[-1])
+ dst_device = self.__task_executor.get_device(DeviceId(**json_device_id(dst_device_uuid)))
+ dst_endpoint = get_endpoint_matching(dst_device, dst_endpoint_uuid)
+ dst_controller = self.__task_executor.get_device_controller(dst_device)
+
+ if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid:
+ raise Exception('Different Src-Dst devices not supported by now')
+ controller = src_controller
+
+ json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), {
+ 'uuid' : service_uuid,
+ 'src_device_name' : src_device.name,
+ 'src_endpoint_name' : src_endpoint.name,
+ 'dst_device_name' : dst_device.name,
+ 'dst_endpoint_name' : dst_endpoint.name,
+ 'encapsulation_type': encap_type,
+ 'vlan_id' : vlan_id,
+ })
+ del controller.device_config.config_rules[:]
+ controller.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(controller)
+ results.append(True)
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid)))
+ results.append(e)
+
+ return results
+
+ @metered_subclass_method(METRICS_POOL)
+ def DeleteEndpoint(
+ self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+ ) -> List[Union[bool, Exception]]:
+
+ chk_type('endpoints', endpoints, list)
+ if len(endpoints) < 2: return []
+
+ service_uuid = self.__service.service_id.service_uuid.uuid
+
+ results = []
+ try:
+ src_device_uuid, _ = get_device_endpoint_uuids(endpoints[0])
+ src_device = self.__task_executor.get_device(DeviceId(**json_device_id(src_device_uuid)))
+ src_controller = self.__task_executor.get_device_controller(src_device)
+
+ dst_device_uuid, _ = get_device_endpoint_uuids(endpoints[1])
+ dst_device = self.__task_executor.get_device(DeviceId(**json_device_id(dst_device_uuid)))
+ dst_controller = self.__task_executor.get_device_controller(dst_device)
+
+ if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid:
+ raise Exception('Different Src-Dst devices not supported by now')
+ controller = src_controller
+
+ json_config_rule = json_config_rule_delete('/services/service[{:s}]'.format(service_uuid), {
+ 'uuid': service_uuid
+ })
+ del controller.device_config.config_rules[:]
+ controller.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(controller)
+ results.append(True)
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Unable to DeleteEndpoint for Service({:s})'.format(str(service_uuid)))
+ results.append(e)
+
+ return results
+
+ @metered_subclass_method(METRICS_POOL)
+ def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+ chk_type('constraints', constraints, list)
+ if len(constraints) == 0: return []
+
+ msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+ LOGGER.warning(msg.format(str(constraints)))
+ return [True for _ in range(len(constraints))]
+
+ @metered_subclass_method(METRICS_POOL)
+ def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+ chk_type('constraints', constraints, list)
+ if len(constraints) == 0: return []
+
+ msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+ LOGGER.warning(msg.format(str(constraints)))
+ return [True for _ in range(len(constraints))]
+
+ @metered_subclass_method(METRICS_POOL)
+ def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+ chk_type('resources', resources, list)
+ if len(resources) == 0: return []
+
+ results = []
+ for resource in resources:
+ try:
+ resource_value = json.loads(resource[1])
+ self.__settings_handler.set(resource[0], resource_value)
+ results.append(True)
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
+ results.append(e)
+
+ return results
+
+ @metered_subclass_method(METRICS_POOL)
+ def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+ chk_type('resources', resources, list)
+ if len(resources) == 0: return []
+
+ results = []
+ for resource in resources:
+ try:
+ self.__settings_handler.delete(resource[0])
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
+ results.append(e)
+
+ return results
diff --git a/src/service/service/service_handlers/l2nm_ietfl2vpn/__init__.py b/src/service/service/service_handlers/l2nm_ietfl2vpn/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/service/service/service_handlers/l2nm_ietfl2vpn/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
diff --git a/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py b/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py
index 07e78d73631342d101d77697098e83961c7dcf26..63e818a8303f9939abe8f776cd34b3066cb84ec1 100644
--- a/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py
+++ b/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py
@@ -20,16 +20,13 @@ def setup_config_rules(
service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str,
service_settings : TreeNode, endpoint_settings : TreeNode
) -> List[Dict]:
-
+
if service_settings is None: return []
if endpoint_settings is None: return []
- json_settings : Dict = service_settings.value
+ #json_settings : Dict = service_settings.value
json_endpoint_settings : Dict = endpoint_settings.value
- json_settings : Dict = {} if service_settings is None else service_settings.value
- json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value
-
#mtu = json_settings.get('mtu', 1450 ) # 1512
#address_families = json_settings.get('address_families', [] ) # ['IPV4']
#bgp_as = json_settings.get('bgp_as', 0 ) # 65000
@@ -41,43 +38,32 @@ def setup_config_rules(
vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400
#address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1'
#address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30
- remote_router = json_endpoint_settings.get('remote_router', '5.5.5.5') # '5.5.5.5'
- circuit_id = json_endpoint_settings.get('circuit_id', '111' ) # '111'
-
+ remote_router = json_endpoint_settings.get('remote_router', '0.0.0.0') # '5.5.5.5'
+ circuit_id = json_endpoint_settings.get('circuit_id', '000' ) # '111'
if_cirid_name = '{:s}.{:s}'.format(endpoint_name, str(circuit_id))
network_instance_name = 'ELAN-AC:{:s}'.format(str(circuit_id))
connection_point_id = 'VC-1'
json_config_rules = [
-
+
json_config_rule_set(
'/network_instance[{:s}]'.format(network_instance_name),
- {'name': network_instance_name,
- 'type': 'L2VSI'}),
+ {'name': network_instance_name, 'type': 'L2VSI'}),
json_config_rule_set(
- '/interface[{:s}]/subinterface[0]'.format(if_cirid_name),
- {'name': if_cirid_name,
- 'type': 'l2vlan',
- 'index': sub_interface_index,
- 'vlan_id': vlan_id}),
+ '/interface[{:s}]/subinterface[{:d}]'.format(if_cirid_name, sub_interface_index),
+ {'name': if_cirid_name, 'type': 'l2vlan', 'index': sub_interface_index, 'vlan_id': vlan_id}),
json_config_rule_set(
'/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_cirid_name),
- {'name': network_instance_name,
- 'id': if_cirid_name,
- 'interface': if_cirid_name,
- 'subinterface': 0
- }),
+ {'name': network_instance_name, 'id': if_cirid_name, 'interface': if_cirid_name,
+ 'subinterface': sub_interface_index}),
json_config_rule_set(
'/network_instance[{:s}]/connection_point[{:s}]'.format(network_instance_name, connection_point_id),
- {'name': network_instance_name,
- 'connection_point': connection_point_id,
- 'VC_ID': circuit_id,
- 'remote_system': remote_router
- }),
+ {'name': network_instance_name, 'connection_point': connection_point_id, 'VC_ID': circuit_id,
+ 'remote_system': remote_router}),
]
return json_config_rules
@@ -86,8 +72,11 @@ def teardown_config_rules(
service_settings : TreeNode, endpoint_settings : TreeNode
) -> List[Dict]:
- #json_settings : Dict = {} if service_settings is None else service_settings.value
- json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value
+ if service_settings is None: return []
+ if endpoint_settings is None: return []
+
+ #json_settings : Dict = service_settings.value
+ json_endpoint_settings : Dict = endpoint_settings.value
#mtu = json_settings.get('mtu', 1450 ) # 1512
#address_families = json_settings.get('address_families', [] ) # ['IPV4']
@@ -96,7 +85,7 @@ def teardown_config_rules(
#router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10'
#route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0' ) # '60001:801'
- #sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1
+ sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1
#vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400
#address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1'
#address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30
@@ -105,17 +94,26 @@ def teardown_config_rules(
if_cirid_name = '{:s}.{:s}'.format(endpoint_name, str(circuit_id))
network_instance_name = 'ELAN-AC:{:s}'.format(str(circuit_id))
- #connection_point_id = 'VC-1'
+ connection_point_id = 'VC-1'
json_config_rules = [
+
+ json_config_rule_delete(
+ '/network_instance[{:s}]/connection_point[{:s}]'.format(network_instance_name, connection_point_id),
+ {'name': network_instance_name, 'connection_point': connection_point_id, 'VC_ID': circuit_id}),
+
+ json_config_rule_delete(
+ '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_cirid_name),
+ {'name': network_instance_name, 'id': if_cirid_name, 'interface': if_cirid_name,
+ 'subinterface': sub_interface_index}),
+
+ json_config_rule_delete(
+ '/interface[{:s}]/subinterface[{:d}]'.format(if_cirid_name, sub_interface_index),
+ {'name': if_cirid_name, 'index': sub_interface_index}),
+
json_config_rule_delete(
'/network_instance[{:s}]'.format(network_instance_name),
{'name': network_instance_name}),
-
- json_config_rule_delete(
- '/interface[{:s}]/subinterface[0]'.format(if_cirid_name),{
- 'name': if_cirid_name,
- }),
-
+
]
return json_config_rules
diff --git a/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py b/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py
index 903ad8cd5ae442a03d54fb49083f3837a3c8187c..f4a46112e778bd01aa76322384d8adee942aaa5b 100644
--- a/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py
+++ b/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py
@@ -21,8 +21,11 @@ def setup_config_rules(
service_settings : TreeNode, endpoint_settings : TreeNode
) -> List[Dict]:
- json_settings : Dict = {} if service_settings is None else service_settings.value
- json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value
+ if service_settings is None: return []
+ if endpoint_settings is None: return []
+
+ json_settings : Dict = service_settings.value
+ json_endpoint_settings : Dict = endpoint_settings.value
service_short_uuid = service_uuid.split('-')[-1]
network_instance_name = '{:s}-NetInst'.format(service_short_uuid)
@@ -142,8 +145,11 @@ def teardown_config_rules(
service_settings : TreeNode, endpoint_settings : TreeNode
) -> List[Dict]:
- json_settings : Dict = {} if service_settings is None else service_settings.value
- json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value
+ if service_settings is None: return []
+ if endpoint_settings is None: return []
+
+ json_settings : Dict = service_settings.value
+ json_endpoint_settings : Dict = endpoint_settings.value
#mtu = json_settings.get('mtu', 1450 ) # 1512
#address_families = json_settings.get('address_families', [] ) # ['IPV4']
diff --git a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
index 47de9c94fbb8a5ddac848336c2ed7936d0126b45..18da03b08c0ea490b60c41df3894392022ddf228 100644
--- a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
+++ b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
@@ -75,10 +75,12 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name,
settings, endpoint_settings)
- del device_obj.device_config.config_rules[:]
- for json_config_rule in json_config_rules:
- device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
- self.__task_executor.configure_device(device_obj)
+ if len(json_config_rules) > 0:
+ del device_obj.device_config.config_rules[:]
+ for json_config_rule in json_config_rules:
+ device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(device_obj)
+
results.append(True)
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
@@ -110,10 +112,12 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name,
settings, endpoint_settings)
- del device_obj.device_config.config_rules[:]
- for json_config_rule in json_config_rules:
- device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
- self.__task_executor.configure_device(device_obj)
+ if len(json_config_rules) > 0:
+ del device_obj.device_config.config_rules[:]
+ for json_config_rule in json_config_rules:
+ device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(device_obj)
+
results.append(True)
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
diff --git a/src/service/service/service_handlers/l3nm_openconfig/ConfigRules.py b/src/service/service/service_handlers/l3nm_openconfig/ConfigRules.py
index ef93dcdda8145cab15ff21c24b6318e9eb00e098..5d260bf86b82c66be8eb2f0caa683a72d8bd0ba5 100644
--- a/src/service/service/service_handlers/l3nm_openconfig/ConfigRules.py
+++ b/src/service/service/service_handlers/l3nm_openconfig/ConfigRules.py
@@ -187,8 +187,8 @@ def teardown_config_rules(
if service_settings is None: return []
if endpoint_settings is None: return []
- json_settings : Dict = {} if service_settings is None else service_settings.value
- json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value
+ json_settings : Dict = service_settings.value
+ json_endpoint_settings : Dict = endpoint_settings.value
service_short_uuid = service_uuid.split('-')[-1]
network_instance_name = '{:s}-NetInst'.format(service_short_uuid)
diff --git a/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py b/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py
index ee64d2fa4ff0110aea9ee4beee97fa83915ab57d..40c87eeee2c8dd1ddd5a39162f8ff7f117344e3b 100644
--- a/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py
+++ b/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py
@@ -61,7 +61,7 @@ class MicrowaveServiceHandler(_ServiceHandler):
device_uuid_dst, endpoint_uuid_dst = get_device_endpoint_uuids(endpoints[1])
if device_uuid_src != device_uuid_dst:
- raise Exception('Diferent Src-Dst devices not supported by now')
+ raise Exception('Different Src-Dst devices not supported by now')
device_uuid = device_uuid_src
device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
@@ -106,7 +106,7 @@ class MicrowaveServiceHandler(_ServiceHandler):
device_uuid_dst, _ = get_device_endpoint_uuids(endpoints[1])
if device_uuid_src != device_uuid_dst:
- raise Exception('Diferent Src-Dst devices not supported by now')
+ raise Exception('Different Src-Dst devices not supported by now')
device_uuid = device_uuid_src
device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
diff --git a/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py
index 8abd12b2a24c49a6c5e50cebe7a2d65dc7ce4eb1..af7d4bc949fc98f057ade66b58d8b9b38e0707ed 100644
--- a/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py
+++ b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py
@@ -19,7 +19,7 @@ from common.proto.context_pb2 import ConfigRule, DeviceId, Service
from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
from common.tools.object_factory.Device import json_device_id
from common.type_checkers.Checkers import chk_type
-from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching
+from service.service.service_handler_api.Tools import get_device_endpoint_uuids
from service.service.service_handler_api._ServiceHandler import _ServiceHandler
from service.service.service_handler_api.SettingsHandler import SettingsHandler
from service.service.task_scheduler.TaskExecutor import TaskExecutor
@@ -42,7 +42,7 @@ class TapiServiceHandler(_ServiceHandler):
) -> List[Union[bool, Exception]]:
chk_type('endpoints', endpoints, list)
- if len(endpoints) != 2: return []
+ if len(endpoints) < 2: return []
service_uuid = self.__service.service_id.service_uuid.uuid
settings = self.__settings_handler.get('/settings')
@@ -55,30 +55,33 @@ class TapiServiceHandler(_ServiceHandler):
results = []
try:
- device_uuid_src, endpoint_uuid_src = get_device_endpoint_uuids(endpoints[0])
- device_uuid_dst, endpoint_uuid_dst = get_device_endpoint_uuids(endpoints[1])
+ src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0])
+ src_device = self.__task_executor.get_device(DeviceId(**json_device_id(src_device_uuid)))
+ src_controller = self.__task_executor.get_device_controller(src_device)
+ if src_controller is None: src_controller = src_device
- if device_uuid_src != device_uuid_dst:
- raise Exception('Diferent Src-Dst devices not supported by now')
- device_uuid = device_uuid_src
+ dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[-1])
+ dst_device = self.__task_executor.get_device(DeviceId(**json_device_id(dst_device_uuid)))
+ dst_controller = self.__task_executor.get_device_controller(dst_device)
+ if dst_controller is None: dst_controller = dst_device
- device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
- endpoint_name_src = get_endpoint_matching(device_obj, endpoint_uuid_src).name
- endpoint_name_dst = get_endpoint_matching(device_obj, endpoint_uuid_dst).name
+ if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid:
+ raise Exception('Different Src-Dst devices not supported by now')
+ controller = src_controller
json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), {
'uuid' : service_uuid,
- 'input_sip' : endpoint_name_src,
- 'output_sip' : endpoint_name_dst,
+ 'input_sip_uuid' : src_endpoint_uuid,
+ 'output_sip_uuid' : dst_endpoint_uuid,
'capacity_unit' : capacity_unit,
'capacity_value' : capacity_value,
'layer_protocol_name' : layer_proto_name,
'layer_protocol_qualifier': layer_proto_qual,
'direction' : direction,
})
- del device_obj.device_config.config_rules[:]
- device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
- self.__task_executor.configure_device(device_obj)
+ del controller.device_config.config_rules[:]
+ controller.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(controller)
results.append(True)
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid)))
@@ -92,27 +95,32 @@ class TapiServiceHandler(_ServiceHandler):
) -> List[Union[bool, Exception]]:
chk_type('endpoints', endpoints, list)
- if len(endpoints) != 2: return []
+ if len(endpoints) < 2: return []
service_uuid = self.__service.service_id.service_uuid.uuid
results = []
try:
- device_uuid_src, _ = get_device_endpoint_uuids(endpoints[0])
- device_uuid_dst, _ = get_device_endpoint_uuids(endpoints[1])
+ src_device_uuid, _ = get_device_endpoint_uuids(endpoints[0])
+ src_device = self.__task_executor.get_device(DeviceId(**json_device_id(src_device_uuid)))
+ src_controller = self.__task_executor.get_device_controller(src_device)
+ if src_controller is None: src_controller = src_device
- if device_uuid_src != device_uuid_dst:
- raise Exception('Diferent Src-Dst devices not supported by now')
- device_uuid = device_uuid_src
+ dst_device_uuid, _ = get_device_endpoint_uuids(endpoints[1])
+ dst_device = self.__task_executor.get_device(DeviceId(**json_device_id(dst_device_uuid)))
+ dst_controller = self.__task_executor.get_device_controller(dst_device)
+ if dst_controller is None: dst_controller = dst_device
- device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+ if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid:
+ raise Exception('Different Src-Dst devices not supported by now')
+ controller = src_controller
json_config_rule = json_config_rule_delete('/services/service[{:s}]'.format(service_uuid), {
'uuid': service_uuid
})
- del device_obj.device_config.config_rules[:]
- device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
- self.__task_executor.configure_device(device_obj)
+ del controller.device_config.config_rules[:]
+ controller.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(controller)
results.append(True)
except Exception as e: # pylint: disable=broad-except
LOGGER.exception('Unable to DeleteEndpoint for Service({:s})'.format(str(service_uuid)))
diff --git a/src/service/service/service_handlers/tapi_xr/TapiXrServiceHandler.py b/src/service/service/service_handlers/tapi_xr/TapiXrServiceHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1e1b8a6fff9436d6cdff13b95b0ecd43f6fa661
--- /dev/null
+++ b/src/service/service/service_handlers/tapi_xr/TapiXrServiceHandler.py
@@ -0,0 +1,176 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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 json, logging
+from typing import Any, Dict, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+from common.tools.object_factory.Device import json_device_id
+from common.type_checkers.Checkers import chk_type
+from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching
+from service.service.service_handler_api._ServiceHandler import _ServiceHandler
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
+from service.service.task_scheduler.TaskExecutor import TaskExecutor
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'tapi_xr'})
+
+class TapiXrServiceHandler(_ServiceHandler):
+ def __init__( # pylint: disable=super-init-not-called
+ self, service : Service, task_executor : TaskExecutor, **settings
+ ) -> None:
+ self.__service = service
+ self.__task_executor = task_executor
+ self.__settings_handler = SettingsHandler(service.service_config, **settings)
+
+ @metered_subclass_method(METRICS_POOL)
+ def SetEndpoint(
+ self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+ ) -> List[Union[bool, Exception]]:
+
+ chk_type('endpoints', endpoints, list)
+ if len(endpoints) != 4: return []
+
+ service_uuid = self.__service.service_id.service_uuid.uuid
+ settings = self.__settings_handler.get('/settings')
+ json_settings : Dict = {} if settings is None else settings.value
+ capacity_value = json_settings.get('capacity_value', 50.0)
+ capacity_unit = json_settings.get('capacity_unit', 'GHz')
+
+ results = []
+ try:
+ src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[1])
+ src_device = self.__task_executor.get_device(DeviceId(**json_device_id(src_device_uuid)))
+ src_endpoint = get_endpoint_matching(src_device, src_endpoint_uuid)
+ src_controller = self.__task_executor.get_device_controller(src_device)
+ if src_controller is None: src_controller = src_device
+
+ dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[2])
+ dst_device = self.__task_executor.get_device(DeviceId(**json_device_id(dst_device_uuid)))
+ dst_endpoint = get_endpoint_matching(dst_device, dst_endpoint_uuid)
+ dst_controller = self.__task_executor.get_device_controller(dst_device)
+ if dst_controller is None: dst_controller = dst_device
+
+ if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid:
+ raise Exception('Different Src-Dst devices not supported by now')
+ controller = src_controller
+
+ json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), {
+ 'uuid' : service_uuid,
+ 'input_sip_name' : '|'.join([src_device.name, src_endpoint.name]),
+ 'output_sip_name': '|'.join([dst_device.name, dst_endpoint.name]),
+ 'capacity_unit' : capacity_unit,
+ 'capacity_value' : capacity_value,
+ })
+
+ del controller.device_config.config_rules[:]
+ controller.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(controller)
+ results.append(True)
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid)))
+ results.append(e)
+
+ return results
+
+ @metered_subclass_method(METRICS_POOL)
+ def DeleteEndpoint(
+ self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+ ) -> List[Union[bool, Exception]]:
+
+ chk_type('endpoints', endpoints, list)
+ if len(endpoints) < 2: return []
+
+ service_uuid = self.__service.service_id.service_uuid.uuid
+
+ results = []
+ try:
+ src_device_uuid, _ = get_device_endpoint_uuids(endpoints[0])
+ src_device = self.__task_executor.get_device(DeviceId(**json_device_id(src_device_uuid)))
+ src_controller = self.__task_executor.get_device_controller(src_device)
+ if src_controller is None: src_controller = src_device
+
+ dst_device_uuid, _ = get_device_endpoint_uuids(endpoints[1])
+ dst_device = self.__task_executor.get_device(DeviceId(**json_device_id(dst_device_uuid)))
+ dst_controller = self.__task_executor.get_device_controller(dst_device)
+ if dst_controller is None: dst_controller = dst_device
+
+ if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid:
+ raise Exception('Different Src-Dst devices not supported by now')
+ controller = src_controller
+
+ json_config_rule = json_config_rule_delete('/services/service[{:s}]'.format(service_uuid), {
+ 'uuid': service_uuid
+ })
+ del controller.device_config.config_rules[:]
+ controller.device_config.config_rules.append(ConfigRule(**json_config_rule))
+ self.__task_executor.configure_device(controller)
+ results.append(True)
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Unable to DeleteEndpoint for Service({:s})'.format(str(service_uuid)))
+ results.append(e)
+
+ return results
+
+ @metered_subclass_method(METRICS_POOL)
+ def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+ chk_type('constraints', constraints, list)
+ if len(constraints) == 0: return []
+
+ msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+ LOGGER.warning(msg.format(str(constraints)))
+ return [True for _ in range(len(constraints))]
+
+ @metered_subclass_method(METRICS_POOL)
+ def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+ chk_type('constraints', constraints, list)
+ if len(constraints) == 0: return []
+
+ msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+ LOGGER.warning(msg.format(str(constraints)))
+ return [True for _ in range(len(constraints))]
+
+ @metered_subclass_method(METRICS_POOL)
+ def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+ chk_type('resources', resources, list)
+ if len(resources) == 0: return []
+
+ results = []
+ for resource in resources:
+ try:
+ resource_value = json.loads(resource[1])
+ self.__settings_handler.set(resource[0], resource_value)
+ results.append(True)
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
+ results.append(e)
+
+ return results
+
+ @metered_subclass_method(METRICS_POOL)
+ def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+ chk_type('resources', resources, list)
+ if len(resources) == 0: return []
+
+ results = []
+ for resource in resources:
+ try:
+ self.__settings_handler.delete(resource[0])
+ except Exception as e: # pylint: disable=broad-except
+ LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
+ results.append(e)
+
+ return results
diff --git a/src/service/service/service_handlers/tapi_xr/__init__.py b/src/service/service/service_handlers/tapi_xr/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/service/service/service_handlers/tapi_xr/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py
index 932c56e2b1934e12e7849a60c22d3ca1be7f8093..3d157e3145d4b195c0251a4ab79f710a38726569 100644
--- a/src/service/service/task_scheduler/TaskExecutor.py
+++ b/src/service/service/task_scheduler/TaskExecutor.py
@@ -12,10 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import json
from enum import Enum
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
from common.method_wrappers.ServiceExceptions import NotFoundException
from common.proto.context_pb2 import Connection, ConnectionId, Device, DeviceId, Service, ServiceId
+from common.tools.object_factory.Device import json_device_id
from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory, get_service_handler_class
@@ -103,13 +105,38 @@ class TaskExecutor:
self._device_client.ConfigureDevice(device)
self._store_grpc_object(CacheableObjectType.DEVICE, device_key, device)
- def get_devices_from_connection(self, connection : Connection) -> Dict[str, Device]:
+ def get_device_controller(self, device : Device) -> Optional[Device]:
+ json_controller = None
+ for config_rule in device.device_config.config_rules:
+ if config_rule.WhichOneof('config_rule') != 'custom': continue
+ if config_rule.custom.resource_key != '_controller': continue
+ json_controller = json.loads(config_rule.custom.resource_value)
+ break
+
+ if json_controller is None: return None
+
+ controller_uuid = json_controller['uuid']
+ controller = self.get_device(DeviceId(**json_device_id(controller_uuid)))
+ controller_uuid = controller.device_id.device_uuid.uuid
+ if controller is None: raise Exception('Device({:s}) not found'.format(str(controller_uuid)))
+ return controller
+
+ def get_devices_from_connection(
+ self, connection : Connection, exclude_managed_by_controller : bool = False
+ ) -> Dict[str, Device]:
devices = dict()
for endpoint_id in connection.path_hops_endpoint_ids:
device = self.get_device(endpoint_id.device_id)
device_uuid = endpoint_id.device_id.device_uuid.uuid
if device is None: raise Exception('Device({:s}) not found'.format(str(device_uuid)))
- devices[device_uuid] = device
+
+ controller = self.get_device_controller(device)
+ if controller is None:
+ devices[device_uuid] = device
+ else:
+ if not exclude_managed_by_controller:
+ devices[device_uuid] = device
+ devices[controller.device_id.device_uuid.uuid] = controller
return devices
# ----- Service-related methods ------------------------------------------------------------------------------------
@@ -139,6 +166,6 @@ class TaskExecutor:
def get_service_handler(
self, connection : Connection, service : Service, **service_handler_settings
) -> '_ServiceHandler':
- connection_devices = self.get_devices_from_connection(connection)
+ connection_devices = self.get_devices_from_connection(connection, exclude_managed_by_controller=True)
service_handler_class = get_service_handler_class(self._service_handler_factory, service, connection_devices)
return service_handler_class(service, self, **service_handler_settings)
diff --git a/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py b/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py
index 5a47005b304836050dd8c0882214dd9cebd5d8b5..4367ffdee4d6d5b9edfc9fd30d0d6b6f48da8a75 100644
--- a/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py
+++ b/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py
@@ -32,7 +32,7 @@ class Task_ConnectionConfigure(_Task):
def connection_id(self) -> ConnectionId: return self._connection_id
@staticmethod
- def build_key(connection_id : ConnectionId) -> str:
+ def build_key(connection_id : ConnectionId) -> str: # pylint: disable=arguments-differ
str_connection_id = get_connection_key(connection_id)
return KEY_TEMPLATE.format(connection_id=str_connection_id)
diff --git a/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py b/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py
index 5736054febd2fb9e8a36b5a2235ca3f412e0e174..70f41566ef5e69605a527cc0392b77acb866ec2c 100644
--- a/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py
+++ b/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py
@@ -32,7 +32,7 @@ class Task_ConnectionDeconfigure(_Task):
def connection_id(self) -> ConnectionId: return self._connection_id
@staticmethod
- def build_key(connection_id : ConnectionId) -> str:
+ def build_key(connection_id : ConnectionId) -> str: # pylint: disable=arguments-differ
str_connection_id = get_connection_key(connection_id)
return KEY_TEMPLATE.format(connection_id=str_connection_id)
diff --git a/src/service/service/task_scheduler/tasks/Task_ServiceDelete.py b/src/service/service/task_scheduler/tasks/Task_ServiceDelete.py
index 6a4e11b540cd9b85028d92cf86899ee098056c36..0f021b6ca65da1c6b5e44d8577bf9dd6875eb17a 100644
--- a/src/service/service/task_scheduler/tasks/Task_ServiceDelete.py
+++ b/src/service/service/task_scheduler/tasks/Task_ServiceDelete.py
@@ -28,7 +28,7 @@ class Task_ServiceDelete(_Task):
def service_id(self) -> ServiceId: return self._service_id
@staticmethod
- def build_key(service_id : ServiceId) -> str:
+ def build_key(service_id : ServiceId) -> str: # pylint: disable=arguments-differ
str_service_id = get_service_key(service_id)
return KEY_TEMPLATE.format(service_id=str_service_id)
diff --git a/src/service/service/task_scheduler/tasks/Task_ServiceSetStatus.py b/src/service/service/task_scheduler/tasks/Task_ServiceSetStatus.py
index 815cb33c3d540755704153b661e889fc2660d268..d5360fe85eae68085298406fc0ed19dd105f187e 100644
--- a/src/service/service/task_scheduler/tasks/Task_ServiceSetStatus.py
+++ b/src/service/service/task_scheduler/tasks/Task_ServiceSetStatus.py
@@ -32,7 +32,7 @@ class Task_ServiceSetStatus(_Task):
def new_status(self) -> ServiceStatusEnum: return self._new_status
@staticmethod
- def build_key(service_id : ServiceId, new_status : ServiceStatusEnum) -> str:
+ def build_key(service_id : ServiceId, new_status : ServiceStatusEnum) -> str: # pylint: disable=arguments-differ
str_service_id = get_service_key(service_id)
str_new_status = ServiceStatusEnum.Name(new_status)
return KEY_TEMPLATE.format(service_id=str_service_id, new_status=str_new_status)
diff --git a/src/slice/service/slice_grouper/SliceGrouper.py b/src/slice/service/slice_grouper/SliceGrouper.py
index 735d028993eb11e83138caebde1e32ebc830093f..2f1a791819f6a8d0951e9e93ca22d071ea66c1f7 100644
--- a/src/slice/service/slice_grouper/SliceGrouper.py
+++ b/src/slice/service/slice_grouper/SliceGrouper.py
@@ -29,6 +29,7 @@ class SliceGrouper:
def __init__(self) -> None:
self._lock = threading.Lock()
self._is_enabled = is_slice_grouping_enabled()
+ LOGGER.info('Slice Grouping: {:s}'.format('ENABLED' if self._is_enabled else 'DISABLED'))
if not self._is_enabled: return
metrics_exporter = MetricsExporter()
diff --git a/src/tests/ofc23/.gitignore b/src/tests/ofc23/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..0a3f4400d5c88b1af32c7667d69d2fdc12d5424e
--- /dev/null
+++ b/src/tests/ofc23/.gitignore
@@ -0,0 +1,2 @@
+# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc.
+descriptors_real.json
diff --git a/src/tests/ofc23/MultiIngressController.txt b/src/tests/ofc23/MultiIngressController.txt
new file mode 100644
index 0000000000000000000000000000000000000000..190e6df7425983db43d8b1888f29861ec9056ed6
--- /dev/null
+++ b/src/tests/ofc23/MultiIngressController.txt
@@ -0,0 +1,23 @@
+# Ref: https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/
+# Ref: https://fabianlee.org/2021/07/29/kubernetes-microk8s-with-multiple-metallb-endpoints-and-nginx-ingress-controllers/
+
+# Check node limits
+kubectl describe nodes
+
+# Create secondary ingress controllers
+kubectl apply -f ofc23/nginx-ingress-controller-parent.yaml
+kubectl apply -f ofc23/nginx-ingress-controller-child.yaml
+
+# Delete secondary ingress controllers
+kubectl delete -f ofc23/nginx-ingress-controller-parent.yaml
+kubectl delete -f ofc23/nginx-ingress-controller-child.yaml
+
+source ofc23/deploy_specs_parent.sh
+./deploy/all.sh
+
+source ofc23/deploy_specs_child.sh
+./deploy/all.sh
+
+# Manually deploy ingresses for instances
+kubectl --namespace tfs-parent apply -f ofc23/tfs-ingress-parent.yaml
+kubectl --namespace tfs-child apply -f ofc23/tfs-ingress-child.yaml
diff --git a/src/tests/ofc23/__init__.py b/src/tests/ofc23/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/tests/ofc23/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
diff --git a/src/tests/ofc23/delete_hierar.sh b/src/tests/ofc23/delete_hierar.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4a03dad1cc29cff72347f68bc7b1a082924a9211
--- /dev/null
+++ b/src/tests/ofc23/delete_hierar.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# Delete old namespaces
+kubectl delete namespace tfs-parent tfs-child
+
+# Delete secondary ingress controllers
+kubectl delete -f ofc23/nginx-ingress-controller-parent.yaml
+kubectl delete -f ofc23/nginx-ingress-controller-child.yaml
diff --git a/src/tests/ofc23/delete_sligrp.sh b/src/tests/ofc23/delete_sligrp.sh
new file mode 100755
index 0000000000000000000000000000000000000000..cce0bd53febc4765f9d455619f49ea4de8dfe870
--- /dev/null
+++ b/src/tests/ofc23/delete_sligrp.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# Delete old namespaces
+kubectl delete namespace tfs
diff --git a/src/tests/ofc23/deploy_child.sh b/src/tests/ofc23/deploy_child.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9b05ed88739114bf9029d8afaf491d7fec726bff
--- /dev/null
+++ b/src/tests/ofc23/deploy_child.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# Delete old namespaces
+kubectl delete namespace tfs-child
+
+# Delete secondary ingress controllers
+kubectl delete -f ofc23/nginx-ingress-controller-child.yaml
+
+# Create secondary ingress controllers
+kubectl apply -f ofc23/nginx-ingress-controller-child.yaml
+
+# Deploy TFS for Child
+source ofc23/deploy_specs_child.sh
+./deploy/all.sh
+mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_child.sh
diff --git a/src/tests/ofc23/deploy_hierar.sh b/src/tests/ofc23/deploy_hierar.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4874688ad7561156b1b5fc4c80b72a9745feb6a0
--- /dev/null
+++ b/src/tests/ofc23/deploy_hierar.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# Delete old namespaces
+kubectl delete namespace tfs-parent tfs-child
+
+# Delete secondary ingress controllers
+kubectl delete -f ofc23/nginx-ingress-controller-parent.yaml
+kubectl delete -f ofc23/nginx-ingress-controller-child.yaml
+
+# Create secondary ingress controllers
+kubectl apply -f ofc23/nginx-ingress-controller-parent.yaml
+kubectl apply -f ofc23/nginx-ingress-controller-child.yaml
+
+# Deploy TFS for Parent
+source ofc23/deploy_specs_parent.sh
+./deploy/all.sh
+mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_parent.sh
+
+# Deploy TFS for Child
+source ofc23/deploy_specs_child.sh
+./deploy/all.sh
+mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_child.sh
diff --git a/src/tests/ofc23/deploy_parent.sh b/src/tests/ofc23/deploy_parent.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ac4a2954213cf577efe1b1a8e499635d80ea3548
--- /dev/null
+++ b/src/tests/ofc23/deploy_parent.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# Delete old namespaces
+kubectl delete namespace tfs-parent
+
+# Delete secondary ingress controllers
+kubectl delete -f ofc23/nginx-ingress-controller-parent.yaml
+
+# Create secondary ingress controllers
+kubectl apply -f ofc23/nginx-ingress-controller-parent.yaml
+
+# Deploy TFS for Parent
+source ofc23/deploy_specs_parent.sh
+./deploy/all.sh
+mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_parent.sh
diff --git a/src/tests/ofc23/deploy_sligrp.sh b/src/tests/ofc23/deploy_sligrp.sh
new file mode 100755
index 0000000000000000000000000000000000000000..62a9df5cf006af856f168add4058d63eaa905784
--- /dev/null
+++ b/src/tests/ofc23/deploy_sligrp.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# Delete old namespaces
+kubectl delete namespace tfs-sligrp
+
+# Deploy TFS for Slice Goruping
+source ofc23/deploy_specs_sligrp.sh
+./deploy/all.sh
+mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_sligrp.sh
diff --git a/src/tests/ofc23/deploy_specs_child.sh b/src/tests/ofc23/deploy_specs_child.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4d2b3502294925d82f675263fd6bddea62ec181a
--- /dev/null
+++ b/src/tests/ofc23/deploy_specs_child.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# ----- TeraFlowSDN ------------------------------------------------------------
+
+# Set the URL of the internal MicroK8s Docker registry where the images will be uploaded to.
+export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/"
+
+# Set the list of components, separated by spaces, you want to build images for, and deploy.
+#automation monitoring load_generator
+export TFS_COMPONENTS="context device pathcomp service slice compute webui"
+
+# Set the tag you want to use for your images.
+export TFS_IMAGE_TAG="dev"
+
+# Set the name of the Kubernetes namespace to deploy TFS to.
+export TFS_K8S_NAMESPACE="tfs-child"
+
+# Set additional manifest files to be applied after the deployment
+export TFS_EXTRA_MANIFESTS="ofc23/tfs-ingress-child.yaml"
+
+# Set the new Grafana admin password
+export TFS_GRAFANA_PASSWORD="admin123+"
+
+# Disable skip-build flag to rebuild the Docker images.
+export TFS_SKIP_BUILD="YES"
+
+
+# ----- CockroachDB ------------------------------------------------------------
+
+# Set the namespace where CockroackDB will be deployed.
+export CRDB_NAMESPACE="crdb"
+
+# Set the external port CockroackDB Postgre SQL interface will be exposed to.
+export CRDB_EXT_PORT_SQL="26257"
+
+# Set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to.
+export CRDB_EXT_PORT_HTTP="8081"
+
+# Set the database username to be used by Context.
+export CRDB_USERNAME="tfs"
+
+# Set the database user's password to be used by Context.
+export CRDB_PASSWORD="tfs123"
+
+# Set the database name to be used by Context.
+export CRDB_DATABASE="tfs_child"
+
+# Set CockroachDB installation mode to 'single'. This option is convenient for development and testing.
+# See ./deploy/all.sh or ./deploy/crdb.sh for additional details
+export CRDB_DEPLOY_MODE="single"
+
+# Disable flag for dropping database, if it exists.
+export CRDB_DROP_DATABASE_IF_EXISTS="YES"
+
+# Disable flag for re-deploying CockroachDB from scratch.
+export CRDB_REDEPLOY=""
+
+
+# ----- NATS -------------------------------------------------------------------
+
+# Set the namespace where NATS will be deployed.
+export NATS_NAMESPACE="nats-child"
+
+# Set the external port NATS Client interface will be exposed to.
+export NATS_EXT_PORT_CLIENT="4224"
+
+# Set the external port NATS HTTP Mgmt GUI interface will be exposed to.
+export NATS_EXT_PORT_HTTP="8224"
+
+# Disable flag for re-deploying NATS from scratch.
+export NATS_REDEPLOY=""
+
+
+# ----- QuestDB ----------------------------------------------------------------
+
+# Set the namespace where QuestDB will be deployed.
+export QDB_NAMESPACE="qdb-child"
+
+# Set the external port QuestDB Postgre SQL interface will be exposed to.
+export QDB_EXT_PORT_SQL="8814"
+
+# Set the external port QuestDB Influx Line Protocol interface will be exposed to.
+export QDB_EXT_PORT_ILP="9012"
+
+# Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to.
+export QDB_EXT_PORT_HTTP="9002"
+
+# Set the database username to be used for QuestDB.
+export QDB_USERNAME="admin"
+
+# Set the database user's password to be used for QuestDB.
+export QDB_PASSWORD="quest"
+
+# Set the table name to be used by Monitoring for KPIs.
+export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis"
+
+# Set the table name to be used by Slice for plotting groups.
+export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups"
+
+# Disable flag for dropping tables if they exist.
+export QDB_DROP_TABLES_IF_EXIST="YES"
+
+# Disable flag for re-deploying QuestDB from scratch.
+export QDB_REDEPLOY=""
diff --git a/src/tests/ofc23/deploy_specs_parent.sh b/src/tests/ofc23/deploy_specs_parent.sh
new file mode 100755
index 0000000000000000000000000000000000000000..808f4e28734be71e6eb7fb2aced39211fd8e7f24
--- /dev/null
+++ b/src/tests/ofc23/deploy_specs_parent.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# ----- TeraFlowSDN ------------------------------------------------------------
+
+# Set the URL of the internal MicroK8s Docker registry where the images will be uploaded to.
+export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/"
+
+# Set the list of components, separated by spaces, you want to build images for, and deploy.
+#automation monitoring load_generator
+export TFS_COMPONENTS="context device pathcomp service slice compute webui"
+
+# Set the tag you want to use for your images.
+export TFS_IMAGE_TAG="dev"
+
+# Set the name of the Kubernetes namespace to deploy TFS to.
+export TFS_K8S_NAMESPACE="tfs-parent"
+
+# Set additional manifest files to be applied after the deployment
+export TFS_EXTRA_MANIFESTS="ofc23/tfs-ingress-parent.yaml"
+
+# Set the new Grafana admin password
+export TFS_GRAFANA_PASSWORD="admin123+"
+
+# Disable skip-build flag to rebuild the Docker images.
+export TFS_SKIP_BUILD=""
+
+
+# ----- CockroachDB ------------------------------------------------------------
+
+# Set the namespace where CockroackDB will be deployed.
+export CRDB_NAMESPACE="crdb"
+
+# Set the external port CockroackDB Postgre SQL interface will be exposed to.
+export CRDB_EXT_PORT_SQL="26257"
+
+# Set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to.
+export CRDB_EXT_PORT_HTTP="8081"
+
+# Set the database username to be used by Context.
+export CRDB_USERNAME="tfs"
+
+# Set the database user's password to be used by Context.
+export CRDB_PASSWORD="tfs123"
+
+# Set the database name to be used by Context.
+export CRDB_DATABASE="tfs_parent"
+
+# Set CockroachDB installation mode to 'single'. This option is convenient for development and testing.
+# See ./deploy/all.sh or ./deploy/crdb.sh for additional details
+export CRDB_DEPLOY_MODE="single"
+
+# Disable flag for dropping database, if it exists.
+export CRDB_DROP_DATABASE_IF_EXISTS="YES"
+
+# Disable flag for re-deploying CockroachDB from scratch.
+export CRDB_REDEPLOY=""
+
+
+# ----- NATS -------------------------------------------------------------------
+
+# Set the namespace where NATS will be deployed.
+export NATS_NAMESPACE="nats-parent"
+
+# Set the external port NATS Client interface will be exposed to.
+export NATS_EXT_PORT_CLIENT="4223"
+
+# Set the external port NATS HTTP Mgmt GUI interface will be exposed to.
+export NATS_EXT_PORT_HTTP="8223"
+
+# Disable flag for re-deploying NATS from scratch.
+export NATS_REDEPLOY=""
+
+
+# ----- QuestDB ----------------------------------------------------------------
+
+# Set the namespace where QuestDB will be deployed.
+export QDB_NAMESPACE="qdb-parent"
+
+# Set the external port QuestDB Postgre SQL interface will be exposed to.
+export QDB_EXT_PORT_SQL="8813"
+
+# Set the external port QuestDB Influx Line Protocol interface will be exposed to.
+export QDB_EXT_PORT_ILP="9011"
+
+# Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to.
+export QDB_EXT_PORT_HTTP="9001"
+
+# Set the database username to be used for QuestDB.
+export QDB_USERNAME="admin"
+
+# Set the database user's password to be used for QuestDB.
+export QDB_PASSWORD="quest"
+
+# Set the table name to be used by Monitoring for KPIs.
+export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis"
+
+# Set the table name to be used by Slice for plotting groups.
+export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups"
+
+# Disable flag for dropping tables if they exist.
+export QDB_DROP_TABLES_IF_EXIST="YES"
+
+# Disable flag for re-deploying QuestDB from scratch.
+export QDB_REDEPLOY=""
diff --git a/src/tests/ofc23/deploy_specs_sligrp.sh b/src/tests/ofc23/deploy_specs_sligrp.sh
new file mode 100755
index 0000000000000000000000000000000000000000..90bea4567bd35d845abf943670f8aa33070dff57
--- /dev/null
+++ b/src/tests/ofc23/deploy_specs_sligrp.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+# ----- TeraFlowSDN ------------------------------------------------------------
+
+# Set the URL of the internal MicroK8s Docker registry where the images will be uploaded to.
+export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/"
+
+# Set the list of components, separated by spaces, you want to build images for, and deploy.
+#automation monitoring load_generator
+export TFS_COMPONENTS="context device pathcomp service slice webui load_generator"
+
+# Set the tag you want to use for your images.
+export TFS_IMAGE_TAG="dev"
+
+# Set the name of the Kubernetes namespace to deploy TFS to.
+export TFS_K8S_NAMESPACE="tfs-sligrp"
+
+# Set additional manifest files to be applied after the deployment
+export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml"
+
+# Set the new Grafana admin password
+export TFS_GRAFANA_PASSWORD="admin123+"
+
+# Disable skip-build flag to rebuild the Docker images.
+export TFS_SKIP_BUILD=""
+
+
+# ----- CockroachDB ------------------------------------------------------------
+
+# Set the namespace where CockroackDB will be deployed.
+export CRDB_NAMESPACE="crdb"
+
+# Set the external port CockroackDB Postgre SQL interface will be exposed to.
+export CRDB_EXT_PORT_SQL="26257"
+
+# Set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to.
+export CRDB_EXT_PORT_HTTP="8081"
+
+# Set the database username to be used by Context.
+export CRDB_USERNAME="tfs"
+
+# Set the database user's password to be used by Context.
+export CRDB_PASSWORD="tfs123"
+
+# Set the database name to be used by Context.
+export CRDB_DATABASE="tfs_sligrp"
+
+# Set CockroachDB installation mode to 'single'. This option is convenient for development and testing.
+# See ./deploy/all.sh or ./deploy/crdb.sh for additional details
+export CRDB_DEPLOY_MODE="single"
+
+# Disable flag for dropping database, if it exists.
+export CRDB_DROP_DATABASE_IF_EXISTS="YES"
+
+# Disable flag for re-deploying CockroachDB from scratch.
+export CRDB_REDEPLOY=""
+
+
+# ----- NATS -------------------------------------------------------------------
+
+# Set the namespace where NATS will be deployed.
+export NATS_NAMESPACE="nats-sligrp"
+
+# Set the external port NATS Client interface will be exposed to.
+export NATS_EXT_PORT_CLIENT="4222"
+
+# Set the external port NATS HTTP Mgmt GUI interface will be exposed to.
+export NATS_EXT_PORT_HTTP="8222"
+
+# Disable flag for re-deploying NATS from scratch.
+export NATS_REDEPLOY=""
+
+
+# ----- QuestDB ----------------------------------------------------------------
+
+# Set the namespace where QuestDB will be deployed.
+export QDB_NAMESPACE="qdb-sligrp"
+
+# Set the external port QuestDB Postgre SQL interface will be exposed to.
+export QDB_EXT_PORT_SQL="8812"
+
+# Set the external port QuestDB Influx Line Protocol interface will be exposed to.
+export QDB_EXT_PORT_ILP="9010"
+
+# Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to.
+export QDB_EXT_PORT_HTTP="9000"
+
+# Set the database username to be used for QuestDB.
+export QDB_USERNAME="admin"
+
+# Set the database user's password to be used for QuestDB.
+export QDB_PASSWORD="quest"
+
+# Set the table name to be used by Monitoring for KPIs.
+export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis"
+
+# Set the table name to be used by Slice for plotting groups.
+export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups"
+
+# Disable flag for dropping tables if they exist.
+export QDB_DROP_TABLES_IF_EXIST="YES"
+
+# Disable flag for re-deploying QuestDB from scratch.
+export QDB_REDEPLOY=""
diff --git a/src/tests/ofc23/descriptors/adva-interfaces.txt b/src/tests/ofc23/descriptors/adva-interfaces.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a634735058aa9490ddbd43e8e9a0752fbd4a6ee6
--- /dev/null
+++ b/src/tests/ofc23/descriptors/adva-interfaces.txt
@@ -0,0 +1,89 @@
+R199
+eth-1/0/1
+eth-1/0/10
+eth-1/0/11
+eth-1/0/12
+eth-1/0/13
+eth-1/0/14
+eth-1/0/15
+eth-1/0/16
+eth-1/0/17
+eth-1/0/18
+eth-1/0/19
+eth-1/0/2
+eth-1/0/20
+eth-1/0/21
+eth-1/0/22
+eth-1/0/23
+eth-1/0/24
+eth-1/0/25
+eth-1/0/26
+eth-1/0/27
+eth-1/0/28
+eth-1/0/29
+eth-1/0/3
+eth-1/0/30
+eth-1/0/4
+eth-1/0/5
+eth-1/0/6
+eth-1/0/7
+eth-1/0/8
+eth-1/0/9
+
+R155
+eth-1/0/1
+eth-1/0/10
+eth-1/0/11
+eth-1/0/12
+eth-1/0/13
+eth-1/0/14
+eth-1/0/15
+eth-1/0/16
+eth-1/0/17
+eth-1/0/18
+eth-1/0/19
+eth-1/0/2
+eth-1/0/20
+eth-1/0/21
+eth-1/0/22
+eth-1/0/23
+eth-1/0/24
+eth-1/0/25
+eth-1/0/26
+eth-1/0/27
+eth-1/0/3
+eth-1/0/4
+eth-1/0/5
+eth-1/0/6
+eth-1/0/7
+eth-1/0/8
+eth-1/0/9
+
+R149
+eth-1/0/1
+eth-1/0/10
+eth-1/0/11
+eth-1/0/12
+eth-1/0/13
+eth-1/0/14
+eth-1/0/15
+eth-1/0/16
+eth-1/0/17
+eth-1/0/18
+eth-1/0/19
+eth-1/0/2
+eth-1/0/20
+eth-1/0/21
+eth-1/0/22
+eth-1/0/23
+eth-1/0/24
+eth-1/0/25
+eth-1/0/26
+eth-1/0/27
+eth-1/0/3
+eth-1/0/4
+eth-1/0/5
+eth-1/0/6
+eth-1/0/7
+eth-1/0/8
+eth-1/0/9
diff --git a/src/tests/ofc23/descriptors/backup/dc-2-dc-service.json b/src/tests/ofc23/descriptors/backup/dc-2-dc-service.json
new file mode 100644
index 0000000000000000000000000000000000000000..3a83afa6de81f137204aecc5f0eca476aad71e61
--- /dev/null
+++ b/src/tests/ofc23/descriptors/backup/dc-2-dc-service.json
@@ -0,0 +1,37 @@
+{
+ "services": [
+ {
+ "service_id": {
+ "context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "dc-2-dc-svc"}
+ },
+ "service_type": 2,
+ "service_status": {"service_status": 1},
+ "service_endpoint_ids": [
+ {"device_id":{"device_uuid":{"uuid":"DC1"}},"endpoint_uuid":{"uuid":"int"}},
+ {"device_id":{"device_uuid":{"uuid":"DC2"}},"endpoint_uuid":{"uuid":"int"}}
+ ],
+ "service_constraints": [
+ {"sla_capacity": {"capacity_gbps": 10.0}},
+ {"sla_latency": {"e2e_latency_ms": 15.2}}
+ ],
+ "service_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "/settings", "resource_value": {
+ "address_families": ["IPV4"], "bgp_as": 65000, "bgp_route_target": "65000:123",
+ "mtu": 1512, "vlan_id": 111
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[R149]/endpoint[eth-1/0/22]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "5.5.5.5",
+ "address_ip": "172.16.4.1", "address_prefix": 24, "sub_interface_index": 0, "vlan_id": 111
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[R155]/endpoint[eth-1/0/22]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "5.5.5.1",
+ "address_ip": "172.16.2.1", "address_prefix": 24, "sub_interface_index": 0, "vlan_id": 111
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[R199]/endpoint[eth-1/0/21]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "5.5.5.6",
+ "address_ip": "172.16.1.1", "address_prefix": 24, "sub_interface_index": 0, "vlan_id": 111
+ }}}
+ ]}
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/backup/descriptor_child.json b/src/tests/ofc23/descriptors/backup/descriptor_child.json
new file mode 100644
index 0000000000000000000000000000000000000000..eea9571531cfbebfcc53dba0679d1bd1b6900b2f
--- /dev/null
+++ b/src/tests/ofc23/descriptors/backup/descriptor_child.json
@@ -0,0 +1,183 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "R199"}}, "device_type": "packet-router", "device_drivers": [1],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.95.86.199"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "830"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin",
+ "force_running": false, "hostkey_verify": false, "look_for_keys": false,
+ "allow_agent": false, "commit_per_rule": true, "device_params": {"name": "huaweiyang"},
+ "manager_params": {"timeout" : 120},
+ "endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/3"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/4"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/5"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/6"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/7"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/8"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/9"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/10"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/11"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/12"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/13"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/14"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/15"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/16"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/17"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/18"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/19"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/20"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/21"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/22"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/23"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/24"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/25"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/26"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/27"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/28"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/29"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/30"}
+ ]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "R155"}}, "device_type": "packet-router", "device_drivers": [1],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.95.86.155"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "830"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin",
+ "force_running": false, "hostkey_verify": false, "look_for_keys": false,
+ "allow_agent": false, "commit_per_rule": true, "device_params": {"name": "huaweiyang"},
+ "manager_params": {"timeout" : 120},
+ "endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/3"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/4"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/5"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/6"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/7"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/8"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/9"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/10"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/11"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/12"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/13"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/14"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/15"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/16"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/17"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/18"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/19"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/20"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/21"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/22"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/23"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/24"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/25"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/26"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/27"}
+ ]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "R149"}}, "device_type": "packet-router", "device_drivers": [1],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.95.86.149"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "830"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin",
+ "force_running": false, "hostkey_verify": false, "look_for_keys": false,
+ "allow_agent": false, "commit_per_rule": true, "device_params": {"name": "huaweiyang"},
+ "manager_params": {"timeout" : 120},
+ "endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/3"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/4"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/5"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/6"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/7"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/8"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/9"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/10"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/11"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/12"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/13"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/14"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/15"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/16"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/17"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/18"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/19"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/20"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/21"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/22"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/23"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/24"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/25"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/26"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth-1/0/27"}
+ ]
+ }}}
+ ]}
+ }
+ ],
+ "links": [
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/19==R155/eth-1/0/19"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/19"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/19"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/19==R199/eth-1/0/19"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/19"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/19"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/20==R149/eth-1/0/20"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/20==R199/eth-1/0/20"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/25==R155/eth-1/0/25"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/25==R149/eth-1/0/25"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}}
+ ]
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/backup/descriptor_parent.json b/src/tests/ofc23/descriptors/backup/descriptor_parent.json
new file mode 100644
index 0000000000000000000000000000000000000000..42b60e3cf09285955fbfbc567d977e60f78956be
--- /dev/null
+++ b/src/tests/ofc23/descriptors/backup/descriptor_parent.json
@@ -0,0 +1,258 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "TFS-IP"}}, "device_type": "teraflowsdn", "device_drivers": [7],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8002"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "scheme": "http", "username": "admin", "password": "admin"
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "MW"}}, "device_type": "microwave-radio-system", "device_drivers": [4, 5],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "nms5ux", "password": "nms5ux", "timeout": 120, "scheme": "https",
+ "node_ids": ["192.168.27.139", "192.168.27.140"]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "OLS"}}, "device_type": "open-line-system", "device_drivers": [2],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "cttc-ols.cttc-ols.svc.cluster.local"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "4900"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"timeout": 120}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "IPM"}}, "device_type": "xr-constellation", "device_drivers": [6],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8444"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "xr-user-1", "password": "xr-user-1", "hub_module_name": "OFC HUB 1",
+ "consistency-mode": "lifecycle", "import_topology": "devices"
+ }}}
+ ]}
+ },
+
+
+ {
+ "device_id": {"device_uuid": {"uuid": "DC1"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "device_type": "emu-optical-splitter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "optical/internal", "uuid": "common"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf1"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf2"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf3"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf4"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "DC2"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ }
+ ],
+ "links": [
+ {
+ "link_id": {"link_uuid": {"uuid": "DC1/eth1==R149/eth-1/0/22"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/22==DC1/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}},
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/9==MW/192.168.27.140:5"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/9"}},
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.140:5"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "MW/192.168.27.140:5==R149/eth-1/0/9"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.140:5"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/9"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "MW/192.168.27.139:5==OFC HUB 1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.139:5"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/1/1==MW/192.168.27.139:5"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.139:5"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/XR-T1==Optical-Splitter/common"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/common==OFC HUB 1/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf1==OLS/aade6001-f00b-5e2f-a357-6a0a9d3de870"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "aade6001-f00b-5e2f-a357-6a0a9d3de870"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/79516f5e-55a0-5671-977a-1f5cc934e700==Optical-Splitter/leaf1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "79516f5e-55a0-5671-977a-1f5cc934e700"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf2==OLS/eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/30d9323e-b916-51ce-a9a8-cf88f62eb77f==Optical-Splitter/leaf2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "30d9323e-b916-51ce-a9a8-cf88f62eb77f"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/XR-T1==OLS/0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/68ac012e-54d4-5846-b5dc-6ec356404f90==OFC LEAF 1/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "68ac012e-54d4-5846-b5dc-6ec356404f90"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 2/XR-T1==OLS/50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/367b19b1-3172-54d8-bdd4-12d3ac5604f6==OFC LEAF 2/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "367b19b1-3172-54d8-bdd4-12d3ac5604f6"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/1/1==R155/eth-1/0/25"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/25==OFC LEAF 1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 2/1/1==R199/eth-1/0/20"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/20==OFC LEAF 2/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/22==DC2/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth1==R155/eth-1/0/22"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/21==DC2/eth2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/21"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth2==R199/eth-1/0/21"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/21"}}
+ ]
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/emulated/dc-2-dc-service.json b/src/tests/ofc23/descriptors/emulated/dc-2-dc-service.json
new file mode 100644
index 0000000000000000000000000000000000000000..7c3be015d0965d4bdaed8e225e79da072a7de6f3
--- /dev/null
+++ b/src/tests/ofc23/descriptors/emulated/dc-2-dc-service.json
@@ -0,0 +1,41 @@
+{
+ "services": [
+ {
+ "service_id": {
+ "context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "dc-2-dc-svc"}
+ },
+ "service_type": 2,
+ "service_status": {"service_status": 1},
+ "service_endpoint_ids": [
+ {"device_id":{"device_uuid":{"uuid":"DC1"}},"endpoint_uuid":{"uuid":"int"}},
+ {"device_id":{"device_uuid":{"uuid":"DC2"}},"endpoint_uuid":{"uuid":"int"}}
+ ],
+ "service_constraints": [
+ {"sla_capacity": {"capacity_gbps": 10.0}},
+ {"sla_latency": {"e2e_latency_ms": 15.2}}
+ ],
+ "service_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "/settings", "resource_value": {
+ "address_families": ["IPV4"], "bgp_as": 65000, "bgp_route_target": "65000:123",
+ "mtu": 1512, "vlan_id": 300
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[PE1]/endpoint[1/1]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "10.0.0.1",
+ "address_ip": "3.3.1.1", "address_prefix": 24, "sub_interface_index": 1, "vlan_id": 300
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[PE2]/endpoint[1/1]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "10.0.0.2",
+ "address_ip": "3.3.2.1", "address_prefix": 24, "sub_interface_index": 1, "vlan_id": 300
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[PE3]/endpoint[1/1]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "10.0.0.3",
+ "address_ip": "3.3.3.1", "address_prefix": 24, "sub_interface_index": 1, "vlan_id": 300
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[PE4]/endpoint[1/1]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "10.0.0.4",
+ "address_ip": "3.3.4.1", "address_prefix": 24, "sub_interface_index": 1, "vlan_id": 300
+ }}}
+ ]}
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/emulated/descriptor_child.json b/src/tests/ofc23/descriptors/emulated/descriptor_child.json
new file mode 100644
index 0000000000000000000000000000000000000000..1dc6fd35531db1989b9b85c846b6fc8d0524f08f
--- /dev/null
+++ b/src/tests/ofc23/descriptors/emulated/descriptor_child.json
@@ -0,0 +1,149 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "PE1"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/3"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/4"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "PE2"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/3"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/4"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "PE3"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/3"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/4"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "PE4"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/3"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "2/4"}
+ ]}}}
+ ]}
+ }
+ ],
+ "links": [
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE1/2/2==PE2/2/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "2/2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "2/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE1/2/3==PE3/2/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "2/3"}},
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "2/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE1/2/4==PE4/2/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "2/4"}},
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "2/1"}}
+ ]
+ },
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE2/2/1==PE1/2/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "2/1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "2/2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE2/2/3==PE3/2/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "2/3"}},
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "2/2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE2/2/4==PE4/2/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "2/4"}},
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "2/2"}}
+ ]
+ },
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE3/2/1==PE1/2/3"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "2/1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "2/3"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE3/2/2==PE2/2/3"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "2/2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "2/3"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE4/2/2==PE2/2/4"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "2/2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "2/4"}}
+ ]
+ },
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE4/2/1==PE1/2/4"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "2/1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "2/4"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE4/2/2==PE2/2/4"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "2/2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "2/4"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE4/2/3==PE3/2/4"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "2/3"}},
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "2/4"}}
+ ]
+ }
+
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/emulated/descriptor_parent.json b/src/tests/ofc23/descriptors/emulated/descriptor_parent.json
new file mode 100644
index 0000000000000000000000000000000000000000..1b1f5dbfd57b2e1543e86ba8d2633a0e944fced5
--- /dev/null
+++ b/src/tests/ofc23/descriptors/emulated/descriptor_parent.json
@@ -0,0 +1,258 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "TFS-IP"}}, "device_type": "teraflowsdn", "device_drivers": [7],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8002"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "scheme": "http", "username": "admin", "password": "admin"
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "MW"}}, "device_type": "microwave-radio-system", "device_drivers": [4, 5],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin", "timeout": 120, "scheme": "https",
+ "node_ids": ["192.168.27.139", "192.168.27.140"]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "OLS"}}, "device_type": "open-line-system", "device_drivers": [2],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "cttc-ols.cttc-ols.svc.cluster.local"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "4900"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"timeout": 120}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "IPM"}}, "device_type": "xr-constellation", "device_drivers": [6],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8444"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "xr-user-1", "password": "xr-user-1", "hub_module_name": "OFC HUB 1",
+ "consistency-mode": "lifecycle", "import_topology": "devices"
+ }}}
+ ]}
+ },
+
+
+ {
+ "device_id": {"device_uuid": {"uuid": "DC1"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "device_type": "emu-optical-splitter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "optical/internal", "uuid": "common"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf1"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf2"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf3"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf4"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "DC2"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ }
+ ],
+ "links": [
+ {
+ "link_id": {"link_uuid": {"uuid": "DC1/eth1==R149/eth-1/0/22"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/22==DC1/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}},
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/9==MW/192.168.27.140:5"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/9"}},
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.140:5"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "MW/192.168.27.140:5==R149/eth-1/0/9"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.140:5"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/9"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "MW/192.168.27.139:5==OFC HUB 1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.139:5"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/1/1==MW/192.168.27.139:5"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.139:5"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/XR-T1==Optical-Splitter/common"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/common==OFC HUB 1/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf1==OLS/aade6001-f00b-5e2f-a357-6a0a9d3de870"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "aade6001-f00b-5e2f-a357-6a0a9d3de870"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/79516f5e-55a0-5671-977a-1f5cc934e700==Optical-Splitter/leaf1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "79516f5e-55a0-5671-977a-1f5cc934e700"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf2==OLS/eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/30d9323e-b916-51ce-a9a8-cf88f62eb77f==Optical-Splitter/leaf2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "30d9323e-b916-51ce-a9a8-cf88f62eb77f"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/XR-T1==OLS/0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/68ac012e-54d4-5846-b5dc-6ec356404f90==OFC LEAF 1/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "68ac012e-54d4-5846-b5dc-6ec356404f90"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 2/XR-T1==OLS/50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/367b19b1-3172-54d8-bdd4-12d3ac5604f6==OFC LEAF 2/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "367b19b1-3172-54d8-bdd4-12d3ac5604f6"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/1/1==R155/eth-1/0/25"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/25==OFC LEAF 1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 2/1/1==R199/eth-1/0/20"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/20==OFC LEAF 2/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/22==DC2/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth1==R155/eth-1/0/22"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/21==DC2/eth2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/21"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth2==R199/eth-1/0/21"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/21"}}
+ ]
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/emulated/descriptor_parent_noxr.json b/src/tests/ofc23/descriptors/emulated/descriptor_parent_noxr.json
new file mode 100644
index 0000000000000000000000000000000000000000..c4a6646ede081fc2f6ee449d7771de3dbcbd77ec
--- /dev/null
+++ b/src/tests/ofc23/descriptors/emulated/descriptor_parent_noxr.json
@@ -0,0 +1,332 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "TFS-IP"}}, "device_type": "teraflowsdn", "device_drivers": [7],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8002"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "scheme": "http", "username": "admin", "password": "admin"
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "MW1-2"}}, "device_type": "microwave-radio-system", "device_drivers": [5],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin", "timeout": 120, "scheme": "https",
+ "node_ids": ["172.18.0.1", "172.18.0.2"]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "MW3-4"}}, "device_type": "microwave-radio-system", "device_drivers": [5],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin", "timeout": 120, "scheme": "https",
+ "node_ids": ["172.18.0.3", "172.18.0.4"]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "OLS"}}, "device_type": "open-line-system", "device_drivers": [2],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "cttc-ols.cttc-ols.svc.cluster.local"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "4900"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"timeout": 120}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "DC1"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "R1"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/3"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "device_type": "emu-optical-splitter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "optical/internal", "uuid": "common"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf1"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf2"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf3"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf4"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "R2"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/2"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "1/2"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "DC2"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ }
+ ],
+ "links": [
+ {
+ "link_id": {"link_uuid": {"uuid": "DC1/eth1==PE1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE1/1/1==DC1/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "DC1/eth2==PE2/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE2/1/1==DC1/eth2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE1/1/2==MW1-2/172.18.0.1:1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "MW1-2"}}, "endpoint_uuid": {"uuid": "172.18.0.1:1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "MW1-2/172.18.0.1:1==PE1/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW1-2"}}, "endpoint_uuid": {"uuid": "172.18.0.1:1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "MW1-2/172.18.0.2:1==R1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW1-2"}}, "endpoint_uuid": {"uuid": "172.18.0.2:1"}},
+ {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R1/1/1==MW1-2/172.18.0.2:1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "MW1-2"}}, "endpoint_uuid": {"uuid": "172.18.0.2:1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE2/1/2==MW3-4/172.18.0.3:1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "MW3-4"}}, "endpoint_uuid": {"uuid": "172.18.0.3:1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "MW3-4/172.18.0.3:1==PE2/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW3-4"}}, "endpoint_uuid": {"uuid": "172.18.0.3:1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "MW3-4/172.18.0.4:1==R1/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW3-4"}}, "endpoint_uuid": {"uuid": "172.18.0.4:1"}},
+ {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R1/1/2==MW3-4/172.18.0.4:1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "MW3-4"}}, "endpoint_uuid": {"uuid": "172.18.0.4:1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R1/1/3==Optical-Splitter/common"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/3"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/common==R1/1/3"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}},
+ {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/3"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf1==OLS/aade6001-f00b-5e2f-a357-6a0a9d3de870"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "aade6001-f00b-5e2f-a357-6a0a9d3de870"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/79516f5e-55a0-5671-977a-1f5cc934e700==Optical-Splitter/leaf1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "79516f5e-55a0-5671-977a-1f5cc934e700"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf2==OLS/eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/30d9323e-b916-51ce-a9a8-cf88f62eb77f==Optical-Splitter/leaf2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "30d9323e-b916-51ce-a9a8-cf88f62eb77f"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R2/1/1==OLS/0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/68ac012e-54d4-5846-b5dc-6ec356404f90==R2/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "68ac012e-54d4-5846-b5dc-6ec356404f90"}},
+ {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R3/1/1==OLS/50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/367b19b1-3172-54d8-bdd4-12d3ac5604f6==R3/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "367b19b1-3172-54d8-bdd4-12d3ac5604f6"}},
+ {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R2/1/2==PE3/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE3/1/2==R2/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R3/1/2==PE4/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE4/1/2==R3/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE3/1/1==DC2/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth1==PE3/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE4/1/1==DC2/eth2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth2==PE4/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/emulated/ipm-ctrl.json b/src/tests/ofc23/descriptors/emulated/ipm-ctrl.json
new file mode 100644
index 0000000000000000000000000000000000000000..91e9de611dac2627525bb11f81755ea651887e74
--- /dev/null
+++ b/src/tests/ofc23/descriptors/emulated/ipm-ctrl.json
@@ -0,0 +1,25 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"topology_uuid": {"uuid": "admin"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "XR-CONSTELLATION"}},
+ "device_type": "xr-constellation",
+ "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8444"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "xr-user-1", "password": "xr-user-1", "hub_module_name": "OFC HUB 1",
+ "consistency-mode": "lifecycle"
+ }}}
+ ]},
+ "device_operational_status": 1,
+ "device_drivers": [6],
+ "device_endpoints": []
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/emulated/old/descriptor_parent.json b/src/tests/ofc23/descriptors/emulated/old/descriptor_parent.json
new file mode 100644
index 0000000000000000000000000000000000000000..413b7566292d7841777547aeb665c7eb3b8ca293
--- /dev/null
+++ b/src/tests/ofc23/descriptors/emulated/old/descriptor_parent.json
@@ -0,0 +1,311 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "TFS-IP"}}, "device_type": "teraflowsdn", "device_drivers": [7],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8002"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "scheme": "http", "username": "admin", "password": "admin"
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "MW1-2"}}, "device_type": "microwave-radio-system", "device_drivers": [5],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin", "timeout": 120, "scheme": "https",
+ "node_ids": ["172.18.0.1", "172.18.0.2"]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "MW3-4"}}, "device_type": "microwave-radio-system", "device_drivers": [5],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin", "timeout": 120, "scheme": "https",
+ "node_ids": ["172.18.0.3", "172.18.0.4"]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "OLS"}}, "device_type": "open-line-system", "device_drivers": [2],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "cttc-ols.cttc-ols.svc.cluster.local"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "4900"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"timeout": 120}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "IPM"}}, "device_type": "xr-constellation", "device_drivers": [6],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8444"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "xr-user-1", "password": "xr-user-1", "hub_module_name": "OFC HUB 1",
+ "consistency-mode": "lifecycle", "import_topology": "devices"
+ }}}
+ ]}
+ },
+
+
+ {
+ "device_id": {"device_uuid": {"uuid": "DC1"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "device_type": "emu-optical-splitter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "optical/internal", "uuid": "common"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf1"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf2"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf3"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf4"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "DC2"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ }
+ ],
+ "links": [
+ {
+ "link_id": {"link_uuid": {"uuid": "DC1/eth1==PE1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE1/1/1==DC1/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "DC1/eth2==PE2/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE2/1/1==DC1/eth2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE1/1/2==MW1-2/172.18.0.1:1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "MW1-2"}}, "endpoint_uuid": {"uuid": "172.18.0.1:1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "MW1-2/172.18.0.1:1==PE1/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW1-2"}}, "endpoint_uuid": {"uuid": "172.18.0.1:1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "MW1-2/172.18.0.2:1==OFC HUB 1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW1-2"}}, "endpoint_uuid": {"uuid": "172.18.0.2:1"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/1/1==MW1-2/172.18.0.2:1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "MW1-2"}}, "endpoint_uuid": {"uuid": "172.18.0.2:1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE2/1/2==MW3-4/172.18.0.3:1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "MW3-4"}}, "endpoint_uuid": {"uuid": "172.18.0.3:1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "MW3-4/172.18.0.3:1==PE2/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW3-4"}}, "endpoint_uuid": {"uuid": "172.18.0.3:1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "MW3-4/172.18.0.4:1==OFC HUB 1/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW3-4"}}, "endpoint_uuid": {"uuid": "172.18.0.4:1"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/1/2==MW3-4/172.18.0.4:1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "MW3-4"}}, "endpoint_uuid": {"uuid": "172.18.0.4:1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/XR-T1==Optical-Splitter/common"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/common==OFC HUB 1/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf1==OLS/aade6001-f00b-5e2f-a357-6a0a9d3de870"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "aade6001-f00b-5e2f-a357-6a0a9d3de870"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/79516f5e-55a0-5671-977a-1f5cc934e700==Optical-Splitter/leaf1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "79516f5e-55a0-5671-977a-1f5cc934e700"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf2==OLS/eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/30d9323e-b916-51ce-a9a8-cf88f62eb77f==Optical-Splitter/leaf2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "30d9323e-b916-51ce-a9a8-cf88f62eb77f"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/XR-T1==OLS/0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/68ac012e-54d4-5846-b5dc-6ec356404f90==OFC LEAF 1/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "68ac012e-54d4-5846-b5dc-6ec356404f90"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 2/XR-T1==OLS/50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/367b19b1-3172-54d8-bdd4-12d3ac5604f6==OFC LEAF 2/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "367b19b1-3172-54d8-bdd4-12d3ac5604f6"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/1/1==PE3/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE3/1/2==OFC LEAF 1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 2/1/1==PE4/1/2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "PE4/1/2==OFC LEAF 2/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/2"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE3/1/1==DC2/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth1==PE3/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "PE4/1/1==DC2/eth2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth2==PE4/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}},
+ {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/real/dc-2-dc-service.json b/src/tests/ofc23/descriptors/real/dc-2-dc-service.json
new file mode 100644
index 0000000000000000000000000000000000000000..3a83afa6de81f137204aecc5f0eca476aad71e61
--- /dev/null
+++ b/src/tests/ofc23/descriptors/real/dc-2-dc-service.json
@@ -0,0 +1,37 @@
+{
+ "services": [
+ {
+ "service_id": {
+ "context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "dc-2-dc-svc"}
+ },
+ "service_type": 2,
+ "service_status": {"service_status": 1},
+ "service_endpoint_ids": [
+ {"device_id":{"device_uuid":{"uuid":"DC1"}},"endpoint_uuid":{"uuid":"int"}},
+ {"device_id":{"device_uuid":{"uuid":"DC2"}},"endpoint_uuid":{"uuid":"int"}}
+ ],
+ "service_constraints": [
+ {"sla_capacity": {"capacity_gbps": 10.0}},
+ {"sla_latency": {"e2e_latency_ms": 15.2}}
+ ],
+ "service_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "/settings", "resource_value": {
+ "address_families": ["IPV4"], "bgp_as": 65000, "bgp_route_target": "65000:123",
+ "mtu": 1512, "vlan_id": 111
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[R149]/endpoint[eth-1/0/22]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "5.5.5.5",
+ "address_ip": "172.16.4.1", "address_prefix": 24, "sub_interface_index": 0, "vlan_id": 111
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[R155]/endpoint[eth-1/0/22]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "5.5.5.1",
+ "address_ip": "172.16.2.1", "address_prefix": 24, "sub_interface_index": 0, "vlan_id": 111
+ }}},
+ {"action": 1, "custom": {"resource_key": "/device[R199]/endpoint[eth-1/0/21]/settings", "resource_value": {
+ "route_distinguisher": "65000:123", "router_id": "5.5.5.6",
+ "address_ip": "172.16.1.1", "address_prefix": 24, "sub_interface_index": 0, "vlan_id": 111
+ }}}
+ ]}
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/real/descriptor_child.json b/src/tests/ofc23/descriptors/real/descriptor_child.json
new file mode 100644
index 0000000000000000000000000000000000000000..8d695cfd2d419f263b554d0f2bf648b92cdde672
--- /dev/null
+++ b/src/tests/ofc23/descriptors/real/descriptor_child.json
@@ -0,0 +1,93 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "R199"}}, "device_type": "packet-router", "device_drivers": [1],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.95.86.199"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "830"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin",
+ "force_running": false, "hostkey_verify": false, "look_for_keys": false,
+ "allow_agent": false, "commit_per_rule": true, "device_params": {"name": "huaweiyang"},
+ "manager_params": {"timeout" : 86400}
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "R149"}}, "device_type": "packet-router", "device_drivers": [1],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.95.86.149"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "830"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin",
+ "force_running": false, "hostkey_verify": false, "look_for_keys": false,
+ "allow_agent": false, "commit_per_rule": true, "device_params": {"name": "huaweiyang"},
+ "manager_params": {"timeout" : 86400}
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "R155"}}, "device_type": "packet-router", "device_drivers": [1],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.95.86.155"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "830"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "admin", "password": "admin",
+ "force_running": false, "hostkey_verify": false, "look_for_keys": false,
+ "allow_agent": false, "commit_per_rule": true, "device_params": {"name": "huaweiyang"},
+ "manager_params": {"timeout" : 86400}
+ }}}
+ ]}
+ }
+ ],
+ "links": [
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/19==R155/eth-1/0/19"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/19"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/19"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/19==R199/eth-1/0/19"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/19"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/19"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/20==R149/eth-1/0/20"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/20==R199/eth-1/0/20"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/25==R155/eth-1/0/25"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/25==R149/eth-1/0/25"}},
+ "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}}
+ ]
+ }
+ ]
+}
diff --git a/src/tests/ofc23/descriptors/real/descriptor_parent.json b/src/tests/ofc23/descriptors/real/descriptor_parent.json
new file mode 100644
index 0000000000000000000000000000000000000000..3317d46edaf6d270125d8b094c3b6384a3dd52fd
--- /dev/null
+++ b/src/tests/ofc23/descriptors/real/descriptor_parent.json
@@ -0,0 +1,258 @@
+{
+ "contexts": [
+ {"context_id": {"context_uuid": {"uuid": "admin"}}}
+ ],
+ "topologies": [
+ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+ ],
+ "devices": [
+ {
+ "device_id": {"device_uuid": {"uuid": "TFS-IP"}}, "device_type": "teraflowsdn", "device_drivers": [7],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8002"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "scheme": "http", "username": "admin", "password": "admin"
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "MW"}}, "device_type": "microwave-radio-system", "device_drivers": [4, 5],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "192.168.27.136"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "nms5ux", "password": "nms5ux", "timeout": 120, "scheme": "https",
+ "node_ids": ["192.168.27.139", "192.168.27.140"]
+ }}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "OLS"}}, "device_type": "open-line-system", "device_drivers": [2],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "cttc-ols.cttc-ols.svc.cluster.local"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "4900"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"timeout": 120}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "IPM"}}, "device_type": "xr-constellation", "device_drivers": [6],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.95.86.126"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "443"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+ "username": "xr-user-1", "password": "xr-user-1", "hub_module_name": "OFC HUB 1",
+ "consistency-mode": "lifecycle", "import_topology": "devices"
+ }}}
+ ]}
+ },
+
+
+ {
+ "device_id": {"device_uuid": {"uuid": "DC1"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "device_type": "emu-optical-splitter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "optical/internal", "uuid": "common"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf1"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf2"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf3"},
+ {"sample_types": [], "type": "optical/internal", "uuid": "leaf4"}
+ ]}}}
+ ]}
+ },
+ {
+ "device_id": {"device_uuid": {"uuid": "DC2"}}, "device_type": "emu-datacenter", "device_drivers": [0],
+ "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [
+ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+ {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+ {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth1"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "eth2"},
+ {"sample_types": [], "type": "copper/internal", "uuid": "int"}
+ ]}}}
+ ]}
+ }
+ ],
+ "links": [
+ {
+ "link_id": {"link_uuid": {"uuid": "DC1/eth1==R149/eth-1/0/22"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/22==DC1/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}},
+ {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R149/eth-1/0/9==MW/192.168.27.140:5"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/9"}},
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.140:5"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "MW/192.168.27.140:5==R149/eth-1/0/9"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.140:5"}},
+ {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/9"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "MW/192.168.27.139:5==OFC HUB 1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.139:5"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/1/1==MW/192.168.27.139:5"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.139:5"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC HUB 1/XR-T1==Optical-Splitter/common"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/common==OFC HUB 1/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "common"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC HUB 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf1==OLS/aade6001-f00b-5e2f-a357-6a0a9d3de870"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "aade6001-f00b-5e2f-a357-6a0a9d3de870"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/79516f5e-55a0-5671-977a-1f5cc934e700==Optical-Splitter/leaf1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "79516f5e-55a0-5671-977a-1f5cc934e700"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "Optical-Splitter/leaf2==OLS/eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/30d9323e-b916-51ce-a9a8-cf88f62eb77f==Optical-Splitter/leaf2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "30d9323e-b916-51ce-a9a8-cf88f62eb77f"}},
+ {"device_id": {"device_uuid": {"uuid": "Optical-Splitter"}}, "endpoint_uuid": {"uuid": "leaf2"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/XR-T1==OLS/0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/68ac012e-54d4-5846-b5dc-6ec356404f90==OFC LEAF 1/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "68ac012e-54d4-5846-b5dc-6ec356404f90"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 2/XR-T1==OLS/50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "XR-T1"}},
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "OLS/367b19b1-3172-54d8-bdd4-12d3ac5604f6==OFC LEAF 2/XR-T1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "367b19b1-3172-54d8-bdd4-12d3ac5604f6"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "XR-T1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/1/1==R155/eth-1/0/25"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/25==OFC LEAF 1/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 1"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "OFC LEAF 2/1/1==R199/eth-1/0/20"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "1/1"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/20==OFC LEAF 2/1/1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}},
+ {"device_id": {"device_uuid": {"uuid": "OFC LEAF 2"}}, "endpoint_uuid": {"uuid": "1/1"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R155/eth-1/0/22==DC2/eth1"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth1==R155/eth-1/0/22"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}},
+ {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}}
+ ]
+ },
+
+
+ {
+ "link_id": {"link_uuid": {"uuid": "R199/eth-1/0/21==DC2/eth2"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/21"}},
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}}
+ ]
+ },
+ {
+ "link_id": {"link_uuid": {"uuid": "DC2/eth2==R199/eth-1/0/21"}}, "link_endpoint_ids": [
+ {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}},
+ {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/21"}}
+ ]
+ }
+ ]
+}
diff --git a/src/tests/ofc23/dump_logs.sh b/src/tests/ofc23/dump_logs.sh
new file mode 100755
index 0000000000000000000000000000000000000000..cc3162b337c1b35e9ff158b400a8d5c47931bdca
--- /dev/null
+++ b/src/tests/ofc23/dump_logs.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+rm -rf tmp/exec
+
+echo "Collecting logs for Parent..."
+mkdir -p tmp/exec/parent
+kubectl --namespace tfs-parent logs deployments/contextservice server > tmp/exec/parent/context.log
+kubectl --namespace tfs-parent logs deployments/deviceservice server > tmp/exec/parent/device.log
+kubectl --namespace tfs-parent logs deployments/serviceservice server > tmp/exec/parent/service.log
+kubectl --namespace tfs-parent logs deployments/pathcompservice frontend > tmp/exec/parent/pathcomp-frontend.log
+kubectl --namespace tfs-parent logs deployments/pathcompservice backend > tmp/exec/parent/pathcomp-backend.log
+kubectl --namespace tfs-parent logs deployments/sliceservice server > tmp/exec/parent/slice.log
+printf "\n"
+
+echo "Collecting logs for Child..."
+mkdir -p tmp/exec/child
+kubectl --namespace tfs-child logs deployments/contextservice server > tmp/exec/child/context.log
+kubectl --namespace tfs-child logs deployments/deviceservice server > tmp/exec/child/device.log
+kubectl --namespace tfs-child logs deployments/serviceservice server > tmp/exec/child/service.log
+kubectl --namespace tfs-child logs deployments/pathcompservice frontend > tmp/exec/child/pathcomp-frontend.log
+kubectl --namespace tfs-child logs deployments/pathcompservice backend > tmp/exec/child/pathcomp-backend.log
+kubectl --namespace tfs-child logs deployments/sliceservice server > tmp/exec/child/slice.log
+printf "\n"
+
+echo "Done!"
diff --git a/src/tests/ofc23/fast_redeploy.sh b/src/tests/ofc23/fast_redeploy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..58d1193ded582d4fdff3222d5bcdc0fe510a7034
--- /dev/null
+++ b/src/tests/ofc23/fast_redeploy.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+
+kubectl delete namespace tfs-parent tfs-child
+
+echo "Deploying tfs-parent ..."
+kubectl delete -f ofc23/nginx-ingress-controller-parent.yaml > ./tmp/logs/deploy-tfs-parent.log
+kubectl create namespace tfs-parent > ./tmp/logs/deploy-tfs-parent.log
+kubectl apply -f ofc23/nginx-ingress-controller-parent.yaml > ./tmp/logs/deploy-tfs-parent.log
+kubectl --namespace tfs-parent apply -f ./tmp/manifests/contextservice.yaml > ./tmp/logs/deploy-tfs-parent.log
+kubectl --namespace tfs-parent apply -f ./tmp/manifests/deviceservice.yaml > ./tmp/logs/deploy-tfs-parent.log
+kubectl --namespace tfs-parent apply -f ./tmp/manifests/pathcompservice.yaml > ./tmp/logs/deploy-tfs-parent.log
+kubectl --namespace tfs-parent apply -f ./tmp/manifests/serviceservice.yaml > ./tmp/logs/deploy-tfs-parent.log
+kubectl --namespace tfs-parent apply -f ./tmp/manifests/sliceservice.yaml > ./tmp/logs/deploy-tfs-parent.log
+kubectl --namespace tfs-parent apply -f ./tmp/manifests/webuiservice.yaml > ./tmp/logs/deploy-tfs-parent.log
+kubectl --namespace tfs-parent apply -f ofc23/tfs-ingress-parent.yaml > ./tmp/logs/deploy-tfs-parent.log
+printf "\n"
+
+echo "Deploying tfs-child ..."
+kubectl delete -f ofc23/nginx-ingress-controller-child.yaml > ./tmp/logs/deploy-tfs-child.log
+kubectl create namespace tfs-child > ./tmp/logs/deploy-tfs-child.log
+kubectl apply -f ofc23/nginx-ingress-controller-child.yaml > ./tmp/logs/deploy-tfs-child.log
+kubectl --namespace tfs-child apply -f ./tmp/manifests/contextservice.yaml > ./tmp/logs/deploy-tfs-child.log
+kubectl --namespace tfs-child apply -f ./tmp/manifests/deviceservice.yaml > ./tmp/logs/deploy-tfs-child.log
+kubectl --namespace tfs-child apply -f ./tmp/manifests/pathcompservice.yaml > ./tmp/logs/deploy-tfs-child.log
+kubectl --namespace tfs-child apply -f ./tmp/manifests/serviceservice.yaml > ./tmp/logs/deploy-tfs-child.log
+kubectl --namespace tfs-child apply -f ./tmp/manifests/sliceservice.yaml > ./tmp/logs/deploy-tfs-child.log
+kubectl --namespace tfs-child apply -f ./tmp/manifests/webuiservice.yaml > ./tmp/logs/deploy-tfs-child.log
+kubectl --namespace tfs-child apply -f ofc23/tfs-ingress-child.yaml > ./tmp/logs/deploy-tfs-child.log
+printf "\n"
+
+echo "Waiting tfs-parent ..."
+kubectl wait --namespace tfs-parent --for='condition=available' --timeout=300s deployment/contextservice
+kubectl wait --namespace tfs-parent --for='condition=available' --timeout=300s deployment/deviceservice
+kubectl wait --namespace tfs-parent --for='condition=available' --timeout=300s deployment/pathcompservice
+kubectl wait --namespace tfs-parent --for='condition=available' --timeout=300s deployment/serviceservice
+kubectl wait --namespace tfs-parent --for='condition=available' --timeout=300s deployment/sliceservice
+kubectl wait --namespace tfs-parent --for='condition=available' --timeout=300s deployment/webuiservice
+printf "\n"
+
+echo "Waiting tfs-child ..."
+kubectl wait --namespace tfs-child --for='condition=available' --timeout=300s deployment/contextservice
+kubectl wait --namespace tfs-child --for='condition=available' --timeout=300s deployment/deviceservice
+kubectl wait --namespace tfs-child --for='condition=available' --timeout=300s deployment/pathcompservice
+kubectl wait --namespace tfs-child --for='condition=available' --timeout=300s deployment/serviceservice
+kubectl wait --namespace tfs-child --for='condition=available' --timeout=300s deployment/sliceservice
+kubectl wait --namespace tfs-child --for='condition=available' --timeout=300s deployment/webuiservice
+printf "\n"
+
+echo "Done!"
diff --git a/src/tests/ofc23/nginx-ingress-controller-child.yaml b/src/tests/ofc23/nginx-ingress-controller-child.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..00a64d75e9a9a2cb93cdbd6d89790e26b0730eb6
--- /dev/null
+++ b/src/tests/ofc23/nginx-ingress-controller-child.yaml
@@ -0,0 +1,134 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: nginx-load-balancer-microk8s-conf-child
+ namespace: ingress
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: nginx-ingress-udp-microk8s-conf-child
+ namespace: ingress
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: nginx-ingress-tcp-microk8s-conf-child
+ namespace: ingress
+---
+apiVersion: networking.k8s.io/v1
+kind: IngressClass
+metadata:
+ name: tfs-ingress-class-child
+ annotations:
+ ingressclass.kubernetes.io/is-default-class: "false"
+spec:
+ controller: tfs.etsi.org/controller-class-child
+---
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+ name: nginx-ingress-microk8s-controller-child
+ namespace: ingress
+ labels:
+ microk8s-application: nginx-ingress-microk8s-child
+spec:
+ selector:
+ matchLabels:
+ name: nginx-ingress-microk8s-child
+ updateStrategy:
+ rollingUpdate:
+ maxSurge: 0
+ maxUnavailable: 1
+ type: RollingUpdate
+ template:
+ metadata:
+ labels:
+ name: nginx-ingress-microk8s-child
+ spec:
+ terminationGracePeriodSeconds: 60
+ restartPolicy: Always
+ serviceAccountName: nginx-ingress-microk8s-serviceaccount
+ containers:
+ - image: k8s.gcr.io/ingress-nginx/controller:v1.2.0
+ imagePullPolicy: IfNotPresent
+ name: nginx-ingress-microk8s
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ successThreshold: 1
+ failureThreshold: 3
+ timeoutSeconds: 5
+ readinessProbe:
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ periodSeconds: 10
+ successThreshold: 1
+ failureThreshold: 3
+ timeoutSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command:
+ - /wait-shutdown
+ securityContext:
+ capabilities:
+ add:
+ - NET_BIND_SERVICE
+ drop:
+ - ALL
+ runAsUser: 101 # www-data
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.namespace
+ ports:
+ - name: http
+ containerPort: 80
+ hostPort: 8002
+ protocol: TCP
+ - name: https
+ containerPort: 443
+ hostPort: 4432
+ protocol: TCP
+ - name: health
+ containerPort: 10254
+ hostPort: 12542
+ protocol: TCP
+ args:
+ - /nginx-ingress-controller
+ - --configmap=$(POD_NAMESPACE)/nginx-load-balancer-microk8s-conf-child
+ - --tcp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-tcp-microk8s-conf-child
+ - --udp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-udp-microk8s-conf-child
+ - --election-id=ingress-controller-leader-child
+ - --controller-class=tfs.etsi.org/controller-class-child
+ - --ingress-class=tfs-ingress-class-child
+ - ' '
+ - --publish-status-address=127.0.0.1
diff --git a/src/tests/ofc23/nginx-ingress-controller-parent.yaml b/src/tests/ofc23/nginx-ingress-controller-parent.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c504c2e6766c1ad5b81a2479b4d05a09ba46d906
--- /dev/null
+++ b/src/tests/ofc23/nginx-ingress-controller-parent.yaml
@@ -0,0 +1,134 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: nginx-load-balancer-microk8s-conf-parent
+ namespace: ingress
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: nginx-ingress-udp-microk8s-conf-parent
+ namespace: ingress
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: nginx-ingress-tcp-microk8s-conf-parent
+ namespace: ingress
+---
+apiVersion: networking.k8s.io/v1
+kind: IngressClass
+metadata:
+ name: tfs-ingress-class-parent
+ annotations:
+ ingressclass.kubernetes.io/is-default-class: "false"
+spec:
+ controller: tfs.etsi.org/controller-class-parent
+---
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+ name: nginx-ingress-microk8s-controller-parent
+ namespace: ingress
+ labels:
+ microk8s-application: nginx-ingress-microk8s-parent
+spec:
+ selector:
+ matchLabels:
+ name: nginx-ingress-microk8s-parent
+ updateStrategy:
+ rollingUpdate:
+ maxSurge: 0
+ maxUnavailable: 1
+ type: RollingUpdate
+ template:
+ metadata:
+ labels:
+ name: nginx-ingress-microk8s-parent
+ spec:
+ terminationGracePeriodSeconds: 60
+ restartPolicy: Always
+ serviceAccountName: nginx-ingress-microk8s-serviceaccount
+ containers:
+ - image: k8s.gcr.io/ingress-nginx/controller:v1.2.0
+ imagePullPolicy: IfNotPresent
+ name: nginx-ingress-microk8s
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ successThreshold: 1
+ failureThreshold: 3
+ timeoutSeconds: 5
+ readinessProbe:
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ periodSeconds: 10
+ successThreshold: 1
+ failureThreshold: 3
+ timeoutSeconds: 5
+ lifecycle:
+ preStop:
+ exec:
+ command:
+ - /wait-shutdown
+ securityContext:
+ capabilities:
+ add:
+ - NET_BIND_SERVICE
+ drop:
+ - ALL
+ runAsUser: 101 # www-data
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.namespace
+ ports:
+ - name: http
+ containerPort: 80
+ hostPort: 8001
+ protocol: TCP
+ - name: https
+ containerPort: 443
+ hostPort: 4431
+ protocol: TCP
+ - name: health
+ containerPort: 10254
+ hostPort: 12541
+ protocol: TCP
+ args:
+ - /nginx-ingress-controller
+ - --configmap=$(POD_NAMESPACE)/nginx-load-balancer-microk8s-conf-parent
+ - --tcp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-tcp-microk8s-conf-parent
+ - --udp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-udp-microk8s-conf-parent
+ - --election-id=ingress-controller-leader-parent
+ - --controller-class=tfs.etsi.org/controller-class-parent
+ - --ingress-class=tfs-ingress-class-parent
+ - ' '
+ - --publish-status-address=127.0.0.1
diff --git a/src/tests/ofc23/show_deploy.sh b/src/tests/ofc23/show_deploy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d4e112b0f494e4a6dba964eda4e31652b8548043
--- /dev/null
+++ b/src/tests/ofc23/show_deploy.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+########################################################################################################################
+# Automated steps start here
+########################################################################################################################
+
+echo "Deployment Resources:"
+kubectl --namespace tfs-parent get all
+printf "\n"
+
+echo "Deployment Ingress:"
+kubectl --namespace tfs-parent get ingress
+printf "\n"
+
+echo "Deployment Resources:"
+kubectl --namespace tfs-child get all
+printf "\n"
+
+echo "Deployment Ingress:"
+kubectl --namespace tfs-child get ingress
+printf "\n"
diff --git a/src/tests/ofc23/show_deploy_sligrp.sh b/src/tests/ofc23/show_deploy_sligrp.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b5e3600ba99ec2e4de4a953f99c44ed2d88bba57
--- /dev/null
+++ b/src/tests/ofc23/show_deploy_sligrp.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+########################################################################################################################
+# Automated steps start here
+########################################################################################################################
+
+echo "Deployment Resources:"
+kubectl --namespace tfs get all
+printf "\n"
+
+echo "Deployment Ingress:"
+kubectl --namespace tfs get ingress
+printf "\n"
diff --git a/src/tests/ofc23/tfs-ingress-child.yaml b/src/tests/ofc23/tfs-ingress-child.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a93b9321c9f25c78e8423413c4225f78c7aee719
--- /dev/null
+++ b/src/tests/ofc23/tfs-ingress-child.yaml
@@ -0,0 +1,53 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: tfs-ingress-child
+ annotations:
+ nginx.ingress.kubernetes.io/rewrite-target: /$2
+spec:
+ ingressClassName: tfs-ingress-class-child
+ rules:
+ - http:
+ paths:
+ - path: /webui(/|$)(.*)
+ pathType: Prefix
+ backend:
+ service:
+ name: webuiservice
+ port:
+ number: 8004
+ - path: /grafana(/|$)(.*)
+ pathType: Prefix
+ backend:
+ service:
+ name: webuiservice
+ port:
+ number: 3000
+ - path: /context(/|$)(.*)
+ pathType: Prefix
+ backend:
+ service:
+ name: contextservice
+ port:
+ number: 8080
+ - path: /()(restconf/.*)
+ pathType: Prefix
+ backend:
+ service:
+ name: computeservice
+ port:
+ number: 8080
diff --git a/src/tests/ofc23/tfs-ingress-parent.yaml b/src/tests/ofc23/tfs-ingress-parent.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..baf506dd90f320a1913beb8becd39164daa21370
--- /dev/null
+++ b/src/tests/ofc23/tfs-ingress-parent.yaml
@@ -0,0 +1,53 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: tfs-ingress-parent
+ annotations:
+ nginx.ingress.kubernetes.io/rewrite-target: /$2
+spec:
+ ingressClassName: tfs-ingress-class-parent
+ rules:
+ - http:
+ paths:
+ - path: /webui(/|$)(.*)
+ pathType: Prefix
+ backend:
+ service:
+ name: webuiservice
+ port:
+ number: 8004
+ - path: /grafana(/|$)(.*)
+ pathType: Prefix
+ backend:
+ service:
+ name: webuiservice
+ port:
+ number: 3000
+ - path: /context(/|$)(.*)
+ pathType: Prefix
+ backend:
+ service:
+ name: contextservice
+ port:
+ number: 8080
+ - path: /()(restconf/.*)
+ pathType: Prefix
+ backend:
+ service:
+ name: computeservice
+ port:
+ number: 8080
diff --git a/src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py b/src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py
new file mode 100644
index 0000000000000000000000000000000000000000..ecac81be7e3bdff1dcbac458142ea15bf367a1a1
--- /dev/null
+++ b/src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py
@@ -0,0 +1,192 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# 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.
+
+# Mock IPM controller (implements minimal support)
+
+import functools, json, logging, sys, time, uuid
+from typing import Any, Dict, Optional, Tuple
+from flask import Flask, jsonify, make_response, request
+from flask_restful import Api, Resource
+
+BIND_ADDRESS = '0.0.0.0'
+BIND_PORT = 8444
+IPM_USERNAME = 'xr-user-1'
+IPM_PASSWORD = 'xr-user-1'
+STR_ENDPOINT = 'https://{:s}:{:s}'.format(str(BIND_ADDRESS), str(BIND_PORT))
+LOG_LEVEL = logging.DEBUG
+
+CONSTELLATION = {
+ 'id': 'ofc-constellation',
+ 'hubModule': {'state': {
+ 'module': {'moduleName': 'OFC HUB 1', 'trafficMode': 'L1Mode', 'capacity': 100},
+ 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}, {'moduleIf': {'clientIfAid': 'XR-T4'}}]
+ }},
+ 'leafModules': [
+ {'state': {
+ 'module': {'moduleName': 'OFC LEAF 1', 'trafficMode': 'L1Mode', 'capacity': 100},
+ 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}]
+ }},
+ {'state': {
+ 'module': {'moduleName': 'OFC LEAF 2', 'trafficMode': 'L1Mode', 'capacity': 100},
+ 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}]
+ }}
+ ]
+}
+
+CONNECTIONS : Dict[str, Any] = dict()
+STATE_NAME_TO_CONNECTION : Dict[str, str] = dict()
+
+def select_module_state(module_name : str) -> Optional[Dict]:
+ hub_module_state = CONSTELLATION.get('hubModule', {}).get('state', {})
+ if module_name == hub_module_state.get('module', {}).get('moduleName'): return hub_module_state
+ for leaf_module in CONSTELLATION.get('leafModules', []):
+ leaf_module_state = leaf_module.get('state', {})
+ if module_name == leaf_module_state.get('module', {}).get('moduleName'): return leaf_module_state
+ return None
+
+def select_endpoint(module_state : Dict, module_if : str) -> Optional[Dict]:
+ for endpoint in module_state.get('endpoints', []):
+ if module_if == endpoint.get('moduleIf', {}).get('clientIfAid'): return endpoint
+ return None
+
+def select_module_endpoint(selector : Dict) -> Optional[Tuple[Dict, Dict]]:
+ selected_module_name = selector['moduleIfSelectorByModuleName']['moduleName']
+ selected_module_if = selector['moduleIfSelectorByModuleName']['moduleClientIfAid']
+ module_state = select_module_state(selected_module_name)
+ if module_state is None: return None
+ return module_state, select_endpoint(module_state, selected_module_if)
+
+def compose_endpoint(endpoint_selector : Dict) -> Dict:
+ module, endpoint = select_module_endpoint(endpoint_selector['selector'])
+ return {
+ 'href': '/' + str(uuid.uuid4()),
+ 'state': {
+ 'moduleIf': {
+ 'moduleName': module['module']['moduleName'],
+ 'clientIfAid': endpoint['moduleIf']['clientIfAid'],
+ },
+ 'capacity': module['module']['capacity'],
+ }
+ }
+
+logging.basicConfig(level=LOG_LEVEL, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s")
+LOGGER = logging.getLogger(__name__)
+
+logging.getLogger('werkzeug').setLevel(logging.WARNING)
+
+def log_request(logger : logging.Logger, response):
+ timestamp = time.strftime('[%Y-%b-%d %H:%M]')
+ logger.info('%s %s %s %s %s', timestamp, request.remote_addr, request.method, request.full_path, response.status)
+ return response
+
+class OpenIdConnect(Resource):
+ ACCESS_TOKENS = {}
+
+ def post(self):
+ if request.content_type != 'application/x-www-form-urlencoded': return make_response('bad content type', 400)
+ if request.content_length == 0: return make_response('bad content length', 400)
+ request_form = request.form
+ if request_form.get('client_id') != 'xr-web-client': return make_response('bad client_id', 403)
+ if request_form.get('client_secret') != 'xr-web-client': return make_response('bad client_secret', 403)
+ if request_form.get('grant_type') != 'password': return make_response('bad grant_type', 403)
+ if request_form.get('username') != IPM_USERNAME: return make_response('bad username', 403)
+ if request_form.get('password') != IPM_PASSWORD: return make_response('bad password', 403)
+ access_token = OpenIdConnect.ACCESS_TOKENS.setdefault(IPM_USERNAME, uuid.uuid4())
+ reply = {'access_token': access_token, 'expires_in': 86400}
+ return make_response(jsonify(reply), 200)
+
+class XrNetworks(Resource):
+ def get(self):
+ query = json.loads(request.args.get('q'))
+ hub_module_name = query.get('hubModule.state.module.moduleName')
+ if hub_module_name != 'OFC HUB 1': return make_response('unexpected hub module', 404)
+ return make_response(jsonify([CONSTELLATION]), 200)
+
+class XrNetworkConnections(Resource):
+ def get(self):
+ query = json.loads(request.args.get('q'))
+ state_name = query.get('state.name')
+ if state_name is None:
+ connections = [connection for connection in CONNECTIONS.values()]
+ else:
+ connection_uuid = STATE_NAME_TO_CONNECTION.get(state_name)
+ if connection_uuid is None: return make_response('state name not found', 404)
+ connection = CONNECTIONS.get(connection_uuid)
+ if connection is None: return make_response('connection for state name not found', 404)
+ connections = [connection]
+ return make_response(jsonify(connections), 200)
+
+ def post(self):
+ if request.content_type != 'application/json': return make_response('bad content type', 400)
+ if request.content_length == 0: return make_response('bad content length', 400)
+ request_json = request.json
+ if not isinstance(request_json, list): return make_response('content is not list', 400)
+ reply = []
+ for connection in request_json:
+ connection_uuid = str(uuid.uuid4())
+ state_name = connection['name']
+
+ if state_name is not None: STATE_NAME_TO_CONNECTION[state_name] = connection_uuid
+ CONNECTIONS[connection_uuid] = {
+ 'href': '/network-connections/{:s}'.format(str(connection_uuid)),
+ 'config': {
+ 'implicitTransportCapacity': connection['implicitTransportCapacity']
+ # 'mc': ??
+ },
+ 'state': {
+ 'name': state_name,
+ 'serviceMode': connection['serviceMode']
+ # 'outerVID' : ??
+ },
+ 'endpoints': [
+ compose_endpoint(endpoint)
+ for endpoint in connection['endpoints']
+ ]
+ }
+ reply.append(CONNECTIONS[connection_uuid])
+ return make_response(jsonify(reply), 202)
+
+class XrNetworkConnection(Resource):
+ def get(self, connection_uuid : str):
+ connection = CONNECTIONS.get(connection_uuid)
+ if connection is None: return make_response('unexpected connection id', 404)
+ return make_response(jsonify(connection), 200)
+
+ def delete(self, connection_uuid : str):
+ connection = CONNECTIONS.pop(connection_uuid, None)
+ if connection is None: return make_response('unexpected connection id', 404)
+ state_name = connection['state']['name']
+ STATE_NAME_TO_CONNECTION.pop(state_name, None)
+ return make_response(jsonify({}), 202)
+
+def main():
+ LOGGER.info('Starting...')
+
+ app = Flask(__name__)
+ app.after_request(functools.partial(log_request, LOGGER))
+
+ api = Api(app)
+ api.add_resource(OpenIdConnect, '/realms/xr-cm/protocol/openid-connect/token')
+ api.add_resource(XrNetworks, '/api/v1/xr-networks')
+ api.add_resource(XrNetworkConnections, '/api/v1/network-connections')
+ api.add_resource(XrNetworkConnection, '/api/v1/network-connections/