From 8f898f427a51002b2339b65db6f2f2884b8f72f5 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 20 Feb 2023 15:13:14 +0000 Subject: [PATCH 01/77] Device component - IETF L2VPN Driver: - Added files --- .../drivers/ietf_l2vpn/IetfL2VpnDriver.py | 17 +++++++++++++++++ .../service/drivers/ietf_l2vpn/__init__.py | 13 +++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py create mode 100644 src/device/service/drivers/ietf_l2vpn/__init__.py diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py new file mode 100644 index 000000000..1b1217636 --- /dev/null +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -0,0 +1,17 @@ +# 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. + +class IetfL2VpnDriver: + def __init__(self) -> None: + pass diff --git a/src/device/service/drivers/ietf_l2vpn/__init__.py b/src/device/service/drivers/ietf_l2vpn/__init__.py new file mode 100644 index 000000000..38d04994f --- /dev/null +++ b/src/device/service/drivers/ietf_l2vpn/__init__.py @@ -0,0 +1,13 @@ +# 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. -- GitLab From 4af69bbb6cc438391df5f5d1200c821477ad39fb Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 20 Feb 2023 16:01:29 +0000 Subject: [PATCH 02/77] Common / Proto / Components: - Added device type "TeraFlowSDN controller" - Added driver type for IETF L2VPN - Updated corresponding components --- proto/context.proto | 1 + .../java/eu/teraflow/automation/Serializer.java | 4 ++++ .../eu/teraflow/automation/SerializerTest.java | 2 ++ .../grpc/context/ContextOuterClass.java | 9 +++++++++ src/common/DeviceTypes.py | 5 ++++- src/common/tools/object_factory/Device.py | 10 ++++++++++ src/common/type_checkers/Assertions.py | 1 + .../database/models/enums/DeviceDriver.py | 1 + .../main/java/eu/teraflow/policy/Serializer.java | 4 ++++ .../java/eu/teraflow/policy/SerializerTest.java | 2 ++ .../grpc/context/ContextOuterClass.java | 9 +++++++++ .../service/service_handler_api/FilterFields.py | 3 ++- src/webui/service/device/forms.py | 1 + src/webui/service/device/routes.py | 2 ++ .../static/topology_icons/teraflowsdn.png | Bin 0 -> 8751 bytes 15 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/webui/service/static/topology_icons/teraflowsdn.png diff --git a/proto/context.proto b/proto/context.proto index 49d16229c..2dfbb7805 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -191,6 +191,7 @@ enum DeviceDriverEnum { DEVICEDRIVER_IETF_NETWORK_TOPOLOGY = 4; DEVICEDRIVER_ONF_TR_352 = 5; DEVICEDRIVER_XR = 6; + DEVICEDRIVER_IETF_L2VPN = 7; } enum DeviceOperationalStatusEnum { diff --git a/src/automation/src/main/java/eu/teraflow/automation/Serializer.java b/src/automation/src/main/java/eu/teraflow/automation/Serializer.java index 08691b526..b0729aa55 100644 --- a/src/automation/src/main/java/eu/teraflow/automation/Serializer.java +++ b/src/automation/src/main/java/eu/teraflow/automation/Serializer.java @@ -853,6 +853,8 @@ public class Serializer { return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352; case XR: return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_XR; + case IETF_L2VPN: + return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN; case UNDEFINED: default: return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED; @@ -874,6 +876,8 @@ public class Serializer { return DeviceDriverEnum.ONF_TR_352; case DEVICEDRIVER_XR: return DeviceDriverEnum.XR; + case DEVICEDRIVER_IETF_L2VPN: + return DeviceDriverEnum.IETF_L2VPN; case DEVICEDRIVER_UNDEFINED: case UNRECOGNIZED: default: diff --git a/src/automation/src/test/java/eu/teraflow/automation/SerializerTest.java b/src/automation/src/test/java/eu/teraflow/automation/SerializerTest.java index 494e608a1..0931054c6 100644 --- a/src/automation/src/test/java/eu/teraflow/automation/SerializerTest.java +++ b/src/automation/src/test/java/eu/teraflow/automation/SerializerTest.java @@ -1215,6 +1215,8 @@ class SerializerTest { DeviceDriverEnum.ONF_TR_352, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352), Arguments.of(DeviceDriverEnum.XR, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_XR), + Arguments.of( + DeviceDriverEnum.IETF_L2VPN, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN), Arguments.of( DeviceDriverEnum.UNDEFINED, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED)); } diff --git a/src/automation/target/generated-sources/grpc/context/ContextOuterClass.java b/src/automation/target/generated-sources/grpc/context/ContextOuterClass.java index 060e81a55..b1bccdecc 100644 --- a/src/automation/target/generated-sources/grpc/context/ContextOuterClass.java +++ b/src/automation/target/generated-sources/grpc/context/ContextOuterClass.java @@ -177,6 +177,10 @@ public final class ContextOuterClass { * 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 99255defd..2cb1fb4f0 100644 --- a/src/common/DeviceTypes.py +++ b/src/common/DeviceTypes.py @@ -38,4 +38,7 @@ class DeviceTypeEnum(Enum): P4_SWITCH = 'p4-switch' 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 0cc4555d4..66c87b14d 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 c0442d877..ba82e535e 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/context/service/database/models/enums/DeviceDriver.py b/src/context/service/database/models/enums/DeviceDriver.py index 6997e7dfb..a612803e2 100644 --- a/src/context/service/database/models/enums/DeviceDriver.py +++ b/src/context/service/database/models/enums/DeviceDriver.py @@ -24,6 +24,7 @@ class ORM_DeviceDriverEnum(enum.Enum): IETF_NETWORK_TOPOLOGY = DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY ONF_TR_352 = DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352 XR = DeviceDriverEnum.DEVICEDRIVER_XR + IETF_L2VPN = DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN grpc_to_enum__device_driver = functools.partial( grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum) diff --git a/src/policy/src/main/java/eu/teraflow/policy/Serializer.java b/src/policy/src/main/java/eu/teraflow/policy/Serializer.java index 529ec6334..967d1d6e6 100644 --- a/src/policy/src/main/java/eu/teraflow/policy/Serializer.java +++ b/src/policy/src/main/java/eu/teraflow/policy/Serializer.java @@ -2245,6 +2245,8 @@ public class Serializer { return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352; case XR: return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_XR; + case IETF_L2VPN: + return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN; case UNDEFINED: default: return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED; @@ -2266,6 +2268,8 @@ public class Serializer { return DeviceDriverEnum.ONF_TR_352; case DEVICEDRIVER_XR: return DeviceDriverEnum.XR; + case DEVICEDRIVER_IETF_L2VPN: + return DeviceDriverEnum.IETF_L2VPN; case DEVICEDRIVER_UNDEFINED: case UNRECOGNIZED: default: diff --git a/src/policy/src/test/java/eu/teraflow/policy/SerializerTest.java b/src/policy/src/test/java/eu/teraflow/policy/SerializerTest.java index d284840b8..641026461 100644 --- a/src/policy/src/test/java/eu/teraflow/policy/SerializerTest.java +++ b/src/policy/src/test/java/eu/teraflow/policy/SerializerTest.java @@ -3600,6 +3600,8 @@ class SerializerTest { DeviceDriverEnum.ONF_TR_352, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352), Arguments.of(DeviceDriverEnum.XR, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_XR), + Arguments.of( + DeviceDriverEnum.IETF_L2VPN, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN), Arguments.of( DeviceDriverEnum.UNDEFINED, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED)); } diff --git a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java index fbbba62a2..53252341b 100644 --- a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java +++ b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java @@ -177,6 +177,10 @@ public final class ContextOuterClass { * 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 a73ec53f3..3ec71dc64 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/webui/service/device/forms.py b/src/webui/service/device/forms.py index c6bacac9b..24bc92b3a 100644 --- a/src/webui/service/device/forms.py +++ b/src/webui/service/device/forms.py @@ -29,6 +29,7 @@ class AddDeviceForm(FlaskForm): device_drivers_ietf_network_topology = BooleanField('IETF_NETWORK_TOPOLOGY') device_drivers_onf_tr_352 = BooleanField('ONF_TR_352') device_drivers_xr = BooleanField('XR') + device_drivers_ietf_l2vpn = BooleanField('IETF L2VPN') device_config_address = StringField('connect/address',default='127.0.0.1',validators=[DataRequired(), Length(min=5)]) device_config_port = StringField('connect/port',default='0',validators=[DataRequired(), Length(min=1)]) device_config_settings = TextAreaField('connect/settings',default='{}',validators=[DataRequired(), Length(min=2)]) diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py index ebf77a35f..bc4684770 100644 --- a/src/webui/service/device/routes.py +++ b/src/webui/service/device/routes.py @@ -120,6 +120,8 @@ def add(): device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352) if form.device_drivers_xr.data: device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_XR) + if form.device_drivers_ietf_l2vpn.data: + device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN) device_obj.device_drivers.extend(device_drivers) # pylint: disable=no-member try: diff --git a/src/webui/service/static/topology_icons/teraflowsdn.png b/src/webui/service/static/topology_icons/teraflowsdn.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2232e8223a39eb0d829e0e50975a697b0660fc GIT binary patch literal 8751 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 literal 0 HcmV?d00001 -- GitLab From b19977682da1b798442fae6a7db282acd2e51c62 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 20 Feb 2023 16:28:39 +0000 Subject: [PATCH 03/77] Device component - IETF L2VPN Driver: - backup WORK IN PROGRESS --- src/device/service/drivers/__init__.py | 9 + .../drivers/ietf_l2vpn/IetfL2VpnDriver.py | 118 +++- .../service/drivers/ietf_l2vpn/MockOSM.py | 62 ++ .../service/drivers/ietf_l2vpn/Tools.py | 184 ++++++ .../ietf_l2vpn/WimconnectorIETFL2VPN.py | 543 ++++++++++++++++++ .../service/drivers/ietf_l2vpn/__init__.py | 14 + .../drivers/ietf_l2vpn/acknowledgements.txt | 3 + .../service/drivers/ietf_l2vpn/sdnconn.py | 242 ++++++++ 8 files changed, 1172 insertions(+), 3 deletions(-) create mode 100644 src/device/service/drivers/ietf_l2vpn/MockOSM.py create mode 100644 src/device/service/drivers/ietf_l2vpn/Tools.py create mode 100644 src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py create mode 100644 src/device/service/drivers/ietf_l2vpn/acknowledgements.txt create mode 100644 src/device/service/drivers/ietf_l2vpn/sdnconn.py diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py index 469abcad3..b3b485a47 100644 --- a/src/device/service/drivers/__init__.py +++ b/src/device/service/drivers/__init__.py @@ -74,6 +74,15 @@ DRIVERS.append( #} ])) +from .ietf_l2vpn.IetfL2VpnDriver import IetfL2VpnDriver # pylint: disable=wrong-import-position +DRIVERS.append( + (IetfL2VpnDriver, [ + { + FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN, + } + ])) + if LOAD_ALL_DEVICE_DRIVERS: from .openconfig.OpenConfigDriver import OpenConfigDriver # pylint: disable=wrong-import-position DRIVERS.append( diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py index 1b1217636..3823f569b 100644 --- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -12,6 +12,118 @@ # See the License for the specific language governing permissions and # limitations under the License. -class IetfL2VpnDriver: - def __init__(self) -> None: - pass +import logging, requests, threading +from requests.auth import HTTPBasicAuth +from typing import Any, Iterator, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.type_checkers.Checkers import chk_string, chk_type +from device.service.driver_api._Driver import _Driver +from . import ALL_RESOURCE_KEYS +from .Tools import create_connectivity_service, find_key, config_getter, delete_connectivity_service + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'ietf_l2vpn'}) + +class IetfL2VpnDriver(_Driver): + def __init__(self, address: str, port: int, **settings) -> None: # pylint: disable=super-init-not-called + self.__lock = threading.Lock() + self.__started = threading.Event() + self.__terminate = threading.Event() + username = settings.get('username') + password = settings.get('password') + self.__auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None + scheme = settings.get('scheme', 'http') + self.__tapi_root = '{:s}://{:s}:{:d}'.format(scheme, address, int(port)) + self.__timeout = int(settings.get('timeout', 120)) + + def Connect(self) -> bool: + url = self.__tapi_root + '/restconf/data/tapi-common:context' + with self.__lock: + if self.__started.is_set(): return True + try: + requests.get(url, timeout=self.__timeout, verify=False, auth=self.__auth) + except requests.exceptions.Timeout: + LOGGER.exception('Timeout connecting {:s}'.format(str(self.__tapi_root))) + return False + except Exception: # pylint: disable=broad-except + LOGGER.exception('Exception connecting {:s}'.format(str(self.__tapi_root))) + return False + else: + self.__started.set() + return True + + def Disconnect(self) -> bool: + with self.__lock: + self.__terminate.set() + return True + + @metered_subclass_method(METRICS_POOL) + def GetInitialConfig(self) -> List[Tuple[str, Any]]: + with self.__lock: + return [] + + @metered_subclass_method(METRICS_POOL) + def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: + chk_type('resources', resource_keys, list) + results = [] + with self.__lock: + if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS + for i, resource_key in enumerate(resource_keys): + str_resource_name = 'resource_key[#{:d}]'.format(i) + chk_string(str_resource_name, resource_key, allow_empty=False) + results.extend(config_getter( + self.__tapi_root, resource_key, timeout=self.__timeout, auth=self.__auth)) + return results + + @metered_subclass_method(METRICS_POOL) + def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + results = [] + if len(resources) == 0: + return results + with self.__lock: + for resource in resources: + LOGGER.info('resource = {:s}'.format(str(resource))) + + input_sip = find_key(resource, 'input_sip') + output_sip = find_key(resource, 'output_sip') + uuid = find_key(resource, 'uuid') + capacity_value = find_key(resource, 'capacity_value') + capacity_unit = find_key(resource, 'capacity_unit') + layer_protocol_name = find_key(resource, 'layer_protocol_name') + layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier') + direction = find_key(resource, 'direction') + + data = create_connectivity_service( + self.__tapi_root, uuid, input_sip, output_sip, direction, capacity_value, capacity_unit, + layer_protocol_name, layer_protocol_qualifier, timeout=self.__timeout, auth=self.__auth) + results.extend(data) + return results + + @metered_subclass_method(METRICS_POOL) + def DeleteConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + results = [] + if len(resources) == 0: return results + with self.__lock: + for resource in resources: + LOGGER.info('resource = {:s}'.format(str(resource))) + uuid = find_key(resource, 'uuid') + results.extend(delete_connectivity_service( + self.__tapi_root, uuid, timeout=self.__timeout, auth=self.__auth)) + return results + + @metered_subclass_method(METRICS_POOL) + def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: + # TODO: TAPI does not support monitoring by now + return [False for _ in subscriptions] + + @metered_subclass_method(METRICS_POOL) + def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: + # TODO: TAPI does not support monitoring by now + return [False for _ in subscriptions] + + def GetState( + self, blocking=False, terminate : Optional[threading.Event] = None + ) -> Iterator[Tuple[float, str, Any]]: + # TODO: TAPI does not support monitoring by now + return [] diff --git a/src/device/service/drivers/ietf_l2vpn/MockOSM.py b/src/device/service/drivers/ietf_l2vpn/MockOSM.py new file mode 100644 index 000000000..338db0e19 --- /dev/null +++ b/src/device/service/drivers/ietf_l2vpn/MockOSM.py @@ -0,0 +1,62 @@ +# 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 .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN + +LOGGER = logging.getLogger(__name__) + +class MockOSM: + def __init__(self, url, mapping, username, password): + wim = {'wim_url': url} + wim_account = {'user': username, 'password': password} + config = {'mapping_not_needed': False, 'service_endpoint_mapping': mapping} + self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config) + self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors + + def create_connectivity_service(self, service_type, connection_points): + LOGGER.info('[create_connectivity_service] service_type={:s}'.format(str(service_type))) + LOGGER.info('[create_connectivity_service] connection_points={:s}'.format(str(connection_points))) + self.wim.check_credentials() + result = self.wim.create_connectivity_service(service_type, connection_points) + LOGGER.info('[create_connectivity_service] result={:s}'.format(str(result))) + service_uuid, conn_info = result + self.conn_info[service_uuid] = conn_info + return service_uuid + + def get_connectivity_service_status(self, service_uuid): + LOGGER.info('[get_connectivity_service] service_uuid={:s}'.format(str(service_uuid))) + conn_info = self.conn_info.get(service_uuid) + if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid))) + LOGGER.info('[get_connectivity_service] conn_info={:s}'.format(str(conn_info))) + self.wim.check_credentials() + result = self.wim.get_connectivity_service_status(service_uuid, conn_info=conn_info) + LOGGER.info('[get_connectivity_service] result={:s}'.format(str(result))) + return result + + def edit_connectivity_service(self, service_uuid, connection_points): + LOGGER.info('[edit_connectivity_service] service_uuid={:s}'.format(str(service_uuid))) + LOGGER.info('[edit_connectivity_service] connection_points={:s}'.format(str(connection_points))) + conn_info = self.conn_info.get(service_uuid) + if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid))) + LOGGER.info('[edit_connectivity_service] conn_info={:s}'.format(str(conn_info))) + self.wim.edit_connectivity_service(service_uuid, conn_info=conn_info, connection_points=connection_points) + + def delete_connectivity_service(self, service_uuid): + LOGGER.info('[delete_connectivity_service] service_uuid={:s}'.format(str(service_uuid))) + conn_info = self.conn_info.get(service_uuid) + if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid))) + LOGGER.info('[delete_connectivity_service] conn_info={:s}'.format(str(conn_info))) + self.wim.check_credentials() + self.wim.delete_connectivity_service(service_uuid, conn_info=conn_info) diff --git a/src/device/service/drivers/ietf_l2vpn/Tools.py b/src/device/service/drivers/ietf_l2vpn/Tools.py new file mode 100644 index 000000000..8188c77df --- /dev/null +++ b/src/device/service/drivers/ietf_l2vpn/Tools.py @@ -0,0 +1,184 @@ +# 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, operator, requests +from requests.auth import HTTPBasicAuth +from typing import Dict, Optional +from device.service.driver_api._Driver import RESOURCE_ENDPOINTS + +LOGGER = logging.getLogger(__name__) + +HTTP_OK_CODES = { + 200, # OK + 201, # Created + 202, # Accepted + 204, # No Content +} + +def find_key(resource, key): + return json.loads(resource[1])[key] + + +def config_getter( + root_url : str, resource_key : str, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None +): + url = '{:s}/restconf/data/tapi-common:context'.format(root_url) + result = [] + try: + response = requests.get(url, timeout=timeout, verify=False, auth=auth) + except requests.exceptions.Timeout: + LOGGER.exception('Timeout connecting {:s}'.format(url)) + return result + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception retrieving {:s}'.format(resource_key)) + result.append((resource_key, e)) + return result + + try: + context = json.loads(response.content) + except Exception as e: # pylint: disable=broad-except + LOGGER.warning('Unable to decode reply: {:s}'.format(str(response.content))) + result.append((resource_key, e)) + return result + + if resource_key != RESOURCE_ENDPOINTS: return result + + if 'tapi-common:context' in context: + context = context['tapi-common:context'] + elif 'context' in context: + context = context['context'] + + for sip in context['service-interface-point']: + layer_protocol_name = sip.get('layer-protocol-name', '?') + supportable_spectrum = sip.get('tapi-photonic-media:media-channel-service-interface-point-spec', {}) + supportable_spectrum = supportable_spectrum.get('mc-pool', {}) + supportable_spectrum = supportable_spectrum.get('supportable-spectrum', []) + supportable_spectrum = supportable_spectrum[0] if len(supportable_spectrum) == 1 else {} + grid_type = supportable_spectrum.get('frequency-constraint', {}).get('grid-type') + granularity = supportable_spectrum.get('frequency-constraint', {}).get('adjustment-granularity') + direction = sip.get('direction', '?') + endpoint_type = [layer_protocol_name, grid_type, granularity, direction] + str_endpoint_type = ':'.join(filter(lambda i: operator.is_not(i, None), endpoint_type)) + endpoint_url = '/endpoints/endpoint[{:s}]'.format(sip['uuid']) + endpoint_data = {'uuid': sip['uuid'], 'type': str_endpoint_type} + result.append((endpoint_url, endpoint_data)) + + return result + +def create_connectivity_service( + root_url, uuid, input_sip, output_sip, direction, capacity_value, capacity_unit, layer_protocol_name, + layer_protocol_qualifier, + auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None +): + + url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context'.format(root_url) + headers = {'content-type': 'application/json'} + data = { + 'tapi-connectivity:connectivity-service': [ + { + 'uuid': uuid, + 'connectivity-constraint': { + 'requested-capacity': { + 'total-size': { + 'value': capacity_value, + 'unit': capacity_unit + } + }, + 'connectivity-direction': direction + }, + 'end-point': [ + { + 'service-interface-point': { + 'service-interface-point-uuid': input_sip + }, + 'layer-protocol-name': layer_protocol_name, + 'layer-protocol-qualifier': layer_protocol_qualifier, + 'local-id': input_sip + }, + { + 'service-interface-point': { + 'service-interface-point-uuid': output_sip + }, + 'layer-protocol-name': layer_protocol_name, + 'layer-protocol-qualifier': layer_protocol_qualifier, + 'local-id': output_sip + } + ] + } + ] + } + results = [] + try: + LOGGER.info('Connectivity service {:s}: {:s}'.format(str(uuid), str(data))) + response = requests.post( + url=url, data=json.dumps(data), timeout=timeout, headers=headers, verify=False, auth=auth) + LOGGER.info('TAPI response: {:s}'.format(str(response))) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception creating ConnectivityService(uuid={:s}, data={:s})'.format(str(uuid), str(data))) + results.append(e) + else: + if response.status_code not in HTTP_OK_CODES: + msg = 'Could not create ConnectivityService(uuid={:s}, data={:s}). status_code={:s} reply={:s}' + LOGGER.error(msg.format(str(uuid), str(data), str(response.status_code), str(response))) + results.append(response.status_code in HTTP_OK_CODES) + return results + +def delete_connectivity_service(root_url, uuid, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None): + url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context/connectivity-service={:s}' + url = url.format(root_url, uuid) + results = [] + try: + response = requests.delete(url=url, timeout=timeout, verify=False, auth=auth) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception deleting ConnectivityService(uuid={:s})'.format(str(uuid))) + results.append(e) + else: + if response.status_code not in HTTP_OK_CODES: + msg = 'Could not delete ConnectivityService(uuid={:s}). status_code={:s} reply={:s}' + LOGGER.error(msg.format(str(uuid), str(response.status_code), str(response))) + results.append(response.status_code in HTTP_OK_CODES) + return results + +def compose_service_endpoint_id(site_id : str, endpoint_id : Dict): + device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] + endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] + return ':'.join([site_id, device_uuid, endpoint_uuid]) + +def wim_mapping(site_id, ce_endpoint_id, pe_device_id : Optional[Dict] = None, priority=None, redundant=[]): + ce_device_uuid = ce_endpoint_id['device_id']['device_uuid']['uuid'] + ce_endpoint_uuid = ce_endpoint_id['endpoint_uuid']['uuid'] + service_endpoint_id = compose_service_endpoint_id(site_id, ce_endpoint_id) + if pe_device_id is None: + bearer = '{:s}:{:s}'.format(ce_device_uuid, ce_endpoint_uuid) + else: + pe_device_uuid = pe_device_id['device_uuid']['uuid'] + bearer = '{:s}:{:s}'.format(ce_device_uuid, pe_device_uuid) + mapping = { + 'service_endpoint_id': service_endpoint_id, + 'datacenter_id': site_id, 'device_id': ce_device_uuid, 'device_interface_id': ce_endpoint_uuid, + 'service_mapping_info': { + 'site-id': site_id, + 'bearer': {'bearer-reference': bearer}, + } + } + if priority is not None: mapping['service_mapping_info']['priority'] = priority + if len(redundant) > 0: mapping['service_mapping_info']['redundant'] = redundant + return service_endpoint_id, mapping + +def connection_point(service_endpoint_id : str, encapsulation_type : str, vlan_id : int): + return { + 'service_endpoint_id': service_endpoint_id, + 'service_endpoint_encapsulation_type': encapsulation_type, + 'service_endpoint_encapsulation_info': {'vlan': vlan_id} + } diff --git a/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py new file mode 100644 index 000000000..aa4ca045f --- /dev/null +++ b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py @@ -0,0 +1,543 @@ +# -*- coding: utf-8 -*- +## +# Copyright 2018 Telefonica +# All Rights Reserved. +# +# Contributors: Oscar Gonzalez de Dios, Manuel Lopez Bravo, Guillermo Pajares Martin +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This work has been performed in the context of the Metro-Haul project - +# funded by the European Commission under Grant number 761727 through the +# Horizon 2020 program. +## +"""The SDN/WIM connector is responsible for establishing wide area network +connectivity. + +This SDN/WIM connector implements the standard IETF RFC 8466 "A YANG Data + Model for Layer 2 Virtual Private Network (L2VPN) Service Delivery" + +It receives the endpoints and the necessary details to request +the Layer 2 service. +""" +import requests +import uuid +import logging +import copy +#from osm_ro_plugin.sdnconn import SdnConnectorBase, SdnConnectorError +from .sdnconn import SdnConnectorBase, SdnConnectorError + +"""Check layer where we move it""" + + +class WimconnectorIETFL2VPN(SdnConnectorBase): + def __init__(self, wim, wim_account, config=None, logger=None): + """IETF L2VPN WIM connector + + Arguments: (To be completed) + wim (dict): WIM record, as stored in the database + wim_account (dict): WIM account record, as stored in the database + """ + self.logger = logging.getLogger("ro.sdn.ietfl2vpn") + super().__init__(wim, wim_account, config, logger) + self.headers = {"Content-Type": "application/json"} + self.mappings = { + m["service_endpoint_id"]: m for m in self.service_endpoint_mapping + } + self.user = wim_account.get("user") + self.passwd = wim_account.get("password") # replace "passwordd" -> "password" + + if self.user and self.passwd is not None: + self.auth = (self.user, self.passwd) + else: + self.auth = None + + self.logger.info("IETFL2VPN Connector Initialized.") + + def check_credentials(self): + endpoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format( + self.wim["wim_url"] + ) + + try: + response = requests.get(endpoint, auth=self.auth) + http_code = response.status_code + except requests.exceptions.RequestException as e: + raise SdnConnectorError(e.response, http_code=503) + + if http_code != 200: + raise SdnConnectorError("Failed while authenticating", http_code=http_code) + + self.logger.info("Credentials checked") + + def get_connectivity_service_status(self, service_uuid, conn_info=None): + """Monitor the status of the connectivity service stablished + + Arguments: + service_uuid: Connectivity service unique identifier + + Returns: + Examples:: + {'sdn_status': 'ACTIVE'} + {'sdn_status': 'INACTIVE'} + {'sdn_status': 'DOWN'} + {'sdn_status': 'ERROR'} + """ + try: + self.logger.info("Sending get connectivity service stuatus") + servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format( + self.wim["wim_url"], service_uuid + ) + response = requests.get(servicepoint, auth=self.auth) + self.logger.warning('response.status_code={:s}'.format(str(response.status_code))) + if response.status_code != requests.codes.ok: + raise SdnConnectorError( + "Unable to obtain connectivity servcice status", + http_code=response.status_code, + ) + + service_status = {"sdn_status": "ACTIVE"} + + return service_status + except requests.exceptions.ConnectionError: + raise SdnConnectorError("Request Timeout", http_code=408) + + def search_mapp(self, connection_point): + id = connection_point["service_endpoint_id"] + if id not in self.mappings: + raise SdnConnectorError("Endpoint {} not located".format(str(id))) + else: + return self.mappings[id] + + def create_connectivity_service(self, service_type, connection_points, **kwargs): + """Stablish WAN connectivity between the endpoints + + Arguments: + service_type (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2), + ``L3``. + connection_points (list): each point corresponds to + an entry point from the DC to the transport network. One + connection point serves to identify the specific access and + some other service parameters, such as encapsulation type. + Represented by a dict as follows:: + + { + "service_endpoint_id": ..., (str[uuid]) + "service_endpoint_encapsulation_type": ..., + (enum: none, dot1q, ...) + "service_endpoint_encapsulation_info": { + ... (dict) + "vlan": ..., (int, present if encapsulation is dot1q) + "vni": ... (int, present if encapsulation is vxlan), + "peers": [(ipv4_1), (ipv4_2)] + (present if encapsulation is vxlan) + } + } + + The service endpoint ID should be previously informed to the WIM + engine in the RO when the WIM port mapping is registered. + + Keyword Arguments: + bandwidth (int): value in kilobytes + latency (int): value in milliseconds + + Other QoS might be passed as keyword arguments. + + Returns: + tuple: ``(service_id, conn_info)`` containing: + - *service_uuid* (str): UUID of the established connectivity + service + - *conn_info* (dict or None): Information to be stored at the + database (or ``None``). This information will be provided to + the :meth:`~.edit_connectivity_service` and :obj:`~.delete`. + **MUST** be JSON/YAML-serializable (plain data structures). + + Raises: + SdnConnectorException: In case of error. + """ + SETTINGS = { # min_endpoints, max_endpoints, vpn_service_type + 'ELINE': (2, 2, 'vpws'), # Virtual Private Wire Service + 'ELAN' : (2, None, 'vpls'), # Virtual Private LAN Service + } + settings = SETTINGS.get(service_type) + if settings is None: raise NotImplementedError('Unsupported service_type({:s})'.format(str(service_type))) + min_endpoints, max_endpoints, vpn_service_type = settings + + if max_endpoints is not None and len(connection_points) > max_endpoints: + msg = "Connections between more than {:d} endpoints are not supported for service_type {:s}" + raise SdnConnectorError(msg.format(max_endpoints, service_type)) + + if min_endpoints is not None and len(connection_points) < min_endpoints: + msg = "Connections must be of at least {:d} endpoints for service_type {:s}" + raise SdnConnectorError(msg.format(min_endpoints, service_type)) + + """First step, create the vpn service""" + uuid_l2vpn = str(uuid.uuid4()) + vpn_service = {} + vpn_service["vpn-id"] = uuid_l2vpn + vpn_service["vpn-svc-type"] = vpn_service_type + vpn_service["svc-topo"] = "any-to-any" + vpn_service["customer-name"] = "osm" + vpn_service_list = [] + vpn_service_list.append(vpn_service) + vpn_service_l = {"ietf-l2vpn-svc:vpn-service": vpn_service_list} + response_service_creation = None + conn_info = [] + self.logger.info("Sending vpn-service :{}".format(vpn_service_l)) + + try: + endpoint_service_creation = ( + "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format( + self.wim["wim_url"] + ) + ) + response_service_creation = requests.post( + endpoint_service_creation, + headers=self.headers, + json=vpn_service_l, + auth=self.auth, + ) + except requests.exceptions.ConnectionError: + raise SdnConnectorError( + "Request to create service Timeout", http_code=408 + ) + + if response_service_creation.status_code == 409: + raise SdnConnectorError( + "Service already exists", + http_code=response_service_creation.status_code, + ) + elif response_service_creation.status_code != requests.codes.created: + raise SdnConnectorError( + "Request to create service not accepted", + http_code=response_service_creation.status_code, + ) + + self.logger.info('connection_points = {:s}'.format(str(connection_points))) + + # Check if protected paths are requested + extended_connection_points = [] + for connection_point in connection_points: + extended_connection_points.append(connection_point) + + connection_point_wan_info = self.search_mapp(connection_point) + service_mapping_info = connection_point_wan_info.get('service_mapping_info', {}) + redundant_service_endpoint_ids = service_mapping_info.get('redundant') + + if redundant_service_endpoint_ids is None: continue + if len(redundant_service_endpoint_ids) == 0: continue + + for redundant_service_endpoint_id in redundant_service_endpoint_ids: + redundant_connection_point = copy.deepcopy(connection_point) + redundant_connection_point['service_endpoint_id'] = redundant_service_endpoint_id + extended_connection_points.append(redundant_connection_point) + + self.logger.info('extended_connection_points = {:s}'.format(str(extended_connection_points))) + + """Second step, create the connections and vpn attachments""" + for connection_point in extended_connection_points: + connection_point_wan_info = self.search_mapp(connection_point) + site_network_access = {} + connection = {} + + if connection_point["service_endpoint_encapsulation_type"] != "none": + if ( + connection_point["service_endpoint_encapsulation_type"] + == "dot1q" + ): + """The connection is a VLAN""" + connection["encapsulation-type"] = "dot1q-vlan-tagged" + tagged = {} + tagged_interf = {} + service_endpoint_encapsulation_info = connection_point[ + "service_endpoint_encapsulation_info" + ] + + if service_endpoint_encapsulation_info["vlan"] is None: + raise SdnConnectorError("VLAN must be provided") + + tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info[ + "vlan" + ] + tagged["dot1q-vlan-tagged"] = tagged_interf + connection["tagged-interface"] = tagged + else: + raise NotImplementedError("Encapsulation type not implemented") + + site_network_access["connection"] = connection + self.logger.info("Sending connection:{}".format(connection)) + vpn_attach = {} + vpn_attach["vpn-id"] = uuid_l2vpn + vpn_attach["site-role"] = vpn_service["svc-topo"] + "-role" + site_network_access["vpn-attachment"] = vpn_attach + self.logger.info("Sending vpn-attachement :{}".format(vpn_attach)) + uuid_sna = str(uuid.uuid4()) + site_network_access["network-access-id"] = uuid_sna + site_network_access["bearer"] = connection_point_wan_info[ + "service_mapping_info" + ]["bearer"] + + access_priority = connection_point_wan_info["service_mapping_info"].get("priority") + if access_priority is not None: + availability = {} + availability["access-priority"] = access_priority + availability["single-active"] = [None] + site_network_access["availability"] = availability + + constraint = {} + constraint['constraint-type'] = 'end-to-end-diverse' + constraint['target'] = {'all-other-accesses': [None]} + + access_diversity = {} + access_diversity['constraints'] = {'constraint': []} + access_diversity['constraints']['constraint'].append(constraint) + site_network_access["access-diversity"] = access_diversity + + site_network_accesses = {} + site_network_access_list = [] + site_network_access_list.append(site_network_access) + site_network_accesses[ + "ietf-l2vpn-svc:site-network-access" + ] = site_network_access_list + conn_info_d = {} + conn_info_d["site"] = connection_point_wan_info["service_mapping_info"][ + "site-id" + ] + conn_info_d["site-network-access-id"] = site_network_access[ + "network-access-id" + ] + conn_info_d["mapping"] = None + conn_info.append(conn_info_d) + + try: + endpoint_site_network_access_creation = ( + "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/" + "sites/site={}/site-network-accesses/".format( + self.wim["wim_url"], + connection_point_wan_info["service_mapping_info"][ + "site-id" + ], + ) + ) + response_endpoint_site_network_access_creation = requests.post( + endpoint_site_network_access_creation, + headers=self.headers, + json=site_network_accesses, + auth=self.auth, + ) + + if ( + response_endpoint_site_network_access_creation.status_code + == 409 + ): + self.delete_connectivity_service(vpn_service["vpn-id"]) + + raise SdnConnectorError( + "Site_Network_Access with ID '{}' already exists".format( + site_network_access["network-access-id"] + ), + http_code=response_endpoint_site_network_access_creation.status_code, + ) + elif ( + response_endpoint_site_network_access_creation.status_code + == 400 + ): + self.delete_connectivity_service(vpn_service["vpn-id"]) + + raise SdnConnectorError( + "Site {} does not exist".format( + connection_point_wan_info["service_mapping_info"][ + "site-id" + ] + ), + http_code=response_endpoint_site_network_access_creation.status_code, + ) + elif ( + response_endpoint_site_network_access_creation.status_code + != requests.codes.created + and response_endpoint_site_network_access_creation.status_code + != requests.codes.no_content + ): + self.delete_connectivity_service(vpn_service["vpn-id"]) + + raise SdnConnectorError( + "Request not accepted", + http_code=response_endpoint_site_network_access_creation.status_code, + ) + except requests.exceptions.ConnectionError: + self.delete_connectivity_service(vpn_service["vpn-id"]) + + raise SdnConnectorError("Request Timeout", http_code=408) + + return uuid_l2vpn, conn_info + + def delete_connectivity_service(self, service_uuid, conn_info=None): + """Disconnect multi-site endpoints previously connected + + This method should receive as the first argument the UUID generated by + the ``create_connectivity_service`` + """ + try: + self.logger.info("Sending delete") + servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format( + self.wim["wim_url"], service_uuid + ) + response = requests.delete(servicepoint, auth=self.auth) + + if response.status_code != requests.codes.no_content: + raise SdnConnectorError( + "Error in the request", http_code=response.status_code + ) + except requests.exceptions.ConnectionError: + raise SdnConnectorError("Request Timeout", http_code=408) + + def edit_connectivity_service( + self, service_uuid, conn_info=None, connection_points=None, **kwargs + ): + """Change an existing connectivity service, see + ``create_connectivity_service``""" + # sites = {"sites": {}} + # site_list = [] + vpn_service = {} + vpn_service["svc-topo"] = "any-to-any" + counter = 0 + + for connection_point in connection_points: + site_network_access = {} + connection_point_wan_info = self.search_mapp(connection_point) + params_site = {} + params_site["site-id"] = connection_point_wan_info["service_mapping_info"][ + "site-id" + ] + params_site["site-vpn-flavor"] = "site-vpn-flavor-single" + device_site = {} + device_site["device-id"] = connection_point_wan_info["device-id"] + params_site["devices"] = device_site + # network_access = {} + connection = {} + + if connection_point["service_endpoint_encapsulation_type"] != "none": + if connection_point["service_endpoint_encapsulation_type"] == "dot1q": + """The connection is a VLAN""" + connection["encapsulation-type"] = "dot1q-vlan-tagged" + tagged = {} + tagged_interf = {} + service_endpoint_encapsulation_info = connection_point[ + "service_endpoint_encapsulation_info" + ] + + if service_endpoint_encapsulation_info["vlan"] is None: + raise SdnConnectorError("VLAN must be provided") + + tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info[ + "vlan" + ] + tagged["dot1q-vlan-tagged"] = tagged_interf + connection["tagged-interface"] = tagged + else: + raise NotImplementedError("Encapsulation type not implemented") + + site_network_access["connection"] = connection + vpn_attach = {} + vpn_attach["vpn-id"] = service_uuid + vpn_attach["site-role"] = vpn_service["svc-topo"] + "-role" + site_network_access["vpn-attachment"] = vpn_attach + uuid_sna = conn_info[counter]["site-network-access-id"] + site_network_access["network-access-id"] = uuid_sna + site_network_access["bearer"] = connection_point_wan_info[ + "service_mapping_info" + ]["bearer"] + site_network_accesses = {} + site_network_access_list = [] + site_network_access_list.append(site_network_access) + site_network_accesses[ + "ietf-l2vpn-svc:site-network-access" + ] = site_network_access_list + + try: + endpoint_site_network_access_edit = ( + "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/" + "sites/site={}/site-network-accesses/".format( + self.wim["wim_url"], + connection_point_wan_info["service_mapping_info"]["site-id"], + ) + ) + response_endpoint_site_network_access_creation = requests.put( + endpoint_site_network_access_edit, + headers=self.headers, + json=site_network_accesses, + auth=self.auth, + ) + + if response_endpoint_site_network_access_creation.status_code == 400: + raise SdnConnectorError( + "Service does not exist", + http_code=response_endpoint_site_network_access_creation.status_code, + ) + elif ( + response_endpoint_site_network_access_creation.status_code != 201 + and response_endpoint_site_network_access_creation.status_code + != 204 + ): + raise SdnConnectorError( + "Request no accepted", + http_code=response_endpoint_site_network_access_creation.status_code, + ) + except requests.exceptions.ConnectionError: + raise SdnConnectorError("Request Timeout", http_code=408) + + counter += 1 + + return None + + def clear_all_connectivity_services(self): + """Delete all WAN Links corresponding to a WIM""" + try: + self.logger.info("Sending clear all connectivity services") + servicepoint = ( + "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format( + self.wim["wim_url"] + ) + ) + response = requests.delete(servicepoint, auth=self.auth) + + if response.status_code != requests.codes.no_content: + raise SdnConnectorError( + "Unable to clear all connectivity services", + http_code=response.status_code, + ) + except requests.exceptions.ConnectionError: + raise SdnConnectorError("Request Timeout", http_code=408) + + def get_all_active_connectivity_services(self): + """Provide information about all active connections provisioned by a + WIM + """ + try: + self.logger.info("Sending get all connectivity services") + servicepoint = ( + "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format( + self.wim["wim_url"] + ) + ) + response = requests.get(servicepoint, auth=self.auth) + + if response.status_code != requests.codes.ok: + raise SdnConnectorError( + "Unable to get all connectivity services", + http_code=response.status_code, + ) + + return response + except requests.exceptions.ConnectionError: + raise SdnConnectorError("Request Timeout", http_code=408) diff --git a/src/device/service/drivers/ietf_l2vpn/__init__.py b/src/device/service/drivers/ietf_l2vpn/__init__.py index 38d04994f..2d3f6df32 100644 --- a/src/device/service/drivers/ietf_l2vpn/__init__.py +++ b/src/device/service/drivers/ietf_l2vpn/__init__.py @@ -11,3 +11,17 @@ # 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. + +from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES + +ALL_RESOURCE_KEYS = [ + RESOURCE_ENDPOINTS, + RESOURCE_INTERFACES, + RESOURCE_NETWORK_INSTANCES, +] + +RESOURCE_KEY_MAPPINGS = { + RESOURCE_ENDPOINTS : 'component', + RESOURCE_INTERFACES : 'interface', + RESOURCE_NETWORK_INSTANCES: 'network_instance', +} diff --git a/src/device/service/drivers/ietf_l2vpn/acknowledgements.txt b/src/device/service/drivers/ietf_l2vpn/acknowledgements.txt new file mode 100644 index 000000000..3a7ed47ad --- /dev/null +++ b/src/device/service/drivers/ietf_l2vpn/acknowledgements.txt @@ -0,0 +1,3 @@ +IETF L2VPN Driver is based on source code taken from: +https://osm.etsi.org/gitlab/osm/ro/-/blob/master/RO-plugin/osm_ro_plugin/sdnconn.py +https://osm.etsi.org/gitlab/osm/ro/-/blob/master/RO-SDN-ietfl2vpn/osm_rosdn_ietfl2vpn/wimconn_ietfl2vpn.py diff --git a/src/device/service/drivers/ietf_l2vpn/sdnconn.py b/src/device/service/drivers/ietf_l2vpn/sdnconn.py new file mode 100644 index 000000000..a1849c9ef --- /dev/null +++ b/src/device/service/drivers/ietf_l2vpn/sdnconn.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +## +# Copyright 2018 University of Bristol - High Performance Networks Research +# Group +# All Rights Reserved. +# +# Contributors: Anderson Bravalheri, Dimitrios Gkounis, Abubakar Siddique +# Muqaddas, Navdeep Uniyal, Reza Nejabati and Dimitra Simeonidou +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: +# +# Neither the name of the University of Bristol nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# This work has been performed in the context of DCMS UK 5G Testbeds +# & Trials Programme and in the framework of the Metro-Haul project - +# funded by the European Commission under Grant number 761727 through the +# Horizon 2020 and 5G-PPP programmes. +## +"""The SDN connector is responsible for establishing both wide area network connectivity (WIM) +and intranet SDN connectivity. + +It receives information from ports to be connected . +""" + +import logging +from http import HTTPStatus + + +class SdnConnectorError(Exception): + """Base Exception for all connector related errors + provide the parameter 'http_code' (int) with the error code: + Bad_Request = 400 + Unauthorized = 401 (e.g. credentials are not valid) + Not_Found = 404 (e.g. try to edit or delete a non existing connectivity service) + Forbidden = 403 + Method_Not_Allowed = 405 + Not_Acceptable = 406 + Request_Timeout = 408 (e.g timeout reaching server, or cannot reach the server) + Conflict = 409 + Service_Unavailable = 503 + Internal_Server_Error = 500 + """ + + def __init__(self, message, http_code=HTTPStatus.INTERNAL_SERVER_ERROR.value): + Exception.__init__(self, message) + self.http_code = http_code + + +class SdnConnectorBase(object): + """Abstract base class for all the SDN connectors + + Arguments: + wim (dict): WIM record, as stored in the database + wim_account (dict): WIM account record, as stored in the database + config + The arguments of the constructor are converted to object attributes. + An extra property, ``service_endpoint_mapping`` is created from ``config``. + """ + + def __init__(self, wim, wim_account, config=None, logger=None): + """ + :param wim: (dict). Contains among others 'wim_url' + :param wim_account: (dict). Contains among others 'uuid' (internal id), 'name', + 'sdn' (True if is intended for SDN-assist or False if intended for WIM), 'user', 'password'. + :param config: (dict or None): Particular information of plugin. These keys if present have a common meaning: + 'mapping_not_needed': (bool) False by default or if missing, indicates that mapping is not needed. + 'service_endpoint_mapping': (list) provides the internal endpoint mapping. The meaning is: + KEY meaning for WIM meaning for SDN assist + -------- -------- -------- + device_id pop_switch_dpid compute_id + device_interface_id pop_switch_port compute_pci_address + service_endpoint_id wan_service_endpoint_id SDN_service_endpoint_id + service_mapping_info wan_service_mapping_info SDN_service_mapping_info + contains extra information if needed. Text in Yaml format + switch_dpid wan_switch_dpid SDN_switch_dpid + switch_port wan_switch_port SDN_switch_port + datacenter_id vim_account vim_account + id: (internal, do not use) + wim_id: (internal, do not use) + :param logger (logging.Logger): optional logger object. If none is passed 'openmano.sdn.sdnconn' is used. + """ + self.logger = logger or logging.getLogger("ro.sdn") + self.wim = wim + self.wim_account = wim_account + self.config = config or {} + self.service_endpoint_mapping = self.config.get("service_endpoint_mapping", []) + + def check_credentials(self): + """Check if the connector itself can access the SDN/WIM with the provided url (wim.wim_url), + user (wim_account.user), and password (wim_account.password) + + Raises: + SdnConnectorError: Issues regarding authorization, access to + external URLs, etc are detected. + """ + raise NotImplementedError + + def get_connectivity_service_status(self, service_uuid, conn_info=None): + """Monitor the status of the connectivity service established + + Arguments: + service_uuid (str): UUID of the connectivity service + conn_info (dict or None): Information returned by the connector + during the service creation/edition and subsequently stored in + the database. + + Returns: + dict: JSON/YAML-serializable dict that contains a mandatory key + ``sdn_status`` associated with one of the following values:: + + {'sdn_status': 'ACTIVE'} + # The service is up and running. + + {'sdn_status': 'INACTIVE'} + # The service was created, but the connector + # cannot determine yet if connectivity exists + # (ideally, the caller needs to wait and check again). + + {'sdn_status': 'DOWN'} + # Connection was previously established, + # but an error/failure was detected. + + {'sdn_status': 'ERROR'} + # An error occurred when trying to create the service/ + # establish the connectivity. + + {'sdn_status': 'BUILD'} + # Still trying to create the service, the caller + # needs to wait and check again. + + Additionally ``error_msg``(**str**) and ``sdn_info``(**dict**) + keys can be used to provide additional status explanation or + new information available for the connectivity service. + """ + raise NotImplementedError + + def create_connectivity_service(self, service_type, connection_points, **kwargs): + """ + Establish SDN/WAN connectivity between the endpoints + :param service_type: (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2), ``L3``. + :param connection_points: (list): each point corresponds to + an entry point to be connected. For WIM: from the DC to the transport network. + For SDN: Compute/PCI to the transport network. One + connection point serves to identify the specific access and + some other service parameters, such as encapsulation type. + Each item of the list is a dict with: + "service_endpoint_id": (str)(uuid) Same meaning that for 'service_endpoint_mapping' (see __init__) + In case the config attribute mapping_not_needed is True, this value is not relevant. In this case + it will contain the string "device_id:device_interface_id" + "service_endpoint_encapsulation_type": None, "dot1q", ... + "service_endpoint_encapsulation_info": (dict) with: + "vlan": ..., (int, present if encapsulation is dot1q) + "vni": ... (int, present if encapsulation is vxlan), + "peers": [(ipv4_1), (ipv4_2)] (present if encapsulation is vxlan) + "mac": ... + "device_id": ..., same meaning that for 'service_endpoint_mapping' (see __init__) + "device_interface_id": same meaning that for 'service_endpoint_mapping' (see __init__) + "switch_dpid": ..., present if mapping has been found for this device_id,device_interface_id + "swith_port": ... present if mapping has been found for this device_id,device_interface_id + "service_mapping_info": present if mapping has been found for this device_id,device_interface_id + :param kwargs: For future versions: + bandwidth (int): value in kilobytes + latency (int): value in milliseconds + Other QoS might be passed as keyword arguments. + :return: tuple: ``(service_id, conn_info)`` containing: + - *service_uuid* (str): UUID of the established connectivity service + - *conn_info* (dict or None): Information to be stored at the database (or ``None``). + This information will be provided to the :meth:`~.edit_connectivity_service` and :obj:`~.delete`. + **MUST** be JSON/YAML-serializable (plain data structures). + :raises: SdnConnectorException: In case of error. Nothing should be created in this case. + Provide the parameter http_code + """ + raise NotImplementedError + + def delete_connectivity_service(self, service_uuid, conn_info=None): + """ + Disconnect multi-site endpoints previously connected + + :param service_uuid: The one returned by create_connectivity_service + :param conn_info: The one returned by last call to 'create_connectivity_service' or 'edit_connectivity_service' + if they do not return None + :return: None + :raises: SdnConnectorException: In case of error. The parameter http_code must be filled + """ + raise NotImplementedError + + def edit_connectivity_service( + self, service_uuid, conn_info=None, connection_points=None, **kwargs + ): + """Change an existing connectivity service. + + This method's arguments and return value follow the same convention as + :meth:`~.create_connectivity_service`. + + :param service_uuid: UUID of the connectivity service. + :param conn_info: (dict or None): Information previously returned by last call to create_connectivity_service + or edit_connectivity_service + :param connection_points: (list): If provided, the old list of connection points will be replaced. + :param kwargs: Same meaning that create_connectivity_service + :return: dict or None: Information to be updated and stored at the database. + When ``None`` is returned, no information should be changed. + When an empty dict is returned, the database record will be deleted. + **MUST** be JSON/YAML-serializable (plain data structures). + Raises: + SdnConnectorException: In case of error. + """ + + def clear_all_connectivity_services(self): + """Delete all WAN Links in a WIM. + + This method is intended for debugging only, and should delete all the + connections controlled by the WIM/SDN, not only the connections that + a specific RO is aware of. + + Raises: + SdnConnectorException: In case of error. + """ + raise NotImplementedError + + def get_all_active_connectivity_services(self): + """Provide information about all active connections provisioned by a + WIM. + + Raises: + SdnConnectorException: In case of error. + """ + raise NotImplementedError -- GitLab From 33ba41e5a9ce7024fccc0e8f71e34ae9d177253c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 20 Feb 2023 16:31:52 +0000 Subject: [PATCH 04/77] Service component - L2NM IETF L2VPN Service Handler: - backup WORK IN PROGRESS --- .../service/service_handlers/__init__.py | 7 + .../L2NM_IETFL2VPN_ServiceHandler.py | 171 ++++++++++++++++++ .../l2nm_ietfl2vpn/__init__.py | 14 ++ 3 files changed, 192 insertions(+) create mode 100644 src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py create mode 100644 src/service/service/service_handlers/l2nm_ietfl2vpn/__init__.py diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index 4c9059779..9b4e442e5 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -21,6 +21,7 @@ from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceH from .p4.p4_service_handler import P4ServiceHandler from .tapi_tapi.TapiServiceHandler import TapiServiceHandler from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler +from .l2nm_ietfl2vpn.L2NM_IETFL2VPN_ServiceHandler import L2NM_IETFL2VPN_ServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ @@ -65,4 +66,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_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py new file mode 100644 index 000000000..1f5817fe8 --- /dev/null +++ b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py @@ -0,0 +1,171 @@ +# 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 + capacity_value = json_settings.get('capacity_value', 50.0) + capacity_unit = json_settings.get('capacity_unit', 'GHz') + layer_proto_name = json_settings.get('layer_proto_name', 'PHOTONIC_MEDIA') + layer_proto_qual = json_settings.get('layer_proto_qual', 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC') + direction = json_settings.get('direction', 'UNIDIRECTIONAL') + + 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]) + + if device_uuid_src != device_uuid_dst: + raise Exception('Diferent 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))) + endpoint_name_src = get_endpoint_matching(device_obj, endpoint_uuid_src).name + endpoint_name_dst = get_endpoint_matching(device_obj, endpoint_uuid_dst).name + + 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, + '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) + 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: + device_uuid_src, _ = get_device_endpoint_uuids(endpoints[0]) + 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') + device_uuid = device_uuid_src + + device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + + 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) + 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 000000000..1549d9811 --- /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. + -- GitLab From 13d9c046d7eb4ad4dde1f6e53aa88337f0f50c3c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 20 Feb 2023 17:26:17 +0000 Subject: [PATCH 05/77] Device component: - Added missing special resource key --- src/device/service/driver_api/_Driver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/device/service/driver_api/_Driver.py b/src/device/service/driver_api/_Driver.py index cc9f7a2c6..947bc8570 100644 --- a/src/device/service/driver_api/_Driver.py +++ b/src/device/service/driver_api/_Driver.py @@ -22,6 +22,7 @@ RESOURCE_ENDPOINTS = '__endpoints__' RESOURCE_INTERFACES = '__interfaces__' RESOURCE_NETWORK_INSTANCES = '__network_instances__' RESOURCE_ROUTING_POLICIES = '__routing_policies__' +RESOURCE_SERVICES = '__services__' RESOURCE_ACL = '__acl__' -- GitLab From 7e2ca0af3dffcc1d2fdccbee39d8b77d7b8f78e2 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 20 Feb 2023 17:26:45 +0000 Subject: [PATCH 06/77] Device component - IETF L2VPN Driver: - added pseudocode - backup WORK IN PROGRESS --- .../drivers/ietf_l2vpn/IetfL2VpnDriver.py | 96 ++++++++++++------- .../service/drivers/ietf_l2vpn/MockOSM.py | 62 ------------ .../ietf_l2vpn/WimconnectorIETFL2VPN.py | 4 +- .../service/drivers/ietf_l2vpn/__init__.py | 14 --- 4 files changed, 63 insertions(+), 113 deletions(-) delete mode 100644 src/device/service/drivers/ietf_l2vpn/MockOSM.py diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py index 3823f569b..609221963 100644 --- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -12,17 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, requests, threading -from requests.auth import HTTPBasicAuth +import logging, threading from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_string, chk_type -from device.service.driver_api._Driver import _Driver -from . import ALL_RESOURCE_KEYS -from .Tools import create_connectivity_service, find_key, config_getter, delete_connectivity_service +from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES +from .Tools import find_key +from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN LOGGER = logging.getLogger(__name__) +ALL_RESOURCE_KEYS = [ + RESOURCE_ENDPOINTS, + RESOURCE_SERVICES, +] + METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'ietf_l2vpn'}) class IetfL2VpnDriver(_Driver): @@ -32,20 +36,17 @@ class IetfL2VpnDriver(_Driver): self.__terminate = threading.Event() username = settings.get('username') password = settings.get('password') - self.__auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None scheme = settings.get('scheme', 'http') - self.__tapi_root = '{:s}://{:s}:{:d}'.format(scheme, address, int(port)) - self.__timeout = int(settings.get('timeout', 120)) + wim = {'wim_url': '{:s}://{:s}:{:d}'.format(scheme, address, int(port))} + wim_account = {'user': username, 'password': password} + config = {'mapping_not_needed': False, 'service_endpoint_mapping': mapping} + self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config) + self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors def Connect(self) -> bool: - url = self.__tapi_root + '/restconf/data/tapi-common:context' with self.__lock: - if self.__started.is_set(): return True try: - requests.get(url, timeout=self.__timeout, verify=False, auth=self.__auth) - except requests.exceptions.Timeout: - LOGGER.exception('Timeout connecting {:s}'.format(str(self.__tapi_root))) - return False + self.wim.check_credentials() except Exception: # pylint: disable=broad-except LOGGER.exception('Exception connecting {:s}'.format(str(self.__tapi_root))) return False @@ -68,36 +69,53 @@ class IetfL2VpnDriver(_Driver): chk_type('resources', resource_keys, list) results = [] with self.__lock: + self.wim.check_credentials() if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS for i, resource_key in enumerate(resource_keys): str_resource_name = 'resource_key[#{:d}]'.format(i) chk_string(str_resource_name, resource_key, allow_empty=False) - results.extend(config_getter( - self.__tapi_root, resource_key, timeout=self.__timeout, auth=self.__auth)) + + if resource_key == RESOURCE_ENDPOINTS: + # return endpoints through debug-api and list-devices method + endpoints = self.debug_api.get_endpoints() + for endpoint in endpoints: results.append(process_endpoint(endpoint)) + elif resource_key == RESOURCE_SERVICES: + # return all services through + services = self.wim.get_all_active_connectivity_services() + for service in services: results.append(process_service(service)) + else: + # assume single-service retrieval + service = self.wim.get_connectivity_service() + results.append(process_service(service)) return results @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: results = [] - if len(resources) == 0: - return results + if len(resources) == 0: return results with self.__lock: + self.wim.check_credentials() for resource in resources: LOGGER.info('resource = {:s}'.format(str(resource))) - input_sip = find_key(resource, 'input_sip') - output_sip = find_key(resource, 'output_sip') uuid = find_key(resource, 'uuid') - capacity_value = find_key(resource, 'capacity_value') - capacity_unit = find_key(resource, 'capacity_unit') - layer_protocol_name = find_key(resource, 'layer_protocol_name') - layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier') - direction = find_key(resource, 'direction') - - data = create_connectivity_service( - self.__tapi_root, uuid, input_sip, output_sip, direction, capacity_value, capacity_unit, - layer_protocol_name, layer_protocol_qualifier, timeout=self.__timeout, auth=self.__auth) - results.extend(data) + #input_sip = find_key(resource, 'input_sip') + #output_sip = find_key(resource, 'output_sip') + #capacity_value = find_key(resource, 'capacity_value') + #capacity_unit = find_key(resource, 'capacity_unit') + #layer_protocol_name = find_key(resource, 'layer_protocol_name') + #layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier') + #direction = find_key(resource, 'direction') + + result = self.wim.get_connectivity_service_status( + service_uuid, conn_info=conn_info) + if service_exists(result): + result = self.wim.create_connectivity_service( + service_type, connection_points) + else: + result = self.wim.edit_connectivity_service( + service_uuid, conn_info=conn_info, connection_points=connection_points) + results.extend(process_result(result)) return results @metered_subclass_method(METRICS_POOL) @@ -105,25 +123,33 @@ class IetfL2VpnDriver(_Driver): results = [] if len(resources) == 0: return results with self.__lock: + self.wim.check_credentials() for resource in resources: LOGGER.info('resource = {:s}'.format(str(resource))) uuid = find_key(resource, 'uuid') - results.extend(delete_connectivity_service( - self.__tapi_root, uuid, timeout=self.__timeout, auth=self.__auth)) + + result = self.wim.get_connectivity_service_status( + service_uuid, conn_info=conn_info) + if service_exists(result): + result = self.wim.delete_connectivity_service( + service_uuid, conn_info=conn_info) + else: + result = False + results.append(process_result(result)) return results @metered_subclass_method(METRICS_POOL) def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: - # TODO: TAPI does not support monitoring by now + # TODO: IETF L2VPN does not support monitoring by now return [False for _ in subscriptions] @metered_subclass_method(METRICS_POOL) def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: - # TODO: TAPI does not support monitoring by now + # TODO: IETF L2VPN does not support monitoring by now return [False for _ in subscriptions] def GetState( self, blocking=False, terminate : Optional[threading.Event] = None ) -> Iterator[Tuple[float, str, Any]]: - # TODO: TAPI does not support monitoring by now + # TODO: IETF L2VPN does not support monitoring by now return [] diff --git a/src/device/service/drivers/ietf_l2vpn/MockOSM.py b/src/device/service/drivers/ietf_l2vpn/MockOSM.py deleted file mode 100644 index 338db0e19..000000000 --- a/src/device/service/drivers/ietf_l2vpn/MockOSM.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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 .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN - -LOGGER = logging.getLogger(__name__) - -class MockOSM: - def __init__(self, url, mapping, username, password): - wim = {'wim_url': url} - wim_account = {'user': username, 'password': password} - config = {'mapping_not_needed': False, 'service_endpoint_mapping': mapping} - self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config) - self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors - - def create_connectivity_service(self, service_type, connection_points): - LOGGER.info('[create_connectivity_service] service_type={:s}'.format(str(service_type))) - LOGGER.info('[create_connectivity_service] connection_points={:s}'.format(str(connection_points))) - self.wim.check_credentials() - result = self.wim.create_connectivity_service(service_type, connection_points) - LOGGER.info('[create_connectivity_service] result={:s}'.format(str(result))) - service_uuid, conn_info = result - self.conn_info[service_uuid] = conn_info - return service_uuid - - def get_connectivity_service_status(self, service_uuid): - LOGGER.info('[get_connectivity_service] service_uuid={:s}'.format(str(service_uuid))) - conn_info = self.conn_info.get(service_uuid) - if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid))) - LOGGER.info('[get_connectivity_service] conn_info={:s}'.format(str(conn_info))) - self.wim.check_credentials() - result = self.wim.get_connectivity_service_status(service_uuid, conn_info=conn_info) - LOGGER.info('[get_connectivity_service] result={:s}'.format(str(result))) - return result - - def edit_connectivity_service(self, service_uuid, connection_points): - LOGGER.info('[edit_connectivity_service] service_uuid={:s}'.format(str(service_uuid))) - LOGGER.info('[edit_connectivity_service] connection_points={:s}'.format(str(connection_points))) - conn_info = self.conn_info.get(service_uuid) - if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid))) - LOGGER.info('[edit_connectivity_service] conn_info={:s}'.format(str(conn_info))) - self.wim.edit_connectivity_service(service_uuid, conn_info=conn_info, connection_points=connection_points) - - def delete_connectivity_service(self, service_uuid): - LOGGER.info('[delete_connectivity_service] service_uuid={:s}'.format(str(service_uuid))) - conn_info = self.conn_info.get(service_uuid) - if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid))) - LOGGER.info('[delete_connectivity_service] conn_info={:s}'.format(str(conn_info))) - self.wim.check_credentials() - self.wim.delete_connectivity_service(service_uuid, conn_info=conn_info) diff --git a/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py index aa4ca045f..b3721b2d5 100644 --- a/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py +++ b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py @@ -55,9 +55,9 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): m["service_endpoint_id"]: m for m in self.service_endpoint_mapping } self.user = wim_account.get("user") - self.passwd = wim_account.get("password") # replace "passwordd" -> "password" + self.passwd = wim_account.get("password") - if self.user and self.passwd is not None: + if self.user is not None and self.passwd is not None: self.auth = (self.user, self.passwd) else: self.auth = None diff --git a/src/device/service/drivers/ietf_l2vpn/__init__.py b/src/device/service/drivers/ietf_l2vpn/__init__.py index 2d3f6df32..38d04994f 100644 --- a/src/device/service/drivers/ietf_l2vpn/__init__.py +++ b/src/device/service/drivers/ietf_l2vpn/__init__.py @@ -11,17 +11,3 @@ # 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. - -from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES - -ALL_RESOURCE_KEYS = [ - RESOURCE_ENDPOINTS, - RESOURCE_INTERFACES, - RESOURCE_NETWORK_INSTANCES, -] - -RESOURCE_KEY_MAPPINGS = { - RESOURCE_ENDPOINTS : 'component', - RESOURCE_INTERFACES : 'interface', - RESOURCE_NETWORK_INSTANCES: 'network_instance', -} -- GitLab From a6ff6475546773c63d009ac73897dde7c775fb0e Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 21 Feb 2023 16:36:09 +0000 Subject: [PATCH 07/77] Device component - IETF L2VPN Driver: - Created placeholders to start implementation --- .../drivers/ietf_l2vpn/IetfL2VpnDriver.py | 57 ++++++++++++++----- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py index 609221963..68b8090e5 100644 --- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -17,16 +17,30 @@ from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_string, chk_type from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES -from .Tools import find_key +from .Tools import connection_point, find_key, wim_mapping from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN LOGGER = logging.getLogger(__name__) +def process_endpoint(method : str, endpoint : Any) -> Any: + LOGGER.warning('[{:s}][process_endpoint] endpoint={:s}'.format(str(method), str(endpoint))) + return endpoint + +def process_connectivity_service(method : str, service : Any) -> Any: + LOGGER.warning('[{:s}][process_connectivity_service] service={:s}'.format(str(method), str(service))) + return service + +def service_exists(param : Any) -> bool: + LOGGER.warning('[service_exists] param={:s}'.format(str(param))) + return False + ALL_RESOURCE_KEYS = [ RESOURCE_ENDPOINTS, RESOURCE_SERVICES, ] +SERVICE_TYPE = 'ELINE' + METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'ietf_l2vpn'}) class IetfL2VpnDriver(_Driver): @@ -39,7 +53,8 @@ class IetfL2VpnDriver(_Driver): scheme = settings.get('scheme', 'http') wim = {'wim_url': '{:s}://{:s}:{:d}'.format(scheme, address, int(port))} wim_account = {'user': username, 'password': password} - config = {'mapping_not_needed': False, 'service_endpoint_mapping': mapping} + # Mapping updated dynamically with each request + config = {'mapping_not_needed': False, 'service_endpoint_mapping': []} self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config) self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors @@ -48,7 +63,7 @@ class IetfL2VpnDriver(_Driver): try: self.wim.check_credentials() except Exception: # pylint: disable=broad-except - LOGGER.exception('Exception connecting {:s}'.format(str(self.__tapi_root))) + LOGGER.exception('Exception checking credentials') return False else: self.__started.set() @@ -78,15 +93,15 @@ class IetfL2VpnDriver(_Driver): if resource_key == RESOURCE_ENDPOINTS: # return endpoints through debug-api and list-devices method endpoints = self.debug_api.get_endpoints() - for endpoint in endpoints: results.append(process_endpoint(endpoint)) + for endpoint in endpoints: results.append(process_endpoint('GetConfig', endpoint)) elif resource_key == RESOURCE_SERVICES: # return all services through services = self.wim.get_all_active_connectivity_services() - for service in services: results.append(process_service(service)) + for service in services: results.append(process_connectivity_service('GetConfig', service)) else: # assume single-service retrieval service = self.wim.get_connectivity_service() - results.append(process_service(service)) + results.append(process_connectivity_service('GetConfig', service)) return results @metered_subclass_method(METRICS_POOL) @@ -98,24 +113,34 @@ class IetfL2VpnDriver(_Driver): for resource in resources: LOGGER.info('resource = {:s}'.format(str(resource))) - uuid = find_key(resource, 'uuid') - #input_sip = find_key(resource, 'input_sip') - #output_sip = find_key(resource, 'output_sip') + service_uuid = find_key(resource, 'uuid') + a_endpoint = find_key(resource, 'a_endpoint') + z_endpoint = find_key(resource, 'z_endpoint') #capacity_value = find_key(resource, 'capacity_value') #capacity_unit = find_key(resource, 'capacity_unit') #layer_protocol_name = find_key(resource, 'layer_protocol_name') #layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier') #direction = find_key(resource, 'direction') + encapsulation_type = find_key(resource, 'encapsulation_type') + vlan_id = find_key(resource, 'vlan_id') + + conn_info = {} result = self.wim.get_connectivity_service_status( service_uuid, conn_info=conn_info) + + connection_points = [] + for endpoint_id in [a_endpoint, z_endpoint]: + site_id = str(endpoint_id) + self.wim.mappings[endpoint_id] = wim_mapping(site_id, endpoint_id) + connection_points.append(connection_point(endpoint_id, encapsulation_type, vlan_id)) if service_exists(result): result = self.wim.create_connectivity_service( - service_type, connection_points) + SERVICE_TYPE, connection_points) else: - result = self.wim.edit_connectivity_service( + self.wim.edit_connectivity_service( service_uuid, conn_info=conn_info, connection_points=connection_points) - results.extend(process_result(result)) + results.extend(process_connectivity_service('SetConfig', None)) return results @metered_subclass_method(METRICS_POOL) @@ -126,16 +151,18 @@ class IetfL2VpnDriver(_Driver): self.wim.check_credentials() for resource in resources: LOGGER.info('resource = {:s}'.format(str(resource))) - uuid = find_key(resource, 'uuid') + service_uuid = find_key(resource, 'uuid') + + conn_info = {} result = self.wim.get_connectivity_service_status( service_uuid, conn_info=conn_info) if service_exists(result): - result = self.wim.delete_connectivity_service( + self.wim.delete_connectivity_service( service_uuid, conn_info=conn_info) else: result = False - results.append(process_result(result)) + results.extend(process_connectivity_service('DeleteConfig', None)) return results @metered_subclass_method(METRICS_POOL) -- GitLab From 7c908a97c049941abdf7a078aa7bf2e0d8ad028a Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 22 Feb 2023 08:22:08 +0000 Subject: [PATCH 08/77] Scenario OFC23 definition - initial partial definition --- TODO.txt | 9 + src/tests/ofc23/.gitignore | 2 + src/tests/ofc23/MultiIngressController.txt | 23 +++ src/tests/ofc23/__init__.py | 14 ++ src/tests/ofc23/delete_all.sh | 24 +++ src/tests/ofc23/deploy_all.sh | 56 ++++++ src/tests/ofc23/deploy_specs_child.sh | 35 ++++ src/tests/ofc23/deploy_specs_parent.sh | 35 ++++ src/tests/ofc23/descriptors/domain1.json | 148 ++++++++++++++++ src/tests/ofc23/descriptors/domain2.json | 166 ++++++++++++++++++ src/tests/ofc23/descriptors/idc-slice.json | 20 +++ src/tests/ofc23/dump_logs.sh | 42 +++++ src/tests/ofc23/fast_redeploy.sh | 63 +++++++ .../ofc23/nginx-ingress-controller-child.yaml | 134 ++++++++++++++ .../nginx-ingress-controller-parent.yaml | 134 ++++++++++++++ src/tests/ofc23/show_deploy.sh | 34 ++++ src/tests/ofc23/tfs-ingress-child.yaml | 53 ++++++ src/tests/ofc23/tfs-ingress-parent.yaml | 53 ++++++ 18 files changed, 1045 insertions(+) create mode 100644 TODO.txt create mode 100644 src/tests/ofc23/.gitignore create mode 100644 src/tests/ofc23/MultiIngressController.txt create mode 100644 src/tests/ofc23/__init__.py create mode 100755 src/tests/ofc23/delete_all.sh create mode 100755 src/tests/ofc23/deploy_all.sh create mode 100755 src/tests/ofc23/deploy_specs_child.sh create mode 100755 src/tests/ofc23/deploy_specs_parent.sh create mode 100644 src/tests/ofc23/descriptors/domain1.json create mode 100644 src/tests/ofc23/descriptors/domain2.json create mode 100644 src/tests/ofc23/descriptors/idc-slice.json create mode 100755 src/tests/ofc23/dump_logs.sh create mode 100755 src/tests/ofc23/fast_redeploy.sh create mode 100644 src/tests/ofc23/nginx-ingress-controller-child.yaml create mode 100644 src/tests/ofc23/nginx-ingress-controller-parent.yaml create mode 100755 src/tests/ofc23/show_deploy.sh create mode 100644 src/tests/ofc23/tfs-ingress-child.yaml create mode 100644 src/tests/ofc23/tfs-ingress-parent.yaml diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 000000000..343d54e28 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,9 @@ +- confirm with Hakim everything is in branch hakim-develop-patch-44885 +- merge branch hakim-develop-patch-44885 into feat/device-ietf-l2vpn +- delete branch hakim-develop-patch-44885 + +- update OFC'23 to deploy 2 instances without blockchain +dom1 => parent +dom2 => child +dom3/4 => remove +replace nfvsdn22 => ofc23 diff --git a/src/tests/ofc23/.gitignore b/src/tests/ofc23/.gitignore new file mode 100644 index 000000000..0a3f4400d --- /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 000000000..190e6df74 --- /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 000000000..1549d9811 --- /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_all.sh b/src/tests/ofc23/delete_all.sh new file mode 100755 index 000000000..6a838d985 --- /dev/null +++ b/src/tests/ofc23/delete_all.sh @@ -0,0 +1,24 @@ +#!/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-dom1 tfs-dom2 tfs-dom3 tfs-dom4 tfs-bchain + +# Delete secondary ingress controllers +kubectl delete -f nfvsdn22/nginx-ingress-controller-dom1.yaml +kubectl delete -f nfvsdn22/nginx-ingress-controller-dom2.yaml +kubectl delete -f nfvsdn22/nginx-ingress-controller-dom3.yaml +kubectl delete -f nfvsdn22/nginx-ingress-controller-dom4.yaml diff --git a/src/tests/ofc23/deploy_all.sh b/src/tests/ofc23/deploy_all.sh new file mode 100755 index 000000000..19ea0e6db --- /dev/null +++ b/src/tests/ofc23/deploy_all.sh @@ -0,0 +1,56 @@ +#!/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-dom1 tfs-dom2 tfs-dom3 tfs-dom4 + +# Delete secondary ingress controllers +kubectl delete -f nfvsdn22/nginx-ingress-controller-dom1.yaml +kubectl delete -f nfvsdn22/nginx-ingress-controller-dom2.yaml +kubectl delete -f nfvsdn22/nginx-ingress-controller-dom3.yaml +kubectl delete -f nfvsdn22/nginx-ingress-controller-dom4.yaml + +# Delete MockBlockchain +#kubectl delete namespace tfs-bchain + +# Create secondary ingress controllers +kubectl apply -f nfvsdn22/nginx-ingress-controller-dom1.yaml +kubectl apply -f nfvsdn22/nginx-ingress-controller-dom2.yaml +kubectl apply -f nfvsdn22/nginx-ingress-controller-dom3.yaml +kubectl apply -f nfvsdn22/nginx-ingress-controller-dom4.yaml + +# Create MockBlockchain +#./deploy_mock_blockchain.sh + +# Deploy TFS for Domain 1 +source nfvsdn22/deploy_specs_dom1.sh +./deploy.sh +mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_dom1.sh + +# Deploy TFS for Domain 2 +source nfvsdn22/deploy_specs_dom2.sh +./deploy.sh +mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_dom2.sh + +# Deploy TFS for Domain 3 +source nfvsdn22/deploy_specs_dom3.sh +./deploy.sh +mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_dom3.sh + +# Deploy TFS for Domain 4 +source nfvsdn22/deploy_specs_dom4.sh +./deploy.sh +mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_dom4.sh diff --git a/src/tests/ofc23/deploy_specs_child.sh b/src/tests/ofc23/deploy_specs_child.sh new file mode 100755 index 000000000..bafec4080 --- /dev/null +++ b/src/tests/ofc23/deploy_specs_child.sh @@ -0,0 +1,35 @@ +# 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. + +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +export TFS_COMPONENTS="context device pathcomp service slice 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 to. +export TFS_K8S_NAMESPACE="tfs-child" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="nfvsdn22/tfs-ingress-child.yaml" + +# Set the neew Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" + +# If not already set, disable skip-build flag. +# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. +export TFS_SKIP_BUILD="YES" diff --git a/src/tests/ofc23/deploy_specs_parent.sh b/src/tests/ofc23/deploy_specs_parent.sh new file mode 100755 index 000000000..5c2816c74 --- /dev/null +++ b/src/tests/ofc23/deploy_specs_parent.sh @@ -0,0 +1,35 @@ +# 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. + +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +export TFS_COMPONENTS="context device pathcomp service slice 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 to. +export TFS_K8S_NAMESPACE="tfs-parent" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="nfvsdn22/tfs-ingress-parent.yaml" + +# Set the neew Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" + +# If not already set, disable skip-build flag. +# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. +export TFS_SKIP_BUILD="NO" diff --git a/src/tests/ofc23/descriptors/domain1.json b/src/tests/ofc23/descriptors/domain1.json new file mode 100644 index 000000000..043b3955f --- /dev/null +++ b/src/tests/ofc23/descriptors/domain1.json @@ -0,0 +1,148 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "D1"}} + ], "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "D1"}}, + "device_ids": [ + {"device_uuid": {"uuid": "DC1"}}, + {"device_uuid": {"uuid": "R1@D1"}}, + {"device_uuid": {"uuid": "R2@D1"}}, + {"device_uuid": {"uuid": "R3@D1"}}, + {"device_uuid": {"uuid": "R4@D1"}}, + {"device_uuid": {"uuid": "R5@D1"}} + ], "link_ids": [ + {"link_uuid": {"uuid": "DC1/D1==R1@D1/DC1"}}, + {"link_uuid": {"uuid": "R1@D1/2==R2@D1/1"}}, + {"link_uuid": {"uuid": "R2@D1/3==R3@D1/2"}}, + {"link_uuid": {"uuid": "R2@D1/5==R5@D1/2"}}, + {"link_uuid": {"uuid": "R3@D1/4==R4@D1/3"}}, + {"link_uuid": {"uuid": "R4@D1/5==R5@D1/4"}}, + {"link_uuid": {"uuid": "R5@D1/1==R1@D1/5"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "DC1"}}, "device_type": "emu-datacenter", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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/border", "uuid": "D1"}, + {"sample_types": [], "type": "copper/internal", "uuid": "int"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R1@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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": "2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "5"}, + {"sample_types": [], "type": "copper/border", "uuid": "DC1"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R2@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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"}, + {"sample_types": [], "type": "copper/internal", "uuid": "3"}, + {"sample_types": [], "type": "copper/internal", "uuid": "5"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R3@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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": "2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "4"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R4@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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": "3"}, + {"sample_types": [], "type": "copper/internal", "uuid": "5"}, + {"sample_types": [], "type": "copper/border", "uuid": "D2"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R5@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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"}, + {"sample_types": [], "type": "copper/internal", "uuid": "2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "4"}, + {"sample_types": [], "type": "copper/border", "uuid": "D3"} + ]}}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "DC1/D1==R1@D1/DC1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "D1"}}, + {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "DC1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R1@D1/2==R2@D1/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "2"}}, + {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2@D1/3==R3@D1/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "3"}}, + {"device_id": {"device_uuid": {"uuid": "R3@D1"}}, "endpoint_uuid": {"uuid": "2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2@D1/5==R5@D1/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "5"}}, + {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3@D1/4==R4@D1/3"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3@D1"}}, "endpoint_uuid": {"uuid": "4"}}, + {"device_id": {"device_uuid": {"uuid": "R4@D1"}}, "endpoint_uuid": {"uuid": "3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R4@D1/5==R5@D1/4"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R4@D1"}}, "endpoint_uuid": {"uuid": "5"}}, + {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R5@D1/1==R1@D1/5"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "1"}}, + {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "5"}} + ] + } + ] +} diff --git a/src/tests/ofc23/descriptors/domain2.json b/src/tests/ofc23/descriptors/domain2.json new file mode 100644 index 000000000..81d397abf --- /dev/null +++ b/src/tests/ofc23/descriptors/domain2.json @@ -0,0 +1,166 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "D2"}} + ], "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "D2"}}, + "device_ids": [ + {"device_uuid": {"uuid": "R1@D2"}}, + {"device_uuid": {"uuid": "R2@D2"}}, + {"device_uuid": {"uuid": "R3@D2"}}, + {"device_uuid": {"uuid": "R4@D2"}}, + {"device_uuid": {"uuid": "R5@D2"}}, + {"device_uuid": {"uuid": "R6@D2"}} + ], "link_ids": [ + {"link_uuid": {"uuid": "R1@D2/2==R2@D2/1"}}, + {"link_uuid": {"uuid": "R1@D2/6==R6@D2/1"}}, + {"link_uuid": {"uuid": "R1@D2/5==R5@D2/1"}}, + {"link_uuid": {"uuid": "R2@D2/3==R3@D2/2"}}, + {"link_uuid": {"uuid": "R2@D2/4==R4@D2/2"}}, + {"link_uuid": {"uuid": "R2@D2/5==R5@D2/2"}}, + {"link_uuid": {"uuid": "R2@D2/6==R6@D2/2"}}, + {"link_uuid": {"uuid": "R3@D2/6==R6@D2/3"}}, + {"link_uuid": {"uuid": "R4@D2/5==R5@D2/4"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "R1@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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": "2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "5"}, + {"sample_types": [], "type": "copper/internal", "uuid": "6"}, + {"sample_types": [], "type": "copper/border", "uuid": "D1"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R2@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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"}, + {"sample_types": [], "type": "copper/internal", "uuid": "3"}, + {"sample_types": [], "type": "copper/internal", "uuid": "4"}, + {"sample_types": [], "type": "copper/internal", "uuid": "5"}, + {"sample_types": [], "type": "copper/internal", "uuid": "6"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R3@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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": "2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "6"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R4@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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": "2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "5"}, + {"sample_types": [], "type": "copper/border", "uuid": "D4"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R5@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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"}, + {"sample_types": [], "type": "copper/internal", "uuid": "2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "4"}, + {"sample_types": [], "type": "copper/border", "uuid": "D3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R6@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 2, "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"}, + {"sample_types": [], "type": "copper/internal", "uuid": "2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "3"} + ]}}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "R1@D2/2==R2@D2/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1@D2"}}, "endpoint_uuid": {"uuid": "2"}}, + {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R1@D2/6==R6@D2/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1@D2"}}, "endpoint_uuid": {"uuid": "6"}}, + {"device_id": {"device_uuid": {"uuid": "R6@D2"}}, "endpoint_uuid": {"uuid": "1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R1@D2/5==R5@D2/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1@D2"}}, "endpoint_uuid": {"uuid": "5"}}, + {"device_id": {"device_uuid": {"uuid": "R5@D2"}}, "endpoint_uuid": {"uuid": "1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2@D2/3==R3@D2/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "3"}}, + {"device_id": {"device_uuid": {"uuid": "R3@D2"}}, "endpoint_uuid": {"uuid": "2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2@D2/4==R4@D2/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "4"}}, + {"device_id": {"device_uuid": {"uuid": "R4@D2"}}, "endpoint_uuid": {"uuid": "2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2@D2/5==R5@D2/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "5"}}, + {"device_id": {"device_uuid": {"uuid": "R5@D2"}}, "endpoint_uuid": {"uuid": "2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2@D2/6==R6@D2/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "6"}}, + {"device_id": {"device_uuid": {"uuid": "R6@D2"}}, "endpoint_uuid": {"uuid": "2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3@D2/6==R6@D2/3"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3@D2"}}, "endpoint_uuid": {"uuid": "6"}}, + {"device_id": {"device_uuid": {"uuid": "R6@D2"}}, "endpoint_uuid": {"uuid": "3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R4@D2/5==R5@D2/4"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R4@D2"}}, "endpoint_uuid": {"uuid": "5"}}, + {"device_id": {"device_uuid": {"uuid": "R5@D2"}}, "endpoint_uuid": {"uuid": "4"}} + ] + } + ] +} diff --git a/src/tests/ofc23/descriptors/idc-slice.json b/src/tests/ofc23/descriptors/idc-slice.json new file mode 100644 index 000000000..634209284 --- /dev/null +++ b/src/tests/ofc23/descriptors/idc-slice.json @@ -0,0 +1,20 @@ +{ + "slices":[ + { + "slice_id":{"context_id":{"context_uuid":{"uuid":"admin"}},"slice_uuid":{"uuid":"idc-slice"}}, + "slice_endpoint_ids":[ + {"device_id":{"device_uuid":{"uuid":"DC1"}},"endpoint_uuid":{"uuid":"int"}}, + {"device_id":{"device_uuid":{"uuid":"DC2"}},"endpoint_uuid":{"uuid":"int"}} + ], + "slice_status":{"slice_status":1}, + "slice_service_ids":[], + "slice_subslice_ids":[], + "slice_constraints":[], + "slice_config":{"config_rules":[ + {"action":1,"custom":{"resource_key":"/settings","resource_value":"{}"}}, + {"action":1,"custom":{"resource_key":"/device[DC1]/endpoint[int]/settings","resource_value":"{}"}}, + {"action":1,"custom":{"resource_key":"/device[DC2]/endpoint[int]/settings","resource_value":"{}"}} + ]} + } + ] +} diff --git a/src/tests/ofc23/dump_logs.sh b/src/tests/ofc23/dump_logs.sh new file mode 100755 index 000000000..533879c2a --- /dev/null +++ b/src/tests/ofc23/dump_logs.sh @@ -0,0 +1,42 @@ +#!/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 +kubectl --namespace tfs-child logs deployments/interdomainservice server > tmp/exec/child/interdomain.log +kubectl --namespace tfs-child logs deployments/dltservice connector > tmp/exec/child/dlt-connector.log +kubectl --namespace tfs-child logs deployments/dltservice gateway > tmp/exec/child/dlt-gateway.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 000000000..3c942a799 --- /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 nfvsdn22/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 nfvsdn22/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 nfvsdn22/tfs-ingress-parent.yaml > ./tmp/logs/deploy-tfs-parent.log +printf "\n" + +echo "Deploying tfs-child ..." +kubectl delete -f nfvsdn22/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 nfvsdn22/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 nfvsdn22/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 000000000..00a64d75e --- /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 000000000..c504c2e67 --- /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 000000000..d4e112b0f --- /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/tfs-ingress-child.yaml b/src/tests/ofc23/tfs-ingress-child.yaml new file mode 100644 index 000000000..a93b9321c --- /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 000000000..baf506dd9 --- /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 -- GitLab From 76c911ac8ac1cbad6005c83cc4cef17cebf0860a Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 22 Feb 2023 16:00:38 +0000 Subject: [PATCH 09/77] Deploy Scripts: - Added settings to define custom port numbers where dependencies should be exposed. - Corrected order of commands in dependency deploy scripts - Extended NATS deployment to support multiple instances - Extended TFS deployment to support configuration of multiple Grafana instances - Removed constraint of installing monitoring component to deploy Grafana - Updated example my_deploy.sh --- deploy/all.sh | 21 +++++++++++++++++++ deploy/crdb.sh | 57 ++++++++++++++++++++++++++++++-------------------- deploy/nats.sh | 36 ++++++++++++++++++------------- deploy/qdb.sh | 40 ++++++++++++++++++++++------------- deploy/tfs.sh | 31 ++++++++++++++++++++++++--- my_deploy.sh | 25 ++++++++++++++++++++-- 6 files changed, 153 insertions(+), 57 deletions(-) diff --git a/deploy/all.sh b/deploy/all.sh index 6f5592cb4..6f8331b76 100755 --- a/deploy/all.sh +++ b/deploy/all.sh @@ -51,6 +51,12 @@ export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-""} # If not already set, set the namespace where CockroackDB will be deployed. export CRDB_NAMESPACE=${CRDB_NAMESPACE:-"crdb"} +# If not already set, set the external port CockroackDB Postgre SQL interface will be exposed to. +export CRDB_EXT_PORT_SQL=${CRDB_EXT_PORT_SQL:-"26257"} + +# If not already set, set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to. +export CRDB_EXT_PORT_HTTP=${CRDB_EXT_PORT_HTTP:-"8081"} + # If not already set, set the database username to be used by Context. export CRDB_USERNAME=${CRDB_USERNAME:-"tfs"} @@ -90,6 +96,12 @@ export CRDB_REDEPLOY=${CRDB_REDEPLOY:-""} # If not already set, set the namespace where NATS will be deployed. export NATS_NAMESPACE=${NATS_NAMESPACE:-"nats"} +# If not already set, set the external port NATS Client interface will be exposed to. +export NATS_EXT_PORT_CLIENT=${NATS_EXT_PORT_CLIENT:-"4222"} + +# If not already set, set the external port NATS HTTP Mgmt GUI interface will be exposed to. +export NATS_EXT_PORT_HTTP=${NATS_EXT_PORT_HTTP:-"8222"} + # If not already set, disable flag for re-deploying NATS from scratch. # WARNING: ACTIVATING THIS FLAG IMPLIES LOOSING THE MESSAGE BROKER INFORMATION! # If NATS_REDEPLOY is "YES", the message broker will be dropped while checking/deploying NATS. @@ -101,6 +113,15 @@ export NATS_REDEPLOY=${NATS_REDEPLOY:-""} # If not already set, set the namespace where QuestDB will be deployed. export QDB_NAMESPACE=${QDB_NAMESPACE:-"qdb"} +# If not already set, set the external port QuestDB Postgre SQL interface will be exposed to. +export QDB_EXT_PORT_SQL=${QDB_EXT_PORT_SQL:-"8812"} + +# If not already set, set the external port QuestDB Influx Line Protocol interface will be exposed to. +export QDB_EXT_PORT_ILP=${QDB_EXT_PORT_ILP:-"9009"} + +# If not already set, set the external port QuestDB HTTP Mgmt GUI interface will be exposed to. +export QDB_EXT_PORT_HTTP=${QDB_EXT_PORT_HTTP:-"9000"} + # If not already set, set the database username to be used for QuestDB. export QDB_USERNAME=${QDB_USERNAME:-"admin"} diff --git a/deploy/crdb.sh b/deploy/crdb.sh index 4e8cfe2c3..216339117 100755 --- a/deploy/crdb.sh +++ b/deploy/crdb.sh @@ -21,6 +21,12 @@ # If not already set, set the namespace where CockroackDB will be deployed. export CRDB_NAMESPACE=${CRDB_NAMESPACE:-"crdb"} +# If not already set, set the external port CockroackDB Postgre SQL interface will be exposed to. +export CRDB_EXT_PORT_SQL=${CRDB_EXT_PORT_SQL:-"26257"} + +# If not already set, set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to. +export CRDB_EXT_PORT_HTTP=${CRDB_EXT_PORT_HTTP:-"8081"} + # If not already set, set the database username to be used by Context. export CRDB_USERNAME=${CRDB_USERNAME:-"tfs"} @@ -109,24 +115,23 @@ function crdb_deploy_single() { echo echo "CockroachDB Port Mapping" - echo ">>> Expose CockroachDB SQL port (26257->26257)" - CRDB_SQL_PORT=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') - PATCH='{"data": {"'${CRDB_SQL_PORT}'": "'${CRDB_NAMESPACE}'/cockroachdb-public:'${CRDB_SQL_PORT}'"}}' + echo ">>> Expose CockroachDB SQL port (26257->${CRDB_EXT_PORT_SQL})" + CRDB_PORT_SQL=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') + PATCH='{"data": {"'${CRDB_EXT_PORT_SQL}'": "'${CRDB_NAMESPACE}'/cockroachdb-public:'${CRDB_PORT_SQL}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${CRDB_SQL_PORT}', "hostPort": '${CRDB_SQL_PORT}'}' + PORT_MAP='{"containerPort": '${CRDB_EXT_PORT_SQL}', "hostPort": '${CRDB_EXT_PORT_SQL}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" echo - echo ">>> Expose CockroachDB HTTP Mgmt GUI port (8080->8081)" - CRDB_GUI_PORT_EXT="8081" - CRDB_GUI_PORT=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') - PATCH='{"data": {"'${CRDB_GUI_PORT_EXT}'": "'${CRDB_NAMESPACE}'/cockroachdb-public:'${CRDB_GUI_PORT}'"}}' + echo ">>> Expose CockroachDB HTTP Mgmt GUI port (8080->${CRDB_EXT_PORT_HTTP})" + CRDB_PORT_HTTP=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') + PATCH='{"data": {"'${CRDB_EXT_PORT_HTTP}'": "'${CRDB_NAMESPACE}'/cockroachdb-public:'${CRDB_PORT_HTTP}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${CRDB_GUI_PORT_EXT}', "hostPort": '${CRDB_GUI_PORT_EXT}'}' + PORT_MAP='{"containerPort": '${CRDB_EXT_PORT_HTTP}', "hostPort": '${CRDB_EXT_PORT_HTTP}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" @@ -153,7 +158,8 @@ function crdb_undeploy_single() { function crdb_drop_database_single() { echo "Drop database if exists" - CRDB_CLIENT_URL="postgresql://${CRDB_USERNAME}:${CRDB_PASSWORD}@cockroachdb-0:${CRDB_SQL_PORT}/defaultdb?sslmode=require" + CRDB_PORT_SQL=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') + CRDB_CLIENT_URL="postgresql://${CRDB_USERNAME}:${CRDB_PASSWORD}@cockroachdb-0:${CRDB_PORT_SQL}/defaultdb?sslmode=require" kubectl exec -it --namespace ${CRDB_NAMESPACE} cockroachdb-0 -- \ ./cockroach sql --certs-dir=/cockroach/cockroach-certs --url=${CRDB_CLIENT_URL} \ --execute "DROP DATABASE IF EXISTS ${CRDB_DATABASE};" @@ -263,24 +269,23 @@ function crdb_deploy_cluster() { echo echo "CockroachDB Port Mapping" - echo ">>> Expose CockroachDB SQL port (26257)" - CRDB_SQL_PORT=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') - PATCH='{"data": {"'${CRDB_SQL_PORT}'": "'${CRDB_NAMESPACE}'/cockroachdb-public:'${CRDB_SQL_PORT}'"}}' + echo ">>> Expose CockroachDB SQL port (26257->${CRDB_EXT_PORT_SQL})" + CRDB_PORT_SQL=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') + PATCH='{"data": {"'${CRDB_EXT_PORT_SQL}'": "'${CRDB_NAMESPACE}'/cockroachdb-public:'${CRDB_PORT_SQL}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${CRDB_SQL_PORT}', "hostPort": '${CRDB_SQL_PORT}'}' + PORT_MAP='{"containerPort": '${CRDB_EXT_PORT_SQL}', "hostPort": '${CRDB_EXT_PORT_SQL}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" echo - echo ">>> Expose CockroachDB HTTP Mgmt GUI port (8080->8081)" - CRDB_GUI_PORT_EXT="8081" - CRDB_GUI_PORT=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') - PATCH='{"data": {"'${CRDB_GUI_PORT_EXT}'": "'${CRDB_NAMESPACE}'/cockroachdb-public:'${CRDB_GUI_PORT}'"}}' + echo ">>> Expose CockroachDB HTTP Mgmt GUI port (8080->${CRDB_EXT_PORT_HTTP})" + CRDB_PORT_HTTP=$(kubectl --namespace ${CRDB_NAMESPACE} get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') + PATCH='{"data": {"'${CRDB_EXT_PORT_HTTP}'": "'${CRDB_NAMESPACE}'/cockroachdb-public:'${CRDB_PORT_HTTP}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${CRDB_GUI_PORT_EXT}', "hostPort": '${CRDB_GUI_PORT_EXT}'}' + PORT_MAP='{"containerPort": '${CRDB_EXT_PORT_HTTP}', "hostPort": '${CRDB_EXT_PORT_HTTP}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" @@ -342,17 +347,23 @@ function crdb_drop_database_cluster() { if [ "$CRDB_DEPLOY_MODE" == "single" ]; then if [ "$CRDB_REDEPLOY" == "YES" ]; then crdb_undeploy_single - elif [ "$CRDB_DROP_DATABASE_IF_EXISTS" == "YES" ]; then - crdb_drop_database_single fi + crdb_deploy_single + + if [ "$CRDB_DROP_DATABASE_IF_EXISTS" == "YES" ]; then + crdb_drop_database_single + fi elif [ "$CRDB_DEPLOY_MODE" == "cluster" ]; then if [ "$CRDB_REDEPLOY" == "YES" ]; then crdb_undeploy_cluster - elif [ "$CRDB_DROP_DATABASE_IF_EXISTS" == "YES" ]; then - crdb_drop_database_cluster fi + crdb_deploy_cluster + + if [ "$CRDB_DROP_DATABASE_IF_EXISTS" == "YES" ]; then + crdb_drop_database_cluster + fi else echo "Unsupported value: CRDB_DEPLOY_MODE=$CRDB_DEPLOY_MODE" fi diff --git a/deploy/nats.sh b/deploy/nats.sh index 9edbc7765..aa082b54b 100755 --- a/deploy/nats.sh +++ b/deploy/nats.sh @@ -21,6 +21,12 @@ # If not already set, set the namespace where NATS will be deployed. export NATS_NAMESPACE=${NATS_NAMESPACE:-"nats"} +# If not already set, set the external port NATS Client interface will be exposed to. +export NATS_EXT_PORT_CLIENT=${NATS_EXT_PORT_CLIENT:-"4222"} + +# If not already set, set the external port NATS HTTP Mgmt GUI interface will be exposed to. +export NATS_EXT_PORT_HTTP=${NATS_EXT_PORT_HTTP:-"8222"} + # If not already set, disable flag for re-deploying NATS from scratch. # WARNING: ACTIVATING THIS FLAG IMPLIES LOOSING THE MESSAGE BROKER INFORMATION! # If NATS_REDEPLOY is "YES", the message broker will be dropped while checking/deploying NATS. @@ -43,14 +49,14 @@ function nats_deploy_single() { echo "Install NATS (single-node)" echo ">>> Checking if NATS is deployed..." - if kubectl get --namespace ${NATS_NAMESPACE} statefulset/nats &> /dev/null; then + if kubectl get --namespace ${NATS_NAMESPACE} statefulset/${NATS_NAMESPACE} &> /dev/null; then echo ">>> NATS is present; skipping step." else echo ">>> Deploy NATS" - helm3 install nats nats/nats --namespace ${NATS_NAMESPACE} --set nats.image.tag=2.9-alpine + helm3 install ${NATS_NAMESPACE} nats/nats --namespace ${NATS_NAMESPACE} --set nats.image.tag=2.9-alpine echo ">>> Waiting NATS statefulset to be created..." - while ! kubectl get --namespace ${NATS_NAMESPACE} statefulset/nats &> /dev/null; do + while ! kubectl get --namespace ${NATS_NAMESPACE} statefulset/${NATS_NAMESPACE} &> /dev/null; do printf "%c" "." sleep 1 done @@ -64,32 +70,32 @@ function nats_deploy_single() { #kubectl wait --namespace ${NATS_NAMESPACE} --for=jsonpath='{.status.readyReplicas}'=3 --timeout=300s \ # statefulset/nats echo ">>> NATS statefulset created. Waiting NATS pods to be created..." - while ! kubectl get --namespace ${NATS_NAMESPACE} pod/nats-0 &> /dev/null; do + while ! kubectl get --namespace ${NATS_NAMESPACE} pod/${NATS_NAMESPACE}-0 &> /dev/null; do printf "%c" "." sleep 1 done - kubectl wait --namespace ${NATS_NAMESPACE} --for=condition=Ready --timeout=300s pod/nats-0 + kubectl wait --namespace ${NATS_NAMESPACE} --for=condition=Ready --timeout=300s pod/${NATS_NAMESPACE}-0 fi echo echo "NATS Port Mapping" - echo ">>> Expose NATS Client port (4222)" - NATS_CLIENT_PORT=$(kubectl --namespace ${NATS_NAMESPACE} get service nats -o 'jsonpath={.spec.ports[?(@.name=="client")].port}') - PATCH='{"data": {"'${NATS_CLIENT_PORT}'": "'${NATS_NAMESPACE}'/nats:'${NATS_CLIENT_PORT}'"}}' + echo ">>> Expose NATS Client port (4222->${NATS_EXT_PORT_CLIENT})" + NATS_PORT_CLIENT=$(kubectl --namespace ${NATS_NAMESPACE} get service ${NATS_NAMESPACE} -o 'jsonpath={.spec.ports[?(@.name=="client")].port}') + PATCH='{"data": {"'${NATS_EXT_PORT_CLIENT}'": "'${NATS_NAMESPACE}'/'${NATS_NAMESPACE}':'${NATS_PORT_CLIENT}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${NATS_CLIENT_PORT}', "hostPort": '${NATS_CLIENT_PORT}'}' + PORT_MAP='{"containerPort": '${NATS_EXT_PORT_CLIENT}', "hostPort": '${NATS_EXT_PORT_CLIENT}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" echo - echo ">>> Expose NATS HTTP Mgmt GUI port (8222)" - NATS_GUI_PORT=$(kubectl --namespace ${NATS_NAMESPACE} get service nats -o 'jsonpath={.spec.ports[?(@.name=="monitor")].port}') - PATCH='{"data": {"'${NATS_GUI_PORT}'": "'${NATS_NAMESPACE}'/nats:'${NATS_GUI_PORT}'"}}' + echo ">>> Expose NATS HTTP Mgmt GUI port (8222->${NATS_EXT_PORT_HTTP})" + NATS_PORT_HTTP=$(kubectl --namespace ${NATS_NAMESPACE} get service ${NATS_NAMESPACE} -o 'jsonpath={.spec.ports[?(@.name=="monitor")].port}') + PATCH='{"data": {"'${NATS_EXT_PORT_HTTP}'": "'${NATS_NAMESPACE}'/'${NATS_NAMESPACE}':'${NATS_PORT_HTTP}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${NATS_GUI_PORT}', "hostPort": '${NATS_GUI_PORT}'}' + PORT_MAP='{"containerPort": '${NATS_EXT_PORT_HTTP}', "hostPort": '${NATS_EXT_PORT_HTTP}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" @@ -99,9 +105,9 @@ function nats_deploy_single() { function nats_undeploy_single() { echo "NATS" echo ">>> Checking if NATS is deployed..." - if kubectl get --namespace ${NATS_NAMESPACE} statefulset/nats &> /dev/null; then + if kubectl get --namespace ${NATS_NAMESPACE} statefulset/${NATS_NAMESPACE} &> /dev/null; then echo ">>> Undeploy NATS" - helm3 uninstall --namespace ${NATS_NAMESPACE} nats + helm3 uninstall --namespace ${NATS_NAMESPACE} ${NATS_NAMESPACE} else echo ">>> NATS is not present; skipping step." fi diff --git a/deploy/qdb.sh b/deploy/qdb.sh index d94c000bf..cba8a5c00 100755 --- a/deploy/qdb.sh +++ b/deploy/qdb.sh @@ -21,6 +21,15 @@ # If not already set, set the namespace where QuestDB will be deployed. export QDB_NAMESPACE=${QDB_NAMESPACE:-"qdb"} +# If not already set, set the external port QuestDB Postgre SQL interface will be exposed to. +export QDB_EXT_PORT_SQL=${QDB_EXT_PORT_SQL:-"8812"} + +# If not already set, set the external port QuestDB Influx Line Protocol interface will be exposed to. +export QDB_EXT_PORT_ILP=${QDB_EXT_PORT_ILP:-"9009"} + +# If not already set, set the external port QuestDB HTTP Mgmt GUI interface will be exposed to. +export QDB_EXT_PORT_HTTP=${QDB_EXT_PORT_HTTP:-"9000"} + # If not already set, set the database username to be used for QuestDB. export QDB_USERNAME=${QDB_USERNAME:-"admin"} @@ -96,34 +105,34 @@ function qdb_deploy() { echo echo "QuestDB Port Mapping" - echo ">>> Expose QuestDB SQL port (8812->8812)" - QDB_SQL_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') - PATCH='{"data": {"'${QDB_SQL_PORT}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_SQL_PORT}'"}}' + echo ">>> Expose QuestDB SQL port (8812->${QDB_EXT_PORT_SQL})" + QDB_PORT_SQL=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') + PATCH='{"data": {"'${QDB_EXT_PORT_SQL}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_PORT_SQL}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${QDB_SQL_PORT}', "hostPort": '${QDB_SQL_PORT}'}' + PORT_MAP='{"containerPort": '${QDB_EXT_PORT_SQL}', "hostPort": '${QDB_EXT_PORT_SQL}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" echo - echo ">>> Expose QuestDB Influx Line Protocol port (9009->9009)" - QDB_ILP_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="ilp")].port}') - PATCH='{"data": {"'${QDB_ILP_PORT}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_ILP_PORT}'"}}' + echo ">>> Expose QuestDB Influx Line Protocol port (9009->${QDB_EXT_PORT_ILP})" + QDB_PORT_ILP=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="ilp")].port}') + PATCH='{"data": {"'${QDB_EXT_PORT_ILP}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_PORT_ILP}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${QDB_ILP_PORT}', "hostPort": '${QDB_ILP_PORT}'}' + PORT_MAP='{"containerPort": '${QDB_EXT_PORT_ILP}', "hostPort": '${QDB_EXT_PORT_ILP}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" echo - echo ">>> Expose QuestDB HTTP Mgmt GUI port (9000->9000)" - QDB_GUI_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') - PATCH='{"data": {"'${QDB_GUI_PORT}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_GUI_PORT}'"}}' + echo ">>> Expose QuestDB HTTP Mgmt GUI port (9000->${QDB_EXT_PORT_HTTP})" + QDB_PORT_HTTP=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') + PATCH='{"data": {"'${QDB_EXT_PORT_HTTP}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_PORT_HTTP}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" - PORT_MAP='{"containerPort": '${QDB_GUI_PORT}', "hostPort": '${QDB_GUI_PORT}'}' + PORT_MAP='{"containerPort": '${QDB_EXT_PORT_HTTP}', "hostPort": '${QDB_EXT_PORT_HTTP}'}' CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" @@ -161,7 +170,10 @@ function qdb_drop_tables() { if [ "$QDB_REDEPLOY" == "YES" ]; then qdb_undeploy -elif [ "$QDB_DROP_TABLES_IF_EXIST" == "YES" ]; then - qdb_drop_tables fi + qdb_deploy + +if [ "$QDB_DROP_TABLES_IF_EXIST" == "YES" ]; then + qdb_drop_tables +fi diff --git a/deploy/tfs.sh b/deploy/tfs.sh index 16cf5c13b..4c6dc95d2 100755 --- a/deploy/tfs.sh +++ b/deploy/tfs.sh @@ -51,6 +51,12 @@ export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-""} # If not already set, set the namespace where CockroackDB will be deployed. export CRDB_NAMESPACE=${CRDB_NAMESPACE:-"crdb"} +# If not already set, set the external port CockroackDB Postgre SQL interface will be exposed to. +export CRDB_EXT_PORT_SQL=${CRDB_EXT_PORT_SQL:-"26257"} + +# If not already set, set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to. +export CRDB_EXT_PORT_HTTP=${CRDB_EXT_PORT_HTTP:-"8081"} + # If not already set, set the database username to be used by Context. export CRDB_USERNAME=${CRDB_USERNAME:-"tfs"} @@ -66,12 +72,27 @@ export CRDB_DATABASE=${CRDB_DATABASE:-"tfs"} # If not already set, set the namespace where NATS will be deployed. export NATS_NAMESPACE=${NATS_NAMESPACE:-"nats"} +# If not already set, set the external port NATS Client interface will be exposed to. +export NATS_EXT_PORT_CLIENT=${NATS_EXT_PORT_CLIENT:-"4222"} + +# If not already set, set the external port NATS HTTP Mgmt GUI interface will be exposed to. +export NATS_EXT_PORT_HTTP=${NATS_EXT_PORT_HTTP:-"8222"} + # ----- QuestDB ---------------------------------------------------------------- # If not already set, set the namespace where QuestDB will be deployed. export QDB_NAMESPACE=${QDB_NAMESPACE:-"qdb"} +# If not already set, set the external port QuestDB Postgre SQL interface will be exposed to. +export QDB_EXT_PORT_SQL=${QDB_EXT_PORT_SQL:-"8812"} + +# If not already set, set the external port QuestDB Influx Line Protocol interface will be exposed to. +export QDB_EXT_PORT_ILP=${QDB_EXT_PORT_ILP:-"9009"} + +# If not already set, set the external port QuestDB HTTP Mgmt GUI interface will be exposed to. +export QDB_EXT_PORT_HTTP=${QDB_EXT_PORT_HTTP:-"9000"} + # If not already set, set the database username to be used for QuestDB. export QDB_USERNAME=${QDB_USERNAME:-"admin"} @@ -116,7 +137,7 @@ kubectl create secret generic crdb-data --namespace ${TFS_K8S_NAMESPACE} --type= printf "\n" echo "Create secret with NATS data" -NATS_CLIENT_PORT=$(kubectl --namespace ${NATS_NAMESPACE} get service nats -o 'jsonpath={.spec.ports[?(@.name=="client")].port}') +NATS_CLIENT_PORT=$(kubectl --namespace ${NATS_NAMESPACE} get service ${NATS_NAMESPACE} -o 'jsonpath={.spec.ports[?(@.name=="client")].port}') kubectl create secret generic nats-data --namespace ${TFS_K8S_NAMESPACE} --type='Opaque' \ --from-literal=NATS_NAMESPACE=${NATS_NAMESPACE} \ --from-literal=NATS_CLIENT_PORT=${NATS_CLIENT_PORT} @@ -303,12 +324,16 @@ for COMPONENT in $TFS_COMPONENTS; do printf "\n" done -if [[ "$TFS_COMPONENTS" == *"webui"* ]] && [[ "$TFS_COMPONENTS" == *"monitoring"* ]]; then +if [[ "$TFS_COMPONENTS" == *"webui"* ]]; then echo "Configuring WebUI DataStores and Dashboards..." sleep 5 + INGRESS_CTRL_NAME=$(echo "${TFS_K8S_NAMESPACE}" | sed "s/tfs/nginx-ingress-microk8s-controller/g") + EXT_HTTP_PORT=$(kubectl get daemonsets.apps --namespace ingress ${INGRESS_CTRL_NAME} \ + -o 'jsonpath={.spec.template.spec.containers[?(@.name=="nginx-ingress-microk8s")].ports[?(@.name=="http")].hostPort}') + # Exposed through the ingress controller "tfs-ingress" - GRAFANA_URL="127.0.0.1:80/grafana" + GRAFANA_URL="127.0.0.1:${EXT_HTTP_PORT}/grafana" # Default Grafana credentials GRAFANA_USERNAME="admin" diff --git a/my_deploy.sh b/my_deploy.sh index 6a360812b..22a7ae815 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -43,6 +43,12 @@ export TFS_SKIP_BUILD="" # 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" @@ -57,7 +63,7 @@ export CRDB_DATABASE="tfs" export CRDB_DEPLOY_MODE="single" # Disable flag for dropping database, if it exists. -export CRDB_DROP_DATABASE_IF_EXISTS="YES" +export CRDB_DROP_DATABASE_IF_EXISTS="" # Disable flag for re-deploying CockroachDB from scratch. export CRDB_REDEPLOY="" @@ -68,6 +74,12 @@ export CRDB_REDEPLOY="" # Set the namespace where NATS will be deployed. export NATS_NAMESPACE="nats" +# 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="" @@ -77,6 +89,15 @@ export NATS_REDEPLOY="" # Set the namespace where QuestDB will be deployed. export QDB_NAMESPACE="qdb" +# 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="9009" + +# 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" @@ -90,7 +111,7 @@ export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis" export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups" # Disable flag for dropping tables if they exist. -export QDB_DROP_TABLES_IF_EXIST="YES" +export QDB_DROP_TABLES_IF_EXIST="" # Disable flag for re-deploying QuestDB from scratch. export QDB_REDEPLOY="" -- GitLab From 7aad02e4c343f1f2f225559a2a75c3bf30c0046c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 22 Feb 2023 16:14:54 +0000 Subject: [PATCH 10/77] OFC23 Scenario: - Completed preparation of scripts - Created descriptor for child TFS controller - Created preliminary descriptors for parent TFS controller --- src/tests/ofc23/delete_all.sh | 8 +- src/tests/ofc23/deploy_all.sh | 50 ++-- src/tests/ofc23/deploy_specs_child.sh | 103 +++++++- src/tests/ofc23/deploy_specs_parent.sh | 103 +++++++- .../ofc23/descriptors/descriptor_child.json | 55 ++++ .../ofc23/descriptors/descriptor_parent.json | 245 ++++++++++++++++++ .../descriptors/descriptor_parent_l2vpn.json | 20 ++ src/tests/ofc23/descriptors/domain1.json | 148 ----------- src/tests/ofc23/descriptors/domain2.json | 166 ------------ src/tests/ofc23/dump_logs.sh | 3 - src/tests/ofc23/fast_redeploy.sh | 40 +-- 11 files changed, 544 insertions(+), 397 deletions(-) create mode 100644 src/tests/ofc23/descriptors/descriptor_child.json create mode 100644 src/tests/ofc23/descriptors/descriptor_parent.json create mode 100644 src/tests/ofc23/descriptors/descriptor_parent_l2vpn.json delete mode 100644 src/tests/ofc23/descriptors/domain1.json delete mode 100644 src/tests/ofc23/descriptors/domain2.json diff --git a/src/tests/ofc23/delete_all.sh b/src/tests/ofc23/delete_all.sh index 6a838d985..4a03dad1c 100755 --- a/src/tests/ofc23/delete_all.sh +++ b/src/tests/ofc23/delete_all.sh @@ -15,10 +15,8 @@ # Delete old namespaces -kubectl delete namespace tfs-dom1 tfs-dom2 tfs-dom3 tfs-dom4 tfs-bchain +kubectl delete namespace tfs-parent tfs-child # Delete secondary ingress controllers -kubectl delete -f nfvsdn22/nginx-ingress-controller-dom1.yaml -kubectl delete -f nfvsdn22/nginx-ingress-controller-dom2.yaml -kubectl delete -f nfvsdn22/nginx-ingress-controller-dom3.yaml -kubectl delete -f nfvsdn22/nginx-ingress-controller-dom4.yaml +kubectl delete -f ofc23/nginx-ingress-controller-parent.yaml +kubectl delete -f ofc23/nginx-ingress-controller-child.yaml diff --git a/src/tests/ofc23/deploy_all.sh b/src/tests/ofc23/deploy_all.sh index 19ea0e6db..4874688ad 100755 --- a/src/tests/ofc23/deploy_all.sh +++ b/src/tests/ofc23/deploy_all.sh @@ -15,42 +15,22 @@ # Delete old namespaces -kubectl delete namespace tfs-dom1 tfs-dom2 tfs-dom3 tfs-dom4 +kubectl delete namespace tfs-parent tfs-child # Delete secondary ingress controllers -kubectl delete -f nfvsdn22/nginx-ingress-controller-dom1.yaml -kubectl delete -f nfvsdn22/nginx-ingress-controller-dom2.yaml -kubectl delete -f nfvsdn22/nginx-ingress-controller-dom3.yaml -kubectl delete -f nfvsdn22/nginx-ingress-controller-dom4.yaml - -# Delete MockBlockchain -#kubectl delete namespace tfs-bchain +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 nfvsdn22/nginx-ingress-controller-dom1.yaml -kubectl apply -f nfvsdn22/nginx-ingress-controller-dom2.yaml -kubectl apply -f nfvsdn22/nginx-ingress-controller-dom3.yaml -kubectl apply -f nfvsdn22/nginx-ingress-controller-dom4.yaml - -# Create MockBlockchain -#./deploy_mock_blockchain.sh - -# Deploy TFS for Domain 1 -source nfvsdn22/deploy_specs_dom1.sh -./deploy.sh -mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_dom1.sh - -# Deploy TFS for Domain 2 -source nfvsdn22/deploy_specs_dom2.sh -./deploy.sh -mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_dom2.sh - -# Deploy TFS for Domain 3 -source nfvsdn22/deploy_specs_dom3.sh -./deploy.sh -mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_dom3.sh - -# Deploy TFS for Domain 4 -source nfvsdn22/deploy_specs_dom4.sh -./deploy.sh -mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_dom4.sh +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_specs_child.sh b/src/tests/ofc23/deploy_specs_child.sh index bafec4080..2cb323651 100755 --- a/src/tests/ofc23/deploy_specs_child.sh +++ b/src/tests/ofc23/deploy_specs_child.sh @@ -1,10 +1,11 @@ +#!/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 +# 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, @@ -12,24 +13,106 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Set the URL of your local Docker registry where the images will be uploaded to. -export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# ----- 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. -export TFS_COMPONENTS="context device pathcomp service slice webui" +#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 to. +# 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="nfvsdn22/tfs-ingress-child.yaml" +export TFS_EXTRA_MANIFESTS="ofc23/tfs-ingress-child.yaml" -# Set the neew Grafana admin password +# Set the new Grafana admin password export TFS_GRAFANA_PASSWORD="admin123+" -# If not already set, disable skip-build flag. -# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. -export TFS_SKIP_BUILD="YES" +# 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-child" + +# Set the external port CockroackDB Postgre SQL interface will be exposed to. +export CRDB_EXT_PORT_SQL="26258" + +# Set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to. +export CRDB_EXT_PORT_HTTP="8082" + +# 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" + +# 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="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-child" + +# 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="9010" + +# 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_parent.sh b/src/tests/ofc23/deploy_specs_parent.sh index 5c2816c74..fc362c08c 100755 --- a/src/tests/ofc23/deploy_specs_parent.sh +++ b/src/tests/ofc23/deploy_specs_parent.sh @@ -1,10 +1,11 @@ +#!/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 +# 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, @@ -12,24 +13,106 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Set the URL of your local Docker registry where the images will be uploaded to. -export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# ----- 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. -export TFS_COMPONENTS="context device pathcomp service slice webui" +#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 to. +# 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="nfvsdn22/tfs-ingress-parent.yaml" +export TFS_EXTRA_MANIFESTS="ofc23/tfs-ingress-parent.yaml" -# Set the neew Grafana admin password +# Set the new Grafana admin password export TFS_GRAFANA_PASSWORD="admin123+" -# If not already set, disable skip-build flag. -# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. -export TFS_SKIP_BUILD="NO" +# 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-parent" + +# 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" + +# 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="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-parent" + +# 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="9009" + +# 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/descriptor_child.json b/src/tests/ofc23/descriptors/descriptor_child.json new file mode 100644 index 000000000..a167441f4 --- /dev/null +++ b/src/tests/ofc23/descriptors/descriptor_child.json @@ -0,0 +1,55 @@ +{ + "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"} + ]}}} + ]} + }, + { + "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"} + ]}}} + ]} + }, + { + "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"} + ]}}} + ]} + }, + { + "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"} + ]}}} + ]} + } + ], + "links": [] +} diff --git a/src/tests/ofc23/descriptors/descriptor_parent.json b/src/tests/ofc23/descriptors/descriptor_parent.json new file mode 100644 index 000000000..ca3f1e8e1 --- /dev/null +++ b/src/tests/ofc23/descriptors/descriptor_parent.json @@ -0,0 +1,245 @@ +{ + "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": "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": "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"} + ]}}} + ]} + }, + { + "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"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "MW"}}, "device_type": "emu-microwave-radio-system", "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": "192.168.27.139:10"}, + {"sample_types": [], "type": "copper/internal", "uuid": "192.168.27.140:8"} + ], "username": "admin", "password": "admin", "timeout": 120, "scheme": "https", + "node_ids": ["192.168.27.139", "192.168.27.140"] + }}} + ]} + }, + + { + "device_id": {"device_uuid": {"uuid": "IPM"}}, "device_type": "emu-xr-constellation", "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": "192.168.27.139:10"}, + {"sample_types": [], "type": "copper/internal", "uuid": "192.168.27.140:8"} + ], "username": "admin", "password": "admin", "timeout": 120, "scheme": "https", + "node_ids": ["192.168.27.139", "192.168.27.140"] + }}} + ]} + }, + + { + "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"} + ]}}} + ]} + }, + + + { + "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": "XR-HUB"}}, "device_type": "emu-xr-hub", "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": "optical/internal", "uuid": "2/1"} + ]}}} + ]} + }, + + { + "device_id": {"device_uuid": {"uuid": "XR-LEAF1"}}, "device_type": "emu-xr-leaf", "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": "optical/internal", "uuid": "2/1"} + ]}}} + ]} + }, + { + "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": "XR-LEAF2"}}, "device_type": "emu-xr-leaf", "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": "optical/internal", "uuid": "2/1"} + ]}}} + ]} + }, + { + "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": "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"} + ]}}} + ]} + }, + { + "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"} + ]}}} + ]} + }, + { + "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": "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": "R1@D1/2==R2@D1/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "2"}}, + {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2@D1/3==R3@D1/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "3"}}, + {"device_id": {"device_uuid": {"uuid": "R3@D1"}}, "endpoint_uuid": {"uuid": "2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2@D1/5==R5@D1/2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "5"}}, + {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3@D1/4==R4@D1/3"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3@D1"}}, "endpoint_uuid": {"uuid": "4"}}, + {"device_id": {"device_uuid": {"uuid": "R4@D1"}}, "endpoint_uuid": {"uuid": "3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R4@D1/5==R5@D1/4"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R4@D1"}}, "endpoint_uuid": {"uuid": "5"}}, + {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "4"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R5@D1/1==R1@D1/5"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "1"}}, + {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "5"}} + ] + } + ] +} diff --git a/src/tests/ofc23/descriptors/descriptor_parent_l2vpn.json b/src/tests/ofc23/descriptors/descriptor_parent_l2vpn.json new file mode 100644 index 000000000..1eb85eab1 --- /dev/null +++ b/src/tests/ofc23/descriptors/descriptor_parent_l2vpn.json @@ -0,0 +1,20 @@ +{ + "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" + }}} + ]} + } + ] +} diff --git a/src/tests/ofc23/descriptors/domain1.json b/src/tests/ofc23/descriptors/domain1.json deleted file mode 100644 index 043b3955f..000000000 --- a/src/tests/ofc23/descriptors/domain1.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "contexts": [ - { - "context_id": {"context_uuid": {"uuid": "admin"}}, - "topology_ids": [ - {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "D1"}} - ], "service_ids": [] - } - ], - "topologies": [ - { - "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "D1"}}, - "device_ids": [ - {"device_uuid": {"uuid": "DC1"}}, - {"device_uuid": {"uuid": "R1@D1"}}, - {"device_uuid": {"uuid": "R2@D1"}}, - {"device_uuid": {"uuid": "R3@D1"}}, - {"device_uuid": {"uuid": "R4@D1"}}, - {"device_uuid": {"uuid": "R5@D1"}} - ], "link_ids": [ - {"link_uuid": {"uuid": "DC1/D1==R1@D1/DC1"}}, - {"link_uuid": {"uuid": "R1@D1/2==R2@D1/1"}}, - {"link_uuid": {"uuid": "R2@D1/3==R3@D1/2"}}, - {"link_uuid": {"uuid": "R2@D1/5==R5@D1/2"}}, - {"link_uuid": {"uuid": "R3@D1/4==R4@D1/3"}}, - {"link_uuid": {"uuid": "R4@D1/5==R5@D1/4"}}, - {"link_uuid": {"uuid": "R5@D1/1==R1@D1/5"}} - ] - } - ], - "devices": [ - { - "device_id": {"device_uuid": {"uuid": "DC1"}}, "device_type": "emu-datacenter", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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/border", "uuid": "D1"}, - {"sample_types": [], "type": "copper/internal", "uuid": "int"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R1@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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": "2"}, - {"sample_types": [], "type": "copper/internal", "uuid": "5"}, - {"sample_types": [], "type": "copper/border", "uuid": "DC1"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R2@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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"}, - {"sample_types": [], "type": "copper/internal", "uuid": "3"}, - {"sample_types": [], "type": "copper/internal", "uuid": "5"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R3@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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": "2"}, - {"sample_types": [], "type": "copper/internal", "uuid": "4"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R4@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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": "3"}, - {"sample_types": [], "type": "copper/internal", "uuid": "5"}, - {"sample_types": [], "type": "copper/border", "uuid": "D2"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R5@D1"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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"}, - {"sample_types": [], "type": "copper/internal", "uuid": "2"}, - {"sample_types": [], "type": "copper/internal", "uuid": "4"}, - {"sample_types": [], "type": "copper/border", "uuid": "D3"} - ]}}} - ]} - } - ], - "links": [ - { - "link_id": {"link_uuid": {"uuid": "DC1/D1==R1@D1/DC1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "DC1"}}, "endpoint_uuid": {"uuid": "D1"}}, - {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "DC1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R1@D1/2==R2@D1/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "2"}}, - {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R2@D1/3==R3@D1/2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "3"}}, - {"device_id": {"device_uuid": {"uuid": "R3@D1"}}, "endpoint_uuid": {"uuid": "2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R2@D1/5==R5@D1/2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "5"}}, - {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R3@D1/4==R4@D1/3"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R3@D1"}}, "endpoint_uuid": {"uuid": "4"}}, - {"device_id": {"device_uuid": {"uuid": "R4@D1"}}, "endpoint_uuid": {"uuid": "3"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R4@D1/5==R5@D1/4"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R4@D1"}}, "endpoint_uuid": {"uuid": "5"}}, - {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "4"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R5@D1/1==R1@D1/5"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "1"}}, - {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "5"}} - ] - } - ] -} diff --git a/src/tests/ofc23/descriptors/domain2.json b/src/tests/ofc23/descriptors/domain2.json deleted file mode 100644 index 81d397abf..000000000 --- a/src/tests/ofc23/descriptors/domain2.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "contexts": [ - { - "context_id": {"context_uuid": {"uuid": "admin"}}, - "topology_ids": [ - {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "D2"}} - ], "service_ids": [] - } - ], - "topologies": [ - { - "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "D2"}}, - "device_ids": [ - {"device_uuid": {"uuid": "R1@D2"}}, - {"device_uuid": {"uuid": "R2@D2"}}, - {"device_uuid": {"uuid": "R3@D2"}}, - {"device_uuid": {"uuid": "R4@D2"}}, - {"device_uuid": {"uuid": "R5@D2"}}, - {"device_uuid": {"uuid": "R6@D2"}} - ], "link_ids": [ - {"link_uuid": {"uuid": "R1@D2/2==R2@D2/1"}}, - {"link_uuid": {"uuid": "R1@D2/6==R6@D2/1"}}, - {"link_uuid": {"uuid": "R1@D2/5==R5@D2/1"}}, - {"link_uuid": {"uuid": "R2@D2/3==R3@D2/2"}}, - {"link_uuid": {"uuid": "R2@D2/4==R4@D2/2"}}, - {"link_uuid": {"uuid": "R2@D2/5==R5@D2/2"}}, - {"link_uuid": {"uuid": "R2@D2/6==R6@D2/2"}}, - {"link_uuid": {"uuid": "R3@D2/6==R6@D2/3"}}, - {"link_uuid": {"uuid": "R4@D2/5==R5@D2/4"}} - ] - } - ], - "devices": [ - { - "device_id": {"device_uuid": {"uuid": "R1@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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": "2"}, - {"sample_types": [], "type": "copper/internal", "uuid": "5"}, - {"sample_types": [], "type": "copper/internal", "uuid": "6"}, - {"sample_types": [], "type": "copper/border", "uuid": "D1"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R2@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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"}, - {"sample_types": [], "type": "copper/internal", "uuid": "3"}, - {"sample_types": [], "type": "copper/internal", "uuid": "4"}, - {"sample_types": [], "type": "copper/internal", "uuid": "5"}, - {"sample_types": [], "type": "copper/internal", "uuid": "6"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R3@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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": "2"}, - {"sample_types": [], "type": "copper/internal", "uuid": "6"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R4@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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": "2"}, - {"sample_types": [], "type": "copper/internal", "uuid": "5"}, - {"sample_types": [], "type": "copper/border", "uuid": "D4"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R5@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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"}, - {"sample_types": [], "type": "copper/internal", "uuid": "2"}, - {"sample_types": [], "type": "copper/internal", "uuid": "4"}, - {"sample_types": [], "type": "copper/border", "uuid": "D3"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R6@D2"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 2, "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"}, - {"sample_types": [], "type": "copper/internal", "uuid": "2"}, - {"sample_types": [], "type": "copper/internal", "uuid": "3"} - ]}}} - ]} - } - ], - "links": [ - { - "link_id": {"link_uuid": {"uuid": "R1@D2/2==R2@D2/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R1@D2"}}, "endpoint_uuid": {"uuid": "2"}}, - {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R1@D2/6==R6@D2/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R1@D2"}}, "endpoint_uuid": {"uuid": "6"}}, - {"device_id": {"device_uuid": {"uuid": "R6@D2"}}, "endpoint_uuid": {"uuid": "1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R1@D2/5==R5@D2/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R1@D2"}}, "endpoint_uuid": {"uuid": "5"}}, - {"device_id": {"device_uuid": {"uuid": "R5@D2"}}, "endpoint_uuid": {"uuid": "1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R2@D2/3==R3@D2/2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "3"}}, - {"device_id": {"device_uuid": {"uuid": "R3@D2"}}, "endpoint_uuid": {"uuid": "2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R2@D2/4==R4@D2/2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "4"}}, - {"device_id": {"device_uuid": {"uuid": "R4@D2"}}, "endpoint_uuid": {"uuid": "2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R2@D2/5==R5@D2/2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "5"}}, - {"device_id": {"device_uuid": {"uuid": "R5@D2"}}, "endpoint_uuid": {"uuid": "2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R2@D2/6==R6@D2/2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2@D2"}}, "endpoint_uuid": {"uuid": "6"}}, - {"device_id": {"device_uuid": {"uuid": "R6@D2"}}, "endpoint_uuid": {"uuid": "2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R3@D2/6==R6@D2/3"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R3@D2"}}, "endpoint_uuid": {"uuid": "6"}}, - {"device_id": {"device_uuid": {"uuid": "R6@D2"}}, "endpoint_uuid": {"uuid": "3"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R4@D2/5==R5@D2/4"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R4@D2"}}, "endpoint_uuid": {"uuid": "5"}}, - {"device_id": {"device_uuid": {"uuid": "R5@D2"}}, "endpoint_uuid": {"uuid": "4"}} - ] - } - ] -} diff --git a/src/tests/ofc23/dump_logs.sh b/src/tests/ofc23/dump_logs.sh index 533879c2a..cc3162b33 100755 --- a/src/tests/ofc23/dump_logs.sh +++ b/src/tests/ofc23/dump_logs.sh @@ -34,9 +34,6 @@ kubectl --namespace tfs-child logs deployments/serviceservice server > tmp/exec/ 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 -kubectl --namespace tfs-child logs deployments/interdomainservice server > tmp/exec/child/interdomain.log -kubectl --namespace tfs-child logs deployments/dltservice connector > tmp/exec/child/dlt-connector.log -kubectl --namespace tfs-child logs deployments/dltservice gateway > tmp/exec/child/dlt-gateway.log printf "\n" echo "Done!" diff --git a/src/tests/ofc23/fast_redeploy.sh b/src/tests/ofc23/fast_redeploy.sh index 3c942a799..58d1193de 100755 --- a/src/tests/ofc23/fast_redeploy.sh +++ b/src/tests/ofc23/fast_redeploy.sh @@ -17,29 +17,29 @@ kubectl delete namespace tfs-parent tfs-child echo "Deploying tfs-parent ..." -kubectl delete -f nfvsdn22/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 nfvsdn22/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 nfvsdn22/tfs-ingress-parent.yaml > ./tmp/logs/deploy-tfs-parent.log +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 nfvsdn22/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 nfvsdn22/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 nfvsdn22/tfs-ingress-child.yaml > ./tmp/logs/deploy-tfs-child.log +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 ..." -- GitLab From 04be3bc74c1dfed15cfdd25ae5cf7719f7634718 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 22 Feb 2023 16:15:14 +0000 Subject: [PATCH 11/77] OFC23 Scenario: - Added symbolic link --- ofc23 | 1 + 1 file changed, 1 insertion(+) create mode 120000 ofc23 diff --git a/ofc23 b/ofc23 new file mode 120000 index 000000000..a1135d4c5 --- /dev/null +++ b/ofc23 @@ -0,0 +1 @@ +src/tests/ofc23/ \ No newline at end of file -- GitLab From 4aaf2d676fa1d80190796bcb4c79c308a0d6d59a Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 23 Feb 2023 09:41:53 +0000 Subject: [PATCH 12/77] OFC'23 scenario: - Added convenience scripts to deploy only parent or child --- src/tests/ofc23/deploy_child.sh | 29 +++++++++++++++++++++++++++++ src/tests/ofc23/deploy_parent.sh | 29 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100755 src/tests/ofc23/deploy_child.sh create mode 100755 src/tests/ofc23/deploy_parent.sh diff --git a/src/tests/ofc23/deploy_child.sh b/src/tests/ofc23/deploy_child.sh new file mode 100755 index 000000000..9b05ed887 --- /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_parent.sh b/src/tests/ofc23/deploy_parent.sh new file mode 100755 index 000000000..ac4a29542 --- /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 -- GitLab From ee6d01a174d7f5cd4d494dd19807829e1e5383a2 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 23 Feb 2023 16:04:54 +0000 Subject: [PATCH 13/77] Common: - Added missing device types --- src/common/DeviceTypes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py index 2cb1fb4f0..16b94eb0d 100644 --- a/src/common/DeviceTypes.py +++ b/src/common/DeviceTypes.py @@ -25,9 +25,11 @@ 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_ROUTER = 'emu-packet-router' EMULATED_PACKET_SWITCH = 'emu-packet-switch' + EMULATED_XR_CONSTELLATION = 'emu-xr-constellation' # Real device types DATACENTER = 'datacenter' -- GitLab From 92fa87266e093ad77ac0860614096ce5e396a367 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 23 Feb 2023 16:06:18 +0000 Subject: [PATCH 14/77] WebUI component: - Added missing device topology icons - Added stylink to mgmt links in topology plot --- .../topology_icons/Acknowledgements.txt | 3 +++ .../topology_icons/emu-optical-splitter.png | Bin 0 -> 8451 bytes .../topology_icons/emu-xr-constellation.png | Bin 0 -> 17814 bytes .../topology_icons/optical-splitter.png | Bin 0 -> 9244 bytes src/webui/service/templates/js/topology.js | 20 +++++++++++++----- 5 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 src/webui/service/static/topology_icons/emu-optical-splitter.png create mode 100644 src/webui/service/static/topology_icons/emu-xr-constellation.png create mode 100644 src/webui/service/static/topology_icons/optical-splitter.png diff --git a/src/webui/service/static/topology_icons/Acknowledgements.txt b/src/webui/service/static/topology_icons/Acknowledgements.txt index b285d2259..fb04defd4 100644 --- a/src/webui/service/static/topology_icons/Acknowledgements.txt +++ b/src/webui/service/static/topology_icons/Acknowledgements.txt @@ -24,3 +24,6 @@ https://symbols.getvecta.com/stencil_241/213_programmable-sw.32d3794d56.png => e https://symbols.getvecta.com/stencil_240/275_wae.c06b769cd7.png => optical-transponder.png https://symbols.getvecta.com/stencil_241/289_wae.216d930c17.png => emu-optical-transponder.png + +https://symbols.getvecta.com/stencil_240/128_localdirector.c1e561769f.png => optical-splitter.png +https://symbols.getvecta.com/stencil_241/158_local-director.6b38eab9e4.png => emu-optical-splitter.png diff --git a/src/webui/service/static/topology_icons/emu-optical-splitter.png b/src/webui/service/static/topology_icons/emu-optical-splitter.png new file mode 100644 index 0000000000000000000000000000000000000000..12b7727d68ef749b52fcdd592c0427f63b58dc75 GIT binary patch literal 8451 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 A=>Px# literal 0 HcmV?d00001 diff --git a/src/webui/service/static/topology_icons/optical-splitter.png b/src/webui/service/static/topology_icons/optical-splitter.png new file mode 100644 index 0000000000000000000000000000000000000000..90a3d79b8ed4b8ae15f3d4a349cd08d741dcfdaf GIT binary patch literal 9244 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 literal 0 HcmV?d00001 diff --git a/src/webui/service/templates/js/topology.js b/src/webui/service/templates/js/topology.js index 50486d2a6..b852380d0 100644 --- a/src/webui/service/templates/js/topology.js +++ b/src/webui/service/templates/js/topology.js @@ -70,11 +70,21 @@ var simulation = d3.forceSimulation(); // load the data d3.json("{{ url_for('main.topology') }}", function(data) { // set the data and properties of link lines and node circles - link = svg.append("g").attr("class", "links").style('stroke', '#aaa') + link = svg.append("g").attr("class", "links")//.style('stroke', '#aaa') .selectAll("line") .data(data.links) .enter() - .append("line"); + .append("line") + .attr("opacity", 1) + .attr("stroke", function(l) { + return l.name.toLowerCase().includes('mgmt') ? '#AAAAAA' : '#555555'; + }) + .attr("stroke-width", function(l) { + return l.name.toLowerCase().includes('mgmt') ? 1 : 2; + }) + .attr("stroke-dasharray", function(l) { + return l.name.toLowerCase().includes('mgmt') ? "5,5" : "0"; + }); node = svg.append("g").attr("class", "devices").attr('r', 20).style('fill', '#69b3a2') .selectAll("circle") .data(data.devices) @@ -93,9 +103,9 @@ d3.json("{{ url_for('main.topology') }}", function(data) { link.append("title").text(function(l) { return l.name; }); // link style - link - .attr("stroke-width", forceProperties.link.enabled ? 2 : 1) - .attr("opacity", forceProperties.link.enabled ? 1 : 0); + //link + // .attr("stroke-width", forceProperties.link.enabled ? 2 : 1) + // .attr("opacity", forceProperties.link.enabled ? 1 : 0); // set up the simulation and event to update locations after each tick simulation.nodes(data.devices); -- GitLab From f29a712faf726448180a05d50c8dc91e80e4c681 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:29:16 +0000 Subject: [PATCH 15/77] PathComp component - Backend: - Adjusted max number of endpoints per device - Adjusted max number of parallel edges between nodes --- src/pathcomp/backend/pathComp_tools.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pathcomp/backend/pathComp_tools.h b/src/pathcomp/backend/pathComp_tools.h index b6bcea04c..b77078891 100644 --- a/src/pathcomp/backend/pathComp_tools.h +++ b/src/pathcomp/backend/pathComp_tools.h @@ -117,7 +117,7 @@ struct map_nodes_t { }; #define MAX_NUM_VERTICES 20 // 100 # LGR: reduced from 100 to 20 to divide by 5 the memory used -#define MAX_NUM_EDGES 40 // 100 # LGR: reduced from 100 to 40 to divide by 2.5 the memory used +#define MAX_NUM_EDGES 5 // 100 # LGR: reduced from 100 to 5 to divide by 20 the memory used // Structures for the graph composition struct targetNodes_t { // remote / targeted node @@ -247,7 +247,7 @@ struct endPoint_t { // Structure for the device contents /////////////////////////////////////////////////////////////////// #define MAX_DEV_TYPE_SIZE 128 -#define MAX_DEV_ENDPOINT_LENGTH 40 // 10 # LGR: controllers might have large number of endpoints +#define MAX_DEV_ENDPOINT_LENGTH 50 // 10 # LGR: controllers might have large number of endpoints struct device_t { gchar deviceId[UUID_CHAR_LENGTH]; // device ID using UUID (128 bits) -- GitLab From adfd2178252de2975dff95b14a80d9bcbc4faa46 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:34:05 +0000 Subject: [PATCH 16/77] Tools - Mock MW SDN Ctrl: - Renamed folder - Improved error reporting --- .../MockMWSdnCtrl.py | 50 ++++++++++--------- .../README.md | 6 +-- .../scenario}/microwave_deploy.sh | 0 .../scenario}/network_descriptors.json | 0 .../scenario}/service_descriptor.json | 0 .../ssl_not_working/Dockerfile | 35 +++++++++++++ .../mock_mw_sdn_ctrl/ssl_not_working/build.sh | 5 ++ .../ssl_not_working/deploy.sh | 4 ++ .../ssl_not_working/mock-mw-sdn-ctrl.yaml | 46 +++++++++++++++++ .../ssl_not_working/requirements.in | 21 ++++++++ 10 files changed, 141 insertions(+), 26 deletions(-) rename src/tests/tools/{mock_sdn_ctrl => mock_mw_sdn_ctrl}/MockMWSdnCtrl.py (76%) rename src/tests/tools/{mock_sdn_ctrl => mock_mw_sdn_ctrl}/README.md (94%) rename src/tests/tools/{mock_sdn_ctrl => mock_mw_sdn_ctrl/scenario}/microwave_deploy.sh (100%) rename src/tests/tools/{mock_sdn_ctrl => mock_mw_sdn_ctrl/scenario}/network_descriptors.json (100%) rename src/tests/tools/{mock_sdn_ctrl => mock_mw_sdn_ctrl/scenario}/service_descriptor.json (100%) create mode 100644 src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/Dockerfile create mode 100755 src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/build.sh create mode 100755 src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/deploy.sh create mode 100644 src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/mock-mw-sdn-ctrl.yaml create mode 100644 src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/requirements.in diff --git a/src/tests/tools/mock_sdn_ctrl/MockMWSdnCtrl.py b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py similarity index 76% rename from src/tests/tools/mock_sdn_ctrl/MockMWSdnCtrl.py rename to src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py index 63be214b6..f002d2376 100644 --- a/src/tests/tools/mock_sdn_ctrl/MockMWSdnCtrl.py +++ b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py @@ -37,30 +37,30 @@ LOG_LEVEL = logging.DEBUG NETWORK_NODES = [ {'node-id': '172.18.0.1', 'ietf-network-topology:termination-point': [ - {'tp-id': '172.18.0.1:1', 'ietf-te-topology:te': {'name': 'ethernet'}}, - {'tp-id': '172.18.0.1:2', 'ietf-te-topology:te': {'name': 'antena' }}, + {'tp-id': '1', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '2', 'ietf-te-topology:te': {'name': 'antena' }}, ]}, {'node-id': '172.18.0.2', 'ietf-network-topology:termination-point': [ - {'tp-id': '172.18.0.2:1', 'ietf-te-topology:te': {'name': 'ethernet'}}, - {'tp-id': '172.18.0.2:2', 'ietf-te-topology:te': {'name': 'antena' }}, + {'tp-id': '1', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '2', 'ietf-te-topology:te': {'name': 'antena' }}, ]}, {'node-id': '172.18.0.3', 'ietf-network-topology:termination-point': [ - {'tp-id': '172.18.0.3:1', 'ietf-te-topology:te': {'name': 'ethernet'}}, - {'tp-id': '172.18.0.3:2', 'ietf-te-topology:te': {'name': 'antena' }}, + {'tp-id': '1', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '2', 'ietf-te-topology:te': {'name': 'antena' }}, ]}, {'node-id': '172.18.0.4', 'ietf-network-topology:termination-point': [ - {'tp-id': '172.18.0.4:1', 'ietf-te-topology:te': {'name': 'ethernet'}}, - {'tp-id': '172.18.0.4:2', 'ietf-te-topology:te': {'name': 'antena' }}, + {'tp-id': '1', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '2', 'ietf-te-topology:te': {'name': 'antena' }}, ]} ] NETWORK_LINKS = [ { - 'source' : {'source-node': '172.18.0.1', 'source-tp': '172.18.0.1:2'}, - 'destination': {'dest-node' : '172.18.0.2', 'dest-tp' : '172.18.0.2:2'}, + 'source' : {'source-node': '172.18.0.1', 'source-tp': '2'}, + 'destination': {'dest-node' : '172.18.0.2', 'dest-tp' : '2'}, }, { - 'source' : {'source-node': '172.18.0.3', 'source-tp': '172.18.0.3:2'}, - 'destination': {'dest-node' : '172.18.0.4', 'dest-tp' : '172.18.0.4:2'}, + 'source' : {'source-node': '172.18.0.3', 'source-tp': '2'}, + 'destination': {'dest-node' : '172.18.0.4', 'dest-tp' : '2'}, } ] NETWORK_SERVICES = {} @@ -91,17 +91,21 @@ class Services(Resource): return jsonify({'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': services}}) def post(self): - json_request = request.json - if not json_request: abort(400) - if not isinstance(json_request, dict): abort(400) - if 'etht-svc-instances' not in json_request: abort(400) - json_services = json_request['etht-svc-instances'] - if not isinstance(json_services, list): abort(400) - if len(json_services) != 1: abort(400) - svc_data = json_services[0] - etht_svc_name = svc_data['etht-svc-name'] - NETWORK_SERVICES[etht_svc_name] = svc_data - return jsonify({}), 201 + try: + json_request = request.json + if not json_request: abort(400) + if not isinstance(json_request, dict): abort(400) + if 'etht-svc-instances' not in json_request: abort(400) + json_services = json_request['etht-svc-instances'] + if not isinstance(json_services, list): abort(400) + if len(json_services) != 1: abort(400) + svc_data = json_services[0] + etht_svc_name = svc_data['etht-svc-name'] + NETWORK_SERVICES[etht_svc_name] = svc_data + return jsonify({}), 201 + except: + LOGGER.exception('Exception in POST') + class DelServices(Resource): def delete(self, service_uuid : str): diff --git a/src/tests/tools/mock_sdn_ctrl/README.md b/src/tests/tools/mock_mw_sdn_ctrl/README.md similarity index 94% rename from src/tests/tools/mock_sdn_ctrl/README.md rename to src/tests/tools/mock_mw_sdn_ctrl/README.md index d8a6fe6b2..8568c89ed 100644 --- a/src/tests/tools/mock_sdn_ctrl/README.md +++ b/src/tests/tools/mock_mw_sdn_ctrl/README.md @@ -12,8 +12,8 @@ Follow the steps below to perform the test: ## 1. Deploy TeraFlowSDN controller and the scenario Deploy the test scenario "microwave_deploy.sh": ```bash -source src/tests/tools/microwave_deploy.sh -./deploy.sh +source src/tests/tools/mock_mw_sdn_ctrl/scenario/microwave_deploy.sh +./deploy/all.sh ``` ## 2. Install requirements and run the Mock MicroWave SDN controller @@ -27,7 +27,7 @@ pip install Flask==2.1.3 Flask-RESTful==0.3.9 Run the Mock MicroWave SDN Controller as follows: ```bash -python src/tests/tools/mock_sdn_ctrl/MockMWSdnCtrl.py +python src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py ``` ## 3. Deploy the test descriptors diff --git a/src/tests/tools/mock_sdn_ctrl/microwave_deploy.sh b/src/tests/tools/mock_mw_sdn_ctrl/scenario/microwave_deploy.sh similarity index 100% rename from src/tests/tools/mock_sdn_ctrl/microwave_deploy.sh rename to src/tests/tools/mock_mw_sdn_ctrl/scenario/microwave_deploy.sh diff --git a/src/tests/tools/mock_sdn_ctrl/network_descriptors.json b/src/tests/tools/mock_mw_sdn_ctrl/scenario/network_descriptors.json similarity index 100% rename from src/tests/tools/mock_sdn_ctrl/network_descriptors.json rename to src/tests/tools/mock_mw_sdn_ctrl/scenario/network_descriptors.json diff --git a/src/tests/tools/mock_sdn_ctrl/service_descriptor.json b/src/tests/tools/mock_mw_sdn_ctrl/scenario/service_descriptor.json similarity index 100% rename from src/tests/tools/mock_sdn_ctrl/service_descriptor.json rename to src/tests/tools/mock_mw_sdn_ctrl/scenario/service_descriptor.json diff --git a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/Dockerfile b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/Dockerfile new file mode 100644 index 000000000..ad214b97c --- /dev/null +++ b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/Dockerfile @@ -0,0 +1,35 @@ +# 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. + +FROM python:3.9-slim + +# Set Python to show logs as they occur +ENV PYTHONUNBUFFERED=0 + +# Get generic Python packages +RUN python3 -m pip install --upgrade pip +RUN python3 -m pip install --upgrade setuptools wheel +RUN python3 -m pip install --upgrade pip-tools + +# Create component sub-folder, and copy content +RUN mkdir -p /var/teraflow/mock_mw_sdn_ctrl +WORKDIR /var/teraflow/mock_mw_sdn_ctrl +COPY . . + +# Get specific Python packages +RUN pip-compile --quiet --output-file=requirements.txt requirements.in +RUN python3 -m pip install -r requirements.txt + +# Start the service +ENTRYPOINT ["python", "MockMWSdnCtrl.py"] diff --git a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/build.sh b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/build.sh new file mode 100755 index 000000000..4df315cec --- /dev/null +++ b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +docker build -t mock-mw-sdn-ctrl:test -f Dockerfile . +docker tag mock-mw-sdn-ctrl:test localhost:32000/tfs/mock-mw-sdn-ctrl:test +docker push localhost:32000/tfs/mock-mw-sdn-ctrl:test diff --git a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/deploy.sh b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/deploy.sh new file mode 100755 index 000000000..ded232e5c --- /dev/null +++ b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/deploy.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +kubectl delete namespace mocks +kubectl --namespace mocks apply -f mock-mw-sdn-ctrl.yaml diff --git a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/mock-mw-sdn-ctrl.yaml b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/mock-mw-sdn-ctrl.yaml new file mode 100644 index 000000000..05b89f949 --- /dev/null +++ b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/mock-mw-sdn-ctrl.yaml @@ -0,0 +1,46 @@ +kind: Namespace +apiVersion: v1 +metadata: + name: mocks +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mock-mw-sdn-ctrl +spec: + selector: + matchLabels: + app: mock-mw-sdn-ctrl + replicas: 1 + template: + metadata: + labels: + app: mock-mw-sdn-ctrl + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: localhost:32000/tfs/mock-mw-sdn-ctrl:test + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8443 + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 700m + memory: 1024Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: mock-mw-sdn-ctrl +spec: + type: ClusterIP + selector: + app: mock-mw-sdn-ctrl + ports: + - name: https + port: 8443 + targetPort: 8443 diff --git a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/requirements.in b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/requirements.in new file mode 100644 index 000000000..f4bc19106 --- /dev/null +++ b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/requirements.in @@ -0,0 +1,21 @@ +# 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. + +cryptography==39.0.1 +pyopenssl==23.0.0 +Flask==2.1.3 +Flask-HTTPAuth==4.5.0 +Flask-RESTful==0.3.9 +jsonschema==4.4.0 +requests==2.27.1 -- GitLab From 6323b7441b918434e4352a74fbb72ba3c18946e0 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:34:47 +0000 Subject: [PATCH 17/77] WebUI component: - Added Endpoint Name in Link, Service and Slice --- src/webui/service/templates/link/detail.html | 4 ++++ src/webui/service/templates/service/detail.html | 4 ++++ src/webui/service/templates/slice/detail.html | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/webui/service/templates/link/detail.html b/src/webui/service/templates/link/detail.html index acac4a553..916abafde 100644 --- a/src/webui/service/templates/link/detail.html +++ b/src/webui/service/templates/link/detail.html @@ -37,6 +37,7 @@ Endpoint UUID + Name Device Endpoint Type @@ -44,6 +45,9 @@ {% for endpoint in link.link_endpoint_ids %} + + {{ endpoint.endpoint_uuid.uuid }} + {{ endpoints_data.get(endpoint.endpoint_uuid.uuid, (endpoint.endpoint_uuid.uuid, ''))[0] }} diff --git a/src/webui/service/templates/service/detail.html b/src/webui/service/templates/service/detail.html index bee2e93c5..414aa19d0 100644 --- a/src/webui/service/templates/service/detail.html +++ b/src/webui/service/templates/service/detail.html @@ -55,6 +55,7 @@ Endpoint UUID + Name Device Endpoint Type @@ -62,6 +63,9 @@ {% for endpoint in service.service_endpoint_ids %} + + {{ endpoint.endpoint_uuid.uuid }} + {{ endpoints_data.get(endpoint.endpoint_uuid.uuid, (endpoint.endpoint_uuid.uuid, ''))[0] }} diff --git a/src/webui/service/templates/slice/detail.html b/src/webui/service/templates/slice/detail.html index 8f223e44d..13b69defe 100644 --- a/src/webui/service/templates/slice/detail.html +++ b/src/webui/service/templates/slice/detail.html @@ -55,6 +55,7 @@ Endpoint UUID + Name Device Endpoint Type @@ -62,6 +63,9 @@ {% for endpoint in slice.slice_endpoint_ids %} + + {{ endpoint.endpoint_uuid.uuid }} + {{ endpoints_data.get(endpoint.endpoint_uuid.uuid, (endpoint.endpoint_uuid.uuid, ''))[0] }} -- GitLab From ffe1454fe9f04a0ceeeb5005c102d6bdd2979ac3 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:39:50 +0000 Subject: [PATCH 18/77] Service component: - Improved filter to select service handler - Disabled configuration of devices when there is nothing to configure - Corrected TAPI Service Handler to use endpoint uuids instead of names - Extended filters for selecting MicroWave Service Handler - First implementation of L2NM IETF L2VPN Service Handler - Extended service handler selector to be aware of devices managed by a controller - Minor cosmetic changes --- .../ServiceHandlerFactory.py | 3 ++ .../service/service_handlers/__init__.py | 2 +- .../l2nm_emulated/ConfigRules.py | 16 ++++++--- .../L2NM_IETFL2VPN_ServiceHandler.py | 36 ++++++++----------- .../l2nm_openconfig/ConfigRules.py | 19 +++++----- .../l3nm_emulated/ConfigRules.py | 14 +++++--- .../l3nm_openconfig/ConfigRules.py | 4 +-- .../microwave/MicrowaveServiceHandler.py | 4 +-- .../tapi_tapi/TapiServiceHandler.py | 12 +++---- .../service/task_scheduler/TaskExecutor.py | 27 ++++++++++++-- .../tasks/Task_ConnectionConfigure.py | 2 +- .../tasks/Task_ConnectionDeconfigure.py | 2 +- .../tasks/Task_ServiceDelete.py | 2 +- .../tasks/Task_ServiceSetStatus.py | 2 +- 14 files changed, 86 insertions(+), 59 deletions(-) diff --git a/src/service/service/service_handler_api/ServiceHandlerFactory.py b/src/service/service/service_handler_api/ServiceHandlerFactory.py index 6aa21b499..64ea204a2 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 9b4e442e5..fa215af2f 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -57,7 +57,7 @@ SERVICE_HANDLERS = [ (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, [ diff --git a/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py b/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py index c2ea6e213..84467dd2d 100644 --- a/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py +++ b/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py @@ -21,10 +21,13 @@ 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 @@ -80,8 +83,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/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py index 1f5817fe8..f84c8c824 100644 --- a/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py +++ b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py @@ -47,34 +47,28 @@ class L2NM_IETFL2VPN_ServiceHandler(_ServiceHandler): 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') - layer_proto_name = json_settings.get('layer_proto_name', 'PHOTONIC_MEDIA') - layer_proto_qual = json_settings.get('layer_proto_qual', 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC') - direction = json_settings.get('direction', 'UNIDIRECTIONAL') + encap_type = json_settings.get('encapsulation_type', '') + vlan_id = json_settings.get('vlan_id', 100) 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]) + dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[1]) - if device_uuid_src != device_uuid_dst: - raise Exception('Diferent Src-Dst devices not supported by now') - device_uuid = device_uuid_src + if src_device_uuid != dst_device_uuid: + raise Exception('Different Src-Dst devices not supported by now') + device_uuid = src_device_uuid 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 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, - 'capacity_unit' : capacity_unit, - 'capacity_value' : capacity_value, - 'layer_protocol_name' : layer_proto_name, - 'layer_protocol_qualifier': layer_proto_qual, - 'direction' : direction, + 'uuid' : service_uuid, + 'src_device_uuid' : src_device_uuid, + 'src_endpoint_uuid' : src_endpoint_uuid, + 'dst_device_uuid' : dst_device_uuid, + 'dst_endpoint_uuid' : dst_endpoint_uuid, + 'encapsulation_type': encap_type, + 'vlan_id' : vlan_id, }) del device_obj.device_config.config_rules[:] device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule)) @@ -102,7 +96,7 @@ class L2NM_IETFL2VPN_ServiceHandler(_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/l2nm_openconfig/ConfigRules.py b/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py index bbd91df93..58071f28a 100644 --- a/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py +++ b/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py @@ -24,12 +24,9 @@ def setup_config_rules( 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,16 +38,15 @@ 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, @@ -86,8 +82,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/ConfigRules.py b/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py index 903ad8cd5..f4a46112e 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_openconfig/ConfigRules.py b/src/service/service/service_handlers/l3nm_openconfig/ConfigRules.py index 351efe5a5..4984fd77c 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 ee64d2fa4..40c87eeee 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 8abd12b2a..dd9ff092a 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 @@ -59,17 +59,15 @@ class TapiServiceHandler(_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))) - endpoint_name_src = get_endpoint_matching(device_obj, endpoint_uuid_src).name - endpoint_name_dst = get_endpoint_matching(device_obj, endpoint_uuid_dst).name 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' : endpoint_uuid_src, + 'output_sip' : endpoint_uuid_dst, 'capacity_unit' : capacity_unit, 'capacity_value' : capacity_value, 'layer_protocol_name' : layer_proto_name, @@ -102,7 +100,7 @@ class TapiServiceHandler(_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/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index 932c56e2b..e475f29c6 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,32 @@ 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_devices_from_connection( + self, connection : Connection, exclude_managed : 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 + + manager = None + for config_rule in device.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + if config_rule.custom.resource_key != '_manager': continue + manager = json.loads(config_rule.custom.resource_value) + break + + if manager is not None: + if not exclude_managed: + devices[device_uuid] = device + manager_uuid = manager['uuid'] + manager = self.get_device(DeviceId(**json_device_id(manager_uuid))) + manager_uuid = manager.device_id.device_uuid.uuid + if manager is None: raise Exception('Device({:s}) not found'.format(str(manager_uuid))) + devices[manager_uuid] = manager + else: + devices[device_uuid] = device return devices # ----- Service-related methods ------------------------------------------------------------------------------------ @@ -139,6 +160,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=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 5a47005b3..4367ffdee 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 5736054fe..70f41566e 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 6a4e11b54..0f021b6ca 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 815cb33c3..d5360fe85 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) -- GitLab From faa68396aa2ff238d28e3a01f3ee7a90a83f6fd5 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:41:22 +0000 Subject: [PATCH 19/77] OFC'23 test: - Updated child deploy script to prevent re-building images after parent - Updated descriptor files --- src/tests/ofc23/deploy_specs_child.sh | 2 +- .../ofc23/descriptors/dc-2-dc-service.json | 44 ++++ .../ofc23/descriptors/descriptor_parent.json | 212 +++++++----------- .../descriptors/descriptor_parent_l2vpn.json | 20 -- src/tests/ofc23/descriptors/idc-slice.json | 20 -- src/tests/ofc23/descriptors/splitter.json | 18 ++ 6 files changed, 141 insertions(+), 175 deletions(-) create mode 100644 src/tests/ofc23/descriptors/dc-2-dc-service.json delete mode 100644 src/tests/ofc23/descriptors/descriptor_parent_l2vpn.json delete mode 100644 src/tests/ofc23/descriptors/idc-slice.json create mode 100644 src/tests/ofc23/descriptors/splitter.json diff --git a/src/tests/ofc23/deploy_specs_child.sh b/src/tests/ofc23/deploy_specs_child.sh index 2cb323651..0349dba5a 100755 --- a/src/tests/ofc23/deploy_specs_child.sh +++ b/src/tests/ofc23/deploy_specs_child.sh @@ -36,7 +36,7 @@ export TFS_EXTRA_MANIFESTS="ofc23/tfs-ingress-child.yaml" export TFS_GRAFANA_PASSWORD="admin123+" # Disable skip-build flag to rebuild the Docker images. -export TFS_SKIP_BUILD="" +export TFS_SKIP_BUILD="YES" # ----- CockroachDB ------------------------------------------------------------ diff --git a/src/tests/ofc23/descriptors/dc-2-dc-service.json b/src/tests/ofc23/descriptors/dc-2-dc-service.json new file mode 100644 index 000000000..e60b7489b --- /dev/null +++ b/src/tests/ofc23/descriptors/dc-2-dc-service.json @@ -0,0 +1,44 @@ +{ + "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:333", + "mtu": 1512, + "vlan_id": 300 + }}}, + {"action": 1, "custom": {"resource_key": "/device[PE1]/endpoint[1/1]/settings", "resource_value": { + "address_ip": "3.3.2.1", + "address_prefix": 24, + "route_distinguisher": "65000:123", + "router_id": "10.10.10.1", + "sub_interface_index": 400, + "vlan_id": 400 + }}}, + {"action": 1, "custom": {"resource_key": "/device[PE3]/endpoint[1/1]/settings", "resource_value": { + "address_ip": "3.3.1.1", + "address_prefix": 24, + "route_distinguisher": "65000:321", + "router_id": "20.20.20.1", + "sub_interface_index": 400, + "vlan_id": 500 + }}} + ]} + } + ] +} diff --git a/src/tests/ofc23/descriptors/descriptor_parent.json b/src/tests/ofc23/descriptors/descriptor_parent.json index ca3f1e8e1..fd6b23857 100644 --- a/src/tests/ofc23/descriptors/descriptor_parent.json +++ b/src/tests/ofc23/descriptors/descriptor_parent.json @@ -6,118 +6,67 @@ {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}} ], "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": "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"} - ]}}} - ]} - }, - { - "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"} - ]}}} - ]} - }, { - "device_id": {"device_uuid": {"uuid": "MW"}}, "device_type": "emu-microwave-radio-system", "device_drivers": [0], + "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": "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": "192.168.27.139:10"}, - {"sample_types": [], "type": "copper/internal", "uuid": "192.168.27.140:8"} - ], "username": "admin", "password": "admin", "timeout": 120, "scheme": "https", - "node_ids": ["192.168.27.139", "192.168.27.140"] + {"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": "IPM"}}, "device_type": "emu-xr-constellation", "device_drivers": [0], + "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": "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": "192.168.27.139:10"}, - {"sample_types": [], "type": "copper/internal", "uuid": "192.168.27.140:8"} - ], "username": "admin", "password": "admin", "timeout": 120, "scheme": "https", - "node_ids": ["192.168.27.139", "192.168.27.140"] + {"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": "R1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "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": "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"} - ]}}} + {"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": "Optical-Splitter"}}, "device_type": "emu-optical-splitter", "device_drivers": [0], + "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": "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"} - ]}}} + {"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": "XR-HUB"}}, "device_type": "emu-xr-hub", "device_drivers": [0], + "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": "1/1"}, - {"sample_types": [], "type": "optical/internal", "uuid": "2/1"} + {"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": "XR-LEAF1"}}, "device_type": "emu-xr-leaf", "device_drivers": [0], + "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": "optical/internal", "uuid": "2/1"} + {"sample_types": [], "type": "copper/internal", "uuid": "1/2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "1/3"} ]}}} ]} }, @@ -132,18 +81,6 @@ ]}}} ]} }, - - { - "device_id": {"device_uuid": {"uuid": "XR-LEAF2"}}, "device_type": "emu-xr-leaf", "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": "optical/internal", "uuid": "2/1"} - ]}}} - ]} - }, { "device_id": {"device_uuid": {"uuid": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0], "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [ @@ -155,29 +92,6 @@ ]}}} ]} }, - - { - "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"} - ]}}} - ]} - }, - { - "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"} - ]}}} - ]} - }, { "device_id": {"device_uuid": {"uuid": "DC2"}}, "device_type": "emu-datacenter", "device_drivers": [0], "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [ @@ -204,41 +118,71 @@ {"device_id": {"device_uuid": {"uuid": "PE2"}}, "endpoint_uuid": {"uuid": "1/1"}} ] }, - { - "link_id": {"link_uuid": {"uuid": "R1@D1/2==R2@D1/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "2"}}, - {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "1"}} + "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.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": "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.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": "R2@D1/3==R3@D1/2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "3"}}, - {"device_id": {"device_uuid": {"uuid": "R3@D1"}}, "endpoint_uuid": {"uuid": "2"}} + "link_id": {"link_uuid": {"uuid": "R1/1/3==OLS/aade6001-f00b-5e2f-a357-6a0a9d3de870"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/3"}}, + {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "aade6001-f00b-5e2f-a357-6a0a9d3de870"}} ] }, { - "link_id": {"link_uuid": {"uuid": "R2@D1/5==R5@D1/2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2@D1"}}, "endpoint_uuid": {"uuid": "5"}}, - {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "2"}} + "link_id": {"link_uuid": {"uuid": "OLS/0ef74f99-1acc-57bd-ab9d-4b958b06c513==R2/1/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}, + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "1/1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "OLS/50296d99-58cc-5ce7-82f5-fc8ee4eec2ec==R3/1/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}, + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "1/1"}} + ] + }, + + { + "link_id": {"link_uuid": {"uuid": "R2/1/2==PE3/1/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "1/2"}}, + {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/1"}} ] }, { - "link_id": {"link_uuid": {"uuid": "R3@D1/4==R4@D1/3"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R3@D1"}}, "endpoint_uuid": {"uuid": "4"}}, - {"device_id": {"device_uuid": {"uuid": "R4@D1"}}, "endpoint_uuid": {"uuid": "3"}} + "link_id": {"link_uuid": {"uuid": "R3/1/2==PE4/1/1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "1/2"}}, + {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/1"}} ] }, { - "link_id": {"link_uuid": {"uuid": "R4@D1/5==R5@D1/4"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R4@D1"}}, "endpoint_uuid": {"uuid": "5"}}, - {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "4"}} + "link_id": {"link_uuid": {"uuid": "PE3/1/2==DC2/eth1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/2"}}, + {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}} ] }, { - "link_id": {"link_uuid": {"uuid": "R5@D1/1==R1@D1/5"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R5@D1"}}, "endpoint_uuid": {"uuid": "1"}}, - {"device_id": {"device_uuid": {"uuid": "R1@D1"}}, "endpoint_uuid": {"uuid": "5"}} + "link_id": {"link_uuid": {"uuid": "PE4/1/2==DC2/eth2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/2"}}, + {"device_id": {"device_uuid": {"uuid": "DC2"}}, "endpoint_uuid": {"uuid": "eth2"}} ] } ] diff --git a/src/tests/ofc23/descriptors/descriptor_parent_l2vpn.json b/src/tests/ofc23/descriptors/descriptor_parent_l2vpn.json deleted file mode 100644 index 1eb85eab1..000000000 --- a/src/tests/ofc23/descriptors/descriptor_parent_l2vpn.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "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" - }}} - ]} - } - ] -} diff --git a/src/tests/ofc23/descriptors/idc-slice.json b/src/tests/ofc23/descriptors/idc-slice.json deleted file mode 100644 index 634209284..000000000 --- a/src/tests/ofc23/descriptors/idc-slice.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "slices":[ - { - "slice_id":{"context_id":{"context_uuid":{"uuid":"admin"}},"slice_uuid":{"uuid":"idc-slice"}}, - "slice_endpoint_ids":[ - {"device_id":{"device_uuid":{"uuid":"DC1"}},"endpoint_uuid":{"uuid":"int"}}, - {"device_id":{"device_uuid":{"uuid":"DC2"}},"endpoint_uuid":{"uuid":"int"}} - ], - "slice_status":{"slice_status":1}, - "slice_service_ids":[], - "slice_subslice_ids":[], - "slice_constraints":[], - "slice_config":{"config_rules":[ - {"action":1,"custom":{"resource_key":"/settings","resource_value":"{}"}}, - {"action":1,"custom":{"resource_key":"/device[DC1]/endpoint[int]/settings","resource_value":"{}"}}, - {"action":1,"custom":{"resource_key":"/device[DC2]/endpoint[int]/settings","resource_value":"{}"}} - ]} - } - ] -} diff --git a/src/tests/ofc23/descriptors/splitter.json b/src/tests/ofc23/descriptors/splitter.json new file mode 100644 index 000000000..680829e6e --- /dev/null +++ b/src/tests/ofc23/descriptors/splitter.json @@ -0,0 +1,18 @@ +{ + "devices": [ + { + "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"} + ]}}} + ]} + } + ] +} -- GitLab From aa2579a6d2da88755fa3eb5d5251ca9e77734d9e Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:43:20 +0000 Subject: [PATCH 20/77] Device component- IETF L2VPN Driver: - Added DebugAPI client used to acquire remote controller topology info - Extended skeleton and preliminary implemented driver methods - Extended WIM connector with a method to get a specific service --- .../drivers/ietf_l2vpn/IetfL2VpnDriver.py | 98 +++++++++++-------- .../drivers/ietf_l2vpn/TfsDebugApiClient.py | 92 +++++++++++++++++ .../ietf_l2vpn/WimconnectorIETFL2VPN.py | 25 +++++ 3 files changed, 172 insertions(+), 43 deletions(-) create mode 100644 src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py index 68b8090e5..2bcb64384 100644 --- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -15,24 +15,30 @@ import logging, threading from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.EndPoint import json_endpoint_id from common.type_checkers.Checkers import chk_string, chk_type from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES +from device.service.drivers.ietf_l2vpn.TfsDebugApiClient import TfsDebugApiClient from .Tools import connection_point, find_key, wim_mapping from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN LOGGER = logging.getLogger(__name__) -def process_endpoint(method : str, endpoint : Any) -> Any: - LOGGER.warning('[{:s}][process_endpoint] endpoint={:s}'.format(str(method), str(endpoint))) - return endpoint +def process_connectivity_services(method : str, services : Any) -> Any: + LOGGER.warning('[{:s}][process_connectivity_services] services={:s}'.format(str(method), str(services))) + return services def process_connectivity_service(method : str, service : Any) -> Any: LOGGER.warning('[{:s}][process_connectivity_service] service={:s}'.format(str(method), str(service))) return service -def service_exists(param : Any) -> bool: - LOGGER.warning('[service_exists] param={:s}'.format(str(param))) - return False +def service_exists(wim : WimconnectorIETFL2VPN, service_uuid : str) -> bool: + try: + wim.get_connectivity_service_status(service_uuid) + return True + except: # pylint: disable=bare-except + return False ALL_RESOURCE_KEYS = [ RESOURCE_ENDPOINTS, @@ -55,6 +61,7 @@ class IetfL2VpnDriver(_Driver): wim_account = {'user': username, 'password': password} # Mapping updated dynamically with each request config = {'mapping_not_needed': False, 'service_endpoint_mapping': []} + self.dac = TfsDebugApiClient(address, int(port), scheme=scheme, username=username, password=password) self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config) self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors @@ -88,20 +95,22 @@ class IetfL2VpnDriver(_Driver): if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS for i, resource_key in enumerate(resource_keys): str_resource_name = 'resource_key[#{:d}]'.format(i) - chk_string(str_resource_name, resource_key, allow_empty=False) - - if resource_key == RESOURCE_ENDPOINTS: - # return endpoints through debug-api and list-devices method - endpoints = self.debug_api.get_endpoints() - for endpoint in endpoints: results.append(process_endpoint('GetConfig', endpoint)) - elif resource_key == RESOURCE_SERVICES: - # return all services through - services = self.wim.get_all_active_connectivity_services() - for service in services: results.append(process_connectivity_service('GetConfig', service)) - else: - # assume single-service retrieval - service = self.wim.get_connectivity_service() - results.append(process_connectivity_service('GetConfig', service)) + try: + chk_string(str_resource_name, resource_key, allow_empty=False) + if resource_key == RESOURCE_ENDPOINTS: + # return endpoints through debug-api and list-devices method + results.extend(self.dac.get_devices_endpoints()) + elif resource_key == RESOURCE_SERVICES: + # return all services through + reply = self.wim.get_all_active_connectivity_services() + results.extend(process_connectivity_services('GetConfig', reply.json())) + else: + # assume single-service retrieval + reply = self.wim.get_connectivity_service(resource_key) + results.append(process_connectivity_service('GetConfig', reply.json())) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unhandled error processing resource_key({:s})'.format(str(resource_key))) + results.append((resource_key, e)) return results @metered_subclass_method(METRICS_POOL) @@ -114,32 +123,35 @@ class IetfL2VpnDriver(_Driver): LOGGER.info('resource = {:s}'.format(str(resource))) service_uuid = find_key(resource, 'uuid') - a_endpoint = find_key(resource, 'a_endpoint') - z_endpoint = find_key(resource, 'z_endpoint') - #capacity_value = find_key(resource, 'capacity_value') - #capacity_unit = find_key(resource, 'capacity_unit') - #layer_protocol_name = find_key(resource, 'layer_protocol_name') - #layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier') - #direction = find_key(resource, 'direction') - encapsulation_type = find_key(resource, 'encapsulation_type') - vlan_id = find_key(resource, 'vlan_id') - conn_info = {} + if service_exists(self.wim, service_uuid): + exc = NotImplementedError('IETF L2VPN Service Update is still not supported') + results.append((resource[0], exc)) + continue - result = self.wim.get_connectivity_service_status( - service_uuid, conn_info=conn_info) + src_device_uuid = find_key(resource, 'src_device_uuid') + src_endpoint_uuid = find_key(resource, 'src_endpoint_uuid') + dst_device_uuid = find_key(resource, 'dst_device_uuid') + dst_endpoint_uuid = find_key(resource, 'dst_endpoint_uuid') + encap_type = find_key(resource, 'encapsulation_type') + vlan_id = find_key(resource, 'vlan_id') + + src_endpoint_id = json_endpoint_id(json_device_id(src_device_uuid), src_endpoint_uuid) + src_service_endpoint_id, src_mapping = wim_mapping('1', src_endpoint_id) + self.wim.mappings[src_service_endpoint_id] = src_mapping + + dst_endpoint_id = json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid) + dst_service_endpoint_id, dst_mapping = wim_mapping('2', dst_endpoint_id) + self.wim.mappings[dst_service_endpoint_id] = dst_mapping + + connection_points = [ + connection_point(src_service_endpoint_id, encap_type, vlan_id), + connection_point(dst_service_endpoint_id, encap_type, vlan_id), + ] + + result = self.wim.create_connectivity_service(SERVICE_TYPE, connection_points) + LOGGER.info('[SetConfig] CREATE result={:s}'.format(str(result))) - connection_points = [] - for endpoint_id in [a_endpoint, z_endpoint]: - site_id = str(endpoint_id) - self.wim.mappings[endpoint_id] = wim_mapping(site_id, endpoint_id) - connection_points.append(connection_point(endpoint_id, encapsulation_type, vlan_id)) - if service_exists(result): - result = self.wim.create_connectivity_service( - SERVICE_TYPE, connection_points) - else: - self.wim.edit_connectivity_service( - service_uuid, conn_info=conn_info, connection_points=connection_points) results.extend(process_connectivity_service('SetConfig', None)) return results diff --git a/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py b/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py new file mode 100644 index 000000000..f8c4e0d94 --- /dev/null +++ b/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py @@ -0,0 +1,92 @@ +# 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, requests +from requests.auth import HTTPBasicAuth +from typing import Dict, List, Optional + +GET_DEVICES_URL = '{:s}://{:s}:{:d}/restconf/debug-api/devices' +TIMEOUT = 30 + +HTTP_OK_CODES = { + 200, # OK + 201, # Created + 202, # Accepted + 204, # No Content +} + +MAPPING_STATUS = { + 'DEVICEOPERATIONALSTATUS_UNDEFINED': 0, + 'DEVICEOPERATIONALSTATUS_DISABLED' : 1, + 'DEVICEOPERATIONALSTATUS_ENABLED' : 2, +} + +MAPPING_DRIVER = { + 'DEVICEDRIVER_UNDEFINED' : 0, + 'DEVICEDRIVER_OPENCONFIG' : 1, + 'DEVICEDRIVER_TRANSPORT_API' : 2, + 'DEVICEDRIVER_P4' : 3, + 'DEVICEDRIVER_IETF_NETWORK_TOPOLOGY': 4, + 'DEVICEDRIVER_ONF_TR_352' : 5, + 'DEVICEDRIVER_XR' : 6, + 'DEVICEDRIVER_IETF_L2VPN' : 7, +} + +MSG_ERROR = 'Could not retrieve devices in remote TeraFlowSDN instance({:s}). status_code={:s} reply={:s}' + +LOGGER = logging.getLogger(__name__) + +class TfsDebugApiClient: + def __init__( + self, address : str, port : int, scheme : str = 'http', + username : Optional[str] = None, password : Optional[str] = None + ) -> None: + self._url = GET_DEVICES_URL.format(scheme, address, port) + self._auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None + + def get_devices_endpoints(self) -> List[Dict]: + reply = requests.get(self._url, timeout=TIMEOUT, verify=False, auth=self._auth) + if reply.status_code not in HTTP_OK_CODES: + msg = MSG_ERROR.format(str(self._url), str(reply.status_code), str(reply)) + LOGGER.error(msg) + raise Exception(msg) + + result = list() + for json_device in reply.json()['devices']: + device_uuid : str = json_device['device_id']['device_uuid']['uuid'] + device_type : str = json_device['device_type'] + if not device_type.startswith('emu-'): device_type = 'emu-' + device_type + device_status = json_device['device_operational_status'] + device_url = '/devices/device[{:s}]'.format(device_uuid) + device_data = { + 'uuid': json_device['device_id']['device_uuid']['uuid'], + 'name': json_device['name'], + 'type': device_type, + 'status': MAPPING_STATUS[device_status], + 'drivers': [MAPPING_DRIVER[driver] for driver in json_device['device_drivers']], + } + result.append((device_url, device_data)) + + for json_endpoint in json_device['device_endpoints']: + endpoint_uuid = json_endpoint['endpoint_id']['endpoint_uuid']['uuid'] + endpoint_url = '/endpoints/endpoint[{:s}]'.format(endpoint_uuid) + endpoint_data = { + 'device_uuid': device_uuid, + 'uuid': endpoint_uuid, + 'name': json_endpoint['name'], + 'type': json_endpoint['endpoint_type'], + } + result.append((endpoint_url, endpoint_data)) + + return result diff --git a/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py index b3721b2d5..960256cd0 100644 --- a/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py +++ b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py @@ -541,3 +541,28 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): return response except requests.exceptions.ConnectionError: raise SdnConnectorError("Request Timeout", http_code=408) + + def get_connectivity_service(self, service_uuid, conn_info=None): + """Provide information about a specific connection provisioned by a WIM. + + This method should receive as the first argument the UUID generated by + the ``create_connectivity_service`` + """ + try: + self.logger.info("Sending get connectivity service") + servicepoint = ( + "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format( + self.wim["wim_url"], service_uuid + ) + ) + response = requests.get(servicepoint, auth=self.auth) + + if response.status_code != requests.codes.ok: + raise SdnConnectorError( + "Unable to get connectivity service {:s}".format(str(service_uuid)), + http_code=response.status_code, + ) + + return response + except requests.exceptions.ConnectionError: + raise SdnConnectorError("Request Timeout", http_code=408) -- GitLab From 427944404dd56f989cef5fb475aeea1f5e8f05f9 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:44:13 +0000 Subject: [PATCH 21/77] Device component - Microwave Driver: - Added logic to retrieve configured services --- src/device/service/drivers/microwave/Tools.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/device/service/drivers/microwave/Tools.py b/src/device/service/drivers/microwave/Tools.py index 711fb55fd..6245ff0af 100644 --- a/src/device/service/drivers/microwave/Tools.py +++ b/src/device/service/drivers/microwave/Tools.py @@ -14,7 +14,7 @@ import json, logging, requests from requests.auth import HTTPBasicAuth -from typing import Optional, Set +from typing import Dict, Optional, Set from device.service.driver_api._Driver import RESOURCE_ENDPOINTS LOGGER = logging.getLogger(__name__) @@ -43,6 +43,14 @@ def is_exportable_endpoint(node, termination_point_id, links): return False return True +VLAN_CLASSIFICATION_TYPES = {'ietf-eth-tran-types:vlan-classification', 'vlan-classification'} +OUTER_TAG_C_TYPE = {'ietf-eth-tran-types:classify-c-vlan', 'classify-c-vlan'} +def get_vlan_outer_tag(endpoint : Dict) -> Optional[int]: + if endpoint.get('service-classification-type', '') not in VLAN_CLASSIFICATION_TYPES: return None + outer_tag = endpoint.get('outer-tag', {}) + if outer_tag.get('tag-type', '') not in OUTER_TAG_C_TYPE: return None + return outer_tag.get('vlan-value') + def config_getter( root_url : str, resource_key : str, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None, node_ids : Set[str] = set() @@ -92,7 +100,29 @@ def config_getter( for service in service_instances: service_name = service['etht-svc-name'] resource_key = '/services/service[{:s}]'.format(service_name) - result.append((resource_key, service)) + resource_value = {'uuid': service.get('etht-svc-name', '')} + + for endpoint in service.get('etht-svc-end-points', []): + _vlan_id = get_vlan_outer_tag(endpoint) + if _vlan_id is not None: + vlan_id = resource_value.get('vlan_id') + if vlan_id is None: + resource_value['vlan_id'] = _vlan_id + elif vlan_id != _vlan_id: + raise Exception('Incompatible VLAN Ids: {:s}'.format(str(service))) + access_points = endpoint.get('etht-svc-access-points', []) + for access_point in access_points: + if access_point['access-point-id'] == '1': + resource_value['node_id_src'] = access_point['access-node-id'] + resource_value['tp_id_src'] = access_point['access-ltp-id'] + elif access_point['access-point-id'] == '2': + resource_value['node_id_dst'] = access_point['access-node-id'] + resource_value['tp_id_dst'] = access_point['access-ltp-id'] + + if len(node_ids) > 0 and resource_value['node_id_src'] not in node_ids: continue + if len(node_ids) > 0 and resource_value['node_id_dst'] not in node_ids: continue + + result.append((resource_key, resource_value)) except requests.exceptions.Timeout: LOGGER.exception('Timeout connecting {:s}'.format(url)) except Exception as e: # pylint: disable=broad-except -- GitLab From 4d9fa4cf57ffd321857786134439d93be9c4da07 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:44:32 +0000 Subject: [PATCH 22/77] Device component - TAPI Driver: - Added logic to retrieve configured services --- .../service/drivers/transport_api/Tools.py | 91 ++++++++++++++----- .../service/drivers/transport_api/__init__.py | 11 +-- 2 files changed, 71 insertions(+), 31 deletions(-) diff --git a/src/device/service/drivers/transport_api/Tools.py b/src/device/service/drivers/transport_api/Tools.py index 4943648dc..bbd4247f0 100644 --- a/src/device/service/drivers/transport_api/Tools.py +++ b/src/device/service/drivers/transport_api/Tools.py @@ -15,7 +15,7 @@ import json, logging, operator, requests from requests.auth import HTTPBasicAuth from typing import Optional -from device.service.driver_api._Driver import RESOURCE_ENDPOINTS +from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_SERVICES LOGGER = logging.getLogger(__name__) @@ -52,27 +52,74 @@ def config_getter( result.append((resource_key, e)) return result - if resource_key != RESOURCE_ENDPOINTS: return result - - if 'tapi-common:context' in context: - context = context['tapi-common:context'] - elif 'context' in context: - context = context['context'] - - for sip in context['service-interface-point']: - layer_protocol_name = sip.get('layer-protocol-name', '?') - supportable_spectrum = sip.get('tapi-photonic-media:media-channel-service-interface-point-spec', {}) - supportable_spectrum = supportable_spectrum.get('mc-pool', {}) - supportable_spectrum = supportable_spectrum.get('supportable-spectrum', []) - supportable_spectrum = supportable_spectrum[0] if len(supportable_spectrum) == 1 else {} - grid_type = supportable_spectrum.get('frequency-constraint', {}).get('grid-type') - granularity = supportable_spectrum.get('frequency-constraint', {}).get('adjustment-granularity') - direction = sip.get('direction', '?') - endpoint_type = [layer_protocol_name, grid_type, granularity, direction] - str_endpoint_type = ':'.join(filter(lambda i: operator.is_not(i, None), endpoint_type)) - endpoint_url = '/endpoints/endpoint[{:s}]'.format(sip['uuid']) - endpoint_data = {'uuid': sip['uuid'], 'type': str_endpoint_type} - result.append((endpoint_url, endpoint_data)) + if resource_key == RESOURCE_ENDPOINTS: + if 'tapi-common:context' in context: + context = context['tapi-common:context'] + elif 'context' in context: + context = context['context'] + + for sip in context['service-interface-point']: + layer_protocol_name = sip.get('layer-protocol-name', '?') + supportable_spectrum = sip.get('tapi-photonic-media:media-channel-service-interface-point-spec', {}) + supportable_spectrum = supportable_spectrum.get('mc-pool', {}) + supportable_spectrum = supportable_spectrum.get('supportable-spectrum', []) + supportable_spectrum = supportable_spectrum[0] if len(supportable_spectrum) == 1 else {} + grid_type = supportable_spectrum.get('frequency-constraint', {}).get('grid-type') + granularity = supportable_spectrum.get('frequency-constraint', {}).get('adjustment-granularity') + direction = sip.get('direction', '?') + + endpoint_type = [layer_protocol_name, grid_type, granularity, direction] + str_endpoint_type = ':'.join(filter(lambda i: operator.is_not(i, None), endpoint_type)) + sip_uuid = sip['uuid'] + + sip_names = sip.get('name', []) + sip_name = next(iter([ + sip_name['value'] + for sip_name in sip_names + if sip_name['value-name'] == 'local-name' + ]), sip_uuid) + + endpoint_url = '/endpoints/endpoint[{:s}]'.format(sip_uuid) + endpoint_data = {'uuid': sip_uuid, 'name': sip_name, 'type': str_endpoint_type} + result.append((endpoint_url, endpoint_data)) + + elif resource_key == RESOURCE_SERVICES: + if 'tapi-common:context' in context: + context = context['tapi-common:context'] + elif 'context' in context: + context = context['context'] + + if 'tapi-connectivity:connectivity-context' in context: + context = context['tapi-connectivity:connectivity-context'] + elif 'connectivity-context' in context: + context = context['connectivity-context'] + + for conn_svc in context['connectivity-service']: + service_uuid = conn_svc['uuid'] + constraints = conn_svc.get('connectivity-constraint', {}) + total_req_cap = constraints.get('requested-capacity', {}).get('total-size', {}) + + service_url = '/services/service[{:s}]'.format(service_uuid) + service_data = { + 'uuid': service_uuid, + 'direction': constraints.get('connectivity-direction', 'UNIDIRECTIONAL'), + 'capacity_unit': total_req_cap.get('unit', ''), + 'capacity_value': total_req_cap.get('value', ''), + } + + for i,endpoint in enumerate(conn_svc.get('end-point', [])): + layer_protocol_name = endpoint.get('layer-protocol-name') + if layer_protocol_name is not None: + service_data['layer_protocol_name'] = layer_protocol_name + + layer_protocol_qualifier = endpoint.get('layer-protocol-qualifier') + if layer_protocol_qualifier is not None: + service_data['layer_protocol_qualifier'] = layer_protocol_qualifier + + sip = endpoint['service-interface-point']['service-interface-point-uuid'] + service_data['input_sip' if i == 0 else 'output_sip'] = sip + + result.append((service_url, service_data)) return result diff --git a/src/device/service/drivers/transport_api/__init__.py b/src/device/service/drivers/transport_api/__init__.py index 2d3f6df32..d5073c330 100644 --- a/src/device/service/drivers/transport_api/__init__.py +++ b/src/device/service/drivers/transport_api/__init__.py @@ -12,16 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES +from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_SERVICES ALL_RESOURCE_KEYS = [ RESOURCE_ENDPOINTS, - RESOURCE_INTERFACES, - RESOURCE_NETWORK_INSTANCES, + RESOURCE_SERVICES, ] - -RESOURCE_KEY_MAPPINGS = { - RESOURCE_ENDPOINTS : 'component', - RESOURCE_INTERFACES : 'interface', - RESOURCE_NETWORK_INSTANCES: 'network_instance', -} -- GitLab From efed73e32ce34b9c8dda27999afbc99b2d93cda7 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:45:52 +0000 Subject: [PATCH 23/77] Device component: - Added logic to retrieve sub-devices and its endpoints from remote controllers - Added logic to retrieve remote links connecting sub-devices - Added logic to handle device managers, i.e., controllers --- .../service/DeviceServiceServicerImpl.py | 28 +++- src/device/service/ErrorMessages.py | 5 +- src/device/service/Tools.py | 144 +++++++++++++++--- 3 files changed, 151 insertions(+), 26 deletions(-) diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py index be40e64ec..b89766b29 100644 --- a/src/device/service/DeviceServiceServicerImpl.py +++ b/src/device/service/DeviceServiceServicerImpl.py @@ -12,11 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Dict import grpc, logging from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.method_wrappers.ServiceExceptions import NotFoundException, OperationFailedException from common.proto.context_pb2 import ( - Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty) + Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link) from common.proto.device_pb2 import MonitoringSettings from common.proto.device_pb2_grpc import DeviceServiceServicer from common.tools.context_queries.Device import get_device @@ -27,7 +28,7 @@ from .driver_api.DriverInstanceCache import DriverInstanceCache, get_driver from .monitoring.MonitoringLoops import MonitoringLoops from .ErrorMessages import ERROR_MISSING_DRIVER, ERROR_MISSING_KPI from .Tools import ( - check_connect_rules, check_no_endpoints, compute_rules_to_add_delete, configure_rules, deconfigure_rules, + check_connect_rules, check_no_endpoints, compute_rules_to_add_delete, configure_rules, deconfigure_rules, get_device_manager_uuid, populate_config_rules, populate_endpoint_monitoring_resources, populate_endpoints, populate_initial_config_rules, subscribe_kpi, unsubscribe_kpi, update_endpoints) @@ -73,9 +74,16 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): errors = [] + # Sub-devices and sub-links are exposed by intermediate controllers or represent mgmt links. + # They are used to assist in path computation algorithms, and/or to identify dependencies + # (which controller is in charge of which sub-device). + new_sub_devices : Dict[str, Device] = dict() + new_sub_links : Dict[str, Link] = dict() + if len(device.device_endpoints) == 0: # created from request, populate endpoints using driver - errors.extend(populate_endpoints(device, driver, self.monitoring_loops)) + errors.extend(populate_endpoints( + device, driver, self.monitoring_loops, new_sub_devices, new_sub_links)) if len(device.device_config.config_rules) == len(connection_config_rules): # created from request, populate config rules using driver @@ -87,12 +95,20 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): for error in errors: LOGGER.error(error) raise OperationFailedException('AddDevice', extra_details=errors) + device.device_operational_status = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED device_id = context_client.SetDevice(device) + for sub_device in new_sub_devices.values(): + context_client.SetDevice(sub_device) + + for sub_links in new_sub_links.values(): + context_client.SetLink(sub_links) + # Update endpoint monitoring resources with UUIDs device_with_uuids = context_client.GetDevice(device_id) populate_endpoint_monitoring_resources(device_with_uuids, self.monitoring_loops) + context_client.close() return device_id finally: self.mutex_queues.signal_done(device_uuid) @@ -109,6 +125,12 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): if device is None: raise NotFoundException('Device', device_uuid, extra_details='loading in ConfigureDevice') + device_manager_uuid = get_device_manager_uuid(device) + if device_manager_uuid is not None: + device = get_device(context_client, device_manager_uuid, rw_copy=True) + if device is None: + raise NotFoundException('Device', device_manager_uuid, extra_details='loading in ConfigureDevice') + driver : _Driver = get_driver(self.driver_instance_cache, device) if driver is None: msg = ERROR_MISSING_DRIVER.format(device_uuid=str(device_uuid)) diff --git a/src/device/service/ErrorMessages.py b/src/device/service/ErrorMessages.py index 1fbea721f..bb7702e4e 100644 --- a/src/device/service/ErrorMessages.py +++ b/src/device/service/ErrorMessages.py @@ -14,9 +14,9 @@ _DEVICE_ID = 'DeviceId({device_uuid:s})' _ENDPOINT_ID = 'EndpointId({endpoint_uuid:s})' -_ENDPOINT_DATA = 'EndpointId({endpoint_data:s})' _KPI = 'Kpi({kpi_uuid:s})' _DEVICE_ENDPOINT_ID = _DEVICE_ID + '/' + _ENDPOINT_ID +_RESOURCE = 'Resource({resource_data:s})' _RESOURCE_KEY = 'Resource(key={resource_key:s})' _RESOURCE_KEY_VALUE = 'Resource(key={resource_key:s}, value={resource_value:s})' _SUBSCRIPTION = 'Subscription(key={subscr_key:s}, duration={subscr_duration:s}, interval={subscr_interval:s})' @@ -26,7 +26,8 @@ _ERROR = 'Error({error:s})' ERROR_MISSING_DRIVER = _DEVICE_ID + ' has not been added to this Device instance' ERROR_MISSING_KPI = _KPI + ' not found' -ERROR_BAD_ENDPOINT = _DEVICE_ID + ': GetConfig retrieved malformed ' + _ENDPOINT_DATA +ERROR_BAD_RESOURCE = _DEVICE_ID + ': GetConfig retrieved malformed ' + _RESOURCE +ERROR_UNSUP_RESOURCE = _DEVICE_ID + ': GetConfig retrieved unsupported ' + _RESOURCE ERROR_GET = _DEVICE_ID + ': Unable to Get ' + _RESOURCE_KEY + '; ' + _ERROR ERROR_GET_INIT = _DEVICE_ID + ': Unable to Get Initial ' + _RESOURCE_KEY + '; ' + _ERROR diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index 571e8acda..1dccea3ab 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -13,19 +13,20 @@ # limitations under the License. import json, logging -from typing import Any, Dict, List, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple, Union from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME from common.method_wrappers.ServiceExceptions import InvalidArgumentException -from common.proto.context_pb2 import ConfigActionEnum, Device, DeviceConfig +from common.proto.context_pb2 import ConfigActionEnum, Device, DeviceConfig, Link from common.proto.device_pb2 import MonitoringSettings from common.proto.kpi_sample_types_pb2 import KpiSampleType from common.tools.grpc.ConfigRules import update_config_rule_custom from common.tools.grpc.Tools import grpc_message_to_json +from context.client.ContextClient import ContextClient from .driver_api._Driver import _Driver, RESOURCE_ENDPOINTS from .monitoring.MonitoringLoops import MonitoringLoops from .ErrorMessages import ( - ERROR_BAD_ENDPOINT, ERROR_DELETE, ERROR_GET, ERROR_GET_INIT, ERROR_MISSING_KPI, ERROR_SAMPLETYPE, ERROR_SET, - ERROR_SUBSCRIBE, ERROR_UNSUBSCRIBE + ERROR_BAD_RESOURCE, ERROR_DELETE, ERROR_GET, ERROR_GET_INIT, ERROR_MISSING_KPI, ERROR_SAMPLETYPE, ERROR_SET, + ERROR_SUBSCRIBE, ERROR_UNSUBSCRIBE, ERROR_UNSUP_RESOURCE ) LOGGER = logging.getLogger(__name__) @@ -77,19 +78,51 @@ def check_no_endpoints(device_endpoints) -> None: extra_details='RPC method AddDevice does not accept Endpoints. Endpoints are discovered through '\ 'interrogation of the physical device.') -def populate_endpoints(device : Device, driver : _Driver, monitoring_loops : MonitoringLoops) -> List[str]: +def get_device_manager_uuid(device : Device) -> Optional[str]: + for config_rule in device.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + if config_rule.custom.resource_key != '_manager': continue + device_manager_id = json.loads(config_rule.custom.resource_value) + return device_manager_id['uuid'] + return None + +def populate_endpoints( + device : Device, driver : _Driver, monitoring_loops : MonitoringLoops, + new_sub_devices : Dict[str, Device], new_sub_links : Dict[str, Link] +) -> List[str]: device_uuid = device.device_id.device_uuid.uuid + device_name = device.name resources_to_get = [RESOURCE_ENDPOINTS] results_getconfig = driver.GetConfig(resources_to_get) + LOGGER.debug('results_getconfig = {:s}'.format(str(results_getconfig))) + + # first quick pass to identify need of mgmt endpoints and links + add_mgmt_port = False + for resource_data in results_getconfig: + if len(resource_data) != 2: continue + resource_key, _ = resource_data + if resource_key.startswith('/devices/device'): + add_mgmt_port = True + break + + if add_mgmt_port: + # add mgmt port to main device + device_mgmt_endpoint = device.device_endpoints.add() + device_mgmt_endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME + device_mgmt_endpoint.endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME + device_mgmt_endpoint.endpoint_id.device_id.device_uuid.uuid = device_uuid + device_mgmt_endpoint.endpoint_id.endpoint_uuid.uuid = 'mgmt' + device_mgmt_endpoint.name = 'mgmt' + device_mgmt_endpoint.endpoint_type = 'mgmt' errors : List[str] = list() - for endpoint in results_getconfig: - if len(endpoint) != 2: - errors.append(ERROR_BAD_ENDPOINT.format(device_uuid=device_uuid, endpoint_data=str(endpoint))) + for resource_data in results_getconfig: + if len(resource_data) != 2: + errors.append(ERROR_BAD_RESOURCE.format(device_uuid=device_uuid, resource_data=str(resource_data))) continue - resource_key, resource_value = endpoint + resource_key, resource_value = resource_data if isinstance(resource_value, Exception): errors.append(ERROR_GET.format( device_uuid=device_uuid, resource_key=str(resource_key), error=str(resource_value))) @@ -97,19 +130,88 @@ def populate_endpoints(device : Device, driver : _Driver, monitoring_loops : Mon if resource_value is None: continue - endpoint_uuid = resource_value.get('uuid') - - device_endpoint = device.device_endpoints.add() - device_endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME - device_endpoint.endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME - device_endpoint.endpoint_id.device_id.device_uuid.uuid = device_uuid - device_endpoint.endpoint_id.endpoint_uuid.uuid = endpoint_uuid - device_endpoint.endpoint_type = resource_value.get('type') + if resource_key.startswith('/devices/device'): + # create sub-device + _sub_device_uuid = resource_value['uuid'] + _sub_device = Device() + _sub_device.device_id.device_uuid.uuid = _sub_device_uuid # pylint: disable=no-member + _sub_device.name = resource_value['name'] + _sub_device.device_type = resource_value['type'] + _sub_device.device_operational_status = resource_value['status'] + + # Sub-devices should not have a driver assigned. Instead, they should have + # a config rule specifying their manager. + #_sub_device.device_drivers.extend(resource_value['drivers']) # pylint: disable=no-member + manager_config_rule = _sub_device.device_config.config_rules.add() + manager_config_rule.action = ConfigActionEnum.CONFIGACTION_SET + manager_config_rule.custom.resource_key = '_manager' + manager = {'uuid': device_uuid, 'name': device_name} + manager_config_rule.custom.resource_value = json.dumps(manager, indent=0, sort_keys=True) + + new_sub_devices[_sub_device_uuid] = _sub_device + + # add mgmt port to sub-device + _sub_device_mgmt_endpoint = _sub_device.device_endpoints.add() # pylint: disable=no-member + _sub_device_mgmt_endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME + _sub_device_mgmt_endpoint.endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME + _sub_device_mgmt_endpoint.endpoint_id.device_id.device_uuid.uuid = _sub_device_uuid + _sub_device_mgmt_endpoint.endpoint_id.endpoint_uuid.uuid = 'mgmt' + _sub_device_mgmt_endpoint.name = 'mgmt' + _sub_device_mgmt_endpoint.endpoint_type = 'mgmt' + + # add mgmt link + _mgmt_link_uuid = '{:s}/{:s}=={:s}/{:s}'.format(device_name, 'mgmt', _sub_device.name, 'mgmt') + _mgmt_link = Link() + _mgmt_link.link_id.link_uuid.uuid = _mgmt_link_uuid # pylint: disable=no-member + _mgmt_link.name = _mgmt_link_uuid + _mgmt_link.link_endpoint_ids.append(device_mgmt_endpoint.endpoint_id) # pylint: disable=no-member + _mgmt_link.link_endpoint_ids.append(_sub_device_mgmt_endpoint.endpoint_id) # pylint: disable=no-member + new_sub_links[_mgmt_link_uuid] = _mgmt_link + + elif resource_key.startswith('/endpoints/endpoint'): + endpoint_uuid = resource_value['uuid'] + _device_uuid = resource_value.get('device_uuid') + endpoint_name = resource_value.get('name') + + if _device_uuid is None: + # add endpoint to current device + device_endpoint = device.device_endpoints.add() + device_endpoint.endpoint_id.device_id.device_uuid.uuid = device_uuid + else: + # add endpoint to specified device + device_endpoint = new_sub_devices[_device_uuid].device_endpoints.add() + device_endpoint.endpoint_id.device_id.device_uuid.uuid = _device_uuid + + device_endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME + device_endpoint.endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME + + device_endpoint.endpoint_id.endpoint_uuid.uuid = endpoint_uuid + if endpoint_name is not None: device_endpoint.name = endpoint_name + device_endpoint.endpoint_type = resource_value.get('type', '-') + + sample_types : Dict[int, str] = resource_value.get('sample_types', {}) + for kpi_sample_type, monitor_resource_key in sample_types.items(): + device_endpoint.kpi_sample_types.append(kpi_sample_type) + monitoring_loops.add_resource_key(device_uuid, endpoint_uuid, kpi_sample_type, monitor_resource_key) + + elif resource_key.startswith('/links/link'): + # create sub-link + _sub_link_uuid = resource_value['uuid'] + _sub_link = Link() + _sub_link.link_id.link_uuid.uuid = _sub_link_uuid # pylint: disable=no-member + _sub_link.name = resource_value['name'] + new_sub_links[_sub_link_uuid] = _sub_link + + for device_uuid,endpoint_uuid in resource_value['name']: + _sub_link_endpoint_id = _sub_link.link_endpoint_ids.add() # pylint: disable=no-member + _sub_link_endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME + _sub_link_endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME + _sub_link_endpoint_id.device_id.device_uuid.uuid = device_uuid + _sub_link_endpoint_id.endpoint_uuid.uuid = endpoint_uuid - sample_types : Dict[int, str] = resource_value.get('sample_types', {}) - for kpi_sample_type, monitor_resource_key in sample_types.items(): - device_endpoint.kpi_sample_types.append(kpi_sample_type) - monitoring_loops.add_resource_key(device_uuid, endpoint_uuid, kpi_sample_type, monitor_resource_key) + else: + errors.append(ERROR_UNSUP_RESOURCE.format(device_uuid=device_uuid, resource_data=str(resource_data))) + continue return errors -- GitLab From 1731c04e26b92f93400f4ba57d84eb5c1f11d1ee Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 16:46:24 +0000 Subject: [PATCH 24/77] Context component: - Added logic to keep order of link endpoints --- src/context/service/database/Link.py | 3 ++- src/context/service/database/models/LinkModel.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/context/service/database/Link.py b/src/context/service/database/Link.py index 8d195cb1d..299827dbd 100644 --- a/src/context/service/database/Link.py +++ b/src/context/service/database/Link.py @@ -64,13 +64,14 @@ def link_set(db_engine : Engine, request : Link) -> Tuple[Dict, bool]: topology_uuids : Set[str] = set() related_topologies : List[Dict] = list() link_endpoints_data : List[Dict] = list() - for endpoint_id in request.link_endpoint_ids: + for i,endpoint_id in enumerate(request.link_endpoint_ids): endpoint_topology_uuid, _, endpoint_uuid = endpoint_get_uuid( endpoint_id, allow_random=False) link_endpoints_data.append({ 'link_uuid' : link_uuid, 'endpoint_uuid': endpoint_uuid, + 'position' : i, }) if endpoint_topology_uuid not in topology_uuids: diff --git a/src/context/service/database/models/LinkModel.py b/src/context/service/database/models/LinkModel.py index ee591f5c8..e9fd9bc87 100644 --- a/src/context/service/database/models/LinkModel.py +++ b/src/context/service/database/models/LinkModel.py @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from sqlalchemy import Column, DateTime, ForeignKey, String +import operator +from sqlalchemy import CheckConstraint, Column, DateTime, ForeignKey, Integer, String from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship from typing import Dict @@ -38,7 +39,7 @@ class LinkModel(_Base): 'name' : self.link_name, 'link_endpoint_ids': [ link_endpoint.endpoint.dump_id() - for link_endpoint in self.link_endpoints + for link_endpoint in sorted(self.link_endpoints, key=operator.attrgetter('position')) ], } @@ -47,6 +48,11 @@ class LinkEndPointModel(_Base): link_uuid = Column(ForeignKey('link.link_uuid', ondelete='CASCADE' ), primary_key=True) endpoint_uuid = Column(ForeignKey('endpoint.endpoint_uuid', ondelete='RESTRICT'), primary_key=True, index=True) + position = Column(Integer, nullable=False) link = relationship('LinkModel', back_populates='link_endpoints', lazy='joined') endpoint = relationship('EndPointModel', lazy='joined') # back_populates='link_endpoints' + + __table_args__ = ( + CheckConstraint(position >= 0, name='check_position_value'), + ) -- GitLab From 788d91ca854640efaedea2427b0b7d7ea1f0bca5 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:37:36 +0000 Subject: [PATCH 25/77] Compute component: - Added bearers for OFC'23 --- .../rest_server/nbi_plugins/ietf_l2vpn/Constants.py | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 f95b532af..b4f34c12a 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,14 @@ 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/2': ('PE1', '1/1', '10.1.1.1', None, 0, None, None, None, None), + 'PE1:1/3': ('PE1', '1/2', '10.1.1.2', None, 0, None, None, None, None), + 'PE2:1/2': ('PE2', '1/1', '10.2.1.1', None, 0, None, None, None, None), + 'PE2:1/3': ('PE2', '1/2', '10.2.1.2', None, 0, None, None, None, None), + 'PE3:1/2': ('PE3', '1/1', '10.3.1.1', None, 0, None, None, None, None), + 'PE3:1/3': ('PE3', '1/2', '10.3.1.2', None, 0, None, None, None, None), + 'PE4:1/2': ('PE4', '1/1', '10.4.1.1', None, 0, None, None, None, None), + 'PE4:1/3': ('PE4', '1/2', '10.4.1.2', None, 0, None, None, None, None), } -- GitLab From 832ce92478ad2385291bd42779c16581bb150b65 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:38:23 +0000 Subject: [PATCH 26/77] Device component - IETF L2VPN Driver: - Corrected resource parsing in SetConfig and DeleteConfig --- .../drivers/ietf_l2vpn/IetfL2VpnDriver.py | 109 ++++++++++-------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py index 2bcb64384..8c7feb249 100644 --- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, threading +import json, logging, threading from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.tools.object_factory.Device import json_device_id @@ -20,7 +20,7 @@ from common.tools.object_factory.EndPoint import json_endpoint_id from common.type_checkers.Checkers import chk_string, chk_type from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES from device.service.drivers.ietf_l2vpn.TfsDebugApiClient import TfsDebugApiClient -from .Tools import connection_point, find_key, wim_mapping +from .Tools import connection_point, wim_mapping from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN LOGGER = logging.getLogger(__name__) @@ -121,38 +121,55 @@ class IetfL2VpnDriver(_Driver): self.wim.check_credentials() for resource in resources: LOGGER.info('resource = {:s}'.format(str(resource))) - - service_uuid = find_key(resource, 'uuid') - - if service_exists(self.wim, service_uuid): - exc = NotImplementedError('IETF L2VPN Service Update is still not supported') - results.append((resource[0], exc)) - continue - - src_device_uuid = find_key(resource, 'src_device_uuid') - src_endpoint_uuid = find_key(resource, 'src_endpoint_uuid') - dst_device_uuid = find_key(resource, 'dst_device_uuid') - dst_endpoint_uuid = find_key(resource, 'dst_endpoint_uuid') - encap_type = find_key(resource, 'encapsulation_type') - vlan_id = find_key(resource, 'vlan_id') - - src_endpoint_id = json_endpoint_id(json_device_id(src_device_uuid), src_endpoint_uuid) - src_service_endpoint_id, src_mapping = wim_mapping('1', src_endpoint_id) - self.wim.mappings[src_service_endpoint_id] = src_mapping - - dst_endpoint_id = json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid) - dst_service_endpoint_id, dst_mapping = wim_mapping('2', dst_endpoint_id) - self.wim.mappings[dst_service_endpoint_id] = dst_mapping - - connection_points = [ - connection_point(src_service_endpoint_id, encap_type, vlan_id), - connection_point(dst_service_endpoint_id, encap_type, vlan_id), - ] - - result = self.wim.create_connectivity_service(SERVICE_TYPE, connection_points) - LOGGER.info('[SetConfig] CREATE result={:s}'.format(str(result))) - - results.extend(process_connectivity_service('SetConfig', None)) + resource_key,resource_value = resource + try: + resource_value = json.loads(resource_value) + service_uuid = resource_value['uuid'] + + if service_exists(self.wim, service_uuid): + exc = NotImplementedError('IETF L2VPN Service Update is still not supported') + results.append((resource[0], exc)) + continue + + src_device_endpoint = resource_value['src'] + src_device = src_device_endpoint['device'] + #src_device_uuid = src_device['uuid'] + src_device_name = src_device['name'] + src_endpoint = src_device_endpoint['endpoint'] + #src_endpoint_uuid = src_endpoint['uuid'] + src_endpoint_name = src_endpoint['name'] + + dst_device_endpoint = resource_value['dst'] + dst_device = dst_device_endpoint['device'] + #dst_device_uuid = dst_device['uuid'] + dst_device_name = dst_device['name'] + dst_endpoint = dst_device_endpoint['endpoint'] + #dst_endpoint_uuid = dst_endpoint['uuid'] + dst_endpoint_name = dst_endpoint['name'] + + encap_type = resource_value['encapsulation_type'] + vlan_id = resource_value['vlan_id'] + + src_endpoint_id = json_endpoint_id(json_device_id(src_device_name), src_endpoint_name) + src_service_endpoint_id, src_mapping = wim_mapping('1', src_endpoint_id) + self.wim.mappings[src_service_endpoint_id] = src_mapping + + dst_endpoint_id = json_endpoint_id(json_device_id(dst_device_name), dst_endpoint_name) + dst_service_endpoint_id, dst_mapping = wim_mapping('2', dst_endpoint_id) + self.wim.mappings[dst_service_endpoint_id] = dst_mapping + + connection_points = [ + connection_point(src_service_endpoint_id, encap_type, vlan_id), + connection_point(dst_service_endpoint_id, encap_type, vlan_id), + ] + + result = self.wim.create_connectivity_service(SERVICE_TYPE, connection_points) + LOGGER.info('[SetConfig] CREATE result={:s}'.format(str(result))) + + results.extend(process_connectivity_service('SetConfig', None)) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unhandled error processing resource_key({:s})'.format(str(resource_key))) + results.append((resource_key, e)) return results @metered_subclass_method(METRICS_POOL) @@ -163,18 +180,18 @@ class IetfL2VpnDriver(_Driver): self.wim.check_credentials() for resource in resources: LOGGER.info('resource = {:s}'.format(str(resource))) - service_uuid = find_key(resource, 'uuid') - - conn_info = {} - - result = self.wim.get_connectivity_service_status( - service_uuid, conn_info=conn_info) - if service_exists(result): - self.wim.delete_connectivity_service( - service_uuid, conn_info=conn_info) - else: - result = False - results.extend(process_connectivity_service('DeleteConfig', None)) + resource_key,resource_value = resource + try: + resource_value = json.loads(resource_value) + service_uuid = resource_value['uuid'] + + if service_exists(self.wim, service_uuid): + self.wim.delete_connectivity_service(service_uuid) + + results.extend(process_connectivity_service('DeleteConfig', None)) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unhandled error processing resource_key({:s})'.format(str(resource_key))) + results.append((resource_key, e)) return results @metered_subclass_method(METRICS_POOL) -- GitLab From d6a9525f1191558ed7df7546f2c2cadd9aec9db6 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:39:05 +0000 Subject: [PATCH 27/77] Service component - L2NM IETF L2VPN service handler: - Corrected Device request composition --- .../L2NM_IETFL2VPN_ServiceHandler.py | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) 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 index f84c8c824..f58c7809f 100644 --- a/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py +++ b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py @@ -53,26 +53,35 @@ class L2NM_IETFL2VPN_ServiceHandler(_ServiceHandler): 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_manager = self.__task_executor.get_device_manager(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_manager = self.__task_executor.get_device_manager(dst_device) - if src_device_uuid != dst_device_uuid: + if src_manager.device_id.device_uuid.uuid != dst_manager.device_id.device_uuid.uuid: raise Exception('Different Src-Dst devices not supported by now') - device_uuid = src_device_uuid - - device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + manager = src_manager json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), { 'uuid' : service_uuid, - 'src_device_uuid' : src_device_uuid, - 'src_endpoint_uuid' : src_endpoint_uuid, - 'dst_device_uuid' : dst_device_uuid, - 'dst_endpoint_uuid' : dst_endpoint_uuid, 'encapsulation_type': encap_type, 'vlan_id' : vlan_id, + 'src': { + 'device' : {'uuid': src_device_uuid, 'name': src_device.name }, + 'endpoint': {'uuid': src_endpoint_uuid, 'name': src_endpoint.name}, + }, + 'dst': { + 'device' : {'uuid': dst_device_uuid, 'name': dst_device.name }, + 'endpoint': {'uuid': dst_endpoint_uuid, 'name': dst_endpoint.name}, + }, }) - 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 manager.device_config.config_rules[:] + manager.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(manager) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid))) -- GitLab From f31ec4b7ea29b384ef8e384efeabd04deea4e628 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:39:29 +0000 Subject: [PATCH 28/77] Service component: - Added helprt method to get manager for devices --- .../service/task_scheduler/TaskExecutor.py | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index e475f29c6..54aef8c37 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -105,6 +105,22 @@ class TaskExecutor: self._device_client.ConfigureDevice(device) self._store_grpc_object(CacheableObjectType.DEVICE, device_key, device) + def get_device_manager(self, device : Device) -> Optional[Device]: + json_manager = None + for config_rule in device.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + if config_rule.custom.resource_key != '_manager': continue + json_manager = json.loads(config_rule.custom.resource_value) + break + + if json_manager is None: return None + + manager_uuid = json_manager['uuid'] + manager = self.get_device(DeviceId(**json_device_id(manager_uuid))) + manager_uuid = manager.device_id.device_uuid.uuid + if manager is None: raise Exception('Device({:s}) not found'.format(str(manager_uuid))) + return manager + def get_devices_from_connection( self, connection : Connection, exclude_managed : bool = False ) -> Dict[str, Device]: @@ -114,23 +130,13 @@ class TaskExecutor: device_uuid = endpoint_id.device_id.device_uuid.uuid if device is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) - manager = None - for config_rule in device.device_config.config_rules: - if config_rule.WhichOneof('config_rule') != 'custom': continue - if config_rule.custom.resource_key != '_manager': continue - manager = json.loads(config_rule.custom.resource_value) - break - - if manager is not None: + manager = self.get_device_manager(device) + if manager is None: + devices[device_uuid] = device + else: if not exclude_managed: devices[device_uuid] = device - manager_uuid = manager['uuid'] - manager = self.get_device(DeviceId(**json_device_id(manager_uuid))) - manager_uuid = manager.device_id.device_uuid.uuid - if manager is None: raise Exception('Device({:s}) not found'.format(str(manager_uuid))) - devices[manager_uuid] = manager - else: - devices[device_uuid] = device + devices[manager.device_id.device_uuid.uuid] = manager return devices # ----- Service-related methods ------------------------------------------------------------------------------------ -- GitLab From 912d24e50a30732a1f4664a4201bd79628c99360 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:40:08 +0000 Subject: [PATCH 29/77] Tools - Mock MW SDN Ctrl: - Added logs to investigate issue (POST seems not to work) --- .../tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py index f002d2376..d4aabbe5b 100644 --- a/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py +++ b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py @@ -91,22 +91,37 @@ class Services(Resource): return jsonify({'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': services}}) def post(self): + LOGGER.info('[post] begin') try: - json_request = request.json - if not json_request: abort(400) - if not isinstance(json_request, dict): abort(400) - if 'etht-svc-instances' not in json_request: abort(400) + json_request = request.get_json() + LOGGER.info('[post] json_request={:s}'.format(str(json_request))) + if not json_request: + LOGGER.info('[post] abort 1') + abort(400) + if not isinstance(json_request, dict): + LOGGER.info('[post] abort 2') + abort(400) + if 'etht-svc-instances' not in json_request: + LOGGER.info('[post] abort 3') + abort(400) json_services = json_request['etht-svc-instances'] - if not isinstance(json_services, list): abort(400) - if len(json_services) != 1: abort(400) + LOGGER.info('[post] json_services={:s}'.format(str(json_services))) + if not isinstance(json_services, list): + LOGGER.info('[post] abort 4') + abort(400) + if len(json_services) != 1: + LOGGER.info('[post] abort 5') + abort(400) svc_data = json_services[0] + LOGGER.info('[post] svc_data={:s}'.format(str(svc_data))) etht_svc_name = svc_data['etht-svc-name'] + LOGGER.info('[post] etht_svc_name={:s}'.format(str(etht_svc_name))) NETWORK_SERVICES[etht_svc_name] = svc_data + LOGGER.info('[post] done') return jsonify({}), 201 except: LOGGER.exception('Exception in POST') - class DelServices(Resource): def delete(self, service_uuid : str): NETWORK_SERVICES.pop(service_uuid, None) -- GitLab From 1e3dfb1a91e9ec7e26f6eace09f7244fac08c4d9 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:42:01 +0000 Subject: [PATCH 30/77] PathComp component - Frontend: - Added logic to ignore mgmt-related links - Improved composition of subservice config rules - Cleaned up Constants - Updated sub-service computation logic --- .../frontend/service/algorithms/_Algorithm.py | 134 ++++++------ .../algorithms/tools/ComposeConfigRules.py | 89 ++++++++ .../algorithms/tools/ComputeSubServices.py | 195 ++++++++++++++---- .../algorithms/tools/ConstantsMappings.py | 49 ----- 4 files changed, 319 insertions(+), 148 deletions(-) create mode 100644 src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py index b63167749..0286e1420 100644 --- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py +++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py @@ -15,13 +15,12 @@ import json, logging, requests from typing import Dict, List, Optional, Tuple, Union from common.proto.context_pb2 import ( - ConfigRule, Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, - ServiceTypeEnum) + Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum) from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest -from common.tools.object_factory.ConfigRule import json_config_rule_set from pathcomp.frontend.Config import BACKEND_URL -from pathcomp.frontend.service.algorithms.tools.ConstantsMappings import DEVICE_LAYER_TO_SERVICE_TYPE, DeviceLayerEnum from .tools.EroPathToHops import eropath_to_hops +from .tools.ComposeConfigRules import ( + compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules) from .tools.ComposeRequest import compose_device, compose_link, compose_service from .tools.ComputeSubServices import ( convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection) @@ -78,6 +77,8 @@ class _Algorithm: def add_links(self, grpc_links : Union[List[Link], LinkList]) -> None: if isinstance(grpc_links, LinkList): grpc_links = grpc_links.links for grpc_link in grpc_links: + if 'mgmt' in grpc_link.name.lower(): continue + json_link = compose_link(grpc_link) if len(json_link['link_endpoint_ids']) != 2: continue self.link_list.append(json_link) @@ -148,9 +149,8 @@ class _Algorithm: return connection def add_service_to_reply( - self, reply : PathCompReply, context_uuid : str, service_uuid : str, - device_layer : Optional[DeviceLayerEnum] = None, path_hops : List[Dict] = [], - config_rules : List = [] + self, reply : PathCompReply, context_uuid : str, service_uuid : str, service_type : ServiceTypeEnum, + path_hops : List[Dict] = [], config_rules : List = [] ) -> Service: # TODO: implement support for multi-point services # Control deactivated to enable disjoint paths with multiple redundant endpoints on each side @@ -159,44 +159,41 @@ class _Algorithm: service_key = (context_uuid, service_uuid) tuple_service = self.service_dict.get(service_key) - if tuple_service is not None: - service = reply.services.add() - service.CopyFrom(tuple_service[1]) + + service = reply.services.add() + service.service_id.context_id.context_uuid.uuid = context_uuid + service.service_id.service_uuid.uuid = service_uuid + service.service_type = service_type + + if service_type == ServiceTypeEnum.SERVICETYPE_L2NM: + compose_l2nm_config_rules(config_rules, service.service_config.config_rules) + elif service_type == ServiceTypeEnum.SERVICETYPE_L3NM: + compose_l3nm_config_rules(config_rules, service.service_config.config_rules) + elif service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE: + compose_tapi_config_rules(config_rules, service.service_config.config_rules) else: - service = reply.services.add() - service.service_id.context_id.context_uuid.uuid = context_uuid - service.service_id.service_uuid.uuid = service_uuid - - if device_layer is not None: - service_type = DEVICE_LAYER_TO_SERVICE_TYPE.get(device_layer.value) - if service_type is None: - MSG = 'Unable to map DeviceLayer({:s}) to ServiceType' - raise Exception(MSG.format(str(device_layer))) - service.service_type = service_type - - if service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE: - json_tapi_settings = { - 'capacity_value' : 50.0, - 'capacity_unit' : 'GHz', - 'layer_proto_name': 'PHOTONIC_MEDIA', - 'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC', - 'direction' : 'UNIDIRECTIONAL', - } - config_rule = ConfigRule(**json_config_rule_set('/settings', json_tapi_settings)) - service.service_config.config_rules.append(config_rule) - else: - service.service_config.config_rules.extend(config_rules) + MSG = 'Unhandled generic Config Rules for service {:s} {:s}' + self.logger.warning(MSG.format(str(service_uuid), str(ServiceTypeEnum.Name(service_type)))) - service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + compose_device_config_rules(config_rules, service.service_config.config_rules, path_hops) + + if path_hops is not None and len(path_hops) > 0: + ingress_endpoint_id = service.service_endpoint_ids.add() + ingress_endpoint_id.device_id.device_uuid.uuid = path_hops[0]['device'] + ingress_endpoint_id.endpoint_uuid.uuid = path_hops[0]['ingress_ep'] - if path_hops is not None and len(path_hops) > 0: - ingress_endpoint_id = service.service_endpoint_ids.add() - ingress_endpoint_id.device_id.device_uuid.uuid = path_hops[0]['device'] - ingress_endpoint_id.endpoint_uuid.uuid = path_hops[0]['ingress_ep'] + egress_endpoint_id = service.service_endpoint_ids.add() + egress_endpoint_id.device_id.device_uuid.uuid = path_hops[-1]['device'] + egress_endpoint_id.endpoint_uuid.uuid = path_hops[-1]['egress_ep'] - egress_endpoint_id = service.service_endpoint_ids.add() - egress_endpoint_id.device_id.device_uuid.uuid = path_hops[-1]['device'] - egress_endpoint_id.endpoint_uuid.uuid = path_hops[-1]['egress_ep'] + if tuple_service is None: + service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + else: + service.name = tuple_service[1].name + service.service_status.CopyFrom(tuple_service[1].service_status) + service.timestamp.CopyFrom(tuple_service[1].timestamp) + for constraint in tuple_service[1].service_constraints: + service.service_constraints.add().CopyFrom(constraint) return service @@ -206,41 +203,54 @@ class _Algorithm: grpc_services : Dict[Tuple[str, str], Service] = {} grpc_connections : Dict[str, Connection] = {} for response in response_list: - service_id = response['serviceId'] - context_uuid = service_id['contextId'] - service_uuid = service_id['service_uuid'] - service_key = (context_uuid, service_uuid) - upper_service = self.add_service_to_reply(reply, context_uuid, service_uuid) - grpc_services[service_key] = upper_service + orig_service_id = response['serviceId'] + context_uuid = orig_service_id['contextId'] + main_service_uuid = orig_service_id['service_uuid'] + orig_service_key = (context_uuid, main_service_uuid) + _,grpc_orig_service = self.service_dict[orig_service_key] + main_service_type = grpc_orig_service.service_type no_path_issue = response.get('noPath', {}).get('issue') if no_path_issue is not None: # no path found: leave connection with no endpoints # no_path_issue == 1 => no path due to a constraint + grpc_services[service_key] = grpc_orig_service continue + orig_config_rules = grpc_orig_service.service_config.config_rules + for service_path_ero in response['path']: + self.logger.debug('service_path_ero["devices"] = {:s}'.format(str(service_path_ero['devices']))) + _endpoint_to_link_dict = {k:v[0] for k,v in self.endpoint_to_link_dict.items()} + self.logger.debug('self.endpoint_to_link_dict = {:s}'.format(str(_endpoint_to_link_dict))) path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict) + self.logger.debug('path_hops = {:s}'.format(str(path_hops))) try: - connections = convert_explicit_path_hops_to_connections(path_hops, self.device_dict, service_uuid) + _device_dict = {k:v[0] for k,v in self.device_dict.items()} + self.logger.debug('self.device_dict = {:s}'.format(str(_device_dict))) + connections = convert_explicit_path_hops_to_connections( + path_hops, self.device_dict, main_service_uuid, main_service_type) + self.logger.debug('EXTRAPOLATED connections = {:s}'.format(str(connections))) except: # pylint: disable=bare-except - # if not able to extrapolate sub-services and sub-connections, - # assume single service and single connection - connections = convert_explicit_path_hops_to_plain_connection(path_hops, service_uuid) + MSG = ' '.join([ + 'Unable to Extrapolate sub-services and sub-connections.', + 'Assuming single-service and single-connection.', + ]) + self.logger.exception(MSG) + connections = convert_explicit_path_hops_to_plain_connection( + path_hops, main_service_uuid, main_service_type) + self.logger.debug('BASIC connections = {:s}'.format(str(connections))) for connection in connections: - connection_uuid,device_layer,path_hops,_ = connection + connection_uuid,service_type,path_hops,_ = connection service_key = (context_uuid, connection_uuid) - grpc_service = grpc_services.get(service_key) - if grpc_service is None: - config_rules = upper_service.service_config.config_rules - grpc_service = self.add_service_to_reply( - reply, context_uuid, connection_uuid, device_layer=device_layer, path_hops=path_hops, - config_rules=config_rules) - grpc_services[service_key] = grpc_service + grpc_service = self.add_service_to_reply( + reply, context_uuid, connection_uuid, service_type, path_hops=path_hops, + config_rules=orig_config_rules) + grpc_services[service_key] = grpc_service for connection in connections: - connection_uuid,device_layer,path_hops,dependencies = connection + connection_uuid,_,path_hops,dependencies = connection service_key = (context_uuid, connection_uuid) grpc_service = grpc_services.get(service_key) @@ -251,8 +261,8 @@ class _Algorithm: grpc_connection = self.add_connection_to_reply(reply, connection_uuid, grpc_service, path_hops) grpc_connections[connection_uuid] = grpc_connection - for service_uuid in dependencies: - sub_service_key = (context_uuid, service_uuid) + for sub_service_uuid in dependencies: + sub_service_key = (context_uuid, sub_service_uuid) grpc_sub_service = grpc_services.get(sub_service_key) if grpc_sub_service is None: raise Exception('Service({:s}) not found'.format(str(sub_service_key))) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py new file mode 100644 index 000000000..30845bb11 --- /dev/null +++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py @@ -0,0 +1,89 @@ +# 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, re +from typing import Dict, List, Optional +from common.proto.context_pb2 import ConfigRule +from common.tools.object_factory.ConfigRule import json_config_rule_set + +SETTINGS_RULE_NAME = '/settings' + +DEV_EP_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings') + +L2NM_SETTINGS_FIELD_DEFAULTS = { + 'encapsulation_type': 'dot1q', + 'vlan_id' : 100, + 'mtu' : 1450, +} + +L3NM_SETTINGS_FIELD_DEFAULTS = { + 'encapsulation_type': 'dot1q', + 'vlan_id' : 100, + 'mtu' : 1450, +} + +TAPI_SETTINGS_FIELD_DEFAULTS = { + 'capacity_value' : 50.0, + 'capacity_unit' : 'GHz', + 'layer_proto_name': 'PHOTONIC_MEDIA', + 'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC', + 'direction' : 'UNIDIRECTIONAL', +} + +def find_custom_config_rule(config_rules : List, resource_name : str) -> Optional[Dict]: + resource_value : Optional[Dict] = None + for config_rule in config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + if config_rule.custom.resource_key != resource_name: continue + resource_value = json.loads(config_rule.custom.resource_value) + return resource_value + +def compose_config_rules( + main_service_config_rules : List, subservice_config_rules : List, field_defaults : Dict +) -> None: + settings = find_custom_config_rule(main_service_config_rules, SETTINGS_RULE_NAME) + if settings is None: return + + json_settings = {} + for field_name,default_value in field_defaults.items(): + json_settings[field_name] = settings.get(field_name, default_value) + + config_rule = ConfigRule(**json_config_rule_set('/settings', json_settings)) + subservice_config_rules.append(config_rule) + +def compose_l2nm_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None: + compose_config_rules(main_service_config_rules, subservice_config_rules, L2NM_SETTINGS_FIELD_DEFAULTS) + +def compose_l3nm_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None: + compose_config_rules(main_service_config_rules, subservice_config_rules, L3NM_SETTINGS_FIELD_DEFAULTS) + +def compose_tapi_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None: + compose_config_rules(main_service_config_rules, subservice_config_rules, TAPI_SETTINGS_FIELD_DEFAULTS) + +def compose_device_config_rules(config_rules : List, subservice_config_rules : List, path_hops : List) -> None: + endpoints_traversed = set() + for path_hop in path_hops: + device_uuid_or_name = path_hop['device'] + endpoints_traversed.add((device_uuid_or_name, path_hop['ingress_ep'])) + endpoints_traversed.add((device_uuid_or_name, path_hop['egress_ep'])) + + for config_rule in config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + match = DEV_EP_SETTINGS.match(config_rule.custom.resource_key) + if match is None: continue + device_uuid_or_name = match.group(1) + endpoint_uuid_or_name = match.group(2) + dev_ep_kep = (device_uuid_or_name, endpoint_uuid_or_name) + if dev_ep_kep not in endpoints_traversed: continue + subservice_config_rules.append(config_rule) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py index b92a19b52..75701b99e 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py @@ -30,56 +30,176 @@ # ] # # connections=[ -# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), , [ +# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), ServiceTypeEnum.TAPI, [ # {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'} # ], []), -# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), , [ +# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.L2NM, [ # {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, # {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, # {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, # {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'} # ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]), -# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), , [ +# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), ServiceTypeEnum.L2NM, [ # {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, # {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} # ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) # ] -import queue, uuid -from typing import Dict, List, Tuple -from common.proto.context_pb2 import Device -from .ConstantsMappings import DEVICE_TYPE_TO_LAYER, DeviceLayerEnum +import enum, json, queue, uuid +from typing import Dict, List, Optional, Tuple +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import Device, ServiceTypeEnum + +class StackActionEnum(enum.Enum): + PATH_INGRESS = 'ingress' + CREATE_CONNECTION = 'create' + APPEND_PATH_HOP = 'append' + CHAIN_CONNECTION = 'chain' + TERMINATE_CONNECTION = 'terminate' + +def is_datacenter(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.DATACENTER, DeviceTypeEnum.EMULATED_DATACENTER} + +def is_packet_router(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER} + +def is_packet_switch(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH} + +def is_packet_device(dev_type : Optional[DeviceTypeEnum]) -> bool: + return is_packet_router(dev_type) or is_packet_switch(dev_type) + +def is_tfs_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.TERAFLOWSDN_CONTROLLER} + +def is_mw_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM} + +def is_ipm_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.XR_CONSTELLATION, DeviceTypeEnum.EMULATED_XR_CONSTELLATION} + +def is_ols_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM} + +def is_subdevice(dev_manager : Optional[str]) -> bool: + return dev_manager is not None + +def is_subdevice_equal(dev_manager_a : Optional[str], dev_manager_b : Optional[str]) -> bool: + if dev_manager_a is None and dev_manager_b is None: return True + if dev_manager_a is not None and dev_manager_b is not None: return dev_manager_a == dev_manager_b + return False + +def get_action( + prv_type : Optional[DeviceTypeEnum], prv_manager : Optional[str], + cur_type : DeviceTypeEnum, cur_manager : Optional[str] +) -> StackActionEnum: + if prv_type is None: + return StackActionEnum.PATH_INGRESS + + if is_datacenter(prv_type): + if is_packet_device(cur_type): return StackActionEnum.CREATE_CONNECTION + if is_tfs_controller(cur_type): return StackActionEnum.CREATE_CONNECTION + + if is_packet_device(prv_type): + if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION + if is_packet_device(cur_type): + if is_subdevice_equal(cur_manager, prv_manager): return StackActionEnum.APPEND_PATH_HOP + if is_subdevice(prv_manager) and not is_subdevice(cur_manager): return StackActionEnum.TERMINATE_CONNECTION + if not is_subdevice(prv_manager) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION + + if is_mw_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION + if is_ols_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION + if is_tfs_controller(cur_type) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION + + if is_mw_controller(prv_type) or is_ols_controller(prv_type): + if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION + + if is_tfs_controller(prv_type): + if is_tfs_controller(cur_type) and is_subdevice_equal(prv_manager, cur_manager): return StackActionEnum.APPEND_PATH_HOP + if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION + if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION + if is_mw_controller(cur_type) or is_ols_controller(cur_type): return StackActionEnum.CHAIN_CONNECTION + + str_fields = ', '.join([ + 'prv_type={:s}'.format(str(prv_type)), 'prv_manager={:s}'.format(str(prv_manager)), + 'cur_type={:s}'.format(str(cur_type)), 'cur_manager={:s}'.format(str(cur_manager)), + ]) + raise Exception('Undefined Action for ({:s})'.format(str_fields)) + +def get_device_manager_uuid(device : Device) -> Optional[str]: + for config_rule in device.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + if config_rule.custom.resource_key != '_manager': continue + device_manager_id = json.loads(config_rule.custom.resource_value) + return device_manager_id['uuid'] + return None + +def get_device_type( + grpc_device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str] +) -> DeviceTypeEnum: + if device_manager_uuid is None: + return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member + device_manager_tuple = device_dict.get(device_manager_uuid) + if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid))) + _,grpc_device = device_manager_tuple + return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member + +SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} + +def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: + if is_tfs_controller(device_type) or is_packet_router(device_type): + if prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type + if is_packet_switch(device_type) or is_mw_controller(device_type): + if prv_service_type == ServiceTypeEnum.SERVICETYPE_L2NM: return prv_service_type + if is_ols_controller(device_type) or is_ipm_controller(device_type): + return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE + + str_fields = ', '.join([ + 'device_type={:s}'.format(str(device_type)), + ]) + raise Exception('Undefined Service Type for ({:s})'.format(str_fields)) def convert_explicit_path_hops_to_connections( - path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], main_connection_uuid : str -) -> List[Tuple[str, DeviceLayerEnum, List[str], List[str]]]: + path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], + main_service_uuid : str, main_service_type : ServiceTypeEnum +) -> List[Tuple[str, int, List[str], List[str]]]: connection_stack = queue.LifoQueue() - connections : List[Tuple[str, DeviceLayerEnum, List[str], List[str]]] = list() - old_device_layer = None - last_device_uuid = None + connections : List[Tuple[str, int, List[str], List[str]]] = list() + prv_device_uuid = None + prv_device_type = None + prv_manager_uuid = None + for path_hop in path_hops: device_uuid = path_hop['device'] - if last_device_uuid == device_uuid: continue + if prv_device_uuid == device_uuid: continue device_tuple = device_dict.get(device_uuid) if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) - json_device,_ = device_tuple - device_type = json_device['device_type'] - device_layer = DEVICE_TYPE_TO_LAYER.get(device_type) - if device_layer is None: raise Exception('Undefined Layer for DeviceType({:s})'.format(str(device_type))) - - if old_device_layer is None: - # path ingress - connection_stack.put((main_connection_uuid, device_layer, [path_hop], [])) - elif old_device_layer > device_layer: - # underlying connection begins + _,grpc_device = device_tuple + + manager_uuid = get_device_manager_uuid(grpc_device) + device_type = get_device_type(grpc_device, device_dict, manager_uuid) + action = get_action(prv_device_type, prv_manager_uuid, device_type, manager_uuid) + + if action == StackActionEnum.PATH_INGRESS: + connection_stack.put((main_service_uuid, main_service_type, [path_hop], [])) + elif action == StackActionEnum.CREATE_CONNECTION: connection_uuid = str(uuid.uuid4()) - connection_stack.put((connection_uuid, device_layer, [path_hop], [])) - elif old_device_layer == device_layer: - # same connection continues + prv_service_type = connection_stack.queue[-1][1] + service_type = get_service_type(device_type, prv_service_type) + connection_stack.put((connection_uuid, service_type, [path_hop], [])) + elif action == StackActionEnum.APPEND_PATH_HOP: connection_stack.queue[-1][2].append(path_hop) - elif old_device_layer < device_layer: - # underlying connection ended + elif action == StackActionEnum.CHAIN_CONNECTION: + connection = connection_stack.get() + connections.append(connection) + connection_stack.queue[-1][3].append(connection[0]) + + connection_uuid = str(uuid.uuid4()) + prv_service_type = connection_stack.queue[-1][1] + service_type = get_service_type(device_type, prv_service_type) + connection_stack.put((connection_uuid, service_type, [path_hop], [])) + elif action == StackActionEnum.TERMINATE_CONNECTION: connection = connection_stack.get() connections.append(connection) connection_stack.queue[-1][3].append(connection[0]) @@ -87,8 +207,9 @@ def convert_explicit_path_hops_to_connections( else: raise Exception('Uncontrolled condition') - old_device_layer = device_layer - last_device_uuid = device_uuid + prv_device_uuid = device_uuid + prv_device_type = device_type + prv_manager_uuid = manager_uuid # path egress connections.append(connection_stack.get()) @@ -96,17 +217,17 @@ def convert_explicit_path_hops_to_connections( return connections def convert_explicit_path_hops_to_plain_connection( - path_hops : List[Dict], main_connection_uuid : str -) -> List[Tuple[str, DeviceLayerEnum, List[str], List[str]]]: + path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum +) -> List[Tuple[str, int, List[str], List[str]]]: - connection : Tuple[str, DeviceLayerEnum, List[str], List[str]] = \ - (main_connection_uuid, DeviceLayerEnum.PACKET_DEVICE, [], []) + connection : Tuple[str, int, List[str], List[str]] = \ + (main_service_uuid, main_service_type, [], []) - last_device_uuid = None + prv_device_uuid = None for path_hop in path_hops: device_uuid = path_hop['device'] - if last_device_uuid == device_uuid: continue + if prv_device_uuid == device_uuid: continue connection[2].append(path_hop) - last_device_uuid = device_uuid + prv_device_uuid = device_uuid return [connection] diff --git a/src/pathcomp/frontend/service/algorithms/tools/ConstantsMappings.py b/src/pathcomp/frontend/service/algorithms/tools/ConstantsMappings.py index cd1956a87..bd06e6ba1 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ConstantsMappings.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ConstantsMappings.py @@ -13,8 +13,6 @@ # limitations under the License. from enum import IntEnum -from common.DeviceTypes import DeviceTypeEnum -from common.proto.context_pb2 import ServiceTypeEnum class CapacityUnit(IntEnum): TB = 0 @@ -66,50 +64,3 @@ class LinkForwardingDirection(IntEnum): BIDIRECTIONAL = 0 UNIDIRECTIONAL = 1 UNKNOWN = 2 - -class DeviceLayerEnum(IntEnum): - APPLICATION_CONTROLLER = 41 # Layer 4 domain controller - APPLICATION_DEVICE = 40 # Layer 4 domain device - PACKET_CONTROLLER = 31 # Layer 3 domain controller - PACKET_DEVICE = 30 # Layer 3 domain device - MAC_LAYER_CONTROLLER = 21 # Layer 2 domain controller - MAC_LAYER_DEVICE = 20 # Layer 2 domain device - OPTICAL_CONTROLLER = 1 # Layer 0 domain controller - OPTICAL_DEVICE = 0 # Layer 0 domain device - -DEVICE_TYPE_TO_LAYER = { - DeviceTypeEnum.EMULATED_DATACENTER.value : DeviceLayerEnum.APPLICATION_DEVICE, - DeviceTypeEnum.DATACENTER.value : DeviceLayerEnum.APPLICATION_DEVICE, - DeviceTypeEnum.NETWORK.value : DeviceLayerEnum.APPLICATION_DEVICE, - - DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : DeviceLayerEnum.PACKET_DEVICE, - DeviceTypeEnum.PACKET_ROUTER.value : DeviceLayerEnum.PACKET_DEVICE, - DeviceTypeEnum.EMULATED_PACKET_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE, - DeviceTypeEnum.PACKET_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE, - - DeviceTypeEnum.EMULATED_P4_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE, - DeviceTypeEnum.P4_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE, - - DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM.value : DeviceLayerEnum.MAC_LAYER_CONTROLLER, - DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM.value : DeviceLayerEnum.MAC_LAYER_CONTROLLER, - - DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value : DeviceLayerEnum.OPTICAL_CONTROLLER, - DeviceTypeEnum.OPEN_LINE_SYSTEM.value : DeviceLayerEnum.OPTICAL_CONTROLLER, - DeviceTypeEnum.XR_CONSTELLATION.value : DeviceLayerEnum.OPTICAL_CONTROLLER, - - DeviceTypeEnum.EMULATED_OPTICAL_ROADM.value : DeviceLayerEnum.OPTICAL_DEVICE, - DeviceTypeEnum.OPTICAL_ROADM.value : DeviceLayerEnum.OPTICAL_DEVICE, - DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER.value : DeviceLayerEnum.OPTICAL_DEVICE, - DeviceTypeEnum.OPTICAL_TRANSPONDER.value : DeviceLayerEnum.OPTICAL_DEVICE, -} - -DEVICE_LAYER_TO_SERVICE_TYPE = { - DeviceLayerEnum.APPLICATION_DEVICE.value : ServiceTypeEnum.SERVICETYPE_L3NM, - DeviceLayerEnum.PACKET_DEVICE.value : ServiceTypeEnum.SERVICETYPE_L3NM, - - DeviceLayerEnum.MAC_LAYER_CONTROLLER.value : ServiceTypeEnum.SERVICETYPE_L2NM, - DeviceLayerEnum.MAC_LAYER_DEVICE.value : ServiceTypeEnum.SERVICETYPE_L2NM, - - DeviceLayerEnum.OPTICAL_CONTROLLER.value : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, - DeviceLayerEnum.OPTICAL_DEVICE.value : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, -} -- GitLab From 5ff2ecb993c731189ea1309012f11bd07b9207ac Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:43:47 +0000 Subject: [PATCH 31/77] Updated TODO file --- TODO.txt | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/TODO.txt b/TODO.txt index 343d54e28..714fab583 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,9 +1,28 @@ -- confirm with Hakim everything is in branch hakim-develop-patch-44885 -- merge branch hakim-develop-patch-44885 into feat/device-ietf-l2vpn -- delete branch hakim-develop-patch-44885 - -- update OFC'23 to deploy 2 instances without blockchain -dom1 => parent -dom2 => child -dom3/4 => remove -replace nfvsdn22 => ofc23 +- MW mock sdn ctrl: post does not work +- implement mock IPM controller + +- pending infinera tests +- test with Slice NBI +- integrate Slice NBI in Load Gen +- import subdevices TAPI, MW, INF + +- Device: + - IetfL2VpnDriver should not import links; otherwise, might confuse path computation component + - [] Add flag to control that + - MW Driver should import topology, there should be nothing between antennas + - IPM Driver should import devices but not links, TAPI in between + - TAPI Driver: add flag to choose; might be required in some cases + - enbale to create links (connectivity between subdevices) + +- OLS service: from input to input endpoint ?? + +- pathcomp should not create reversed links +- bidirectional links should be duplicated in frontend +- pathcomp should honor input/output of tapi endpoints + +TO TEST: +- Test with new changes in pathcomp and subservice config rule composition +- Test device manager config in device works; rules should go to manager, not to underlying +- ietf-l2vpn driver: test service is created +- if there is a config rule for a endpoint in subservice, move the rule from main to that subservice + -- GitLab From a23c62f5330f30889464757eead3cba778aaaa1b Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:44:03 +0000 Subject: [PATCH 32/77] Manifests: - updated log level to debug --- manifests/deviceservice.yaml | 2 +- manifests/pathcompservice.yaml | 2 +- manifests/serviceservice.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifests/deviceservice.yaml b/manifests/deviceservice.yaml index ca2c81f0f..ddcc997cd 100644 --- a/manifests/deviceservice.yaml +++ b/manifests/deviceservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:2020"] diff --git a/manifests/pathcompservice.yaml b/manifests/pathcompservice.yaml index fd3599f42..5916d09a6 100644 --- a/manifests/pathcompservice.yaml +++ b/manifests/pathcompservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:10020"] diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index 3fa4a6e0d..801c06f52 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:3030"] -- GitLab From 9a8a1a348155015bb4ceddef4709ebf17d6dc44b Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Feb 2023 21:44:58 +0000 Subject: [PATCH 33/77] Added simple test to check pathcomp subservice composition --- test_pathcomp/ComputeSubServices.py | 255 ++++++++++++++++++++++++++++ test_pathcomp/__init__.py | 14 ++ test_pathcomp/__main__.py | 33 ++++ test_pathcomp/data.py | 115 +++++++++++++ test_pathcomp/format.sh | 56 ++++++ 5 files changed, 473 insertions(+) create mode 100644 test_pathcomp/ComputeSubServices.py create mode 100644 test_pathcomp/__init__.py create mode 100644 test_pathcomp/__main__.py create mode 100644 test_pathcomp/data.py create mode 100755 test_pathcomp/format.sh diff --git a/test_pathcomp/ComputeSubServices.py b/test_pathcomp/ComputeSubServices.py new file mode 100644 index 000000000..e0b229625 --- /dev/null +++ b/test_pathcomp/ComputeSubServices.py @@ -0,0 +1,255 @@ +# 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. + +# Convert the path defined as explicit hops with ingress and egress endpoints per device into a set of connections and +# compute the dependencies among them. +# +# Example: +# o-- int DC1 eth1 -- 10/1 CS1 1/2 -- 1/2 R2 2/1 -- a7.. OLS 60.. -- 2/1 R3 1/1 -- 1/1 CS2 10/1 -- eth1 DC2 int --o +# APP PKT PKT CTRL PKT PKT APP +# +# path_hops = [ +# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, +# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, +# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, +# {'device': 'TN-OLS', 'ingress_ep': 'a7a80b23a703', 'egress_ep': '60519106029e'}, +# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, +# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'}, +# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} +# ] +# +# connections=[ +# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), , [ +# {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'} +# ], []), +# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), , [ +# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, +# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, +# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, +# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'} +# ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]), +# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), , [ +# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, +# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} +# ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) +# ] + +import enum, json, queue, uuid +from typing import Dict, List, Optional, Tuple +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import Device, ServiceTypeEnum #, DeviceDriverEnum as grpc_DeviceDriverEnum +#from .ConstantsMappings import DEVICE_TYPE_TO_LAYER, DeviceLayerEnum + +class StackActionEnum(enum.Enum): + PATH_INGRESS = 'ingress' + CREATE_CONNECTION = 'create' + APPEND_PATH_HOP = 'append' + CHAIN_CONNECTION = 'chain' + TERMINATE_CONNECTION = 'terminate' + +#class DeviceDriverEnum(enum.IntEnum): +# EMULATED = grpc_DeviceDriverEnum.DEVICEDRIVER_UNDEFINED +# OPENCONFIG = grpc_DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG +# TRANSPORT_API = grpc_DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API +# P4 = grpc_DeviceDriverEnum.DEVICEDRIVER_P4 +# IETF_NETWORK_TOPOLOGY = grpc_DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY +# ONF_TR_352 = grpc_DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352 +# XR = grpc_DeviceDriverEnum.DEVICEDRIVER_XR +# IETF_L2VPN = grpc_DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN + +def is_datacenter(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.DATACENTER, DeviceTypeEnum.EMULATED_DATACENTER} + +def is_packet_router(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER} + +def is_packet_switch(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH} + +def is_packet_device(dev_type : Optional[DeviceTypeEnum]) -> bool: + return is_packet_router(dev_type) or is_packet_switch(dev_type) + +def is_tfs_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.TERAFLOWSDN_CONTROLLER} + +def is_mw_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM} + +def is_ipm_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.XR_CONSTELLATION, DeviceTypeEnum.EMULATED_XR_CONSTELLATION} + +def is_ols_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: + return dev_type in {DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM} + +def is_subdevice(dev_manager : Optional[str]) -> bool: + return dev_manager is not None + +def is_subdevice_equal(dev_manager_a : Optional[str], dev_manager_b : Optional[str]) -> bool: + if dev_manager_a is None and dev_manager_b is None: return True + if dev_manager_a is not None and dev_manager_b is not None: return dev_manager_a == dev_manager_b + return False + +#def has_driver(dev_drivers : List[DeviceDriverEnum], dev_driver : DeviceDriverEnum) -> bool: +# return dev_driver in dev_drivers + +def get_action( + prv_type : Optional[DeviceTypeEnum], prv_manager : Optional[str], + cur_type : DeviceTypeEnum, cur_manager : Optional[str] +) -> StackActionEnum: + if prv_type is None: + return StackActionEnum.PATH_INGRESS + + if is_datacenter(prv_type): + if is_packet_device(cur_type): return StackActionEnum.CREATE_CONNECTION + if is_tfs_controller(cur_type): return StackActionEnum.CREATE_CONNECTION + + if is_packet_device(prv_type): + if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION + if is_packet_device(cur_type): + if is_subdevice_equal(cur_manager, prv_manager): return StackActionEnum.APPEND_PATH_HOP + if is_subdevice(prv_manager) and not is_subdevice(cur_manager): return StackActionEnum.TERMINATE_CONNECTION + if not is_subdevice(prv_manager) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION + + if is_mw_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION + if is_ols_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION + if is_tfs_controller(cur_type) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION + + if is_mw_controller(prv_type) or is_ols_controller(prv_type): + if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION + + if is_tfs_controller(prv_type): + if is_tfs_controller(cur_type) and is_subdevice_equal(prv_manager, cur_manager): return StackActionEnum.APPEND_PATH_HOP + if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION + if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION + if is_mw_controller(cur_type) or is_ols_controller(cur_type): return StackActionEnum.CHAIN_CONNECTION + + str_fields = ', '.join([ + 'prv_type={:s}'.format(str(prv_type)), 'prv_manager={:s}'.format(str(prv_manager)), + 'cur_type={:s}'.format(str(cur_type)), 'cur_manager={:s}'.format(str(cur_manager)), + ]) + raise Exception('Undefined Action for ({:s})'.format(str_fields)) + +def get_device_manager_uuid(device : Device) -> Optional[str]: + for config_rule in device.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + if config_rule.custom.resource_key != '_manager': continue + device_manager_id = json.loads(config_rule.custom.resource_value) + return device_manager_id['uuid'] + return None + +def get_device_type( + grpc_device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str] +) -> DeviceTypeEnum: + if device_manager_uuid is None: + return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member + device_manager_tuple = device_dict.get(device_manager_uuid) + if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid))) + _,grpc_device = device_manager_tuple + return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member + + #manager_drivers = set(grpc_device.device_drivers) + #if DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN in manager_drivers: + # device_layer = DeviceLayerEnum.MAC_LAYER_CONTROLLER + #else: + # device_type = json_device['device_type'] + # device_layer = DEVICE_TYPE_TO_LAYER.get(device_type) + # if device_layer is None: raise Exception('Undefined Layer for DeviceType({:s})'.format(str(device_type))) + +SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} + +def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: + if is_tfs_controller(device_type) or is_packet_router(device_type): + if prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type + if is_packet_switch(device_type) or is_mw_controller(device_type): + if prv_service_type == ServiceTypeEnum.SERVICETYPE_L2NM: return prv_service_type + if is_ols_controller(device_type) or is_ipm_controller(device_type): + return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE + + str_fields = ', '.join([ + 'device_type={:s}'.format(str(device_type)), + ]) + raise Exception('Undefined Service Type for ({:s})'.format(str_fields)) + +def convert_explicit_path_hops_to_connections( + path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], + main_service_uuid : str, main_service_type : ServiceTypeEnum +) -> List[Tuple[str, int, List[str], List[str]]]: + + connection_stack = queue.LifoQueue() + connections : List[Tuple[str, int, List[str], List[str]]] = list() + prv_device_uuid = None + prv_device_type = None + prv_manager_uuid = None + + for path_hop in path_hops: + device_uuid = path_hop['device'] + if prv_device_uuid == device_uuid: continue + device_tuple = device_dict.get(device_uuid) + if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) + _,grpc_device = device_tuple + + manager_uuid = get_device_manager_uuid(grpc_device) + device_type = get_device_type(grpc_device, device_dict, manager_uuid) + action = get_action(prv_device_type, prv_manager_uuid, device_type, manager_uuid) + + if action == StackActionEnum.PATH_INGRESS: + connection_stack.put((main_service_uuid, main_service_type, [path_hop], [])) + elif action == StackActionEnum.CREATE_CONNECTION: + connection_uuid = str(uuid.uuid4()) + prv_service_type = connection_stack.queue[-1][1] + service_type = get_service_type(device_type, prv_service_type) + connection_stack.put((connection_uuid, service_type, [path_hop], [])) + elif action == StackActionEnum.APPEND_PATH_HOP: + connection_stack.queue[-1][2].append(path_hop) + elif action == StackActionEnum.CHAIN_CONNECTION: + connection = connection_stack.get() + connections.append(connection) + connection_stack.queue[-1][3].append(connection[0]) + + connection_uuid = str(uuid.uuid4()) + prv_service_type = connection_stack.queue[-1][1] + service_type = get_service_type(device_type, prv_service_type) + connection_stack.put((connection_uuid, service_type, [path_hop], [])) + elif action == StackActionEnum.TERMINATE_CONNECTION: + connection = connection_stack.get() + connections.append(connection) + connection_stack.queue[-1][3].append(connection[0]) + connection_stack.queue[-1][2].append(path_hop) + else: + raise Exception('Uncontrolled condition') + + prv_device_uuid = device_uuid + prv_device_type = device_type + prv_manager_uuid = manager_uuid + + # path egress + connections.append(connection_stack.get()) + assert connection_stack.empty() + return connections + +def convert_explicit_path_hops_to_plain_connection( + path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum +) -> List[Tuple[str, int, List[str], List[str]]]: + + connection : Tuple[str, int, List[str], List[str]] = \ + (main_service_uuid, main_service_type, [], []) + + prv_device_uuid = None + for path_hop in path_hops: + device_uuid = path_hop['device'] + if prv_device_uuid == device_uuid: continue + connection[2].append(path_hop) + prv_device_uuid = device_uuid + + return [connection] diff --git a/test_pathcomp/__init__.py b/test_pathcomp/__init__.py new file mode 100644 index 000000000..1549d9811 --- /dev/null +++ b/test_pathcomp/__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/test_pathcomp/__main__.py b/test_pathcomp/__main__.py new file mode 100644 index 000000000..6af584fe9 --- /dev/null +++ b/test_pathcomp/__main__.py @@ -0,0 +1,33 @@ +# 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, sys +from common.proto.context_pb2 import ServiceTypeEnum +from .data import path_hops, device_dict +from .ComputeSubServices import convert_explicit_path_hops_to_connections + +logging.basicConfig(level=logging.DEBUG) +LOGGER = logging.getLogger(__name__) + +def main(): + service_uuid = 'dc-2-dc-svc' + service_type = ServiceTypeEnum.SERVICETYPE_L2NM + connections = convert_explicit_path_hops_to_connections(path_hops, device_dict, service_uuid, service_type) + str_connections = '\n'.join([' ' + str(connection) for connection in connections]) + LOGGER.debug('connections = [\n{:s}\n]'.format(str_connections)) + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/test_pathcomp/data.py b/test_pathcomp/data.py new file mode 100644 index 000000000..5e94d969e --- /dev/null +++ b/test_pathcomp/data.py @@ -0,0 +1,115 @@ +# 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 +from typing import Dict, Tuple +from common.proto.context_pb2 import ConfigActionEnum, Device + +path_hops = [ + {'device': 'DC1', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, + {'device': 'PE1', 'ingress_ep': '1/1', 'egress_ep': '1/2'}, + {'device': 'MW1-2', 'ingress_ep': '172.18.0.1:1', 'egress_ep': '172.18.0.2:1'}, + {'device': 'R1', 'ingress_ep': '1/1', 'egress_ep': '1/3'}, + {'device': 'OLS', 'ingress_ep': 'aade6001-f00b-5e2f-a357-6a0a9d3de870', 'egress_ep': '0ef74f99-1acc-57bd-ab9d-4b958b06c513'}, + {'device': 'R2', 'ingress_ep': '1/1', 'egress_ep': '1/2'}, + {'device': 'PE3', 'ingress_ep': '1/1', 'egress_ep': '1/2'}, + {'device': 'DC2', 'ingress_ep': 'eth1', 'egress_ep': 'int'} +] + +device_dict = { + 'R3': {'device_Id': 'R3', 'device_type': 'emu-packet-router', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'R3', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'R3', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'} + ]}, + 'PE4': {'device_Id': 'PE4', 'device_type': 'emu-packet-router', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'PE4', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'}, + {'endpoint_id': {'device_id': 'PE4', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'PE4', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'} + ]}, + 'PE2': {'device_Id': 'PE2', 'device_type': 'emu-packet-router', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'PE2', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'PE2', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'}, + {'endpoint_id': {'device_id': 'PE2', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'} + ]}, + 'R1': {'device_Id': 'R1', 'device_type': 'emu-packet-router', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'R1', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'R1', 'endpoint_uuid': '1/3'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'R1', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'} + ]}, + 'PE3': {'device_Id': 'PE3', 'device_type': 'emu-packet-router', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'PE3', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'PE3', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'PE3', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'} + ]}, + 'OLS': {'device_Id': 'OLS', 'device_type': 'emu-open-line-system', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'OLS', 'endpoint_uuid': '0ef74f99-1acc-57bd-ab9d-4b958b06c513'}, 'endpoint_type': 'optical'}, + {'endpoint_id': {'device_id': 'OLS', 'endpoint_uuid': '50296d99-58cc-5ce7-82f5-fc8ee4eec2ec'}, 'endpoint_type': 'optical'}, + {'endpoint_id': {'device_id': 'OLS', 'endpoint_uuid': 'aade6001-f00b-5e2f-a357-6a0a9d3de870'}, 'endpoint_type': 'optical'}, + {'endpoint_id': {'device_id': 'OLS', 'endpoint_uuid': 'eb287d83-f05e-53ec-ab5a-adf6bd2b5418'}, 'endpoint_type': 'optical'} + ]}, + 'PE1': {'device_Id': 'PE1', 'device_type': 'emu-packet-router', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'PE1', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'PE1', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'}, + {'endpoint_id': {'device_id': 'PE1', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'} + ]}, + 'DC2': {'device_Id': 'DC2', 'device_type': 'emu-datacenter', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'DC2', 'endpoint_uuid': 'eth1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'DC2', 'endpoint_uuid': 'eth2'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'DC2', 'endpoint_uuid': 'int'}, 'endpoint_type': 'copper/internal'} + ]}, + 'MW1-2': {'device_Id': 'MW1-2', 'device_type': 'emu-microwave-radio-system', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'MW1-2', 'endpoint_uuid': '172.18.0.1:1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'MW1-2', 'endpoint_uuid': '172.18.0.2:1'}, 'endpoint_type': 'copper/internal'} + ]}, + 'MW3-4': {'device_Id': 'MW3-4', 'device_type': 'emu-microwave-radio-system', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'MW3-4', 'endpoint_uuid': '172.18.0.3:1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'MW3-4', 'endpoint_uuid': '172.18.0.4:1'}, 'endpoint_type': 'copper/internal'} + ]}, + 'TFS': {'device_Id': 'TFS', 'device_type': 'teraflowsdn', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'TFS', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'} + ]}, + 'R2': {'device_Id': 'R2', 'device_type': 'emu-packet-router', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'R2', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'R2', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'} + ]}, + 'DC1': {'device_Id': 'DC1', 'device_type': 'emu-datacenter', 'device_endpoints': [ + {'endpoint_id': {'device_id': 'DC1', 'endpoint_uuid': 'int'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'DC1', 'endpoint_uuid': 'eth1'}, 'endpoint_type': 'copper/internal'}, + {'endpoint_id': {'device_id': 'DC1', 'endpoint_uuid': 'eth2'}, 'endpoint_type': 'copper/internal'} + ]}, +} + +MANAGED_DEVICES = {'PE1', 'PE2', 'PE3', 'PE4'} +MANAGER = 'TFS' + +def process_device(json_device) -> Tuple[Dict, Device]: + device_uuid = json_device['device_Id'] + + grpc_device = Device() + grpc_device.device_id.device_uuid.uuid = device_uuid + grpc_device.device_type = json_device['device_type'] + + if device_uuid in MANAGED_DEVICES: + config_rule = grpc_device.device_config.config_rules.add() + config_rule.action = ConfigActionEnum.CONFIGACTION_SET + config_rule.custom.resource_key = '_manager' + config_rule.custom.resource_value = json.dumps({'uuid': MANAGER}) + + return json_device, grpc_device + +device_dict = { + device_uuid:process_device(json_device) + for device_uuid,json_device in device_dict.items() +} diff --git a/test_pathcomp/format.sh b/test_pathcomp/format.sh new file mode 100755 index 000000000..ceeea5d31 --- /dev/null +++ b/test_pathcomp/format.sh @@ -0,0 +1,56 @@ +#!/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. + + +sed -i 's#0dff8c06-873b-5799-ac54-c0452252bae1#R3#g' log.txt +sed -i 's#1102e0b5-824b-57eb-86a1-d247e2deaf68#PE4#g' log.txt +sed -i 's#29d766ca-d222-5257-bab3-6a060719270a#PE2#g' log.txt +sed -i 's#68741528-2e94-5274-ab3c-fddcd8dc05ef#R1#g' log.txt +sed -i 's#69a3a3f0-5237-5f9e-bc96-d450d0c6c02a#PE3#g' log.txt +sed -i 's#6ab8fa38-ec20-5c32-8d9b-4fd86fce2555#OLS#g' log.txt +sed -i 's#7faa13eb-903d-58f5-936b-1a1174fe98fd#PE1#g' log.txt +sed -i 's#800d5bd4-a7a3-5a66-82ab-d399767ca3d8#DC2#g' log.txt +sed -i 's#90c22ce2-4f8d-51b4-8d7a-762eea9b310a#MW#g' log.txt +sed -i 's#a23cdc36-074d-5423-8abd-4a167a6e6fbc#TFS#g' log.txt +sed -i 's#c944aaeb-bbdf-5f2d-b31c-8cc8903045b6#R2#g' log.txt +sed -i 's#cda90d2f-e7b0-5837-8f2e-2fb29dd9b367#DC1#g' log.txt +sed -i 's#79f4184c-d375-5e2c-a3df-1ae64537c95c#1/1#g' log.txt +sed -i 's#93c853c2-429c-52e8-9ba9-454fcedb9090#1/2#g' log.txt +sed -i 's#1fe2ee1a-fe92-57c9-afd9-260e6f0ecc54#mgmt#g' log.txt +sed -i 's#cd378805-d73e-5681-8514-1d33e656c0e9#1/1#g' log.txt +sed -i 's#e502e939-3ab8-5fee-8277-7fd1c1c0fa93#1/2#g' log.txt +sed -i 's#780f6929-a863-5e6a-a046-3dac2e24bf58#1/1#g' log.txt +sed -i 's#f1082088-a304-587b-a230-b8ce10e5a148#mgmt#g' log.txt +sed -i 's#ffdfb0ce-1684-5d39-bad3-9ff1eb4ffbf8#1/2#g' log.txt +sed -i 's#268b735d-c861-5319-88a4-2ea498f96a04#1/1#g' log.txt +sed -i 's#62c0cba1-9ee8-5db5-82da-ce96d7e0f39f#1/3#g' log.txt +sed -i 's#7d1bf45c-5ab2-525e-87a4-c0ddcd5c18e4#1/2#g' log.txt +sed -i 's#2b11934b-dfd7-5267-87b9-7306a24e0182#1/1#g' log.txt +sed -i 's#ca92338e-2038-5d74-8ef1-2b20a234a8b9#1/2#g' log.txt +sed -i 's#f8955e74-4e93-5d43-a968-9626ee5c9b53#mgmt#g' log.txt +sed -i 's#2b75d88d-095f-5752-a10a-1ff69df8008d#1/2#g' log.txt +sed -i 's#bf0d75db-acf8-53cb-b6db-32d9dc0878c4#mgmt#g' log.txt +sed -i 's#eeade85a-03df-55d2-bfc2-2af7267bbcf3#1/1#g' log.txt +sed -i 's#06bb0b92-8783-5599-aa20-15bfbe241348#eth1#g' log.txt +sed -i 's#6a6859c3-4a13-513c-a7dd-490c8b2931b1#eth2#g' log.txt +sed -i 's#97f57787-cfec-5315-9718-7e850905f11a#int#g' log.txt +sed -i 's#659c63c9-9197-54a2-af34-48275e736aac#192.168.27.140:8#g' log.txt +sed -i 's#fed79944-3444-54ee-8335-efbe6590434b#192.168.27.139:10#g' log.txt +sed -i 's#85bb83b0-8d96-5e76-9959-97c35036d4f9#mgmt#g' log.txt +sed -i 's#313fb1ed-5dee-5e49-884a-cbb3b1e00073#1/2#g' log.txt +sed -i 's#79699f56-df25-5188-a389-04606c24fbfc#1/1#g' log.txt +sed -i 's#37ab67ef-0064-54e3-ae9b-d40100953834#int#g' log.txt +sed -i 's#55e88b6b-ccf7-538f-a062-a41219292ea1#eth1#g' log.txt +sed -i 's#68b5972b-3630-53a8-ba9b-cc54dd31f8b8#eth2#g' log.txt -- GitLab From a7acb1bc8727a1cf961eed590751bdce2019b008 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 27 Feb 2023 08:41:53 +0000 Subject: [PATCH 34/77] Test Tools - MW Mock SDN Ctrl: - Corrected processing of POST/DELETE messages - Added test script --- .../tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py | 53 ++++-------- src/tests/tools/mock_mw_sdn_ctrl/test_mw.py | 84 +++++++++++++++++++ 2 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 src/tests/tools/mock_mw_sdn_ctrl/test_mw.py diff --git a/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py index d4aabbe5b..d3d9de4a2 100644 --- a/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py +++ b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py @@ -25,8 +25,7 @@ # Ref: https://blog.miguelgrinberg.com/post/designing-a-restful-api-using-flask-restful import functools, logging, sys, time -from flask import Flask, abort, request -from flask.json import jsonify +from flask import Flask, abort, jsonify, make_response, request from flask_restful import Api, Resource BIND_ADDRESS = '0.0.0.0' @@ -77,55 +76,37 @@ def log_request(logger : logging.Logger, response): return response class Health(Resource): - def get(self): return jsonify({}) + def get(self): + return make_response(jsonify({}), 200) class Network(Resource): def get(self, network_uuid : str): if network_uuid != 'SIAE-ETH-TOPOLOGY': abort(400) network = {'node': NETWORK_NODES, 'ietf-network-topology:link': NETWORK_LINKS} - return jsonify({'ietf-network:network': network}) + return make_response(jsonify({'ietf-network:network': network}), 200) class Services(Resource): def get(self): services = [service for service in NETWORK_SERVICES.values()] - return jsonify({'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': services}}) + return make_response(jsonify({'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': services}}), 200) def post(self): - LOGGER.info('[post] begin') - try: - json_request = request.get_json() - LOGGER.info('[post] json_request={:s}'.format(str(json_request))) - if not json_request: - LOGGER.info('[post] abort 1') - abort(400) - if not isinstance(json_request, dict): - LOGGER.info('[post] abort 2') - abort(400) - if 'etht-svc-instances' not in json_request: - LOGGER.info('[post] abort 3') - abort(400) - json_services = json_request['etht-svc-instances'] - LOGGER.info('[post] json_services={:s}'.format(str(json_services))) - if not isinstance(json_services, list): - LOGGER.info('[post] abort 4') - abort(400) - if len(json_services) != 1: - LOGGER.info('[post] abort 5') - abort(400) - svc_data = json_services[0] - LOGGER.info('[post] svc_data={:s}'.format(str(svc_data))) - etht_svc_name = svc_data['etht-svc-name'] - LOGGER.info('[post] etht_svc_name={:s}'.format(str(etht_svc_name))) - NETWORK_SERVICES[etht_svc_name] = svc_data - LOGGER.info('[post] done') - return jsonify({}), 201 - except: - LOGGER.exception('Exception in POST') + json_request = request.get_json() + if not json_request: abort(400) + if not isinstance(json_request, dict): abort(400) + if 'etht-svc-instances' not in json_request: abort(400) + json_services = json_request['etht-svc-instances'] + if not isinstance(json_services, list): abort(400) + if len(json_services) != 1: abort(400) + svc_data = json_services[0] + etht_svc_name = svc_data['etht-svc-name'] + NETWORK_SERVICES[etht_svc_name] = svc_data + return make_response(jsonify({}), 201) class DelServices(Resource): def delete(self, service_uuid : str): NETWORK_SERVICES.pop(service_uuid, None) - return jsonify({}), 204 + return make_response(jsonify({}), 204) def main(): LOGGER.info('Starting...') diff --git a/src/tests/tools/mock_mw_sdn_ctrl/test_mw.py b/src/tests/tools/mock_mw_sdn_ctrl/test_mw.py new file mode 100644 index 000000000..0329d30ad --- /dev/null +++ b/src/tests/tools/mock_mw_sdn_ctrl/test_mw.py @@ -0,0 +1,84 @@ +import json, logging, requests +from requests.auth import HTTPBasicAuth +from typing import Optional + +LOGGER = logging.getLogger(__name__) + +HTTP_OK_CODES = { + 200, # OK + 201, # Created + 202, # Accepted + 204, # No Content +} + +def create_connectivity_service( + root_url, uuid, node_id_src, tp_id_src, node_id_dst, tp_id_dst, vlan_id, + auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None +): + + url = '{:s}/nmswebs/restconf/data/ietf-eth-tran-service:etht-svc'.format(root_url) + headers = {'content-type': 'application/json'} + data = { + 'etht-svc-instances': [ + { + 'etht-svc-name': uuid, + 'etht-svc-type': 'ietf-eth-tran-types:p2p-svc', + 'etht-svc-end-points': [ + { + 'etht-svc-access-points': [ + {'access-node-id': node_id_src, 'access-ltp-id': tp_id_src, 'access-point-id': '1'} + ], + 'outer-tag': {'vlan-value': vlan_id, 'tag-type': 'ietf-eth-tran-types:classify-c-vlan'}, + 'etht-svc-end-point-name': '{:s}:{:s}'.format(str(node_id_src), str(tp_id_src)), + 'service-classification-type': 'ietf-eth-tran-types:vlan-classification' + }, + { + 'etht-svc-access-points': [ + {'access-node-id': node_id_dst, 'access-ltp-id': tp_id_dst, 'access-point-id': '2'} + ], + 'outer-tag': {'vlan-value': vlan_id, 'tag-type': 'ietf-eth-tran-types:classify-c-vlan'}, + 'etht-svc-end-point-name': '{:s}:{:s}'.format(str(node_id_dst), str(tp_id_dst)), + 'service-classification-type': 'ietf-eth-tran-types:vlan-classification' + } + ] + } + ] + } + results = [] + try: + LOGGER.info('Connectivity service {:s}: {:s}'.format(str(uuid), str(data))) + response = requests.post( + url=url, data=json.dumps(data), timeout=timeout, headers=headers, verify=False, auth=auth) + LOGGER.info('Microwave Driver response: {:s}'.format(str(response))) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception creating ConnectivityService(uuid={:s}, data={:s})'.format(str(uuid), str(data))) + results.append(e) + else: + if response.status_code not in HTTP_OK_CODES: + msg = 'Could not create ConnectivityService(uuid={:s}, data={:s}). status_code={:s} reply={:s}' + LOGGER.error(msg.format(str(uuid), str(data), str(response.status_code), str(response))) + results.append(response.status_code in HTTP_OK_CODES) + return results + +def delete_connectivity_service(root_url, uuid, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None): + url = '{:s}/nmswebs/restconf/data/ietf-eth-tran-service:etht-svc/etht-svc-instances={:s}' + url = url.format(root_url, uuid) + results = [] + try: + response = requests.delete(url=url, timeout=timeout, verify=False, auth=auth) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception deleting ConnectivityService(uuid={:s})'.format(str(uuid))) + results.append(e) + else: + if response.status_code not in HTTP_OK_CODES: + msg = 'Could not delete ConnectivityService(uuid={:s}). status_code={:s} reply={:s}' + LOGGER.error(msg.format(str(uuid), str(response.status_code), str(response))) + results.append(response.status_code in HTTP_OK_CODES) + return results + +if __name__ == '__main__': + ROOT_URL = 'https://127.0.0.1:8443' + SERVICE_UUID = 'my-service' + + create_connectivity_service(ROOT_URL, SERVICE_UUID, '172.18.0.1', '1', '172.18.0.2', '2', 300) + delete_connectivity_service(ROOT_URL, SERVICE_UUID) -- GitLab From 9f7e132fe9ff7ef53cb7d5813a4106691ad5cc3f Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:39:17 +0000 Subject: [PATCH 35/77] Common: - Added Device Types for Radio Router --- src/common/DeviceTypes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py index 16b94eb0d..bb8948585 100644 --- a/src/common/DeviceTypes.py +++ b/src/common/DeviceTypes.py @@ -27,6 +27,7 @@ class DeviceTypeEnum(Enum): 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' @@ -38,6 +39,7 @@ 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' -- GitLab From 90825c4c6fa041e2e67ed4a24fbf3021af0893ac Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:39:35 +0000 Subject: [PATCH 36/77] Compute component: - Corrected OFC'23 constants --- .../nbi_plugins/ietf_l2vpn/Constants.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 b4f34c12a..84a18b32c 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 @@ -76,12 +76,12 @@ BEARER_MAPPINGS = { 'R4:1/3': ('R4', '1/3', '5.4.1.3', None, 0, None, None, None, None), # OFC'23 - 'PE1:1/2': ('PE1', '1/1', '10.1.1.1', None, 0, None, None, None, None), - 'PE1:1/3': ('PE1', '1/2', '10.1.1.2', None, 0, None, None, None, None), - 'PE2:1/2': ('PE2', '1/1', '10.2.1.1', None, 0, None, None, None, None), - 'PE2:1/3': ('PE2', '1/2', '10.2.1.2', None, 0, None, None, None, None), - 'PE3:1/2': ('PE3', '1/1', '10.3.1.1', None, 0, None, None, None, None), - 'PE3:1/3': ('PE3', '1/2', '10.3.1.2', None, 0, None, None, None, None), - 'PE4:1/2': ('PE4', '1/1', '10.4.1.1', None, 0, None, None, None, None), - 'PE4:1/3': ('PE4', '1/2', '10.4.1.2', None, 0, None, None, None, None), + '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), } -- GitLab From 679ee91c12fd2e878e4dfc96a5cd34b12767166e Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:42:23 +0000 Subject: [PATCH 37/77] Device component: - Corrected underlying link discovery - IETF L2VPN Driver: corrected configuration rules, removed logs, corrected parsing of return messages, minor adaptations in WIM connector - XR Driver: Added topology discovery --- src/device/service/Tools.py | 2 +- .../drivers/ietf_l2vpn/IetfL2VpnDriver.py | 33 ++++--------- .../ietf_l2vpn/WimconnectorIETFL2VPN.py | 13 ++--- src/device/service/drivers/xr/XrDriver.py | 49 ++++++++++++++++++- 4 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index 1dccea3ab..3694ce129 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -202,7 +202,7 @@ def populate_endpoints( _sub_link.name = resource_value['name'] new_sub_links[_sub_link_uuid] = _sub_link - for device_uuid,endpoint_uuid in resource_value['name']: + for device_uuid,endpoint_uuid in resource_value['endpoints']: _sub_link_endpoint_id = _sub_link.link_endpoint_ids.add() # pylint: disable=no-member _sub_link_endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME _sub_link_endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py index 8c7feb249..e08b7625b 100644 --- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -131,24 +131,12 @@ class IetfL2VpnDriver(_Driver): results.append((resource[0], exc)) continue - src_device_endpoint = resource_value['src'] - src_device = src_device_endpoint['device'] - #src_device_uuid = src_device['uuid'] - src_device_name = src_device['name'] - src_endpoint = src_device_endpoint['endpoint'] - #src_endpoint_uuid = src_endpoint['uuid'] - src_endpoint_name = src_endpoint['name'] - - dst_device_endpoint = resource_value['dst'] - dst_device = dst_device_endpoint['device'] - #dst_device_uuid = dst_device['uuid'] - dst_device_name = dst_device['name'] - dst_endpoint = dst_device_endpoint['endpoint'] - #dst_endpoint_uuid = dst_endpoint['uuid'] - dst_endpoint_name = dst_endpoint['name'] - - encap_type = resource_value['encapsulation_type'] - vlan_id = resource_value['vlan_id'] + src_device_name = resource_value['src_device_name'] + src_endpoint_name = resource_value['src_endpoint_name'] + dst_device_name = resource_value['dst_device_name'] + dst_endpoint_name = resource_value['dst_endpoint_name'] + encap_type = resource_value['encapsulation_type'] + vlan_id = resource_value['vlan_id'] src_endpoint_id = json_endpoint_id(json_device_id(src_device_name), src_endpoint_name) src_service_endpoint_id, src_mapping = wim_mapping('1', src_endpoint_id) @@ -163,10 +151,8 @@ class IetfL2VpnDriver(_Driver): connection_point(dst_service_endpoint_id, encap_type, vlan_id), ] - result = self.wim.create_connectivity_service(SERVICE_TYPE, connection_points) - LOGGER.info('[SetConfig] CREATE result={:s}'.format(str(result))) - - results.extend(process_connectivity_service('SetConfig', None)) + self.wim.create_connectivity_service(service_uuid, SERVICE_TYPE, connection_points) + results.append((resource_key, True)) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unhandled error processing resource_key({:s})'.format(str(resource_key))) results.append((resource_key, e)) @@ -187,8 +173,7 @@ class IetfL2VpnDriver(_Driver): if service_exists(self.wim, service_uuid): self.wim.delete_connectivity_service(service_uuid) - - results.extend(process_connectivity_service('DeleteConfig', None)) + results.append((resource_key, True)) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unhandled error processing resource_key({:s})'.format(str(resource_key))) results.append((resource_key, e)) diff --git a/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py index 960256cd0..34ff184c0 100644 --- a/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py +++ b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py @@ -119,7 +119,7 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): else: return self.mappings[id] - def create_connectivity_service(self, service_type, connection_points, **kwargs): + def create_connectivity_service(self, service_uuid, service_type, connection_points, **kwargs): """Stablish WAN connectivity between the endpoints Arguments: @@ -154,9 +154,7 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): Other QoS might be passed as keyword arguments. Returns: - tuple: ``(service_id, conn_info)`` containing: - - *service_uuid* (str): UUID of the established connectivity - service + tuple: ``conn_info``: - *conn_info* (dict or None): Information to be stored at the database (or ``None``). This information will be provided to the :meth:`~.edit_connectivity_service` and :obj:`~.delete`. @@ -182,9 +180,8 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): raise SdnConnectorError(msg.format(min_endpoints, service_type)) """First step, create the vpn service""" - uuid_l2vpn = str(uuid.uuid4()) vpn_service = {} - vpn_service["vpn-id"] = uuid_l2vpn + vpn_service["vpn-id"] = service_uuid vpn_service["vpn-svc-type"] = vpn_service_type vpn_service["svc-topo"] = "any-to-any" vpn_service["customer-name"] = "osm" @@ -277,7 +274,7 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): site_network_access["connection"] = connection self.logger.info("Sending connection:{}".format(connection)) vpn_attach = {} - vpn_attach["vpn-id"] = uuid_l2vpn + vpn_attach["vpn-id"] = service_uuid vpn_attach["site-role"] = vpn_service["svc-topo"] + "-role" site_network_access["vpn-attachment"] = vpn_attach self.logger.info("Sending vpn-attachement :{}".format(vpn_attach)) @@ -379,7 +376,7 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): raise SdnConnectorError("Request Timeout", http_code=408) - return uuid_l2vpn, conn_info + return conn_info def delete_connectivity_service(self, service_uuid, conn_info=None): """Disconnect multi-site endpoints previously connected diff --git a/src/device/service/drivers/xr/XrDriver.py b/src/device/service/drivers/xr/XrDriver.py index 605f4ce8d..83ffd5218 100644 --- a/src/device/service/drivers/xr/XrDriver.py +++ b/src/device/service/drivers/xr/XrDriver.py @@ -16,9 +16,11 @@ import logging import threading import json -from typing import Any, Iterator, List, Optional, Tuple, Union +from typing import Any, Iterator, List, Optional, Set, Tuple, Union import urllib3 +from common.DeviceTypes import DeviceTypeEnum from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum from common.type_checkers.Checkers import chk_type from device.service.driver_api._Driver import _Driver from .cm.cm_connection import CmConnection, ConsistencyMode @@ -98,7 +100,50 @@ class XrDriver(_Driver): constellation = self.__cm_connection.get_constellation_by_hub_name(self.__hub_module_name) if constellation: self.__constellation = constellation - return [(f"/endpoints/endpoint[{ifname}]", {'uuid': ifname, 'type': 'optical', 'sample_types': {}}) for ifname in constellation.ifnames()] + #return [(f"/endpoints/endpoint[{ifname}]", {'uuid': ifname, 'type': 'optical', 'sample_types': {}}) for ifname in constellation.ifnames()] + + devices : Set[str] = set() + pluggables : Set[str] = set() + devices_and_endpoints = [] + for ifname in constellation.ifnames(): + device_name,pluggable_name = ifname.split('|') + + if device_name not in devices: + device_url = '/devices/device[{:s}]'.format(device_name) + device_data = { + 'uuid': device_name, 'name': device_name, + 'type': DeviceTypeEnum.EMULATED_PACKET_ROUTER.value, + 'status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED, + 'drivers': [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED], + } + devices_and_endpoints.append((device_url, device_data)) + + for copper_if_index in range(4): + copper_ifname = '1/{:d}'.format(copper_if_index + 1) + endpoint_url = '/endpoints/endpoint[{:s}]'.format(copper_ifname) + endpoint_data = { + 'device_uuid': device_name, 'uuid': copper_ifname, 'name': copper_ifname, + 'type': 'copper/internal', 'sample_types': {} + } + devices_and_endpoints.append((endpoint_url, endpoint_data)) + + devices.add(device_name) + + if ifname not in pluggables: + endpoint_url = '/endpoints/endpoint[{:s}]'.format(ifname) + if 'hub' in ifname.lower(): + endpoint_type = 'optical/xr-hub' + elif 'leaf' in ifname.lower(): + endpoint_type = 'optical/xr-leaf' + else: + endpoint_type = 'optical/xr' + endpoint_data = { + 'device_uuid': device_name, 'uuid': pluggable_name, 'name': pluggable_name, + 'type': endpoint_type, 'sample_types': {} + } + devices_and_endpoints.append((endpoint_url, endpoint_data)) + + return devices_and_endpoints else: return [] -- GitLab From 1f7e51632d08c8d572fd8511c52319455620a9b2 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:43:15 +0000 Subject: [PATCH 38/77] PathComp component - Backend: - Deactivated bidirectional link auto-creation - Deactivated resource consumption in multi-path computations --- src/pathcomp/backend/pathComp_RESTapi.c | 4 +++- src/pathcomp/backend/pathComp_sp.c | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pathcomp/backend/pathComp_RESTapi.c b/src/pathcomp/backend/pathComp_RESTapi.c index 8ee7f6d82..82d4b38a8 100644 --- a/src/pathcomp/backend/pathComp_RESTapi.c +++ b/src/pathcomp/backend/pathComp_RESTapi.c @@ -1211,7 +1211,9 @@ void parsing_json_obj_pathComp_request(cJSON * root, GIOChannel * source) // In the context information, if solely the list of links are passed for a single direction, // the reverse direction MUST be created sythetically - generate_reverse_linkList(); + + // LGR: deactivated; link duplication needs to be done smartly with TAPI. done manually in topology by now + //generate_reverse_linkList(); } return; } diff --git a/src/pathcomp/backend/pathComp_sp.c b/src/pathcomp/backend/pathComp_sp.c index 447b0d2a6..b143b0493 100644 --- a/src/pathcomp/backend/pathComp_sp.c +++ b/src/pathcomp/backend/pathComp_sp.c @@ -296,8 +296,8 @@ void sp_execution_services(struct compRouteOutputList_t* oPathList) continue; } struct path_t* path = &(pathService->paths[pathService->numPaths - 1]); - allocate_graph_resources(path, service, g); - allocate_graph_reverse_resources(path, service, g); + //allocate_graph_resources(path, service, g); // LGR: crashes in some cases with assymetric topos + //allocate_graph_reverse_resources(path, service, g); // LGR: crashes in some cases with assymetric topos print_graph(g); } return; -- GitLab From 71e7ebce65230cf3ab661927f29289b91be7f495 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:44:48 +0000 Subject: [PATCH 39/77] Service component: - L2NM/L3NM Emulated Service Handler: ignored configurations when there are no rules to configure - IETF L2VPN Service Handler: corrected configuration rule for Device component --- .../L2NMEmulatedServiceHandler.py | 20 +++++++++++-------- .../L2NM_IETFL2VPN_ServiceHandler.py | 12 ++++------- .../L3NMEmulatedServiceHandler.py | 20 +++++++++++-------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py index 9de6c607b..0a2261ceb 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 index f58c7809f..7a2c4e723 100644 --- a/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py +++ b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py @@ -68,16 +68,12 @@ class L2NM_IETFL2VPN_ServiceHandler(_ServiceHandler): 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, - 'src': { - 'device' : {'uuid': src_device_uuid, 'name': src_device.name }, - 'endpoint': {'uuid': src_endpoint_uuid, 'name': src_endpoint.name}, - }, - 'dst': { - 'device' : {'uuid': dst_device_uuid, 'name': dst_device.name }, - 'endpoint': {'uuid': dst_endpoint_uuid, 'name': dst_endpoint.name}, - }, }) del manager.device_config.config_rules[:] manager.device_config.config_rules.append(ConfigRule(**json_config_rule)) diff --git a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py index 47de9c94f..18da03b08 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))) -- GitLab From 014706d30affa0242654a417e2b27def836c5d79 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:47:37 +0000 Subject: [PATCH 40/77] Tools - Mock MW SDN Controller: - Added link identifiers - Added run script --- .../tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py | 4 ++-- src/tests/tools/mock_mw_sdn_ctrl/run.sh | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100755 src/tests/tools/mock_mw_sdn_ctrl/run.sh diff --git a/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py index d3d9de4a2..91542d85b 100644 --- a/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py +++ b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py @@ -53,11 +53,11 @@ NETWORK_NODES = [ ]} ] NETWORK_LINKS = [ - { + { 'link-id' : '172.18.0.1:2--172.18.0.2:2', 'source' : {'source-node': '172.18.0.1', 'source-tp': '2'}, 'destination': {'dest-node' : '172.18.0.2', 'dest-tp' : '2'}, }, - { + { 'link-id' : '172.18.0.3:2--172.18.0.4:2', 'source' : {'source-node': '172.18.0.3', 'source-tp': '2'}, 'destination': {'dest-node' : '172.18.0.4', 'dest-tp' : '2'}, } diff --git a/src/tests/tools/mock_mw_sdn_ctrl/run.sh b/src/tests/tools/mock_mw_sdn_ctrl/run.sh new file mode 100755 index 000000000..415fc1751 --- /dev/null +++ b/src/tests/tools/mock_mw_sdn_ctrl/run.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env 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. + +python MockMWSdnCtrl.py -- GitLab From 956e1ead6b8d6f603d96df47751b2b504407dce6 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:48:36 +0000 Subject: [PATCH 41/77] Tools - Mock IPM SDN Controller: - Initial version (work in progress) --- .../tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py | 131 ++++++++++++++++++ src/tests/tools/mock_ipm_sdn_ctrl/run.sh | 16 +++ 2 files changed, 147 insertions(+) create mode 100644 src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py create mode 100755 src/tests/tools/mock_ipm_sdn_ctrl/run.sh 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 000000000..52a85a00d --- /dev/null +++ b/src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py @@ -0,0 +1,131 @@ +# 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 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'}, + 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}, {'moduleIf': {'clientIfAid': 'XR-T4'}}] + }}, + 'leafModules': [ + {'state': { + 'module': {'moduleName': 'OFC LEAF 1', 'trafficMode': 'L1Mode'}, + 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}] + }}, + {'state': { + 'module': {'moduleName': 'OFC LEAF 2', 'trafficMode': 'L1Mode'}, + 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}] + }} + ] +} + +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 Health(Resource): +# def get(self): +# return make_response(jsonify({}), 200) + +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) + form_request = request.form + if form_request.get('client_id') != 'xr-web-client': return make_response('bad client_id', 403) + if form_request.get('client_secret') != 'xr-web-client': return make_response('bad client_secret', 403) + if form_request.get('grant_type') != 'password': return make_response('bad grant_type', 403) + if form_request.get('username') != IPM_USERNAME: return make_response('bad username', 403) + if form_request.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): + print(str(request.args)) + content = request.args.get('content') + print('content', content) + 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) + print('query', query) + return make_response(jsonify([CONSTELLATION]), 200) + +#class Services(Resource): +# def get(self): +# services = [service for service in NETWORK_SERVICES.values()] +# return make_response(jsonify({'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': services}}), 200) +# +# def post(self): +# json_request = request.get_json() +# if not json_request: abort(400) +# if not isinstance(json_request, dict): abort(400) +# if 'etht-svc-instances' not in json_request: abort(400) +# json_services = json_request['etht-svc-instances'] +# if not isinstance(json_services, list): abort(400) +# if len(json_services) != 1: abort(400) +# svc_data = json_services[0] +# etht_svc_name = svc_data['etht-svc-name'] +# NETWORK_SERVICES[etht_svc_name] = svc_data +# return make_response(jsonify({}), 201) + +#class DelServices(Resource): +# def delete(self, service_uuid : str): +# NETWORK_SERVICES.pop(service_uuid, None) +# return make_response(jsonify({}), 204) + +def main(): + LOGGER.info('Starting...') + + app = Flask(__name__) + app.after_request(functools.partial(log_request, LOGGER)) + + api = Api(app) + #api.add_resource(Health, '/ietf-network:networks') + api.add_resource(OpenIdConnect, '/realms/xr-cm/protocol/openid-connect/token') + api.add_resource(XrNetworks, '/api/v1/xr-networks') + #api.add_resource(Network, '/ietf-network:networks/network=') + #api.add_resource(Services, '/ietf-eth-tran-service:etht-svc') + #api.add_resource(DelServices, '/ietf-eth-tran-service:etht-svc/etht-svc-instances=') + + LOGGER.info('Listening on {:s}...'.format(str(STR_ENDPOINT))) + app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT, ssl_context='adhoc') + + LOGGER.info('Bye') + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/tests/tools/mock_ipm_sdn_ctrl/run.sh b/src/tests/tools/mock_ipm_sdn_ctrl/run.sh new file mode 100755 index 000000000..2aa78712c --- /dev/null +++ b/src/tests/tools/mock_ipm_sdn_ctrl/run.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env 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. + +python MockIPMSdnCtrl.py -- GitLab From 1a4bef03dd71ce6f101a86ad1b3afbbed9767f0d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:55:44 +0000 Subject: [PATCH 42/77] OFC'23 scenario: - Added XR controller in the topology - Created separate file without XR devices - Removed unneeded descriptors - Updated service descriptor --- .../ofc23/descriptors/dc-2-dc-service.json | 31 +- .../ofc23/descriptors/descriptor_parent.json | 216 +++++++++--- .../descriptors/descriptor_parent_noxr.json | 332 ++++++++++++++++++ src/tests/ofc23/descriptors/splitter.json | 18 - 4 files changed, 515 insertions(+), 82 deletions(-) create mode 100644 src/tests/ofc23/descriptors/descriptor_parent_noxr.json delete mode 100644 src/tests/ofc23/descriptors/splitter.json diff --git a/src/tests/ofc23/descriptors/dc-2-dc-service.json b/src/tests/ofc23/descriptors/dc-2-dc-service.json index e60b7489b..7c3be015d 100644 --- a/src/tests/ofc23/descriptors/dc-2-dc-service.json +++ b/src/tests/ofc23/descriptors/dc-2-dc-service.json @@ -16,27 +16,24 @@ ], "service_config": {"config_rules": [ {"action": 1, "custom": {"resource_key": "/settings", "resource_value": { - "address_families": ["IPV4"], - "bgp_as": 65000, - "bgp_route_target": "65000:333", - "mtu": 1512, - "vlan_id": 300 + "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": { - "address_ip": "3.3.2.1", - "address_prefix": 24, - "route_distinguisher": "65000:123", - "router_id": "10.10.10.1", - "sub_interface_index": 400, - "vlan_id": 400 + "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": { - "address_ip": "3.3.1.1", - "address_prefix": 24, - "route_distinguisher": "65000:321", - "router_id": "20.20.20.1", - "sub_interface_index": 400, - "vlan_id": 500 + "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/descriptor_parent.json b/src/tests/ofc23/descriptors/descriptor_parent.json index fd6b23857..acf98c574 100644 --- a/src/tests/ofc23/descriptors/descriptor_parent.json +++ b/src/tests/ofc23/descriptors/descriptor_parent.json @@ -47,48 +47,41 @@ ]} }, { - "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_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": "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"} - ]}}} + {"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_id": {"device_uuid": {"uuid": "R2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "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": "1/1"}, - {"sample_types": [], "type": "copper/internal", "uuid": "1/2"} + {"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": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "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": "copper/internal", "uuid": "1/1"}, - {"sample_types": [], "type": "copper/internal", "uuid": "1/2"} + {"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"} ]}}} ]} }, @@ -112,12 +105,28 @@ {"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"}}, @@ -125,11 +134,27 @@ ] }, { - "link_id": {"link_uuid": {"uuid": "MW1-2/172.18.0.2:1==R1/1/1"}}, "link_endpoint_ids": [ + "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": "R1"}}, "endpoint_uuid": {"uuid": "1/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"}}, @@ -137,53 +162,150 @@ ] }, { - "link_id": {"link_uuid": {"uuid": "MW3-4/172.18.0.4:1==R1/1/2"}}, "link_endpoint_ids": [ + "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": "R1"}}, "endpoint_uuid": {"uuid": "1/2"}} + {"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": "R1/1/3==OLS/aade6001-f00b-5e2f-a357-6a0a9d3de870"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "1/3"}}, + "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/0ef74f99-1acc-57bd-ab9d-4b958b06c513==R2/1/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}}, - {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "1/1"}} + "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": "OLS/50296d99-58cc-5ce7-82f5-fc8ee4eec2ec==R3/1/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "OLS"}}, "endpoint_uuid": {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"}}, - {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "1/1"}} + "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/2==PE3/1/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "1/2"}}, - {"device_id": {"device_uuid": {"uuid": "PE3"}}, "endpoint_uuid": {"uuid": "1/1"}} + "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": "R3/1/2==PE4/1/1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "1/2"}}, - {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/1"}} + "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==DC2/eth1"}}, "link_endpoint_ids": [ + "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": "DC2"}}, "endpoint_uuid": {"uuid": "eth1"}} + {"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==DC2/eth2"}}, "link_endpoint_ids": [ + "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/descriptor_parent_noxr.json b/src/tests/ofc23/descriptors/descriptor_parent_noxr.json new file mode 100644 index 000000000..c4a6646ed --- /dev/null +++ b/src/tests/ofc23/descriptors/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/splitter.json b/src/tests/ofc23/descriptors/splitter.json deleted file mode 100644 index 680829e6e..000000000 --- a/src/tests/ofc23/descriptors/splitter.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "devices": [ - { - "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"} - ]}}} - ]} - } - ] -} -- GitLab From 79f603af82cc449da9d1ca1afdb0ecdbc459cd22 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 16:56:37 +0000 Subject: [PATCH 43/77] WebUI component: - Added topology icons for radio routers - Increased topology canvas size --- .../static/topology_icons/Acknowledgements.txt | 4 ++++ .../topology_icons/emu-packet-radio-router.png | Bin 0 -> 5508 bytes .../topology_icons/packet-radio-router.png | Bin 0 -> 19322 bytes src/webui/service/templates/js/topology.js | 4 ++-- 4 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 src/webui/service/static/topology_icons/emu-packet-radio-router.png create mode 100644 src/webui/service/static/topology_icons/packet-radio-router.png diff --git a/src/webui/service/static/topology_icons/Acknowledgements.txt b/src/webui/service/static/topology_icons/Acknowledgements.txt index fb04defd4..de69c89ce 100644 --- a/src/webui/service/static/topology_icons/Acknowledgements.txt +++ b/src/webui/service/static/topology_icons/Acknowledgements.txt @@ -27,3 +27,7 @@ https://symbols.getvecta.com/stencil_241/289_wae.216d930c17.png => emu-optical-t https://symbols.getvecta.com/stencil_240/128_localdirector.c1e561769f.png => optical-splitter.png https://symbols.getvecta.com/stencil_241/158_local-director.6b38eab9e4.png => emu-optical-splitter.png + + +https://symbols.getvecta.com/stencil_240/197_radio-tower.b6138c8c29.png => radio-router.png +https://symbols.getvecta.com/stencil_241/216_radio-tower.5159339bc0.png => emu-radio-router.png \ No newline at end of file diff --git a/src/webui/service/static/topology_icons/emu-packet-radio-router.png b/src/webui/service/static/topology_icons/emu-packet-radio-router.png new file mode 100644 index 0000000000000000000000000000000000000000..00257d0e2ee357dbdd392a408cfdbe07e006ff2a GIT binary patch literal 5508 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 Date: Tue, 28 Feb 2023 17:01:12 +0000 Subject: [PATCH 44/77] PathComp component - Frontend: - Corrected endpoint-to-link datastructure - Improved identification of config rules by device/endpoint uuid/name - Assumed links are unidirectional - Rewritten sub-serice computation logic to consider intermediate controllers - Rewritten method eropath_to_hops based on new endpoint-to-link datastructure - Minor bug resolutions - Updated test pathcomp temporary code --- .../frontend/service/algorithms/_Algorithm.py | 16 +- .../algorithms/tools/ComposeConfigRules.py | 22 +- .../algorithms/tools/ComposeRequest.py | 4 +- .../algorithms/tools/ComputeSubServices.py | 170 ++++----------- .../service/algorithms/tools/EroPathToHops.py | 30 ++- .../algorithms/tools/ResourceGroups.py | 94 +++++++++ .../service/algorithms/tools/ServiceTypes.py | 53 +++++ test_pathcomp/ComputeSubServices.py | 196 ++++-------------- test_pathcomp/ResourceGroups.py | 93 +++++++++ test_pathcomp/ServiceTypes.py | 53 +++++ test_pathcomp/data.py | 121 ++++------- test_pathcomp/old_ComputeSubServices.py | 119 +++++++++++ 12 files changed, 568 insertions(+), 403 deletions(-) create mode 100644 src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py create mode 100644 src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py create mode 100644 test_pathcomp/ResourceGroups.py create mode 100644 test_pathcomp/ServiceTypes.py create mode 100644 test_pathcomp/old_ComputeSubServices.py diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py index 0286e1420..6cc4c5496 100644 --- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py +++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py @@ -25,6 +25,10 @@ from .tools.ComposeRequest import compose_device, compose_link, compose_service from .tools.ComputeSubServices import ( convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection) +SRC_END = 'src' +DST_END = 'dst' +SENSE = [SRC_END, DST_END] + class _Algorithm: def __init__(self, algorithm_id : str, sync_paths : bool, class_name=__name__) -> None: # algorithm_id: algorithm to be executed @@ -44,7 +48,7 @@ class _Algorithm: self.endpoint_name_mapping : Dict[Tuple[str, str], str] = dict() self.link_list : List[Dict] = list() self.link_dict : Dict[str, Tuple[Dict, Link]] = dict() - self.endpoint_to_link_dict : Dict[Tuple[str, str], Tuple[Dict, Link]] = dict() + self.endpoint_to_link_dict : Dict[Tuple[str, str, str], Tuple[Dict, Link]] = dict() self.service_list : List[Dict] = list() self.service_dict : Dict[Tuple[str, str], Tuple[Dict, Service]] = dict() @@ -86,11 +90,11 @@ class _Algorithm: link_uuid = json_link['link_Id'] self.link_dict[link_uuid] = (json_link, grpc_link) - for link_endpoint_id in json_link['link_endpoint_ids']: + for i,link_endpoint_id in enumerate(json_link['link_endpoint_ids']): link_endpoint_id = link_endpoint_id['endpoint_id'] device_uuid = link_endpoint_id['device_id'] endpoint_uuid = link_endpoint_id['endpoint_uuid'] - endpoint_key = (device_uuid, endpoint_uuid) + endpoint_key = (device_uuid, endpoint_uuid, SENSE[i]) link_tuple = (json_link, grpc_link) self.endpoint_to_link_dict[endpoint_key] = link_tuple @@ -175,7 +179,9 @@ class _Algorithm: MSG = 'Unhandled generic Config Rules for service {:s} {:s}' self.logger.warning(MSG.format(str(service_uuid), str(ServiceTypeEnum.Name(service_type)))) - compose_device_config_rules(config_rules, service.service_config.config_rules, path_hops) + compose_device_config_rules( + config_rules, service.service_config.config_rules, path_hops, + self.device_name_mapping, self.endpoint_name_mapping) if path_hops is not None and len(path_hops) > 0: ingress_endpoint_id = service.service_endpoint_ids.add() @@ -214,7 +220,7 @@ class _Algorithm: if no_path_issue is not None: # no path found: leave connection with no endpoints # no_path_issue == 1 => no path due to a constraint - grpc_services[service_key] = grpc_orig_service + grpc_services[orig_service_key] = grpc_orig_service continue orig_config_rules = grpc_orig_service.service_config.config_rules diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py index 30845bb11..91367e23f 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, re -from typing import Dict, List, Optional +import itertools, json, re +from typing import Dict, List, Optional, Tuple from common.proto.context_pb2 import ConfigRule from common.tools.object_factory.ConfigRule import json_config_rule_set @@ -71,7 +71,11 @@ def compose_l3nm_config_rules(main_service_config_rules : List, subservice_confi def compose_tapi_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None: compose_config_rules(main_service_config_rules, subservice_config_rules, TAPI_SETTINGS_FIELD_DEFAULTS) -def compose_device_config_rules(config_rules : List, subservice_config_rules : List, path_hops : List) -> None: +def compose_device_config_rules( + config_rules : List, subservice_config_rules : List, path_hops : List, + device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str] +) -> None: + endpoints_traversed = set() for path_hop in path_hops: device_uuid_or_name = path_hop['device'] @@ -82,8 +86,16 @@ def compose_device_config_rules(config_rules : List, subservice_config_rules : L if config_rule.WhichOneof('config_rule') != 'custom': continue match = DEV_EP_SETTINGS.match(config_rule.custom.resource_key) if match is None: continue + device_uuid_or_name = match.group(1) + device_name_or_uuid = device_name_mapping[device_uuid_or_name] + device_keys = {device_uuid_or_name, device_name_or_uuid} + endpoint_uuid_or_name = match.group(2) - dev_ep_kep = (device_uuid_or_name, endpoint_uuid_or_name) - if dev_ep_kep not in endpoints_traversed: continue + endpoint_name_or_uuid_1 = endpoint_name_mapping[(device_uuid_or_name, endpoint_uuid_or_name)] + endpoint_name_or_uuid_2 = endpoint_name_mapping[(device_name_or_uuid, endpoint_uuid_or_name)] + endpoint_keys = {endpoint_uuid_or_name, endpoint_name_or_uuid_1, endpoint_name_or_uuid_2} + + device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys)) + if len(device_endpoint_keys.intersection(endpoints_traversed)) == 0: continue subservice_config_rules.append(config_rule) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py index ee85f0bb0..e2c6dc138 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py @@ -118,11 +118,11 @@ def compose_link(grpc_link : Link) -> Dict: for link_endpoint_id in grpc_link.link_endpoint_ids ] - forwarding_direction = LinkForwardingDirection.BIDIRECTIONAL.value + forwarding_direction = LinkForwardingDirection.UNIDIRECTIONAL.value total_potential_capacity = compose_capacity(200, CapacityUnit.MBPS.value) available_capacity = compose_capacity(200, CapacityUnit.MBPS.value) cost_characteristics = compose_cost_characteristics('linkcost', '1', '0') - latency_characteristics = compose_latency_characteristics('2') + latency_characteristics = compose_latency_characteristics('1') return { 'link_Id': link_uuid, 'link_endpoint_ids': endpoint_ids, 'forwarding_direction': forwarding_direction, diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py index 75701b99e..1f2b4df9c 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py @@ -30,145 +30,41 @@ # ] # # connections=[ -# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), ServiceTypeEnum.TAPI, [ +# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, [ # {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'} # ], []), -# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.L2NM, [ +# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.SERVICETYPE_L2NM, [ # {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, # {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, # {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, # {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'} # ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]), -# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), ServiceTypeEnum.L2NM, [ +# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), ServiceTypeEnum.SERVICETYPE_L2NM, [ # {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, # {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} # ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) # ] -import enum, json, queue, uuid +import logging, queue, uuid from typing import Dict, List, Optional, Tuple from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import Device, ServiceTypeEnum +from .ResourceGroups import IGNORED_DEVICE_TYPES, get_resource_classification +from .ServiceTypes import get_service_type -class StackActionEnum(enum.Enum): - PATH_INGRESS = 'ingress' - CREATE_CONNECTION = 'create' - APPEND_PATH_HOP = 'append' - CHAIN_CONNECTION = 'chain' - TERMINATE_CONNECTION = 'terminate' - -def is_datacenter(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.DATACENTER, DeviceTypeEnum.EMULATED_DATACENTER} - -def is_packet_router(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER} - -def is_packet_switch(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH} - -def is_packet_device(dev_type : Optional[DeviceTypeEnum]) -> bool: - return is_packet_router(dev_type) or is_packet_switch(dev_type) - -def is_tfs_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.TERAFLOWSDN_CONTROLLER} - -def is_mw_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM} - -def is_ipm_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.XR_CONSTELLATION, DeviceTypeEnum.EMULATED_XR_CONSTELLATION} - -def is_ols_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM} - -def is_subdevice(dev_manager : Optional[str]) -> bool: - return dev_manager is not None - -def is_subdevice_equal(dev_manager_a : Optional[str], dev_manager_b : Optional[str]) -> bool: - if dev_manager_a is None and dev_manager_b is None: return True - if dev_manager_a is not None and dev_manager_b is not None: return dev_manager_a == dev_manager_b - return False - -def get_action( - prv_type : Optional[DeviceTypeEnum], prv_manager : Optional[str], - cur_type : DeviceTypeEnum, cur_manager : Optional[str] -) -> StackActionEnum: - if prv_type is None: - return StackActionEnum.PATH_INGRESS - - if is_datacenter(prv_type): - if is_packet_device(cur_type): return StackActionEnum.CREATE_CONNECTION - if is_tfs_controller(cur_type): return StackActionEnum.CREATE_CONNECTION - - if is_packet_device(prv_type): - if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION - if is_packet_device(cur_type): - if is_subdevice_equal(cur_manager, prv_manager): return StackActionEnum.APPEND_PATH_HOP - if is_subdevice(prv_manager) and not is_subdevice(cur_manager): return StackActionEnum.TERMINATE_CONNECTION - if not is_subdevice(prv_manager) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION - - if is_mw_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION - if is_ols_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION - if is_tfs_controller(cur_type) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION - - if is_mw_controller(prv_type) or is_ols_controller(prv_type): - if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION - - if is_tfs_controller(prv_type): - if is_tfs_controller(cur_type) and is_subdevice_equal(prv_manager, cur_manager): return StackActionEnum.APPEND_PATH_HOP - if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION - if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION - if is_mw_controller(cur_type) or is_ols_controller(cur_type): return StackActionEnum.CHAIN_CONNECTION - - str_fields = ', '.join([ - 'prv_type={:s}'.format(str(prv_type)), 'prv_manager={:s}'.format(str(prv_manager)), - 'cur_type={:s}'.format(str(cur_type)), 'cur_manager={:s}'.format(str(cur_manager)), - ]) - raise Exception('Undefined Action for ({:s})'.format(str_fields)) - -def get_device_manager_uuid(device : Device) -> Optional[str]: - for config_rule in device.device_config.config_rules: - if config_rule.WhichOneof('config_rule') != 'custom': continue - if config_rule.custom.resource_key != '_manager': continue - device_manager_id = json.loads(config_rule.custom.resource_value) - return device_manager_id['uuid'] - return None - -def get_device_type( - grpc_device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str] -) -> DeviceTypeEnum: - if device_manager_uuid is None: - return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member - device_manager_tuple = device_dict.get(device_manager_uuid) - if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid))) - _,grpc_device = device_manager_tuple - return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member - -SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} - -def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: - if is_tfs_controller(device_type) or is_packet_router(device_type): - if prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type - if is_packet_switch(device_type) or is_mw_controller(device_type): - if prv_service_type == ServiceTypeEnum.SERVICETYPE_L2NM: return prv_service_type - if is_ols_controller(device_type) or is_ipm_controller(device_type): - return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE - - str_fields = ', '.join([ - 'device_type={:s}'.format(str(device_type)), - ]) - raise Exception('Undefined Service Type for ({:s})'.format(str_fields)) +LOGGER = logging.getLogger(__name__) def convert_explicit_path_hops_to_connections( path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], main_service_uuid : str, main_service_type : ServiceTypeEnum ) -> List[Tuple[str, int, List[str], List[str]]]: + LOGGER.debug('path_hops={:s}'.format(str(path_hops))) + connection_stack = queue.LifoQueue() connections : List[Tuple[str, int, List[str], List[str]]] = list() prv_device_uuid = None - prv_device_type = None - prv_manager_uuid = None + prv_res_class : Tuple[Optional[int], Optional[DeviceTypeEnum], Optional[str]] = None, None, None for path_hop in path_hops: device_uuid = path_hop['device'] @@ -177,29 +73,35 @@ def convert_explicit_path_hops_to_connections( if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) _,grpc_device = device_tuple - manager_uuid = get_device_manager_uuid(grpc_device) - device_type = get_device_type(grpc_device, device_dict, manager_uuid) - action = get_action(prv_device_type, prv_manager_uuid, device_type, manager_uuid) + res_class = get_resource_classification(grpc_device, device_dict) + if res_class[1] in IGNORED_DEVICE_TYPES: continue - if action == StackActionEnum.PATH_INGRESS: + if prv_res_class[0] is None: + # path ingress connection_stack.put((main_service_uuid, main_service_type, [path_hop], [])) - elif action == StackActionEnum.CREATE_CONNECTION: - connection_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] - service_type = get_service_type(device_type, prv_service_type) - connection_stack.put((connection_uuid, service_type, [path_hop], [])) - elif action == StackActionEnum.APPEND_PATH_HOP: - connection_stack.queue[-1][2].append(path_hop) - elif action == StackActionEnum.CHAIN_CONNECTION: - connection = connection_stack.get() - connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) - + elif prv_res_class[0] > res_class[0]: + # create underlying connection connection_uuid = str(uuid.uuid4()) prv_service_type = connection_stack.queue[-1][1] - service_type = get_service_type(device_type, prv_service_type) + service_type = get_service_type(res_class[1], prv_service_type) connection_stack.put((connection_uuid, service_type, [path_hop], [])) - elif action == StackActionEnum.TERMINATE_CONNECTION: + elif prv_res_class[0] == res_class[0]: + # same resource group kind + if prv_res_class[1] == res_class[1] and prv_res_class[2] == res_class[2]: + # same device type and device manager: connection continues + connection_stack.queue[-1][2].append(path_hop) + else: + # different device type or device manager: chain connections + connection = connection_stack.get() + connections.append(connection) + connection_stack.queue[-1][3].append(connection[0]) + + connection_uuid = str(uuid.uuid4()) + prv_service_type = connection_stack.queue[-1][1] + service_type = get_service_type(res_class[1], prv_service_type) + connection_stack.put((connection_uuid, service_type, [path_hop], [])) + elif prv_res_class[0] < res_class[0]: + # underlying connection ended connection = connection_stack.get() connections.append(connection) connection_stack.queue[-1][3].append(connection[0]) @@ -208,11 +110,11 @@ def convert_explicit_path_hops_to_connections( raise Exception('Uncontrolled condition') prv_device_uuid = device_uuid - prv_device_type = device_type - prv_manager_uuid = manager_uuid + prv_res_class = res_class # path egress connections.append(connection_stack.get()) + LOGGER.debug('connections={:s}'.format(str(connections))) assert connection_stack.empty() return connections diff --git a/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py b/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py index c8a902999..670757d76 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py +++ b/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py @@ -43,13 +43,17 @@ # import logging -from typing import Dict, List +from typing import Dict, List, Tuple +from common.proto.context_pb2 import Link LOGGER = logging.getLogger(__name__) -def eropath_to_hops(ero_path : List[Dict], endpoint_to_link_dict : Dict) -> List[Dict]: +def eropath_to_hops( + ero_path : List[Dict], endpoint_to_link_dict : Dict[Tuple[str, str, str], Tuple[Dict, Link]] +) -> List[Dict]: try: path_hops = [] + num_ero_hops = len(ero_path) for endpoint in ero_path: device_uuid = endpoint['device_id'] endpoint_uuid = endpoint['endpoint_uuid'] @@ -59,23 +63,17 @@ def eropath_to_hops(ero_path : List[Dict], endpoint_to_link_dict : Dict) -> List continue last_hop = path_hops[-1] - if (last_hop['device'] == device_uuid): - if ('ingress_ep' not in last_hop) or ('egress_ep' in last_hop): continue - last_hop['egress_ep'] = endpoint_uuid - continue + if last_hop['device'] != device_uuid: raise Exception('Malformed path') + last_hop['egress_ep'] = endpoint_uuid + + if num_ero_hops - 1 == len(path_hops): break - endpoint_key = (last_hop['device'], last_hop['egress_ep']) - link_tuple = endpoint_to_link_dict.get(endpoint_key) - ingress = next(iter([ - ep_id for ep_id in link_tuple[0]['link_endpoint_ids'] - if (ep_id['endpoint_id']['device_id'] == device_uuid) and\ - (ep_id['endpoint_id']['endpoint_uuid'] != endpoint_uuid) - ]), None) - if ingress['endpoint_id']['device_id'] != device_uuid: raise Exception('Malformed path') + link_tuple = endpoint_to_link_dict[(device_uuid, endpoint_uuid, 'src')] + if link_tuple is None: raise Exception('Malformed path') + ingress = link_tuple[0]['link_endpoint_ids'][-1] path_hops.append({ 'device': ingress['endpoint_id']['device_id'], - 'ingress_ep': ingress['endpoint_id']['endpoint_uuid'], - 'egress_ep': endpoint_uuid, + 'ingress_ep': ingress['endpoint_id']['endpoint_uuid'] }) return path_hops except: diff --git a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py new file mode 100644 index 000000000..0f6ee63dc --- /dev/null +++ b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py @@ -0,0 +1,94 @@ +# 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 +from typing import Dict, Optional, Tuple +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import Device +from common.tools.grpc.Tools import grpc_message_to_json_string + +DEVICE_TYPE_TO_DEEPNESS = { + DeviceTypeEnum.EMULATED_DATACENTER.value : 90, + DeviceTypeEnum.DATACENTER.value : 90, + DeviceTypeEnum.NETWORK.value : 90, + + DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value : 80, + DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : 70, + DeviceTypeEnum.PACKET_ROUTER.value : 70, + + DeviceTypeEnum.EMULATED_PACKET_SWITCH.value : 60, + DeviceTypeEnum.PACKET_SWITCH.value : 60, + DeviceTypeEnum.EMULATED_P4_SWITCH.value : 60, + DeviceTypeEnum.P4_SWITCH.value : 60, + + DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM.value : 40, + DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM.value : 40, + + DeviceTypeEnum.EMULATED_XR_CONSTELLATION.value : 40, + DeviceTypeEnum.XR_CONSTELLATION.value : 40, + + DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value : 30, + DeviceTypeEnum.OPEN_LINE_SYSTEM.value : 30, + + DeviceTypeEnum.EMULATED_PACKET_RADIO_ROUTER.value : 10, + DeviceTypeEnum.PACKET_RADIO_ROUTER.value : 10, + DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER.value : 10, + DeviceTypeEnum.OPTICAL_TRANSPONDER.value : 10, + DeviceTypeEnum.EMULATED_OPTICAL_ROADM.value : 10, + DeviceTypeEnum.OPTICAL_ROADM.value : 10, + + DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER.value : 0, +} + +IGNORED_DEVICE_TYPES = {DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER} + +def get_device_manager_uuid( + device : Device +) -> Optional[str]: + for config_rule in device.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + if config_rule.custom.resource_key != '_manager': continue + device_manager_id = json.loads(config_rule.custom.resource_value) + return device_manager_id['uuid'] + return None + +def _map_device_type(device : Device) -> DeviceTypeEnum: + device_type = DeviceTypeEnum._value2member_map_.get(device.device_type) # pylint: disable=no-member + if device_type is None: + MSG = 'Unsupported DeviceType({:s}) for Device({:s})' + raise Exception(MSG.format(str(device.device_type), grpc_message_to_json_string(device))) + return device_type + +def _map_resource_to_deepness(device_type : DeviceTypeEnum) -> int: + deepness = DEVICE_TYPE_TO_DEEPNESS.get(device_type.value) + if deepness is None: raise Exception('Unsupported DeviceType({:s})'.format(str(device_type.value))) + return deepness + +def get_device_type( + device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str] +) -> DeviceTypeEnum: + if device_manager_uuid is None: return _map_device_type(device) + device_manager_tuple = device_dict.get(device_manager_uuid) + if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid))) + _,device = device_manager_tuple + return _map_device_type(device) + +def get_resource_classification( + device : Device, device_dict : Dict[str, Tuple[Dict, Device]] +) -> Tuple[int, DeviceTypeEnum, Optional[str]]: + device_manager_uuid = get_device_manager_uuid(device) + device_type = get_device_type(device, device_dict, device_manager_uuid) + resource_deepness = _map_resource_to_deepness(device_type) + return resource_deepness, device_type, device_manager_uuid diff --git a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py new file mode 100644 index 000000000..463b8039b --- /dev/null +++ b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py @@ -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. + + +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import ServiceTypeEnum + +PACKET_DEVICE_TYPES = { + DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER, + DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH, +} + +L2_DEVICE_TYPES = { + DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH, + DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM, + DeviceTypeEnum.PACKET_RADIO_ROUTER, DeviceTypeEnum.EMULATED_PACKET_RADIO_ROUTER, + DeviceTypeEnum.P4_SWITCH, DeviceTypeEnum.EMULATED_P4_SWITCH, +} + +OPTICAL_DEVICE_TYPES = { + DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM, + DeviceTypeEnum.XR_CONSTELLATION, DeviceTypeEnum.EMULATED_XR_CONSTELLATION, + DeviceTypeEnum.OPTICAL_ROADM, DeviceTypeEnum.EMULATED_OPTICAL_ROADM, + DeviceTypeEnum.OPTICAL_TRANSPONDER, DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER, +} + +SERVICE_TYPE_L2NM = {ServiceTypeEnum.SERVICETYPE_L2NM} +SERVICE_TYPE_L3NM = {ServiceTypeEnum.SERVICETYPE_L3NM} +SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} +SERVICE_TYPE_TAPI = {ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE} + +def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: + if device_type in PACKET_DEVICE_TYPES and prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type + if device_type in L2_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_L2NM + if device_type in OPTICAL_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE + + str_fields = ', '.join([ + 'device_type={:s}'.format(str(device_type)), + 'prv_service_type={:s}'.format(str(prv_service_type)), + ]) + raise Exception('Undefined Service Type for ({:s})'.format(str_fields)) diff --git a/test_pathcomp/ComputeSubServices.py b/test_pathcomp/ComputeSubServices.py index e0b229625..1f2b4df9c 100644 --- a/test_pathcomp/ComputeSubServices.py +++ b/test_pathcomp/ComputeSubServices.py @@ -30,167 +30,41 @@ # ] # # connections=[ -# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), , [ +# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, [ # {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'} # ], []), -# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), , [ +# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.SERVICETYPE_L2NM, [ # {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, # {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, # {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, # {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'} # ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]), -# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), , [ +# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), ServiceTypeEnum.SERVICETYPE_L2NM, [ # {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, # {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} # ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) # ] -import enum, json, queue, uuid +import logging, queue, uuid from typing import Dict, List, Optional, Tuple from common.DeviceTypes import DeviceTypeEnum -from common.proto.context_pb2 import Device, ServiceTypeEnum #, DeviceDriverEnum as grpc_DeviceDriverEnum -#from .ConstantsMappings import DEVICE_TYPE_TO_LAYER, DeviceLayerEnum - -class StackActionEnum(enum.Enum): - PATH_INGRESS = 'ingress' - CREATE_CONNECTION = 'create' - APPEND_PATH_HOP = 'append' - CHAIN_CONNECTION = 'chain' - TERMINATE_CONNECTION = 'terminate' - -#class DeviceDriverEnum(enum.IntEnum): -# EMULATED = grpc_DeviceDriverEnum.DEVICEDRIVER_UNDEFINED -# OPENCONFIG = grpc_DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG -# TRANSPORT_API = grpc_DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API -# P4 = grpc_DeviceDriverEnum.DEVICEDRIVER_P4 -# IETF_NETWORK_TOPOLOGY = grpc_DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY -# ONF_TR_352 = grpc_DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352 -# XR = grpc_DeviceDriverEnum.DEVICEDRIVER_XR -# IETF_L2VPN = grpc_DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN - -def is_datacenter(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.DATACENTER, DeviceTypeEnum.EMULATED_DATACENTER} - -def is_packet_router(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER} - -def is_packet_switch(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH} - -def is_packet_device(dev_type : Optional[DeviceTypeEnum]) -> bool: - return is_packet_router(dev_type) or is_packet_switch(dev_type) - -def is_tfs_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.TERAFLOWSDN_CONTROLLER} - -def is_mw_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM} - -def is_ipm_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.XR_CONSTELLATION, DeviceTypeEnum.EMULATED_XR_CONSTELLATION} - -def is_ols_controller(dev_type : Optional[DeviceTypeEnum]) -> bool: - return dev_type in {DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM} - -def is_subdevice(dev_manager : Optional[str]) -> bool: - return dev_manager is not None - -def is_subdevice_equal(dev_manager_a : Optional[str], dev_manager_b : Optional[str]) -> bool: - if dev_manager_a is None and dev_manager_b is None: return True - if dev_manager_a is not None and dev_manager_b is not None: return dev_manager_a == dev_manager_b - return False - -#def has_driver(dev_drivers : List[DeviceDriverEnum], dev_driver : DeviceDriverEnum) -> bool: -# return dev_driver in dev_drivers - -def get_action( - prv_type : Optional[DeviceTypeEnum], prv_manager : Optional[str], - cur_type : DeviceTypeEnum, cur_manager : Optional[str] -) -> StackActionEnum: - if prv_type is None: - return StackActionEnum.PATH_INGRESS - - if is_datacenter(prv_type): - if is_packet_device(cur_type): return StackActionEnum.CREATE_CONNECTION - if is_tfs_controller(cur_type): return StackActionEnum.CREATE_CONNECTION - - if is_packet_device(prv_type): - if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION - if is_packet_device(cur_type): - if is_subdevice_equal(cur_manager, prv_manager): return StackActionEnum.APPEND_PATH_HOP - if is_subdevice(prv_manager) and not is_subdevice(cur_manager): return StackActionEnum.TERMINATE_CONNECTION - if not is_subdevice(prv_manager) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION - - if is_mw_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION - if is_ols_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION - if is_tfs_controller(cur_type) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION - - if is_mw_controller(prv_type) or is_ols_controller(prv_type): - if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION - - if is_tfs_controller(prv_type): - if is_tfs_controller(cur_type) and is_subdevice_equal(prv_manager, cur_manager): return StackActionEnum.APPEND_PATH_HOP - if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION - if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION - if is_mw_controller(cur_type) or is_ols_controller(cur_type): return StackActionEnum.CHAIN_CONNECTION - - str_fields = ', '.join([ - 'prv_type={:s}'.format(str(prv_type)), 'prv_manager={:s}'.format(str(prv_manager)), - 'cur_type={:s}'.format(str(cur_type)), 'cur_manager={:s}'.format(str(cur_manager)), - ]) - raise Exception('Undefined Action for ({:s})'.format(str_fields)) - -def get_device_manager_uuid(device : Device) -> Optional[str]: - for config_rule in device.device_config.config_rules: - if config_rule.WhichOneof('config_rule') != 'custom': continue - if config_rule.custom.resource_key != '_manager': continue - device_manager_id = json.loads(config_rule.custom.resource_value) - return device_manager_id['uuid'] - return None - -def get_device_type( - grpc_device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str] -) -> DeviceTypeEnum: - if device_manager_uuid is None: - return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member - device_manager_tuple = device_dict.get(device_manager_uuid) - if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid))) - _,grpc_device = device_manager_tuple - return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member - - #manager_drivers = set(grpc_device.device_drivers) - #if DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN in manager_drivers: - # device_layer = DeviceLayerEnum.MAC_LAYER_CONTROLLER - #else: - # device_type = json_device['device_type'] - # device_layer = DEVICE_TYPE_TO_LAYER.get(device_type) - # if device_layer is None: raise Exception('Undefined Layer for DeviceType({:s})'.format(str(device_type))) - -SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} - -def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: - if is_tfs_controller(device_type) or is_packet_router(device_type): - if prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type - if is_packet_switch(device_type) or is_mw_controller(device_type): - if prv_service_type == ServiceTypeEnum.SERVICETYPE_L2NM: return prv_service_type - if is_ols_controller(device_type) or is_ipm_controller(device_type): - return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE - - str_fields = ', '.join([ - 'device_type={:s}'.format(str(device_type)), - ]) - raise Exception('Undefined Service Type for ({:s})'.format(str_fields)) +from common.proto.context_pb2 import Device, ServiceTypeEnum +from .ResourceGroups import IGNORED_DEVICE_TYPES, get_resource_classification +from .ServiceTypes import get_service_type + +LOGGER = logging.getLogger(__name__) def convert_explicit_path_hops_to_connections( path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], main_service_uuid : str, main_service_type : ServiceTypeEnum ) -> List[Tuple[str, int, List[str], List[str]]]: + LOGGER.debug('path_hops={:s}'.format(str(path_hops))) + connection_stack = queue.LifoQueue() connections : List[Tuple[str, int, List[str], List[str]]] = list() prv_device_uuid = None - prv_device_type = None - prv_manager_uuid = None + prv_res_class : Tuple[Optional[int], Optional[DeviceTypeEnum], Optional[str]] = None, None, None for path_hop in path_hops: device_uuid = path_hop['device'] @@ -199,29 +73,35 @@ def convert_explicit_path_hops_to_connections( if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) _,grpc_device = device_tuple - manager_uuid = get_device_manager_uuid(grpc_device) - device_type = get_device_type(grpc_device, device_dict, manager_uuid) - action = get_action(prv_device_type, prv_manager_uuid, device_type, manager_uuid) + res_class = get_resource_classification(grpc_device, device_dict) + if res_class[1] in IGNORED_DEVICE_TYPES: continue - if action == StackActionEnum.PATH_INGRESS: + if prv_res_class[0] is None: + # path ingress connection_stack.put((main_service_uuid, main_service_type, [path_hop], [])) - elif action == StackActionEnum.CREATE_CONNECTION: - connection_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] - service_type = get_service_type(device_type, prv_service_type) - connection_stack.put((connection_uuid, service_type, [path_hop], [])) - elif action == StackActionEnum.APPEND_PATH_HOP: - connection_stack.queue[-1][2].append(path_hop) - elif action == StackActionEnum.CHAIN_CONNECTION: - connection = connection_stack.get() - connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) - + elif prv_res_class[0] > res_class[0]: + # create underlying connection connection_uuid = str(uuid.uuid4()) prv_service_type = connection_stack.queue[-1][1] - service_type = get_service_type(device_type, prv_service_type) + service_type = get_service_type(res_class[1], prv_service_type) connection_stack.put((connection_uuid, service_type, [path_hop], [])) - elif action == StackActionEnum.TERMINATE_CONNECTION: + elif prv_res_class[0] == res_class[0]: + # same resource group kind + if prv_res_class[1] == res_class[1] and prv_res_class[2] == res_class[2]: + # same device type and device manager: connection continues + connection_stack.queue[-1][2].append(path_hop) + else: + # different device type or device manager: chain connections + connection = connection_stack.get() + connections.append(connection) + connection_stack.queue[-1][3].append(connection[0]) + + connection_uuid = str(uuid.uuid4()) + prv_service_type = connection_stack.queue[-1][1] + service_type = get_service_type(res_class[1], prv_service_type) + connection_stack.put((connection_uuid, service_type, [path_hop], [])) + elif prv_res_class[0] < res_class[0]: + # underlying connection ended connection = connection_stack.get() connections.append(connection) connection_stack.queue[-1][3].append(connection[0]) @@ -230,11 +110,11 @@ def convert_explicit_path_hops_to_connections( raise Exception('Uncontrolled condition') prv_device_uuid = device_uuid - prv_device_type = device_type - prv_manager_uuid = manager_uuid + prv_res_class = res_class # path egress connections.append(connection_stack.get()) + LOGGER.debug('connections={:s}'.format(str(connections))) assert connection_stack.empty() return connections diff --git a/test_pathcomp/ResourceGroups.py b/test_pathcomp/ResourceGroups.py new file mode 100644 index 000000000..7b3888144 --- /dev/null +++ b/test_pathcomp/ResourceGroups.py @@ -0,0 +1,93 @@ +# 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 +from typing import Dict, Optional, Tuple +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import Device +from common.tools.grpc.Tools import grpc_message_to_json_string + +DEVICE_TYPE_TO_DEEPNESS = { + DeviceTypeEnum.EMULATED_DATACENTER.value : 90, + DeviceTypeEnum.DATACENTER.value : 90, + DeviceTypeEnum.NETWORK.value : 90, + + DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value : 80, + DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : 70, + DeviceTypeEnum.PACKET_ROUTER.value : 70, + + DeviceTypeEnum.EMULATED_PACKET_SWITCH.value : 60, + DeviceTypeEnum.PACKET_SWITCH.value : 60, + DeviceTypeEnum.EMULATED_P4_SWITCH.value : 60, + DeviceTypeEnum.P4_SWITCH.value : 60, + + DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM.value : 40, + DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM.value : 40, + + DeviceTypeEnum.EMULATED_XR_CONSTELLATION.value : 40, + DeviceTypeEnum.XR_CONSTELLATION.value : 40, + + DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value : 30, + DeviceTypeEnum.OPEN_LINE_SYSTEM.value : 30, + + DeviceTypeEnum.EMULATED_PACKET_RADIO_ROUTER.value : 10, + DeviceTypeEnum.PACKET_RADIO_ROUTER.value : 10, + DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER.value : 10, + DeviceTypeEnum.OPTICAL_TRANSPONDER.value : 10, + DeviceTypeEnum.EMULATED_OPTICAL_ROADM.value : 10, + DeviceTypeEnum.OPTICAL_ROADM.value : 10, + + DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER.value : 0, +} + +IGNORED_DEVICE_TYPES = {DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER} + +def get_device_manager_uuid( + device : Device +) -> Optional[str]: + for config_rule in device.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + if config_rule.custom.resource_key != '_manager': continue + device_manager_id = json.loads(config_rule.custom.resource_value) + return device_manager_id['uuid'] + return None + +def _map_device_type(device : Device) -> DeviceTypeEnum: + device_type = DeviceTypeEnum._value2member_map_.get(device.device_type) # pylint: disable=no-member + if device_type is None: + MSG = 'Unsupported DeviceType({:s}) for Device({:s})' + raise Exception(MSG.format(str(device.device_type), grpc_message_to_json_string(device))) + return device_type + +def _map_resource_to_deepness(device_type : DeviceTypeEnum) -> int: + deepness = DEVICE_TYPE_TO_DEEPNESS.get(device_type.value) + if deepness is None: raise Exception('Unsupported DeviceType({:s})'.format(str(device_type.value))) + return deepness + +def get_device_type( + device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str] +) -> DeviceTypeEnum: + if device_manager_uuid is None: return _map_device_type(device) + device_manager_tuple = device_dict.get(device_manager_uuid) + if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid))) + _,device = device_manager_tuple + return _map_device_type(device) + +def get_resource_classification( + device : Device, device_dict : Dict[str, Tuple[Dict, Device]] +) -> Tuple[int, DeviceTypeEnum, Optional[str]]: + device_manager_uuid = get_device_manager_uuid(device) + device_type = get_device_type(device, device_dict, device_manager_uuid) + resource_deepness = _map_resource_to_deepness(device_type) + return resource_deepness, device_type, device_manager_uuid diff --git a/test_pathcomp/ServiceTypes.py b/test_pathcomp/ServiceTypes.py new file mode 100644 index 000000000..463b8039b --- /dev/null +++ b/test_pathcomp/ServiceTypes.py @@ -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. + + +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import ServiceTypeEnum + +PACKET_DEVICE_TYPES = { + DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER, + DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH, +} + +L2_DEVICE_TYPES = { + DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH, + DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM, + DeviceTypeEnum.PACKET_RADIO_ROUTER, DeviceTypeEnum.EMULATED_PACKET_RADIO_ROUTER, + DeviceTypeEnum.P4_SWITCH, DeviceTypeEnum.EMULATED_P4_SWITCH, +} + +OPTICAL_DEVICE_TYPES = { + DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM, + DeviceTypeEnum.XR_CONSTELLATION, DeviceTypeEnum.EMULATED_XR_CONSTELLATION, + DeviceTypeEnum.OPTICAL_ROADM, DeviceTypeEnum.EMULATED_OPTICAL_ROADM, + DeviceTypeEnum.OPTICAL_TRANSPONDER, DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER, +} + +SERVICE_TYPE_L2NM = {ServiceTypeEnum.SERVICETYPE_L2NM} +SERVICE_TYPE_L3NM = {ServiceTypeEnum.SERVICETYPE_L3NM} +SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} +SERVICE_TYPE_TAPI = {ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE} + +def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: + if device_type in PACKET_DEVICE_TYPES and prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type + if device_type in L2_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_L2NM + if device_type in OPTICAL_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE + + str_fields = ', '.join([ + 'device_type={:s}'.format(str(device_type)), + 'prv_service_type={:s}'.format(str(prv_service_type)), + ]) + raise Exception('Undefined Service Type for ({:s})'.format(str_fields)) diff --git a/test_pathcomp/data.py b/test_pathcomp/data.py index 5e94d969e..399c5ff60 100644 --- a/test_pathcomp/data.py +++ b/test_pathcomp/data.py @@ -15,101 +15,56 @@ import json from typing import Dict, Tuple +from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import ConfigActionEnum, Device path_hops = [ - {'device': 'DC1', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, - {'device': 'PE1', 'ingress_ep': '1/1', 'egress_ep': '1/2'}, - {'device': 'MW1-2', 'ingress_ep': '172.18.0.1:1', 'egress_ep': '172.18.0.2:1'}, - {'device': 'R1', 'ingress_ep': '1/1', 'egress_ep': '1/3'}, - {'device': 'OLS', 'ingress_ep': 'aade6001-f00b-5e2f-a357-6a0a9d3de870', 'egress_ep': '0ef74f99-1acc-57bd-ab9d-4b958b06c513'}, - {'device': 'R2', 'ingress_ep': '1/1', 'egress_ep': '1/2'}, - {'device': 'PE3', 'ingress_ep': '1/1', 'egress_ep': '1/2'}, - {'device': 'DC2', 'ingress_ep': 'eth1', 'egress_ep': 'int'} + {'device': 'DC1', 'ingress_ep': 'int', 'egress_ep': 'eth1' }, + {'device': 'PE1', 'ingress_ep': '1/1', 'egress_ep': '1/2' }, + {'device': 'MW1-2', 'ingress_ep': '172.18.0.1:1', 'egress_ep': '172.18.0.2:1' }, + {'device': 'HUB1', 'ingress_ep': '1/1', 'egress_ep': 'XR-T1' }, + {'device': 'splitter', 'ingress_ep': 'common', 'egress_ep': 'leaf1' }, + {'device': 'OLS', 'ingress_ep': 'node_1_port_13-input', 'egress_ep': 'node_4_port_13-output'}, + {'device': 'LEAF2', 'ingress_ep': 'XR-T1', 'egress_ep': '1/1' }, + {'device': 'PE4', 'ingress_ep': '1/1', 'egress_ep': '1/2' }, + {'device': 'DC2', 'ingress_ep': 'eth2', 'egress_ep': 'int' } ] -device_dict = { - 'R3': {'device_Id': 'R3', 'device_type': 'emu-packet-router', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'R3', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'R3', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'} - ]}, - 'PE4': {'device_Id': 'PE4', 'device_type': 'emu-packet-router', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'PE4', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'}, - {'endpoint_id': {'device_id': 'PE4', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'PE4', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'} - ]}, - 'PE2': {'device_Id': 'PE2', 'device_type': 'emu-packet-router', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'PE2', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'PE2', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'}, - {'endpoint_id': {'device_id': 'PE2', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'} - ]}, - 'R1': {'device_Id': 'R1', 'device_type': 'emu-packet-router', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'R1', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'R1', 'endpoint_uuid': '1/3'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'R1', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'} - ]}, - 'PE3': {'device_Id': 'PE3', 'device_type': 'emu-packet-router', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'PE3', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'PE3', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'PE3', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'} - ]}, - 'OLS': {'device_Id': 'OLS', 'device_type': 'emu-open-line-system', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'OLS', 'endpoint_uuid': '0ef74f99-1acc-57bd-ab9d-4b958b06c513'}, 'endpoint_type': 'optical'}, - {'endpoint_id': {'device_id': 'OLS', 'endpoint_uuid': '50296d99-58cc-5ce7-82f5-fc8ee4eec2ec'}, 'endpoint_type': 'optical'}, - {'endpoint_id': {'device_id': 'OLS', 'endpoint_uuid': 'aade6001-f00b-5e2f-a357-6a0a9d3de870'}, 'endpoint_type': 'optical'}, - {'endpoint_id': {'device_id': 'OLS', 'endpoint_uuid': 'eb287d83-f05e-53ec-ab5a-adf6bd2b5418'}, 'endpoint_type': 'optical'} - ]}, - 'PE1': {'device_Id': 'PE1', 'device_type': 'emu-packet-router', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'PE1', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'PE1', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'}, - {'endpoint_id': {'device_id': 'PE1', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'} - ]}, - 'DC2': {'device_Id': 'DC2', 'device_type': 'emu-datacenter', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'DC2', 'endpoint_uuid': 'eth1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'DC2', 'endpoint_uuid': 'eth2'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'DC2', 'endpoint_uuid': 'int'}, 'endpoint_type': 'copper/internal'} - ]}, - 'MW1-2': {'device_Id': 'MW1-2', 'device_type': 'emu-microwave-radio-system', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'MW1-2', 'endpoint_uuid': '172.18.0.1:1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'MW1-2', 'endpoint_uuid': '172.18.0.2:1'}, 'endpoint_type': 'copper/internal'} - ]}, - 'MW3-4': {'device_Id': 'MW3-4', 'device_type': 'emu-microwave-radio-system', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'MW3-4', 'endpoint_uuid': '172.18.0.3:1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'MW3-4', 'endpoint_uuid': '172.18.0.4:1'}, 'endpoint_type': 'copper/internal'} - ]}, - 'TFS': {'device_Id': 'TFS', 'device_type': 'teraflowsdn', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'TFS', 'endpoint_uuid': 'mgmt'}, 'endpoint_type': 'mgmt'} - ]}, - 'R2': {'device_Id': 'R2', 'device_type': 'emu-packet-router', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'R2', 'endpoint_uuid': '1/2'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'R2', 'endpoint_uuid': '1/1'}, 'endpoint_type': 'copper/internal'} - ]}, - 'DC1': {'device_Id': 'DC1', 'device_type': 'emu-datacenter', 'device_endpoints': [ - {'endpoint_id': {'device_id': 'DC1', 'endpoint_uuid': 'int'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'DC1', 'endpoint_uuid': 'eth1'}, 'endpoint_type': 'copper/internal'}, - {'endpoint_id': {'device_id': 'DC1', 'endpoint_uuid': 'eth2'}, 'endpoint_type': 'copper/internal'} - ]}, -} +device_data = { + 'TFS' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.TERAFLOWSDN_CONTROLLER }, + 'IPM' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.XR_CONSTELLATION }, + 'OLS' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.OPEN_LINE_SYSTEM }, + 'MW1-2' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM }, + 'MW3-4' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM }, -MANAGED_DEVICES = {'PE1', 'PE2', 'PE3', 'PE4'} -MANAGER = 'TFS' + 'DC1' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_DATACENTER }, + 'DC2' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_DATACENTER }, -def process_device(json_device) -> Tuple[Dict, Device]: - device_uuid = json_device['device_Id'] + 'PE1' : {'manager_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'PE2' : {'manager_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'PE3' : {'manager_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'PE4' : {'manager_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - grpc_device = Device() - grpc_device.device_id.device_uuid.uuid = device_uuid - grpc_device.device_type = json_device['device_type'] + 'HUB1' : {'manager_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'LEAF1' : {'manager_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'LEAF2' : {'manager_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - if device_uuid in MANAGED_DEVICES: - config_rule = grpc_device.device_config.config_rules.add() + 'splitter': {'manager_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER}, +} + +def process_device(device_uuid, json_device) -> Tuple[Dict, Device]: + grpc_device = Device() + grpc_device.device_id.device_uuid.uuid = device_uuid # pylint: disable=no-member + grpc_device.device_type = json_device['device_type'].value + manager_uuid = json_device.get('manager_uuid') + if manager_uuid is not None: + config_rule = grpc_device.device_config.config_rules.add() # pylint: disable=no-member config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.custom.resource_key = '_manager' - config_rule.custom.resource_value = json.dumps({'uuid': MANAGER}) - + config_rule.custom.resource_value = json.dumps({'uuid': manager_uuid}) return json_device, grpc_device device_dict = { - device_uuid:process_device(json_device) - for device_uuid,json_device in device_dict.items() + device_uuid:process_device(device_uuid, json_device) + for device_uuid,json_device in device_data.items() } diff --git a/test_pathcomp/old_ComputeSubServices.py b/test_pathcomp/old_ComputeSubServices.py new file mode 100644 index 000000000..c1d3115d4 --- /dev/null +++ b/test_pathcomp/old_ComputeSubServices.py @@ -0,0 +1,119 @@ +# 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. + +# Convert the path defined as explicit hops with ingress and egress endpoints per device into a set of connections and +# compute the dependencies among them. +# +# Example: +# o-- int DC1 eth1 -- 10/1 CS1 1/2 -- 1/2 R2 2/1 -- a7.. OLS 60.. -- 2/1 R3 1/1 -- 1/1 CS2 10/1 -- eth1 DC2 int --o +# APP PKT PKT CTRL PKT PKT APP +# +# path_hops = [ +# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, +# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, +# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, +# {'device': 'TN-OLS', 'ingress_ep': 'a7a80b23a703', 'egress_ep': '60519106029e'}, +# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, +# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'}, +# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} +# ] +# +# connections=[ +# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, [ +# {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'} +# ], []), +# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.SERVICETYPE_L2NM, [ +# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, +# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, +# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, +# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'} +# ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]), +# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), ServiceTypeEnum.SERVICETYPE_L2NM, [ +# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, +# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} +# ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) +# ] + +import enum, json, logging, queue, uuid +from typing import Dict, List, Optional, Tuple +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import Device, ServiceTypeEnum +from test_pathcomp.ResourceGroups import ResourceGroupKindEnum + + + +from .ConstantsMappings import DEVICE_TYPE_TO_LAYER, DeviceLayerEnum + +def convert_explicit_path_hops_to_connections( + path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], + main_service_uuid : str, main_service_type : ServiceTypeEnum +) -> List[Tuple[str, int, List[str], List[str]]]: + + connection_stack = queue.LifoQueue() + connections : List[Tuple[str, int, List[str], List[str]]] = list() + prv_device_uuid = None + prv_resource_group : Optional[Tuple[ResourceGroupKindEnum, DeviceTypeEnum, str]] = None + + for path_hop in path_hops: + device_uuid = path_hop['device'] + if prv_device_uuid == device_uuid: continue + device_tuple = device_dict.get(device_uuid) + if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) + json_device,_ = device_tuple + device_type = json_device['device_type'] + resource_group = DEVICE_TYPE_TO_LAYER.get(device_type) + if resource_group is None: raise Exception('Undefined Layer for DeviceType({:s})'.format(str(device_type))) + + if prv_resource_group is None: + # path ingress + connection_stack.put((main_service_uuid, main_service_type, [path_hop], [])) + elif prv_resource_group > resource_group: + # underlying connection begins + connection_uuid = str(uuid.uuid4()) + connection_stack.put((connection_uuid, resource_group, [path_hop], [])) + elif prv_resource_group == resource_group: + # same connection continues + connection_stack.queue[-1][2].append(path_hop) + elif prv_resource_group < resource_group: + # underlying connection ended + connection = connection_stack.get() + connections.append(connection) + connection_stack.queue[-1][3].append(connection[0]) + connection_stack.queue[-1][2].append(path_hop) + else: + raise Exception('Uncontrolled condition') + + prv_resource_group = resource_group + prv_device_uuid = device_uuid + + # path egress + connections.append(connection_stack.get()) + assert connection_stack.empty() + return connections + +def convert_explicit_path_hops_to_plain_connection( + path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum +) -> List[Tuple[str, int, List[str], List[str]]]: + + connection : Tuple[str, int, List[str], List[str]] = \ + (main_service_uuid, main_service_type, [], []) + + prv_device_uuid = None + for path_hop in path_hops: + device_uuid = path_hop['device'] + if prv_device_uuid == device_uuid: continue + connection[2].append(path_hop) + prv_device_uuid = device_uuid + + return [connection] -- GitLab From 20ee05ab89a0600880f1f4d25c58e048725ddb20 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 17:01:25 +0000 Subject: [PATCH 45/77] Updated TODO file --- TODO.txt | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/TODO.txt b/TODO.txt index 714fab583..0e6b21c61 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,28 +1,22 @@ -- MW mock sdn ctrl: post does not work -- implement mock IPM controller - +PENDING: +- no rule is composed for IPM controller +- no rule is composed for L2VPN TFS SBI - pending infinera tests - test with Slice NBI - integrate Slice NBI in Load Gen -- import subdevices TAPI, MW, INF - -- Device: - - IetfL2VpnDriver should not import links; otherwise, might confuse path computation component - - [] Add flag to control that - - MW Driver should import topology, there should be nothing between antennas - - IPM Driver should import devices but not links, TAPI in between - - TAPI Driver: add flag to choose; might be required in some cases - - enbale to create links (connectivity between subdevices) +- sort endpoints in service and slice as done for links -- OLS service: from input to input endpoint ?? - -- pathcomp should not create reversed links -- bidirectional links should be duplicated in frontend -- pathcomp should honor input/output of tapi endpoints +ONGOING: +- implement mock IPM controller TO TEST: -- Test with new changes in pathcomp and subservice config rule composition -- Test device manager config in device works; rules should go to manager, not to underlying -- ietf-l2vpn driver: test service is created -- if there is a config rule for a endpoint in subservice, move the rule from main to that subservice + +OPTIONAL: + - Optimize requests pathcomp--context + - Optimize requests webui--context + - TransportAPIDriver: add flag to import underlying topology options: 'disabled/devices/all' + - IetfL2VpnDriver: add flag to import underlying topology options: 'disabled/devices/all' + - MWDriver should import topology, there should be nothing between antennas + - Enable to create links (connectivity between subdevices) + - bidirectional links should be duplicated by frontend in a smart way -- GitLab From 09d9f0ae890fe9a9ddcbb824673afbf368d9aeb0 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 17:37:33 +0000 Subject: [PATCH 46/77] Device component: - Renamed device "manager" to device "controller" --- .../service/DeviceServiceServicerImpl.py | 15 +++++++------- src/device/service/Tools.py | 20 +++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py index b89766b29..2b08b6c7e 100644 --- a/src/device/service/DeviceServiceServicerImpl.py +++ b/src/device/service/DeviceServiceServicerImpl.py @@ -28,9 +28,9 @@ from .driver_api.DriverInstanceCache import DriverInstanceCache, get_driver from .monitoring.MonitoringLoops import MonitoringLoops from .ErrorMessages import ERROR_MISSING_DRIVER, ERROR_MISSING_KPI from .Tools import ( - check_connect_rules, check_no_endpoints, compute_rules_to_add_delete, configure_rules, deconfigure_rules, get_device_manager_uuid, - populate_config_rules, populate_endpoint_monitoring_resources, populate_endpoints, populate_initial_config_rules, - subscribe_kpi, unsubscribe_kpi, update_endpoints) + check_connect_rules, check_no_endpoints, compute_rules_to_add_delete, configure_rules, deconfigure_rules, + get_device_controller_uuid, populate_config_rules, populate_endpoint_monitoring_resources, populate_endpoints, + populate_initial_config_rules, subscribe_kpi, unsubscribe_kpi, update_endpoints) LOGGER = logging.getLogger(__name__) @@ -125,11 +125,12 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): if device is None: raise NotFoundException('Device', device_uuid, extra_details='loading in ConfigureDevice') - device_manager_uuid = get_device_manager_uuid(device) - if device_manager_uuid is not None: - device = get_device(context_client, device_manager_uuid, rw_copy=True) + device_controller_uuid = get_device_controller_uuid(device) + if device_controller_uuid is not None: + device = get_device(context_client, device_controller_uuid, rw_copy=True) if device is None: - raise NotFoundException('Device', device_manager_uuid, extra_details='loading in ConfigureDevice') + raise NotFoundException( + 'Device', device_controller_uuid, extra_details='loading in ConfigureDevice') driver : _Driver = get_driver(self.driver_instance_cache, device) if driver is None: diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index 3694ce129..cd3af07e3 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -78,12 +78,12 @@ def check_no_endpoints(device_endpoints) -> None: extra_details='RPC method AddDevice does not accept Endpoints. Endpoints are discovered through '\ 'interrogation of the physical device.') -def get_device_manager_uuid(device : Device) -> Optional[str]: +def get_device_controller_uuid(device : Device) -> Optional[str]: for config_rule in device.device_config.config_rules: if config_rule.WhichOneof('config_rule') != 'custom': continue - if config_rule.custom.resource_key != '_manager': continue - device_manager_id = json.loads(config_rule.custom.resource_value) - return device_manager_id['uuid'] + if config_rule.custom.resource_key != '_controller': continue + device_controller_id = json.loads(config_rule.custom.resource_value) + return device_controller_id['uuid'] return None def populate_endpoints( @@ -140,13 +140,13 @@ def populate_endpoints( _sub_device.device_operational_status = resource_value['status'] # Sub-devices should not have a driver assigned. Instead, they should have - # a config rule specifying their manager. + # a config rule specifying their controller. #_sub_device.device_drivers.extend(resource_value['drivers']) # pylint: disable=no-member - manager_config_rule = _sub_device.device_config.config_rules.add() - manager_config_rule.action = ConfigActionEnum.CONFIGACTION_SET - manager_config_rule.custom.resource_key = '_manager' - manager = {'uuid': device_uuid, 'name': device_name} - manager_config_rule.custom.resource_value = json.dumps(manager, indent=0, sort_keys=True) + controller_config_rule = _sub_device.device_config.config_rules.add() + controller_config_rule.action = ConfigActionEnum.CONFIGACTION_SET + controller_config_rule.custom.resource_key = '_controller' + controller = {'uuid': device_uuid, 'name': device_name} + controller_config_rule.custom.resource_value = json.dumps(controller, indent=0, sort_keys=True) new_sub_devices[_sub_device_uuid] = _sub_device -- GitLab From d9711a72097df632980bf338ae1ecbb6e03a654b Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 17:38:03 +0000 Subject: [PATCH 47/77] PathComp component - Frontend: - Renamed device "manager" to device "controller" --- .../algorithms/tools/ComputeSubServices.py | 4 ++-- .../algorithms/tools/ResourceGroups.py | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py index 1f2b4df9c..40cb08576 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py @@ -88,10 +88,10 @@ def convert_explicit_path_hops_to_connections( elif prv_res_class[0] == res_class[0]: # same resource group kind if prv_res_class[1] == res_class[1] and prv_res_class[2] == res_class[2]: - # same device type and device manager: connection continues + # same device type and device controller: connection continues connection_stack.queue[-1][2].append(path_hop) else: - # different device type or device manager: chain connections + # different device type or device controller: chain connections connection = connection_stack.get() connections.append(connection) connection_stack.queue[-1][3].append(connection[0]) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py index 0f6ee63dc..53c89cd12 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py @@ -54,14 +54,14 @@ DEVICE_TYPE_TO_DEEPNESS = { IGNORED_DEVICE_TYPES = {DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER} -def get_device_manager_uuid( +def get_device_controller_uuid( device : Device ) -> Optional[str]: for config_rule in device.device_config.config_rules: if config_rule.WhichOneof('config_rule') != 'custom': continue - if config_rule.custom.resource_key != '_manager': continue - device_manager_id = json.loads(config_rule.custom.resource_value) - return device_manager_id['uuid'] + if config_rule.custom.resource_key != '_controller': continue + device_controller_id = json.loads(config_rule.custom.resource_value) + return device_controller_id['uuid'] return None def _map_device_type(device : Device) -> DeviceTypeEnum: @@ -77,18 +77,18 @@ def _map_resource_to_deepness(device_type : DeviceTypeEnum) -> int: return deepness def get_device_type( - device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str] + device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_controller_uuid : Optional[str] ) -> DeviceTypeEnum: - if device_manager_uuid is None: return _map_device_type(device) - device_manager_tuple = device_dict.get(device_manager_uuid) - if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid))) - _,device = device_manager_tuple + if device_controller_uuid is None: return _map_device_type(device) + device_controller_tuple = device_dict.get(device_controller_uuid) + if device_controller_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_controller_uuid))) + _,device = device_controller_tuple return _map_device_type(device) def get_resource_classification( device : Device, device_dict : Dict[str, Tuple[Dict, Device]] ) -> Tuple[int, DeviceTypeEnum, Optional[str]]: - device_manager_uuid = get_device_manager_uuid(device) - device_type = get_device_type(device, device_dict, device_manager_uuid) + device_controller_uuid = get_device_controller_uuid(device) + device_type = get_device_type(device, device_dict, device_controller_uuid) resource_deepness = _map_resource_to_deepness(device_type) - return resource_deepness, device_type, device_manager_uuid + return resource_deepness, device_type, device_controller_uuid -- GitLab From c0a1c3c605d1cdc83cf0556656920cb28eee431c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 17:39:39 +0000 Subject: [PATCH 48/77] Service component: - Renamed device "manager" to device "controller" - Extended TAPI Service Handler to support optional device controllers - Fixed IETF L2VPN Service Handler delete to support optional device controllers --- .../L2NM_IETFL2VPN_ServiceHandler.py | 35 ++++++------ .../tapi_tapi/TapiServiceHandler.py | 56 +++++++++++-------- .../service/task_scheduler/TaskExecutor.py | 32 +++++------ 3 files changed, 69 insertions(+), 54 deletions(-) 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 index 7a2c4e723..9adc15494 100644 --- a/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py +++ b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py @@ -55,16 +55,16 @@ class L2NM_IETFL2VPN_ServiceHandler(_ServiceHandler): 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_manager = self.__task_executor.get_device_manager(src_device) + 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_manager = self.__task_executor.get_device_manager(dst_device) + dst_controller = self.__task_executor.get_device_controller(dst_device) - if src_manager.device_id.device_uuid.uuid != dst_manager.device_id.device_uuid.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') - manager = src_manager + controller = src_controller json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), { 'uuid' : service_uuid, @@ -75,9 +75,9 @@ class L2NM_IETFL2VPN_ServiceHandler(_ServiceHandler): 'encapsulation_type': encap_type, 'vlan_id' : vlan_id, }) - del manager.device_config.config_rules[:] - manager.device_config.config_rules.append(ConfigRule(**json_config_rule)) - self.__task_executor.configure_device(manager) + 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))) @@ -97,21 +97,24 @@ class L2NM_IETFL2VPN_ServiceHandler(_ServiceHandler): 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 device_uuid_src != device_uuid_dst: - raise Exception('Different 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) - 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_tapi/TapiServiceHandler.py b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py index dd9ff092a..ee9c4a66d 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 +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 @@ -55,28 +55,35 @@ 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]) - - if device_uuid_src != device_uuid_dst: + 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) + if src_controller is None: src_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 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') - device_uuid = device_uuid_src - - device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + controller = src_controller json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), { 'uuid' : service_uuid, - 'input_sip' : endpoint_uuid_src, - 'output_sip' : endpoint_uuid_dst, + 'input_sip' : src_endpoint.name, + 'output_sip' : dst_endpoint.name, '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))) @@ -96,21 +103,26 @@ class TapiServiceHandler(_ServiceHandler): 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('Different 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/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index 54aef8c37..3d157e314 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -105,24 +105,24 @@ class TaskExecutor: self._device_client.ConfigureDevice(device) self._store_grpc_object(CacheableObjectType.DEVICE, device_key, device) - def get_device_manager(self, device : Device) -> Optional[Device]: - json_manager = None + 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 != '_manager': continue - json_manager = json.loads(config_rule.custom.resource_value) + if config_rule.custom.resource_key != '_controller': continue + json_controller = json.loads(config_rule.custom.resource_value) break - if json_manager is None: return None + if json_controller is None: return None - manager_uuid = json_manager['uuid'] - manager = self.get_device(DeviceId(**json_device_id(manager_uuid))) - manager_uuid = manager.device_id.device_uuid.uuid - if manager is None: raise Exception('Device({:s}) not found'.format(str(manager_uuid))) - return manager + 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 : bool = False + self, connection : Connection, exclude_managed_by_controller : bool = False ) -> Dict[str, Device]: devices = dict() for endpoint_id in connection.path_hops_endpoint_ids: @@ -130,13 +130,13 @@ class TaskExecutor: device_uuid = endpoint_id.device_id.device_uuid.uuid if device is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) - manager = self.get_device_manager(device) - if manager is None: + controller = self.get_device_controller(device) + if controller is None: devices[device_uuid] = device else: - if not exclude_managed: + if not exclude_managed_by_controller: devices[device_uuid] = device - devices[manager.device_id.device_uuid.uuid] = manager + devices[controller.device_id.device_uuid.uuid] = controller return devices # ----- Service-related methods ------------------------------------------------------------------------------------ @@ -166,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, exclude_managed=True) + 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) -- GitLab From 0428b3743a7604fd8c0df41bb93014cf48753b07 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 17:40:21 +0000 Subject: [PATCH 49/77] Test Pathcomp: - Renamed device "manager" to device "controller" - Extended TAPI Service Handler to support optional device controllers - Fixed IETF L2VPN Service Handler delete to support optional device controllers --- test_pathcomp/ComputeSubServices.py | 4 +-- test_pathcomp/ResourceGroups.py | 24 +++++++++--------- test_pathcomp/data.py | 38 ++++++++++++++--------------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/test_pathcomp/ComputeSubServices.py b/test_pathcomp/ComputeSubServices.py index 1f2b4df9c..40cb08576 100644 --- a/test_pathcomp/ComputeSubServices.py +++ b/test_pathcomp/ComputeSubServices.py @@ -88,10 +88,10 @@ def convert_explicit_path_hops_to_connections( elif prv_res_class[0] == res_class[0]: # same resource group kind if prv_res_class[1] == res_class[1] and prv_res_class[2] == res_class[2]: - # same device type and device manager: connection continues + # same device type and device controller: connection continues connection_stack.queue[-1][2].append(path_hop) else: - # different device type or device manager: chain connections + # different device type or device controller: chain connections connection = connection_stack.get() connections.append(connection) connection_stack.queue[-1][3].append(connection[0]) diff --git a/test_pathcomp/ResourceGroups.py b/test_pathcomp/ResourceGroups.py index 7b3888144..17991ee33 100644 --- a/test_pathcomp/ResourceGroups.py +++ b/test_pathcomp/ResourceGroups.py @@ -53,14 +53,14 @@ DEVICE_TYPE_TO_DEEPNESS = { IGNORED_DEVICE_TYPES = {DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER} -def get_device_manager_uuid( +def get_device_controller_uuid( device : Device ) -> Optional[str]: for config_rule in device.device_config.config_rules: if config_rule.WhichOneof('config_rule') != 'custom': continue - if config_rule.custom.resource_key != '_manager': continue - device_manager_id = json.loads(config_rule.custom.resource_value) - return device_manager_id['uuid'] + if config_rule.custom.resource_key != '_controller': continue + device_controller_id = json.loads(config_rule.custom.resource_value) + return device_controller_id['uuid'] return None def _map_device_type(device : Device) -> DeviceTypeEnum: @@ -76,18 +76,18 @@ def _map_resource_to_deepness(device_type : DeviceTypeEnum) -> int: return deepness def get_device_type( - device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str] + device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_controller_uuid : Optional[str] ) -> DeviceTypeEnum: - if device_manager_uuid is None: return _map_device_type(device) - device_manager_tuple = device_dict.get(device_manager_uuid) - if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid))) - _,device = device_manager_tuple + if device_controller_uuid is None: return _map_device_type(device) + device_controller_tuple = device_dict.get(device_controller_uuid) + if device_controller_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_controller_uuid))) + _,device = device_controller_tuple return _map_device_type(device) def get_resource_classification( device : Device, device_dict : Dict[str, Tuple[Dict, Device]] ) -> Tuple[int, DeviceTypeEnum, Optional[str]]: - device_manager_uuid = get_device_manager_uuid(device) - device_type = get_device_type(device, device_dict, device_manager_uuid) + device_controller_uuid = get_device_controller_uuid(device) + device_type = get_device_type(device, device_dict, device_controller_uuid) resource_deepness = _map_resource_to_deepness(device_type) - return resource_deepness, device_type, device_manager_uuid + return resource_deepness, device_type, device_controller_uuid diff --git a/test_pathcomp/data.py b/test_pathcomp/data.py index 399c5ff60..aeac5e38a 100644 --- a/test_pathcomp/data.py +++ b/test_pathcomp/data.py @@ -31,37 +31,37 @@ path_hops = [ ] device_data = { - 'TFS' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.TERAFLOWSDN_CONTROLLER }, - 'IPM' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.XR_CONSTELLATION }, - 'OLS' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.OPEN_LINE_SYSTEM }, - 'MW1-2' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM }, - 'MW3-4' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM }, + 'TFS' : {'controller_uuid': None, 'device_type': DeviceTypeEnum.TERAFLOWSDN_CONTROLLER }, + 'IPM' : {'controller_uuid': None, 'device_type': DeviceTypeEnum.XR_CONSTELLATION }, + 'OLS' : {'controller_uuid': None, 'device_type': DeviceTypeEnum.OPEN_LINE_SYSTEM }, + 'MW1-2' : {'controller_uuid': None, 'device_type': DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM }, + 'MW3-4' : {'controller_uuid': None, 'device_type': DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM }, - 'DC1' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_DATACENTER }, - 'DC2' : {'manager_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_DATACENTER }, + 'DC1' : {'controller_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_DATACENTER }, + 'DC2' : {'controller_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_DATACENTER }, - 'PE1' : {'manager_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - 'PE2' : {'manager_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - 'PE3' : {'manager_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - 'PE4' : {'manager_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'PE1' : {'controller_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'PE2' : {'controller_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'PE3' : {'controller_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'PE4' : {'controller_uuid': 'TFS', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - 'HUB1' : {'manager_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - 'LEAF1' : {'manager_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - 'LEAF2' : {'manager_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'HUB1' : {'controller_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'LEAF1' : {'controller_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, + 'LEAF2' : {'controller_uuid': 'IPM', 'device_type': DeviceTypeEnum.PACKET_ROUTER }, - 'splitter': {'manager_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER}, + 'splitter': {'controller_uuid': None, 'device_type': DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER}, } def process_device(device_uuid, json_device) -> Tuple[Dict, Device]: grpc_device = Device() grpc_device.device_id.device_uuid.uuid = device_uuid # pylint: disable=no-member grpc_device.device_type = json_device['device_type'].value - manager_uuid = json_device.get('manager_uuid') - if manager_uuid is not None: + controller_uuid = json_device.get('controller_uuid') + if controller_uuid is not None: config_rule = grpc_device.device_config.config_rules.add() # pylint: disable=no-member config_rule.action = ConfigActionEnum.CONFIGACTION_SET - config_rule.custom.resource_key = '_manager' - config_rule.custom.resource_value = json.dumps({'uuid': manager_uuid}) + config_rule.custom.resource_key = '_controller' + config_rule.custom.resource_value = json.dumps({'uuid': controller_uuid}) return json_device, grpc_device device_dict = { -- GitLab From c2ac8acee836beb772b7f65ab939517354b3069b Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 17:40:45 +0000 Subject: [PATCH 50/77] Moved helper script format.sh to root and renamed to map_names.sh --- map_names.sh | 59 +++++++++++++++++++++++++++++++++++++++++ test_pathcomp/format.sh | 56 -------------------------------------- 2 files changed, 59 insertions(+), 56 deletions(-) create mode 100755 map_names.sh delete mode 100755 test_pathcomp/format.sh diff --git a/map_names.sh b/map_names.sh new file mode 100755 index 000000000..e3bcc5640 --- /dev/null +++ b/map_names.sh @@ -0,0 +1,59 @@ +#!/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. + + +sed -i 's#0dff8c06-873b-5799-ac54-c0452252bae1#R3#g' service.log +sed -i 's#1102e0b5-824b-57eb-86a1-d247e2deaf68#PE4#g' service.log +sed -i 's#29d766ca-d222-5257-bab3-6a060719270a#PE2#g' service.log +sed -i 's#68741528-2e94-5274-ab3c-fddcd8dc05ef#R1#g' service.log +sed -i 's#69a3a3f0-5237-5f9e-bc96-d450d0c6c02a#PE3#g' service.log +sed -i 's#6ab8fa38-ec20-5c32-8d9b-4fd86fce2555#OLS#g' service.log +sed -i 's#7faa13eb-903d-58f5-936b-1a1174fe98fd#PE1#g' service.log +sed -i 's#800d5bd4-a7a3-5a66-82ab-d399767ca3d8#DC2#g' service.log +sed -i 's#93c69975-3870-5892-955b-da0ff36eb884#MW1-2#g' service.log +sed -i 's#f185c8cf-37e5-51cc-9f71-076b775a574d#MW3-4#g' service.log +sed -i 's#a23cdc36-074d-5423-8abd-4a167a6e6fbc#TFS#g' service.log +sed -i 's#c944aaeb-bbdf-5f2d-b31c-8cc8903045b6#R2#g' service.log +sed -i 's#cda90d2f-e7b0-5837-8f2e-2fb29dd9b367#DC1#g' service.log +sed -i 's#79f4184c-d375-5e2c-a3df-1ae64537c95c#1/1#g' service.log +sed -i 's#93c853c2-429c-52e8-9ba9-454fcedb9090#1/2#g' service.log +sed -i 's#1fe2ee1a-fe92-57c9-afd9-260e6f0ecc54#mgmt#g' service.log +sed -i 's#cd378805-d73e-5681-8514-1d33e656c0e9#1/1#g' service.log +sed -i 's#e502e939-3ab8-5fee-8277-7fd1c1c0fa93#1/2#g' service.log +sed -i 's#780f6929-a863-5e6a-a046-3dac2e24bf58#1/1#g' service.log +sed -i 's#f1082088-a304-587b-a230-b8ce10e5a148#mgmt#g' service.log +sed -i 's#ffdfb0ce-1684-5d39-bad3-9ff1eb4ffbf8#1/2#g' service.log +sed -i 's#268b735d-c861-5319-88a4-2ea498f96a04#1/1#g' service.log +sed -i 's#62c0cba1-9ee8-5db5-82da-ce96d7e0f39f#1/3#g' service.log +sed -i 's#7d1bf45c-5ab2-525e-87a4-c0ddcd5c18e4#1/2#g' service.log +sed -i 's#2b11934b-dfd7-5267-87b9-7306a24e0182#1/1#g' service.log +sed -i 's#ca92338e-2038-5d74-8ef1-2b20a234a8b9#1/2#g' service.log +sed -i 's#f8955e74-4e93-5d43-a968-9626ee5c9b53#mgmt#g' service.log +sed -i 's#2b75d88d-095f-5752-a10a-1ff69df8008d#1/2#g' service.log +sed -i 's#bf0d75db-acf8-53cb-b6db-32d9dc0878c4#mgmt#g' service.log +sed -i 's#eeade85a-03df-55d2-bfc2-2af7267bbcf3#1/1#g' service.log +sed -i 's#06bb0b92-8783-5599-aa20-15bfbe241348#eth1#g' service.log +sed -i 's#6a6859c3-4a13-513c-a7dd-490c8b2931b1#eth2#g' service.log +sed -i 's#97f57787-cfec-5315-9718-7e850905f11a#int#g' service.log +sed -i 's#01251c49-6d35-5c70-8480-576991321d15#172.18.0.1:1#g' service.log +sed -i 's#023ac3a2-35bb-53cf-9173-a0b4f979af58#172.18.0.2:1#g' service.log +sed -i 's#1b4e8958-f1c2-5ef7-9297-f4e9756275f9#172.18.0.3:1#g' service.log +sed -i 's#f6cdc04b-1a12-537f-b0cc-daefdb89b61c#172.18.0.4:1#g' service.log +sed -i 's#85bb83b0-8d96-5e76-9959-97c35036d4f9#mgmt#g' service.log +sed -i 's#313fb1ed-5dee-5e49-884a-cbb3b1e00073#1/2#g' service.log +sed -i 's#79699f56-df25-5188-a389-04606c24fbfc#1/1#g' service.log +sed -i 's#37ab67ef-0064-54e3-ae9b-d40100953834#int#g' service.log +sed -i 's#55e88b6b-ccf7-538f-a062-a41219292ea1#eth1#g' service.log +sed -i 's#68b5972b-3630-53a8-ba9b-cc54dd31f8b8#eth2#g' service.log diff --git a/test_pathcomp/format.sh b/test_pathcomp/format.sh deleted file mode 100755 index ceeea5d31..000000000 --- a/test_pathcomp/format.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/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. - - -sed -i 's#0dff8c06-873b-5799-ac54-c0452252bae1#R3#g' log.txt -sed -i 's#1102e0b5-824b-57eb-86a1-d247e2deaf68#PE4#g' log.txt -sed -i 's#29d766ca-d222-5257-bab3-6a060719270a#PE2#g' log.txt -sed -i 's#68741528-2e94-5274-ab3c-fddcd8dc05ef#R1#g' log.txt -sed -i 's#69a3a3f0-5237-5f9e-bc96-d450d0c6c02a#PE3#g' log.txt -sed -i 's#6ab8fa38-ec20-5c32-8d9b-4fd86fce2555#OLS#g' log.txt -sed -i 's#7faa13eb-903d-58f5-936b-1a1174fe98fd#PE1#g' log.txt -sed -i 's#800d5bd4-a7a3-5a66-82ab-d399767ca3d8#DC2#g' log.txt -sed -i 's#90c22ce2-4f8d-51b4-8d7a-762eea9b310a#MW#g' log.txt -sed -i 's#a23cdc36-074d-5423-8abd-4a167a6e6fbc#TFS#g' log.txt -sed -i 's#c944aaeb-bbdf-5f2d-b31c-8cc8903045b6#R2#g' log.txt -sed -i 's#cda90d2f-e7b0-5837-8f2e-2fb29dd9b367#DC1#g' log.txt -sed -i 's#79f4184c-d375-5e2c-a3df-1ae64537c95c#1/1#g' log.txt -sed -i 's#93c853c2-429c-52e8-9ba9-454fcedb9090#1/2#g' log.txt -sed -i 's#1fe2ee1a-fe92-57c9-afd9-260e6f0ecc54#mgmt#g' log.txt -sed -i 's#cd378805-d73e-5681-8514-1d33e656c0e9#1/1#g' log.txt -sed -i 's#e502e939-3ab8-5fee-8277-7fd1c1c0fa93#1/2#g' log.txt -sed -i 's#780f6929-a863-5e6a-a046-3dac2e24bf58#1/1#g' log.txt -sed -i 's#f1082088-a304-587b-a230-b8ce10e5a148#mgmt#g' log.txt -sed -i 's#ffdfb0ce-1684-5d39-bad3-9ff1eb4ffbf8#1/2#g' log.txt -sed -i 's#268b735d-c861-5319-88a4-2ea498f96a04#1/1#g' log.txt -sed -i 's#62c0cba1-9ee8-5db5-82da-ce96d7e0f39f#1/3#g' log.txt -sed -i 's#7d1bf45c-5ab2-525e-87a4-c0ddcd5c18e4#1/2#g' log.txt -sed -i 's#2b11934b-dfd7-5267-87b9-7306a24e0182#1/1#g' log.txt -sed -i 's#ca92338e-2038-5d74-8ef1-2b20a234a8b9#1/2#g' log.txt -sed -i 's#f8955e74-4e93-5d43-a968-9626ee5c9b53#mgmt#g' log.txt -sed -i 's#2b75d88d-095f-5752-a10a-1ff69df8008d#1/2#g' log.txt -sed -i 's#bf0d75db-acf8-53cb-b6db-32d9dc0878c4#mgmt#g' log.txt -sed -i 's#eeade85a-03df-55d2-bfc2-2af7267bbcf3#1/1#g' log.txt -sed -i 's#06bb0b92-8783-5599-aa20-15bfbe241348#eth1#g' log.txt -sed -i 's#6a6859c3-4a13-513c-a7dd-490c8b2931b1#eth2#g' log.txt -sed -i 's#97f57787-cfec-5315-9718-7e850905f11a#int#g' log.txt -sed -i 's#659c63c9-9197-54a2-af34-48275e736aac#192.168.27.140:8#g' log.txt -sed -i 's#fed79944-3444-54ee-8335-efbe6590434b#192.168.27.139:10#g' log.txt -sed -i 's#85bb83b0-8d96-5e76-9959-97c35036d4f9#mgmt#g' log.txt -sed -i 's#313fb1ed-5dee-5e49-884a-cbb3b1e00073#1/2#g' log.txt -sed -i 's#79699f56-df25-5188-a389-04606c24fbfc#1/1#g' log.txt -sed -i 's#37ab67ef-0064-54e3-ae9b-d40100953834#int#g' log.txt -sed -i 's#55e88b6b-ccf7-538f-a062-a41219292ea1#eth1#g' log.txt -sed -i 's#68b5972b-3630-53a8-ba9b-cc54dd31f8b8#eth2#g' log.txt -- GitLab From 0b3afc61aa05a230c11885e4152153c7af6454b1 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 28 Feb 2023 19:10:44 +0000 Subject: [PATCH 51/77] Context component: - Sort Service and Slice endpoints according to set request --- src/context/service/database/Service.py | 1 + src/context/service/database/Slice.py | 1 + src/context/service/database/models/ServiceModel.py | 9 +++++++-- src/context/service/database/models/SliceModel.py | 9 +++++++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/context/service/database/Service.py b/src/context/service/database/Service.py index a81a80c3c..fe12eaf8a 100644 --- a/src/context/service/database/Service.py +++ b/src/context/service/database/Service.py @@ -91,6 +91,7 @@ def service_set(db_engine : Engine, request : Service) -> Tuple[Dict, bool]: service_endpoints_data.append({ 'service_uuid' : service_uuid, 'endpoint_uuid': endpoint_uuid, + 'position' : i, }) constraints = compose_constraints_data(request.service_constraints, now, service_uuid=service_uuid) diff --git a/src/context/service/database/Slice.py b/src/context/service/database/Slice.py index 1d6781d53..724046bfa 100644 --- a/src/context/service/database/Slice.py +++ b/src/context/service/database/Slice.py @@ -91,6 +91,7 @@ def slice_set(db_engine : Engine, request : Slice) -> Tuple[Dict, bool]: slice_endpoints_data.append({ 'slice_uuid' : slice_uuid, 'endpoint_uuid': endpoint_uuid, + 'position' : i, }) slice_services_data : List[Dict] = list() diff --git a/src/context/service/database/models/ServiceModel.py b/src/context/service/database/models/ServiceModel.py index 09ff381b5..f1781c4f8 100644 --- a/src/context/service/database/models/ServiceModel.py +++ b/src/context/service/database/models/ServiceModel.py @@ -13,7 +13,7 @@ # limitations under the License. import operator -from sqlalchemy import Column, DateTime, Enum, ForeignKey, String +from sqlalchemy import CheckConstraint, Column, DateTime, Enum, ForeignKey, Integer, String from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship from typing import Dict @@ -51,7 +51,7 @@ class ServiceModel(_Base): 'service_status' : {'service_status': self.service_status.value}, 'service_endpoint_ids': [ service_endpoint.endpoint.dump_id() - for service_endpoint in self.service_endpoints + for service_endpoint in sorted(self.service_endpoints, key=operator.attrgetter('position')) ], 'service_constraints' : [ constraint.dump() @@ -68,6 +68,11 @@ class ServiceEndPointModel(_Base): service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE' ), primary_key=True) endpoint_uuid = Column(ForeignKey('endpoint.endpoint_uuid', ondelete='RESTRICT'), primary_key=True, index=True) + position = Column(Integer, nullable=False) service = relationship('ServiceModel', back_populates='service_endpoints', lazy='joined') endpoint = relationship('EndPointModel', lazy='joined') # back_populates='service_endpoints' + + __table_args__ = ( + CheckConstraint(position >= 0, name='check_position_value'), + ) diff --git a/src/context/service/database/models/SliceModel.py b/src/context/service/database/models/SliceModel.py index 2d6c88416..7f1550eb2 100644 --- a/src/context/service/database/models/SliceModel.py +++ b/src/context/service/database/models/SliceModel.py @@ -13,7 +13,7 @@ # limitations under the License. import operator -from sqlalchemy import Column, DateTime, Enum, ForeignKey, String +from sqlalchemy import CheckConstraint, Column, DateTime, Enum, ForeignKey, Integer, String from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship from typing import Dict @@ -53,7 +53,7 @@ class SliceModel(_Base): 'slice_status' : {'slice_status': self.slice_status.value}, 'slice_endpoint_ids': [ slice_endpoint.endpoint.dump_id() - for slice_endpoint in self.slice_endpoints + for slice_endpoint in sorted(self.slice_endpoints, key=operator.attrgetter('position')) ], 'slice_constraints' : [ constraint.dump() @@ -82,10 +82,15 @@ class SliceEndPointModel(_Base): slice_uuid = Column(ForeignKey('slice.slice_uuid', ondelete='CASCADE' ), primary_key=True) endpoint_uuid = Column(ForeignKey('endpoint.endpoint_uuid', ondelete='RESTRICT'), primary_key=True, index=True) + position = Column(Integer, nullable=False) slice = relationship('SliceModel', back_populates='slice_endpoints', lazy='joined') endpoint = relationship('EndPointModel', lazy='joined') # back_populates='slice_endpoints' + __table_args__ = ( + CheckConstraint(position >= 0, name='check_position_value'), + ) + class SliceServiceModel(_Base): __tablename__ = 'slice_service' -- GitLab From d15ce17e8313541785cbe67af08c09ff585622cf Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 11:12:54 +0000 Subject: [PATCH 52/77] Device component: - TransportAPI Driver: Updated input_sip/output_sip fields to input_sip_uuid/output_sip_uuid - XR Driver: Updated input_sip/output_sip fields to input_sip_name/output_sip_name --- .../service/drivers/transport_api/TransportApiDriver.py | 4 ++-- src/device/service/drivers/xr/cm-cli.py | 4 ++-- .../service/drivers/xr/cm/tests/test_xr_service_set_config.py | 4 ++-- src/device/service/drivers/xr/cm/tf.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/device/service/drivers/transport_api/TransportApiDriver.py b/src/device/service/drivers/transport_api/TransportApiDriver.py index 8b84274e0..1991a34d0 100644 --- a/src/device/service/drivers/transport_api/TransportApiDriver.py +++ b/src/device/service/drivers/transport_api/TransportApiDriver.py @@ -85,9 +85,9 @@ class TransportApiDriver(_Driver): for resource in resources: LOGGER.info('resource = {:s}'.format(str(resource))) - input_sip = find_key(resource, 'input_sip') - output_sip = find_key(resource, 'output_sip') uuid = find_key(resource, 'uuid') + input_sip = find_key(resource, 'input_sip_uuid') + output_sip = find_key(resource, 'output_sip_uuid') capacity_value = find_key(resource, 'capacity_value') capacity_unit = find_key(resource, 'capacity_unit') layer_protocol_name = find_key(resource, 'layer_protocol_name') diff --git a/src/device/service/drivers/xr/cm-cli.py b/src/device/service/drivers/xr/cm-cli.py index 924ca0c96..14c6d24b6 100755 --- a/src/device/service/drivers/xr/cm-cli.py +++ b/src/device/service/drivers/xr/cm-cli.py @@ -160,8 +160,8 @@ if args.emulate_tf_set_config_service: hub_module_name, uuid, input_sip, output_sip, capacity_value = eargs[0:5] capacity_value = int(capacity_value) config = { - "input_sip": input_sip, - "output_sip": output_sip, + "input_sip_name": input_sip, + "output_sip_name": output_sip, "capacity_value": capacity_value, "capacity_unit": "gigabit" } diff --git a/src/device/service/drivers/xr/cm/tests/test_xr_service_set_config.py b/src/device/service/drivers/xr/cm/tests/test_xr_service_set_config.py index e9b16b620..05f55d0df 100644 --- a/src/device/service/drivers/xr/cm/tests/test_xr_service_set_config.py +++ b/src/device/service/drivers/xr/cm/tests/test_xr_service_set_config.py @@ -44,8 +44,8 @@ def mock_cm(): uuid = "12345ABCDEFGHIJKLMN" config = { - "input_sip": "XR HUB 1|XR-T4;", - "output_sip": "XR LEAF 1|XR-T1", + "input_sip_name": "XR HUB 1|XR-T4;", + "output_sip_name": "XR LEAF 1|XR-T1", "capacity_value": 125, "capacity_unit": "gigabit" } diff --git a/src/device/service/drivers/xr/cm/tf.py b/src/device/service/drivers/xr/cm/tf.py index c44cb0c9f..ace3cd288 100644 --- a/src/device/service/drivers/xr/cm/tf.py +++ b/src/device/service/drivers/xr/cm/tf.py @@ -38,7 +38,7 @@ def _get_capacity(config) -> int: def set_config_for_service(cm_connection: CmConnection, constellation: Constellation, uuid: str, config: Dict[str, any]) -> Union[bool, Exception]: try: - service = TFService(uuid, config["input_sip"], config["output_sip"], _get_capacity(config)) + service = TFService(uuid, config["input_sip_name"], config["output_sip_name"], _get_capacity(config)) if constellation.is_vti_mode(): desired_tc = TransportCapacity(from_tf_service=service) active_tc = cm_connection.get_transport_capacity_by_name(service.name()) -- GitLab From 3ae303ca2dbbbda82b814e374f1ba01322921d8d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 12:25:15 +0000 Subject: [PATCH 53/77] Service component: - IETF L2VPN Service Handler: corrected endpoint conditions and selection - TAPI Service Handler: corrected endpoint conditions and selection - TAPI-XR Service Handler: separated from TAPI to prevent collisions in endpoint selection and config message composition --- .../service/service_handlers/__init__.py | 13 +- .../L2NM_IETFL2VPN_ServiceHandler.py | 6 +- .../tapi_tapi/TapiServiceHandler.py | 14 +- .../tapi_xr/TapiXrServiceHandler.py | 176 ++++++++++++++++++ .../service_handlers/tapi_xr/__init__.py | 14 ++ 5 files changed, 209 insertions(+), 14 deletions(-) create mode 100644 src/service/service/service_handlers/tapi_xr/TapiXrServiceHandler.py create mode 100644 src/service/service/service_handlers/tapi_xr/__init__.py diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index fa215af2f..257bc138f 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -15,13 +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 .l2nm_ietfl2vpn.L2NM_IETFL2VPN_ServiceHandler import L2NM_IETFL2VPN_ServiceHandler +from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ @@ -51,7 +52,13 @@ 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, [ 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 index 9adc15494..880a6c5a2 100644 --- a/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py +++ b/src/service/service/service_handlers/l2nm_ietfl2vpn/L2NM_IETFL2VPN_ServiceHandler.py @@ -42,7 +42,7 @@ class L2NM_IETFL2VPN_ServiceHandler(_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') @@ -57,7 +57,7 @@ class L2NM_IETFL2VPN_ServiceHandler(_ServiceHandler): 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_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) @@ -91,7 +91,7 @@ class L2NM_IETFL2VPN_ServiceHandler(_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 diff --git a/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py index ee9c4a66d..af7d4bc94 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') @@ -57,13 +57,11 @@ class TapiServiceHandler(_ServiceHandler): 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) if src_controller is None: src_controller = src_device - dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[1]) + 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 dst_controller is None: dst_controller = dst_device @@ -73,8 +71,8 @@ class TapiServiceHandler(_ServiceHandler): json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), { 'uuid' : service_uuid, - 'input_sip' : src_endpoint.name, - 'output_sip' : dst_endpoint.name, + '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, @@ -97,7 +95,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 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 000000000..a1e1b8a6f --- /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 000000000..1549d9811 --- /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. + -- GitLab From c796e0bcf39eb9f57af3d7cd4aeb171241ac797a Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 12:26:18 +0000 Subject: [PATCH 54/77] Device component: - Added setting to control how underlying topology should be imported - Extended XR Driver to enable topology import selection --- .../service/driver_api/ImportTopologyEnum.py | 37 ++++++++ src/device/service/drivers/xr/XrDriver.py | 95 +++++++++++-------- 2 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 src/device/service/driver_api/ImportTopologyEnum.py diff --git a/src/device/service/driver_api/ImportTopologyEnum.py b/src/device/service/driver_api/ImportTopologyEnum.py new file mode 100644 index 000000000..06f0ff9c2 --- /dev/null +++ b/src/device/service/driver_api/ImportTopologyEnum.py @@ -0,0 +1,37 @@ +# 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. + +from enum import Enum +from typing import Dict + +class ImportTopologyEnum(Enum): + # While importing underlying resources, the driver just imports endpoints and exposes them directly. + DISABLED = 'disabled' + + # While importing underlying resources, the driver just imports imports sub-devices but not links + # connecting them. The endpoints are exposed in virtual nodes representing the sub-devices. + # (a remotely-controlled transport domain might exist between nodes) + DEVICES = 'devices' + + # While importing underlying resources, the driver just imports imports sub-devices and links + # connecting them. The endpoints are exposed in virtual nodes representing the sub-devices. + # (enables to define constrained connectivity between the sub-devices) + TOPOLOGY = 'topology' + +def get_import_topology(settings : Dict, default : ImportTopologyEnum = ImportTopologyEnum.DISABLED): + str_import_topology = settings.get('import_topology') + if str_import_topology is None: return default + import_topology = ImportTopologyEnum._value2member_map_.get(str_import_topology) # pylint: disable=no-member + if import_topology is None: raise Exception('Unexpected setting value') + return import_topology diff --git a/src/device/service/drivers/xr/XrDriver.py b/src/device/service/drivers/xr/XrDriver.py index 83ffd5218..2b53de8e7 100644 --- a/src/device/service/drivers/xr/XrDriver.py +++ b/src/device/service/drivers/xr/XrDriver.py @@ -22,6 +22,7 @@ from common.DeviceTypes import DeviceTypeEnum from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum from common.type_checkers.Checkers import chk_type +from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum, get_import_topology from device.service.driver_api._Driver import _Driver from .cm.cm_connection import CmConnection, ConsistencyMode from .cm import tf @@ -48,6 +49,14 @@ class XrDriver(_Driver): username = settings.get("username", "xr-user-1") password = settings.get("password", "xr-user-1") + # Options are: + # disabled --> just import endpoints as usual + # devices --> imports sub-devices but not links connecting them. + # (a remotely-controlled transport domain might exist between them) + # topology --> imports sub-devices and links connecting them. + # (not supported by XR driver) + self.__import_topology = get_import_topology(settings, default=ImportTopologyEnum.DISABLED) + # Options are: # asynchronous --> operation considered complete when IPM responds with suitable status code, # including "accepted", that only means request is semantically good and queued. @@ -100,50 +109,56 @@ class XrDriver(_Driver): constellation = self.__cm_connection.get_constellation_by_hub_name(self.__hub_module_name) if constellation: self.__constellation = constellation - #return [(f"/endpoints/endpoint[{ifname}]", {'uuid': ifname, 'type': 'optical', 'sample_types': {}}) for ifname in constellation.ifnames()] - - devices : Set[str] = set() - pluggables : Set[str] = set() - devices_and_endpoints = [] - for ifname in constellation.ifnames(): - device_name,pluggable_name = ifname.split('|') - - if device_name not in devices: - device_url = '/devices/device[{:s}]'.format(device_name) - device_data = { - 'uuid': device_name, 'name': device_name, - 'type': DeviceTypeEnum.EMULATED_PACKET_ROUTER.value, - 'status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED, - 'drivers': [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED], - } - devices_and_endpoints.append((device_url, device_data)) - - for copper_if_index in range(4): - copper_ifname = '1/{:d}'.format(copper_if_index + 1) - endpoint_url = '/endpoints/endpoint[{:s}]'.format(copper_ifname) + if self.__import_topology == ImportTopologyEnum.DISABLED: + return [ + (f"/endpoints/endpoint[{ifname}]", {'uuid': ifname, 'type': 'optical', 'sample_types': {}}) + for ifname in constellation.ifnames() + ] + elif self.__import_topology == ImportTopologyEnum.DEVICES: + devices : Set[str] = set() + pluggables : Set[str] = set() + devices_and_endpoints = [] + for ifname in constellation.ifnames(): + device_name,pluggable_name = ifname.split('|') + + if device_name not in devices: + device_url = '/devices/device[{:s}]'.format(device_name) + device_data = { + 'uuid': device_name, 'name': device_name, + 'type': DeviceTypeEnum.EMULATED_PACKET_ROUTER.value, + 'status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED, + 'drivers': [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED], + } + devices_and_endpoints.append((device_url, device_data)) + + for copper_if_index in range(4): + copper_ifname = '1/{:d}'.format(copper_if_index + 1) + endpoint_url = '/endpoints/endpoint[{:s}]'.format(copper_ifname) + endpoint_data = { + 'device_uuid': device_name, 'uuid': copper_ifname, 'name': copper_ifname, + 'type': 'copper/internal', 'sample_types': {} + } + devices_and_endpoints.append((endpoint_url, endpoint_data)) + + devices.add(device_name) + + if ifname not in pluggables: + endpoint_url = '/endpoints/endpoint[{:s}]'.format(ifname) + if 'hub' in ifname.lower(): + endpoint_type = 'optical/xr-hub' + elif 'leaf' in ifname.lower(): + endpoint_type = 'optical/xr-leaf' + else: + endpoint_type = 'optical/xr' endpoint_data = { - 'device_uuid': device_name, 'uuid': copper_ifname, 'name': copper_ifname, - 'type': 'copper/internal', 'sample_types': {} + 'device_uuid': device_name, 'uuid': pluggable_name, 'name': pluggable_name, + 'type': endpoint_type, 'sample_types': {} } devices_and_endpoints.append((endpoint_url, endpoint_data)) - devices.add(device_name) - - if ifname not in pluggables: - endpoint_url = '/endpoints/endpoint[{:s}]'.format(ifname) - if 'hub' in ifname.lower(): - endpoint_type = 'optical/xr-hub' - elif 'leaf' in ifname.lower(): - endpoint_type = 'optical/xr-leaf' - else: - endpoint_type = 'optical/xr' - endpoint_data = { - 'device_uuid': device_name, 'uuid': pluggable_name, 'name': pluggable_name, - 'type': endpoint_type, 'sample_types': {} - } - devices_and_endpoints.append((endpoint_url, endpoint_data)) - - return devices_and_endpoints + return devices_and_endpoints + else: + raise Exception('Unsupported import_topology mode: {:s}'.format(str(self.__import_topology))) else: return [] -- GitLab From 8ee31f1f1ec94faff3b637ab40ca26281bb2464a Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 12:27:00 +0000 Subject: [PATCH 55/77] OFC'23 demo: - Extended child descriptor with full-mesh topology - Extended parent descriptor with import topology setting for XR --- .../ofc23/descriptors/descriptor_child.json | 104 +++++++++++++++++- .../ofc23/descriptors/descriptor_parent.json | 2 +- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/src/tests/ofc23/descriptors/descriptor_child.json b/src/tests/ofc23/descriptors/descriptor_child.json index a167441f4..1dc6fd355 100644 --- a/src/tests/ofc23/descriptors/descriptor_child.json +++ b/src/tests/ofc23/descriptors/descriptor_child.json @@ -13,7 +13,11 @@ {"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/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"} ]}}} ]} }, @@ -24,7 +28,11 @@ {"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/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"} ]}}} ]} }, @@ -35,7 +43,11 @@ {"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/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"} ]}}} ]} }, @@ -46,10 +58,92 @@ {"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/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": [] + "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/descriptor_parent.json b/src/tests/ofc23/descriptors/descriptor_parent.json index acf98c574..413b75662 100644 --- a/src/tests/ofc23/descriptors/descriptor_parent.json +++ b/src/tests/ofc23/descriptors/descriptor_parent.json @@ -53,7 +53,7 @@ {"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" + "consistency-mode": "lifecycle", "import_topology": "devices" }}} ]} }, -- GitLab From ffb2d6f467c92e07fd463ca8a11d834eca944b5d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 12:27:30 +0000 Subject: [PATCH 56/77] Test - Tools - IPM Mock Controller: - First basic and functional version --- .../tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py | 151 ++++++++++++------ 1 file changed, 106 insertions(+), 45 deletions(-) diff --git a/src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py b/src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py index 52a85a00d..ecac81be7 100644 --- a/src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py +++ b/src/tests/tools/mock_ipm_sdn_ctrl/MockIPMSdnCtrl.py @@ -15,6 +15,7 @@ # 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 @@ -28,21 +29,57 @@ LOG_LEVEL = logging.DEBUG CONSTELLATION = { 'id': 'ofc-constellation', 'hubModule': {'state': { - 'module': {'moduleName': 'OFC HUB 1', 'trafficMode': 'L1Mode'}, + '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'}, + 'module': {'moduleName': 'OFC LEAF 1', 'trafficMode': 'L1Mode', 'capacity': 100}, 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}] }}, {'state': { - 'module': {'moduleName': 'OFC LEAF 2', 'trafficMode': 'L1Mode'}, + '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__) @@ -53,59 +90,85 @@ def log_request(logger : logging.Logger, response): logger.info('%s %s %s %s %s', timestamp, request.remote_addr, request.method, request.full_path, response.status) return response -#class Health(Resource): -# def get(self): -# return make_response(jsonify({}), 200) - 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) - form_request = request.form - if form_request.get('client_id') != 'xr-web-client': return make_response('bad client_id', 403) - if form_request.get('client_secret') != 'xr-web-client': return make_response('bad client_secret', 403) - if form_request.get('grant_type') != 'password': return make_response('bad grant_type', 403) - if form_request.get('username') != IPM_USERNAME: return make_response('bad username', 403) - if form_request.get('password') != IPM_PASSWORD: return make_response('bad password', 403) + 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): - print(str(request.args)) - content = request.args.get('content') - print('content', content) 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) - print('query', query) return make_response(jsonify([CONSTELLATION]), 200) -#class Services(Resource): -# def get(self): -# services = [service for service in NETWORK_SERVICES.values()] -# return make_response(jsonify({'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': services}}), 200) -# -# def post(self): -# json_request = request.get_json() -# if not json_request: abort(400) -# if not isinstance(json_request, dict): abort(400) -# if 'etht-svc-instances' not in json_request: abort(400) -# json_services = json_request['etht-svc-instances'] -# if not isinstance(json_services, list): abort(400) -# if len(json_services) != 1: abort(400) -# svc_data = json_services[0] -# etht_svc_name = svc_data['etht-svc-name'] -# NETWORK_SERVICES[etht_svc_name] = svc_data -# return make_response(jsonify({}), 201) - -#class DelServices(Resource): -# def delete(self, service_uuid : str): -# NETWORK_SERVICES.pop(service_uuid, None) -# return make_response(jsonify({}), 204) +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...') @@ -114,12 +177,10 @@ def main(): app.after_request(functools.partial(log_request, LOGGER)) api = Api(app) - #api.add_resource(Health, '/ietf-network:networks') - api.add_resource(OpenIdConnect, '/realms/xr-cm/protocol/openid-connect/token') - api.add_resource(XrNetworks, '/api/v1/xr-networks') - #api.add_resource(Network, '/ietf-network:networks/network=') - #api.add_resource(Services, '/ietf-eth-tran-service:etht-svc') - #api.add_resource(DelServices, '/ietf-eth-tran-service:etht-svc/etht-svc-instances=') + 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/') LOGGER.info('Listening on {:s}...'.format(str(STR_ENDPOINT))) app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT, ssl_context='adhoc') -- GitLab From db28faaae7d821f0222b84db49f942f065c35020 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 12:27:37 +0000 Subject: [PATCH 57/77] Remove TODO file --- TODO.txt | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 TODO.txt diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index 0e6b21c61..000000000 --- a/TODO.txt +++ /dev/null @@ -1,22 +0,0 @@ -PENDING: -- no rule is composed for IPM controller -- no rule is composed for L2VPN TFS SBI -- pending infinera tests -- test with Slice NBI -- integrate Slice NBI in Load Gen -- sort endpoints in service and slice as done for links - -ONGOING: -- implement mock IPM controller - -TO TEST: - - -OPTIONAL: - - Optimize requests pathcomp--context - - Optimize requests webui--context - - TransportAPIDriver: add flag to import underlying topology options: 'disabled/devices/all' - - IetfL2VpnDriver: add flag to import underlying topology options: 'disabled/devices/all' - - MWDriver should import topology, there should be nothing between antennas - - Enable to create links (connectivity between subdevices) - - bidirectional links should be duplicated by frontend in a smart way -- GitLab From 876a338a68ff80a0c7f1568404b1808635e7cc11 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 13:44:30 +0000 Subject: [PATCH 58/77] Manifests: - Restored log levels --- manifests/deviceservice.yaml | 2 +- manifests/pathcompservice.yaml | 2 +- manifests/serviceservice.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifests/deviceservice.yaml b/manifests/deviceservice.yaml index ddcc997cd..ca2c81f0f 100644 --- a/manifests/deviceservice.yaml +++ b/manifests/deviceservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "DEBUG" + value: "INFO" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:2020"] diff --git a/manifests/pathcompservice.yaml b/manifests/pathcompservice.yaml index 5916d09a6..fd3599f42 100644 --- a/manifests/pathcompservice.yaml +++ b/manifests/pathcompservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "DEBUG" + value: "INFO" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:10020"] diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index 801c06f52..3fa4a6e0d 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -36,7 +36,7 @@ spec: - containerPort: 9192 env: - name: LOG_LEVEL - value: "DEBUG" + value: "INFO" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:3030"] -- GitLab From 9bf07f64dee8ceb1b3789e93afd5cde612f25a37 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 18:31:24 +0000 Subject: [PATCH 59/77] OFC'23 demo: - Created 2 sets of descriptors: real and emulated - Updated real scenario settings --- .../{ => emulated}/dc-2-dc-service.json | 0 .../{ => emulated}/descriptor_child.json | 0 .../{ => emulated}/descriptor_parent.json | 0 .../descriptor_parent_noxr.json | 0 .../descriptors/real/dc-2-dc-service.json | 37 +++ .../descriptors/real/descriptor_child.json | 93 +++++++ .../descriptors/real/descriptor_parent.json | 258 ++++++++++++++++++ 7 files changed, 388 insertions(+) rename src/tests/ofc23/descriptors/{ => emulated}/dc-2-dc-service.json (100%) rename src/tests/ofc23/descriptors/{ => emulated}/descriptor_child.json (100%) rename src/tests/ofc23/descriptors/{ => emulated}/descriptor_parent.json (100%) rename src/tests/ofc23/descriptors/{ => emulated}/descriptor_parent_noxr.json (100%) create mode 100644 src/tests/ofc23/descriptors/real/dc-2-dc-service.json create mode 100644 src/tests/ofc23/descriptors/real/descriptor_child.json create mode 100644 src/tests/ofc23/descriptors/real/descriptor_parent.json diff --git a/src/tests/ofc23/descriptors/dc-2-dc-service.json b/src/tests/ofc23/descriptors/emulated/dc-2-dc-service.json similarity index 100% rename from src/tests/ofc23/descriptors/dc-2-dc-service.json rename to src/tests/ofc23/descriptors/emulated/dc-2-dc-service.json diff --git a/src/tests/ofc23/descriptors/descriptor_child.json b/src/tests/ofc23/descriptors/emulated/descriptor_child.json similarity index 100% rename from src/tests/ofc23/descriptors/descriptor_child.json rename to src/tests/ofc23/descriptors/emulated/descriptor_child.json diff --git a/src/tests/ofc23/descriptors/descriptor_parent.json b/src/tests/ofc23/descriptors/emulated/descriptor_parent.json similarity index 100% rename from src/tests/ofc23/descriptors/descriptor_parent.json rename to src/tests/ofc23/descriptors/emulated/descriptor_parent.json diff --git a/src/tests/ofc23/descriptors/descriptor_parent_noxr.json b/src/tests/ofc23/descriptors/emulated/descriptor_parent_noxr.json similarity index 100% rename from src/tests/ofc23/descriptors/descriptor_parent_noxr.json rename to src/tests/ofc23/descriptors/emulated/descriptor_parent_noxr.json 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 000000000..3a83afa6d --- /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 000000000..aa6d7bbd1 --- /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" : 120} + }}} + ]} + }, + { + "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} + }}} + ]} + }, + { + "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} + }}} + ]} + } + ], + "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 000000000..3317d46ed --- /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"}} + ] + } + ] +} -- GitLab From 6828d7dd46417ff5d727de41b2cab4a250d65c5d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 18:32:45 +0000 Subject: [PATCH 60/77] Device component: - IETF L2VPN Driver: removed force to show emulated devices - Microwave: correcred parsing of existing services - Openconfig: corrected gneration of endpoints; added missing retry decorators --- .../service/drivers/ietf_l2vpn/TfsDebugApiClient.py | 2 +- src/device/service/drivers/microwave/Tools.py | 10 ++++++++-- .../service/drivers/openconfig/OpenConfigDriver.py | 2 ++ .../service/drivers/openconfig/templates/EndPoints.py | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py b/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py index f8c4e0d94..4bf40af03 100644 --- a/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py +++ b/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py @@ -66,7 +66,7 @@ class TfsDebugApiClient: for json_device in reply.json()['devices']: device_uuid : str = json_device['device_id']['device_uuid']['uuid'] device_type : str = json_device['device_type'] - if not device_type.startswith('emu-'): device_type = 'emu-' + device_type + #if not device_type.startswith('emu-'): device_type = 'emu-' + device_type device_status = json_device['device_operational_status'] device_url = '/devices/device[{:s}]'.format(device_uuid) device_data = { diff --git a/src/device/service/drivers/microwave/Tools.py b/src/device/service/drivers/microwave/Tools.py index 6245ff0af..4490c0f63 100644 --- a/src/device/service/drivers/microwave/Tools.py +++ b/src/device/service/drivers/microwave/Tools.py @@ -119,8 +119,14 @@ def config_getter( resource_value['node_id_dst'] = access_point['access-node-id'] resource_value['tp_id_dst'] = access_point['access-ltp-id'] - if len(node_ids) > 0 and resource_value['node_id_src'] not in node_ids: continue - if len(node_ids) > 0 and resource_value['node_id_dst'] not in node_ids: continue + if len(node_ids) > 0: + node_id_src = resource_value.get('node_id_src') + if node_id_src is None: continue + if node_id_src not in node_ids: continue + + node_id_dst = resource_value.get('node_id_dst') + if node_id_dst is None: continue + if node_id_dst not in node_ids: continue result.append((resource_key, resource_value)) except requests.exceptions.Timeout: diff --git a/src/device/service/drivers/openconfig/OpenConfigDriver.py b/src/device/service/drivers/openconfig/OpenConfigDriver.py index ac0352752..569a0400e 100644 --- a/src/device/service/drivers/openconfig/OpenConfigDriver.py +++ b/src/device/service/drivers/openconfig/OpenConfigDriver.py @@ -113,9 +113,11 @@ class NetconfSessionHandler: config, target=target, default_operation=default_operation, test_option=test_option, error_option=error_option, format=format) + @RETRY_DECORATOR def locked(self, target): return self.__manager.locked(target=target) + @RETRY_DECORATOR def commit(self, confirmed=False, timeout=None, persist=None, persist_id=None): return self.__manager.commit(confirmed=confirmed, timeout=timeout, persist=persist, persist_id=persist_id) diff --git a/src/device/service/drivers/openconfig/templates/EndPoints.py b/src/device/service/drivers/openconfig/templates/EndPoints.py index 02fda8f0e..f16f0ffcd 100644 --- a/src/device/service/drivers/openconfig/templates/EndPoints.py +++ b/src/device/service/drivers/openconfig/templates/EndPoints.py @@ -55,5 +55,5 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]: add_value_from_collection(endpoint, 'sample_types', sample_types) if len(endpoint) == 0: continue - response.append(('/endpoint[{:s}]'.format(endpoint['uuid']), endpoint)) + response.append(('/endpoints/endpoint[{:s}]'.format(endpoint['uuid']), endpoint)) return response -- GitLab From 691cd597f4977129b9a28a9bea8cc197cf5fb498 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 18:33:04 +0000 Subject: [PATCH 61/77] Compute component: - Added missing bearer mappings --- .../service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py | 4 ++++ 1 file changed, 4 insertions(+) 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 84a18b32c..ed25dbab3 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 @@ -84,4 +84,8 @@ BEARER_MAPPINGS = { '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'), } -- GitLab From f777ec496c900926c30c26588c65c4de04a142e2 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 1 Mar 2023 18:33:27 +0000 Subject: [PATCH 62/77] Tests - Tools - Mock MW Server: - Added TODO with test cases to check --- src/tests/tools/mock_mw_sdn_ctrl/TODO.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/tests/tools/mock_mw_sdn_ctrl/TODO.txt diff --git a/src/tests/tools/mock_mw_sdn_ctrl/TODO.txt b/src/tests/tools/mock_mw_sdn_ctrl/TODO.txt new file mode 100644 index 000000000..db764fcf2 --- /dev/null +++ b/src/tests/tools/mock_mw_sdn_ctrl/TODO.txt @@ -0,0 +1,9 @@ +extend discovery of mgmt VLANs + +>>> import requests +>>> from requests.auth import HTTPBasicAuth +>>> url = 'https://192.168.27.136:8443/nmswebs/restconf/data/ietf-eth-tran-service:etht-svc' +>>> auth = HTTPBasicAuth('nms5ux', 'nms5ux') +>>> response = requests.get(url, timeout=120, verify=False, auth=auth) +>>> response.content +{"ietf-eth-tran-service:etht-svc":{"etht-svc-instances":[{"te-topology-identifier":{"topology-id":"eth-native-topology","client-id":3373,"provider-id":3373},"etht-svc-name":"MGMT-VLAN-192-168-27-139","admin-status":"ietf-te-types:tunnel-state-up","etht-svc-type":"ietf-eth-tran-types:p2p-svc","etht-svc-end-points":[{"etht-svc-access-points":[{"access-node-id":"192.168.27.139","access-ltp-id":3,"access-point-id":"1"}],"outer-tag":{"vlan-value":100,"tag-type":"ietf-eth-tran-types:classify-c-vlan"},"etht-svc-end-point-name":"192.168.27.139:3","service-classification-type":"ietf-eth-tran-types:port-classification"}],"state":{"operational-state":"ietf-te-types:tunnel-state-up","provisioning-state":"ietf-te-types:lsp-state-up"}},{"te-topology-identifier":{"topology-id":"eth-native-topology","client-id":3373,"provider-id":3373},"etht-svc-name":"MGMT-VLAN-192-168-27-140","admin-status":"ietf-te-types:tunnel-state-up","etht-svc-type":"ietf-eth-tran-types:p2p-svc","etht-svc-end-points":[{"etht-svc-access-points":[{"access-node-id":"192.168.27.140","access-ltp-id":3,"access-point-id":"1"}],"outer-tag":{"vlan-value":100,"tag-type":"ietf-eth-tran-types:classify-c-vlan"},"etht-svc-end-point-name":"192.168.27.140:3","service-classification-type":"ietf-eth-tran-types:vlan-classification"}],"state":{"operational-state":"ietf-te-types:tunnel-state-up","provisioning-state":"ietf-te-types:lsp-state-up"}}],"globals":{}}} -- GitLab From 25c978f3b402caa6831bb88ba4628d8bb3c06faa Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 2 Mar 2023 09:02:13 +0000 Subject: [PATCH 63/77] Manifests: - Corrected limits and replicas of compute and webui components --- manifests/computeservice.yaml | 11 +++++++---- manifests/webuiservice.yaml | 17 ++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/manifests/computeservice.yaml b/manifests/computeservice.yaml index 7e40ef988..378e34b9b 100644 --- a/manifests/computeservice.yaml +++ b/manifests/computeservice.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: app: computeservice + replicas: 1 template: metadata: labels: @@ -44,16 +45,18 @@ spec: command: ["/bin/grpc_health_probe", "-addr=:9090"] resources: requests: - cpu: 250m - memory: 512Mi + cpu: 50m + memory: 64Mi limits: - cpu: 700m - memory: 1024Mi + cpu: 500m + memory: 512Mi --- apiVersion: v1 kind: Service metadata: name: computeservice + labels: + app: computeservice spec: type: ClusterIP selector: diff --git a/manifests/webuiservice.yaml b/manifests/webuiservice.yaml index f25dbf6e5..234075f73 100644 --- a/manifests/webuiservice.yaml +++ b/manifests/webuiservice.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: app: webuiservice + replicas: 1 template: metadata: labels: @@ -55,11 +56,11 @@ spec: timeoutSeconds: 1 resources: requests: - cpu: 100m - memory: 512Mi + cpu: 50m + memory: 64Mi limits: - cpu: 700m - memory: 1024Mi + cpu: 500m + memory: 512Mi - name: grafana image: grafana/grafana:8.5.11 imagePullPolicy: IfNotPresent @@ -92,16 +93,18 @@ spec: timeoutSeconds: 1 resources: requests: - cpu: 250m - memory: 750Mi + cpu: 150m + memory: 512Mi limits: - cpu: 700m + cpu: 500m memory: 1024Mi --- apiVersion: v1 kind: Service metadata: name: webuiservice + labels: + app: webuiservice spec: type: ClusterIP selector: -- GitLab From 9ae4ebf707cb4eec1b133e49a4356f944e255488 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 2 Mar 2023 11:39:10 +0000 Subject: [PATCH 64/77] OFC'23 demo: - Added automation component to the deploy --- src/tests/ofc23/deploy_specs_child.sh | 2 +- src/tests/ofc23/deploy_specs_parent.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/ofc23/deploy_specs_child.sh b/src/tests/ofc23/deploy_specs_child.sh index 0349dba5a..a5ba3a9a0 100755 --- a/src/tests/ofc23/deploy_specs_child.sh +++ b/src/tests/ofc23/deploy_specs_child.sh @@ -21,7 +21,7 @@ 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" +export TFS_COMPONENTS="context device automation pathcomp service slice compute webui" # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" diff --git a/src/tests/ofc23/deploy_specs_parent.sh b/src/tests/ofc23/deploy_specs_parent.sh index fc362c08c..006c139f3 100755 --- a/src/tests/ofc23/deploy_specs_parent.sh +++ b/src/tests/ofc23/deploy_specs_parent.sh @@ -21,7 +21,7 @@ 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" +export TFS_COMPONENTS="context device automation pathcomp service slice compute webui" # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" -- GitLab From f4ed75a8f550f8f485548077b4e6e8c0db247b89 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 2 Mar 2023 11:39:39 +0000 Subject: [PATCH 65/77] Automation component: - Added missing IETF_L2VPN entry to enum --- .../eu/teraflow/automation/context/model/DeviceDriverEnum.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/automation/src/main/java/eu/teraflow/automation/context/model/DeviceDriverEnum.java b/src/automation/src/main/java/eu/teraflow/automation/context/model/DeviceDriverEnum.java index a364bb0e3..3a26937e7 100644 --- a/src/automation/src/main/java/eu/teraflow/automation/context/model/DeviceDriverEnum.java +++ b/src/automation/src/main/java/eu/teraflow/automation/context/model/DeviceDriverEnum.java @@ -23,5 +23,6 @@ public enum DeviceDriverEnum { P4, IETF_NETWORK_TOPOLOGY, ONF_TR_352, - XR + XR, + IETF_L2VPN } -- GitLab From 19328bbc476e6e404bc5fedd473d0db1f7b2f7dd Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 3 Mar 2023 13:24:19 +0000 Subject: [PATCH 66/77] PathComp component - Frontend: - Added missing name-uuid mapping --- src/pathcomp/frontend/service/algorithms/_Algorithm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py index 6cc4c5496..b486ec1b5 100644 --- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py +++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py @@ -64,6 +64,7 @@ class _Algorithm: _device_uuid = grpc_device.device_id.device_uuid.uuid _device_name = grpc_device.name self.device_name_mapping[_device_name] = _device_uuid + self.device_name_mapping[_device_uuid] = _device_uuid device_endpoint_dict : Dict[str, Tuple[Dict, EndPointId]] = dict() for json_endpoint,grpc_endpoint in zip(json_device['device_endpoints'], grpc_device.device_endpoints): @@ -75,6 +76,8 @@ class _Algorithm: _endpoint_name = grpc_endpoint.name self.endpoint_name_mapping[(_device_uuid, _endpoint_name)] = _endpoint_uuid self.endpoint_name_mapping[(_device_name, _endpoint_name)] = _endpoint_uuid + self.endpoint_name_mapping[(_device_uuid, _endpoint_uuid)] = _endpoint_uuid + self.endpoint_name_mapping[(_device_name, _endpoint_uuid)] = _endpoint_uuid self.endpoint_dict[device_uuid] = device_endpoint_dict -- GitLab From 72afa877cded22048b3c42ef4e4e0b1d3669db36 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 3 Mar 2023 13:26:41 +0000 Subject: [PATCH 67/77] Slice component: - added workaround for multi-instance deployments --- src/slice/service/slice_grouper/SliceGrouper.py | 1 + src/slice/service/slice_grouper/Tools.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/slice/service/slice_grouper/SliceGrouper.py b/src/slice/service/slice_grouper/SliceGrouper.py index 735d02899..2f1a79181 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/slice/service/slice_grouper/Tools.py b/src/slice/service/slice_grouper/Tools.py index ca957f3c7..d5a78ba2b 100644 --- a/src/slice/service/slice_grouper/Tools.py +++ b/src/slice/service/slice_grouper/Tools.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging from typing import Dict, List, Optional, Set, Tuple from common.Constants import DEFAULT_CONTEXT_NAME from common.Settings import get_setting @@ -27,7 +28,14 @@ TRUE_VALUES = {'Y', 'YES', 'TRUE', 'T', 'E', 'ENABLE', 'ENABLED'} NO_ISOLATION = IsolationLevelEnum.NO_ISOLATION +LOGGER = logging.getLogger(__name__) + def is_slice_grouping_enabled() -> bool: + # Temporal hack for OFC'23 multi-demo: + metricsdb_hostname = get_setting('METRICSDB_HOSTNAME', default='') + LOGGER.warning('metricsdb_hostname={:s}'.format(str(metricsdb_hostname))) + if '.qdb-sligrp.' in metricsdb_hostname: return True + # end hack is_enabled = get_setting(SETTING_NAME_SLICE_GROUPING, default=None) if is_enabled is None: return False str_is_enabled = str(is_enabled).upper() -- GitLab From 848d6d271f7f40ef43cbd83828e0267ed5a688d1 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 3 Mar 2023 13:28:20 +0000 Subject: [PATCH 68/77] OFC'23 demo: - updated descriptors and scripts --- .../ofc23/{delete_all.sh => delete_hierar.sh} | 0 src/tests/ofc23/delete_sligrp.sh | 18 + .../ofc23/{deploy_all.sh => deploy_hierar.sh} | 0 src/tests/ofc23/deploy_sligrp.sh | 23 ++ src/tests/ofc23/deploy_specs_child.sh | 20 +- src/tests/ofc23/deploy_specs_parent.sh | 16 +- src/tests/ofc23/deploy_specs_sligrp.sh | 118 +++++++ .../ofc23/descriptors/adva-interfaces.txt | 89 +++++ .../descriptors/backup/dc-2-dc-service.json | 37 +++ .../descriptors/backup/descriptor_child.json | 183 +++++++++++ .../descriptors/backup/descriptor_parent.json | 258 +++++++++++++++ .../emulated/descriptor_parent.json | 117 ++----- .../ofc23/descriptors/emulated/ipm-ctrl.json | 25 ++ .../emulated/old/descriptor_parent.json | 311 ++++++++++++++++++ .../descriptors/real/descriptor_child.json | 6 +- src/tests/ofc23/show_deploy_sligrp.sh | 26 ++ 16 files changed, 1141 insertions(+), 106 deletions(-) rename src/tests/ofc23/{delete_all.sh => delete_hierar.sh} (100%) create mode 100755 src/tests/ofc23/delete_sligrp.sh rename src/tests/ofc23/{deploy_all.sh => deploy_hierar.sh} (100%) create mode 100755 src/tests/ofc23/deploy_sligrp.sh create mode 100755 src/tests/ofc23/deploy_specs_sligrp.sh create mode 100644 src/tests/ofc23/descriptors/adva-interfaces.txt create mode 100644 src/tests/ofc23/descriptors/backup/dc-2-dc-service.json create mode 100644 src/tests/ofc23/descriptors/backup/descriptor_child.json create mode 100644 src/tests/ofc23/descriptors/backup/descriptor_parent.json create mode 100644 src/tests/ofc23/descriptors/emulated/ipm-ctrl.json create mode 100644 src/tests/ofc23/descriptors/emulated/old/descriptor_parent.json create mode 100755 src/tests/ofc23/show_deploy_sligrp.sh diff --git a/src/tests/ofc23/delete_all.sh b/src/tests/ofc23/delete_hierar.sh similarity index 100% rename from src/tests/ofc23/delete_all.sh rename to src/tests/ofc23/delete_hierar.sh diff --git a/src/tests/ofc23/delete_sligrp.sh b/src/tests/ofc23/delete_sligrp.sh new file mode 100755 index 000000000..cce0bd53f --- /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_all.sh b/src/tests/ofc23/deploy_hierar.sh similarity index 100% rename from src/tests/ofc23/deploy_all.sh rename to src/tests/ofc23/deploy_hierar.sh diff --git a/src/tests/ofc23/deploy_sligrp.sh b/src/tests/ofc23/deploy_sligrp.sh new file mode 100755 index 000000000..62a9df5cf --- /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 index a5ba3a9a0..4d2b35022 100755 --- a/src/tests/ofc23/deploy_specs_child.sh +++ b/src/tests/ofc23/deploy_specs_child.sh @@ -21,7 +21,7 @@ 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 automation pathcomp service slice compute webui" +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" @@ -42,13 +42,13 @@ export TFS_SKIP_BUILD="YES" # ----- CockroachDB ------------------------------------------------------------ # Set the namespace where CockroackDB will be deployed. -export CRDB_NAMESPACE="crdb-child" +export CRDB_NAMESPACE="crdb" # Set the external port CockroackDB Postgre SQL interface will be exposed to. -export CRDB_EXT_PORT_SQL="26258" +export CRDB_EXT_PORT_SQL="26257" # Set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to. -export CRDB_EXT_PORT_HTTP="8082" +export CRDB_EXT_PORT_HTTP="8081" # Set the database username to be used by Context. export CRDB_USERNAME="tfs" @@ -57,7 +57,7 @@ export CRDB_USERNAME="tfs" export CRDB_PASSWORD="tfs123" # Set the database name to be used by Context. -export CRDB_DATABASE="tfs" +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 @@ -76,10 +76,10 @@ export CRDB_REDEPLOY="" export NATS_NAMESPACE="nats-child" # Set the external port NATS Client interface will be exposed to. -export NATS_EXT_PORT_CLIENT="4223" +export NATS_EXT_PORT_CLIENT="4224" # Set the external port NATS HTTP Mgmt GUI interface will be exposed to. -export NATS_EXT_PORT_HTTP="8223" +export NATS_EXT_PORT_HTTP="8224" # Disable flag for re-deploying NATS from scratch. export NATS_REDEPLOY="" @@ -91,13 +91,13 @@ export NATS_REDEPLOY="" export QDB_NAMESPACE="qdb-child" # Set the external port QuestDB Postgre SQL interface will be exposed to. -export QDB_EXT_PORT_SQL="8813" +export QDB_EXT_PORT_SQL="8814" # Set the external port QuestDB Influx Line Protocol interface will be exposed to. -export QDB_EXT_PORT_ILP="9010" +export QDB_EXT_PORT_ILP="9012" # Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to. -export QDB_EXT_PORT_HTTP="9001" +export QDB_EXT_PORT_HTTP="9002" # Set the database username to be used for QuestDB. export QDB_USERNAME="admin" diff --git a/src/tests/ofc23/deploy_specs_parent.sh b/src/tests/ofc23/deploy_specs_parent.sh index 006c139f3..808f4e287 100755 --- a/src/tests/ofc23/deploy_specs_parent.sh +++ b/src/tests/ofc23/deploy_specs_parent.sh @@ -21,7 +21,7 @@ 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 automation pathcomp service slice compute webui" +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" @@ -42,7 +42,7 @@ export TFS_SKIP_BUILD="" # ----- CockroachDB ------------------------------------------------------------ # Set the namespace where CockroackDB will be deployed. -export CRDB_NAMESPACE="crdb-parent" +export CRDB_NAMESPACE="crdb" # Set the external port CockroackDB Postgre SQL interface will be exposed to. export CRDB_EXT_PORT_SQL="26257" @@ -57,7 +57,7 @@ export CRDB_USERNAME="tfs" export CRDB_PASSWORD="tfs123" # Set the database name to be used by Context. -export CRDB_DATABASE="tfs" +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 @@ -76,10 +76,10 @@ export CRDB_REDEPLOY="" export NATS_NAMESPACE="nats-parent" # Set the external port NATS Client interface will be exposed to. -export NATS_EXT_PORT_CLIENT="4222" +export NATS_EXT_PORT_CLIENT="4223" # Set the external port NATS HTTP Mgmt GUI interface will be exposed to. -export NATS_EXT_PORT_HTTP="8222" +export NATS_EXT_PORT_HTTP="8223" # Disable flag for re-deploying NATS from scratch. export NATS_REDEPLOY="" @@ -91,13 +91,13 @@ export NATS_REDEPLOY="" export QDB_NAMESPACE="qdb-parent" # Set the external port QuestDB Postgre SQL interface will be exposed to. -export QDB_EXT_PORT_SQL="8812" +export QDB_EXT_PORT_SQL="8813" # Set the external port QuestDB Influx Line Protocol interface will be exposed to. -export QDB_EXT_PORT_ILP="9009" +export QDB_EXT_PORT_ILP="9011" # Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to. -export QDB_EXT_PORT_HTTP="9000" +export QDB_EXT_PORT_HTTP="9001" # Set the database username to be used for QuestDB. export QDB_USERNAME="admin" diff --git a/src/tests/ofc23/deploy_specs_sligrp.sh b/src/tests/ofc23/deploy_specs_sligrp.sh new file mode 100755 index 000000000..90bea4567 --- /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 000000000..a63473505 --- /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 000000000..3a83afa6d --- /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 000000000..eea957153 --- /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 000000000..42b60e3cf --- /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/descriptor_parent.json b/src/tests/ofc23/descriptors/emulated/descriptor_parent.json index 413b75662..1b1f5dbfd 100644 --- a/src/tests/ofc23/descriptors/emulated/descriptor_parent.json +++ b/src/tests/ofc23/descriptors/emulated/descriptor_parent.json @@ -17,24 +17,13 @@ ]} }, { - "device_id": {"device_uuid": {"uuid": "MW1-2"}}, "device_type": "microwave-radio-system", "device_drivers": [5], + "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": ["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"] + "node_ids": ["192.168.27.139", "192.168.27.140"] }}} ]} }, @@ -100,85 +89,43 @@ ], "links": [ { - "link_id": {"link_uuid": {"uuid": "DC1/eth1==PE1/1/1"}}, "link_endpoint_ids": [ + "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": "PE1"}}, "endpoint_uuid": {"uuid": "1/1"}} + {"device_id": {"device_uuid": {"uuid": "R149"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}} ] }, { - "link_id": {"link_uuid": {"uuid": "PE1/1/1==DC1/eth1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "PE1"}}, "endpoint_uuid": {"uuid": "1/1"}}, + "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": "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": "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": "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": "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": "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"}}, + "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==MW1-2/172.18.0.2:1"}}, "link_endpoint_ids": [ + "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": "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"}} + {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "192.168.27.139:5"}} ] }, @@ -254,57 +201,57 @@ { - "link_id": {"link_uuid": {"uuid": "OFC LEAF 1/1/1==PE3/1/2"}}, "link_endpoint_ids": [ + "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": "PE3"}}, "endpoint_uuid": {"uuid": "1/2"}} + {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/25"}} ] }, { - "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"}}, + "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==PE4/1/2"}}, "link_endpoint_ids": [ + "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": "PE4"}}, "endpoint_uuid": {"uuid": "1/2"}} + {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/20"}} ] }, { - "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"}}, + "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": "PE3/1/1==DC2/eth1"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "PE3"}}, "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==PE3/1/1"}}, "link_endpoint_ids": [ + "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": "PE3"}}, "endpoint_uuid": {"uuid": "1/1"}} + {"device_id": {"device_uuid": {"uuid": "R155"}}, "endpoint_uuid": {"uuid": "eth-1/0/22"}} ] }, { - "link_id": {"link_uuid": {"uuid": "PE4/1/1==DC2/eth2"}}, "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "PE4"}}, "endpoint_uuid": {"uuid": "1/1"}}, + "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==PE4/1/1"}}, "link_endpoint_ids": [ + "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": "PE4"}}, "endpoint_uuid": {"uuid": "1/1"}} + {"device_id": {"device_uuid": {"uuid": "R199"}}, "endpoint_uuid": {"uuid": "eth-1/0/21"}} ] } ] 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 000000000..91e9de611 --- /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 000000000..413b75662 --- /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/descriptor_child.json b/src/tests/ofc23/descriptors/real/descriptor_child.json index aa6d7bbd1..8d695cfd2 100644 --- a/src/tests/ofc23/descriptors/real/descriptor_child.json +++ b/src/tests/ofc23/descriptors/real/descriptor_child.json @@ -15,7 +15,7 @@ "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} + "manager_params": {"timeout" : 86400} }}} ]} }, @@ -28,7 +28,7 @@ "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} + "manager_params": {"timeout" : 86400} }}} ]} }, @@ -41,7 +41,7 @@ "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} + "manager_params": {"timeout" : 86400} }}} ]} } diff --git a/src/tests/ofc23/show_deploy_sligrp.sh b/src/tests/ofc23/show_deploy_sligrp.sh new file mode 100755 index 000000000..b5e3600ba --- /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" -- GitLab From 5901b3e9e70e81951f350a85ad0a25c8fedf5ab1 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 3 Mar 2023 13:45:03 +0000 Subject: [PATCH 69/77] Tool - Mock MW SDN Controller: - Updated exposed topology --- .../tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py index 91542d85b..c20dde1b9 100644 --- a/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py +++ b/src/tests/tools/mock_mw_sdn_ctrl/MockMWSdnCtrl.py @@ -35,31 +35,51 @@ STR_ENDPOINT = 'https://{:s}:{:s}{:s}'.format(str(BIND_ADDRESS), str(BIND_PORT), LOG_LEVEL = logging.DEBUG NETWORK_NODES = [ - {'node-id': '172.18.0.1', 'ietf-network-topology:termination-point': [ + {'node-id': '192.168.27.139', 'ietf-network-topology:termination-point': [ {'tp-id': '1', 'ietf-te-topology:te': {'name': 'ethernet'}}, - {'tp-id': '2', 'ietf-te-topology:te': {'name': 'antena' }}, + {'tp-id': '2', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '3', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '4', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '5', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '6', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '10', 'ietf-te-topology:te': {'name': 'antena' }}, ]}, - {'node-id': '172.18.0.2', 'ietf-network-topology:termination-point': [ + {'node-id': '192.168.27.140', 'ietf-network-topology:termination-point': [ {'tp-id': '1', 'ietf-te-topology:te': {'name': 'ethernet'}}, - {'tp-id': '2', 'ietf-te-topology:te': {'name': 'antena' }}, + {'tp-id': '2', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '3', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '4', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '5', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '6', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '10', 'ietf-te-topology:te': {'name': 'antena' }}, ]}, - {'node-id': '172.18.0.3', 'ietf-network-topology:termination-point': [ + {'node-id': '192.168.27.141', 'ietf-network-topology:termination-point': [ {'tp-id': '1', 'ietf-te-topology:te': {'name': 'ethernet'}}, - {'tp-id': '2', 'ietf-te-topology:te': {'name': 'antena' }}, + {'tp-id': '2', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '3', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '4', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '5', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '6', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '10', 'ietf-te-topology:te': {'name': 'antena' }}, ]}, - {'node-id': '172.18.0.4', 'ietf-network-topology:termination-point': [ + {'node-id': '192.168.27.142', 'ietf-network-topology:termination-point': [ {'tp-id': '1', 'ietf-te-topology:te': {'name': 'ethernet'}}, - {'tp-id': '2', 'ietf-te-topology:te': {'name': 'antena' }}, + {'tp-id': '2', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '3', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '4', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '5', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '6', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '10', 'ietf-te-topology:te': {'name': 'antena' }}, ]} ] NETWORK_LINKS = [ - { 'link-id' : '172.18.0.1:2--172.18.0.2:2', - 'source' : {'source-node': '172.18.0.1', 'source-tp': '2'}, - 'destination': {'dest-node' : '172.18.0.2', 'dest-tp' : '2'}, + { 'link-id' : '192.168.27.139:10--192.168.27.140:10', + 'source' : {'source-node': '192.168.27.139', 'source-tp': '10'}, + 'destination': {'dest-node' : '192.168.27.140', 'dest-tp' : '10'}, }, - { 'link-id' : '172.18.0.3:2--172.18.0.4:2', - 'source' : {'source-node': '172.18.0.3', 'source-tp': '2'}, - 'destination': {'dest-node' : '172.18.0.4', 'dest-tp' : '2'}, + { 'link-id' : '192.168.27.141:10--192.168.27.142:10', + 'source' : {'source-node': '192.168.27.141', 'source-tp': '10'}, + 'destination': {'dest-node' : '192.168.27.142', 'dest-tp' : '10'}, } ] NETWORK_SERVICES = {} -- GitLab From ec53aaa825656d75f47d18bc39e01d5dc52f7f55 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Tue, 7 Mar 2023 23:28:02 +0000 Subject: [PATCH 70/77] Service component: - Updated config rules for L2NM Emulated/OpenConfig Service Handlers --- .../l2nm_emulated/ConfigRules.py | 64 +++++++++---------- .../l2nm_openconfig/ConfigRules.py | 51 ++++++++------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py b/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py index 84467dd2d..363983b86 100644 --- a/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py +++ b/src/service/service/service_handlers/l2nm_emulated/ConfigRules.py @@ -32,7 +32,7 @@ def setup_config_rules( #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 @@ -46,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), @@ -69,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), @@ -94,7 +94,7 @@ def teardown_config_rules( #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 @@ -105,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}]/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}]/connection_point[{:s}]'.format(network_instance_name, connection_point_id), + # {'name': network_instance_name, 'connection_point': connection_point_id}), - 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_openconfig/ConfigRules.py b/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py index f3f00a3bd..63e818a83 100644 --- a/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py +++ b/src/service/service/service_handlers/l2nm_openconfig/ConfigRules.py @@ -20,7 +20,7 @@ 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 [] @@ -49,31 +49,21 @@ def setup_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 @@ -95,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 @@ -104,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 -- GitLab From dabbc5484b266b9ce93f54280bdd93f546f0f343 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 8 Mar 2023 22:18:57 +0000 Subject: [PATCH 71/77] Slice component: - Remove workaround for OFC'23 demo to auto-start slice grouping --- src/slice/service/slice_grouper/Tools.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/slice/service/slice_grouper/Tools.py b/src/slice/service/slice_grouper/Tools.py index d5a78ba2b..c635ffcf4 100644 --- a/src/slice/service/slice_grouper/Tools.py +++ b/src/slice/service/slice_grouper/Tools.py @@ -31,11 +31,6 @@ NO_ISOLATION = IsolationLevelEnum.NO_ISOLATION LOGGER = logging.getLogger(__name__) def is_slice_grouping_enabled() -> bool: - # Temporal hack for OFC'23 multi-demo: - metricsdb_hostname = get_setting('METRICSDB_HOSTNAME', default='') - LOGGER.warning('metricsdb_hostname={:s}'.format(str(metricsdb_hostname))) - if '.qdb-sligrp.' in metricsdb_hostname: return True - # end hack is_enabled = get_setting(SETTING_NAME_SLICE_GROUPING, default=None) if is_enabled is None: return False str_is_enabled = str(is_enabled).upper() -- GitLab From bfa801f7f20cc51f4abed6e019f65530d143cb07 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Mar 2023 08:30:10 +0000 Subject: [PATCH 72/77] Device component - IETF L2VPN driver: - Pre-merge code cleanup --- .../drivers/ietf_l2vpn/IetfL2VpnDriver.py | 12 +- .../service/drivers/ietf_l2vpn/Tools.py | 136 ------------------ 2 files changed, 2 insertions(+), 146 deletions(-) diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py index e08b7625b..96dfd2c15 100644 --- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py +++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py @@ -25,14 +25,6 @@ from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN LOGGER = logging.getLogger(__name__) -def process_connectivity_services(method : str, services : Any) -> Any: - LOGGER.warning('[{:s}][process_connectivity_services] services={:s}'.format(str(method), str(services))) - return services - -def process_connectivity_service(method : str, service : Any) -> Any: - LOGGER.warning('[{:s}][process_connectivity_service] service={:s}'.format(str(method), str(service))) - return service - def service_exists(wim : WimconnectorIETFL2VPN, service_uuid : str) -> bool: try: wim.get_connectivity_service_status(service_uuid) @@ -103,11 +95,11 @@ class IetfL2VpnDriver(_Driver): elif resource_key == RESOURCE_SERVICES: # return all services through reply = self.wim.get_all_active_connectivity_services() - results.extend(process_connectivity_services('GetConfig', reply.json())) + results.extend(reply.json()) else: # assume single-service retrieval reply = self.wim.get_connectivity_service(resource_key) - results.append(process_connectivity_service('GetConfig', reply.json())) + results.append(reply.json()) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unhandled error processing resource_key({:s})'.format(str(resource_key))) results.append((resource_key, e)) diff --git a/src/device/service/drivers/ietf_l2vpn/Tools.py b/src/device/service/drivers/ietf_l2vpn/Tools.py index 8188c77df..45dfa23c9 100644 --- a/src/device/service/drivers/ietf_l2vpn/Tools.py +++ b/src/device/service/drivers/ietf_l2vpn/Tools.py @@ -12,143 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, logging, operator, requests -from requests.auth import HTTPBasicAuth from typing import Dict, Optional -from device.service.driver_api._Driver import RESOURCE_ENDPOINTS - -LOGGER = logging.getLogger(__name__) - -HTTP_OK_CODES = { - 200, # OK - 201, # Created - 202, # Accepted - 204, # No Content -} - -def find_key(resource, key): - return json.loads(resource[1])[key] - - -def config_getter( - root_url : str, resource_key : str, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None -): - url = '{:s}/restconf/data/tapi-common:context'.format(root_url) - result = [] - try: - response = requests.get(url, timeout=timeout, verify=False, auth=auth) - except requests.exceptions.Timeout: - LOGGER.exception('Timeout connecting {:s}'.format(url)) - return result - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Exception retrieving {:s}'.format(resource_key)) - result.append((resource_key, e)) - return result - - try: - context = json.loads(response.content) - except Exception as e: # pylint: disable=broad-except - LOGGER.warning('Unable to decode reply: {:s}'.format(str(response.content))) - result.append((resource_key, e)) - return result - - if resource_key != RESOURCE_ENDPOINTS: return result - - if 'tapi-common:context' in context: - context = context['tapi-common:context'] - elif 'context' in context: - context = context['context'] - - for sip in context['service-interface-point']: - layer_protocol_name = sip.get('layer-protocol-name', '?') - supportable_spectrum = sip.get('tapi-photonic-media:media-channel-service-interface-point-spec', {}) - supportable_spectrum = supportable_spectrum.get('mc-pool', {}) - supportable_spectrum = supportable_spectrum.get('supportable-spectrum', []) - supportable_spectrum = supportable_spectrum[0] if len(supportable_spectrum) == 1 else {} - grid_type = supportable_spectrum.get('frequency-constraint', {}).get('grid-type') - granularity = supportable_spectrum.get('frequency-constraint', {}).get('adjustment-granularity') - direction = sip.get('direction', '?') - endpoint_type = [layer_protocol_name, grid_type, granularity, direction] - str_endpoint_type = ':'.join(filter(lambda i: operator.is_not(i, None), endpoint_type)) - endpoint_url = '/endpoints/endpoint[{:s}]'.format(sip['uuid']) - endpoint_data = {'uuid': sip['uuid'], 'type': str_endpoint_type} - result.append((endpoint_url, endpoint_data)) - - return result - -def create_connectivity_service( - root_url, uuid, input_sip, output_sip, direction, capacity_value, capacity_unit, layer_protocol_name, - layer_protocol_qualifier, - auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None -): - - url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context'.format(root_url) - headers = {'content-type': 'application/json'} - data = { - 'tapi-connectivity:connectivity-service': [ - { - 'uuid': uuid, - 'connectivity-constraint': { - 'requested-capacity': { - 'total-size': { - 'value': capacity_value, - 'unit': capacity_unit - } - }, - 'connectivity-direction': direction - }, - 'end-point': [ - { - 'service-interface-point': { - 'service-interface-point-uuid': input_sip - }, - 'layer-protocol-name': layer_protocol_name, - 'layer-protocol-qualifier': layer_protocol_qualifier, - 'local-id': input_sip - }, - { - 'service-interface-point': { - 'service-interface-point-uuid': output_sip - }, - 'layer-protocol-name': layer_protocol_name, - 'layer-protocol-qualifier': layer_protocol_qualifier, - 'local-id': output_sip - } - ] - } - ] - } - results = [] - try: - LOGGER.info('Connectivity service {:s}: {:s}'.format(str(uuid), str(data))) - response = requests.post( - url=url, data=json.dumps(data), timeout=timeout, headers=headers, verify=False, auth=auth) - LOGGER.info('TAPI response: {:s}'.format(str(response))) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Exception creating ConnectivityService(uuid={:s}, data={:s})'.format(str(uuid), str(data))) - results.append(e) - else: - if response.status_code not in HTTP_OK_CODES: - msg = 'Could not create ConnectivityService(uuid={:s}, data={:s}). status_code={:s} reply={:s}' - LOGGER.error(msg.format(str(uuid), str(data), str(response.status_code), str(response))) - results.append(response.status_code in HTTP_OK_CODES) - return results - -def delete_connectivity_service(root_url, uuid, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None): - url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context/connectivity-service={:s}' - url = url.format(root_url, uuid) - results = [] - try: - response = requests.delete(url=url, timeout=timeout, verify=False, auth=auth) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Exception deleting ConnectivityService(uuid={:s})'.format(str(uuid))) - results.append(e) - else: - if response.status_code not in HTTP_OK_CODES: - msg = 'Could not delete ConnectivityService(uuid={:s}). status_code={:s} reply={:s}' - LOGGER.error(msg.format(str(uuid), str(response.status_code), str(response))) - results.append(response.status_code in HTTP_OK_CODES) - return results def compose_service_endpoint_id(site_id : str, endpoint_id : Dict): device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] -- GitLab From 974acf01665fa16e88dd017c0bd5ab81cc5e7fb2 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Mar 2023 09:25:09 +0000 Subject: [PATCH 73/77] Slice component: - Pre-merge code cleanup --- src/slice/service/slice_grouper/Tools.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/slice/service/slice_grouper/Tools.py b/src/slice/service/slice_grouper/Tools.py index c635ffcf4..ca957f3c7 100644 --- a/src/slice/service/slice_grouper/Tools.py +++ b/src/slice/service/slice_grouper/Tools.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from typing import Dict, List, Optional, Set, Tuple from common.Constants import DEFAULT_CONTEXT_NAME from common.Settings import get_setting @@ -28,8 +27,6 @@ TRUE_VALUES = {'Y', 'YES', 'TRUE', 'T', 'E', 'ENABLE', 'ENABLED'} NO_ISOLATION = IsolationLevelEnum.NO_ISOLATION -LOGGER = logging.getLogger(__name__) - def is_slice_grouping_enabled() -> bool: is_enabled = get_setting(SETTING_NAME_SLICE_GROUPING, default=None) if is_enabled is None: return False -- GitLab From cb1721a57b9f8292d8343cb4a75ada48bdc0086e Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Mar 2023 09:31:31 +0000 Subject: [PATCH 74/77] Tests - Tools - Mock MW SDN Ctrl: - Pre-merge file cleanup --- src/tests/tools/mock_mw_sdn_ctrl/TODO.txt | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/tests/tools/mock_mw_sdn_ctrl/TODO.txt diff --git a/src/tests/tools/mock_mw_sdn_ctrl/TODO.txt b/src/tests/tools/mock_mw_sdn_ctrl/TODO.txt deleted file mode 100644 index db764fcf2..000000000 --- a/src/tests/tools/mock_mw_sdn_ctrl/TODO.txt +++ /dev/null @@ -1,9 +0,0 @@ -extend discovery of mgmt VLANs - ->>> import requests ->>> from requests.auth import HTTPBasicAuth ->>> url = 'https://192.168.27.136:8443/nmswebs/restconf/data/ietf-eth-tran-service:etht-svc' ->>> auth = HTTPBasicAuth('nms5ux', 'nms5ux') ->>> response = requests.get(url, timeout=120, verify=False, auth=auth) ->>> response.content -{"ietf-eth-tran-service:etht-svc":{"etht-svc-instances":[{"te-topology-identifier":{"topology-id":"eth-native-topology","client-id":3373,"provider-id":3373},"etht-svc-name":"MGMT-VLAN-192-168-27-139","admin-status":"ietf-te-types:tunnel-state-up","etht-svc-type":"ietf-eth-tran-types:p2p-svc","etht-svc-end-points":[{"etht-svc-access-points":[{"access-node-id":"192.168.27.139","access-ltp-id":3,"access-point-id":"1"}],"outer-tag":{"vlan-value":100,"tag-type":"ietf-eth-tran-types:classify-c-vlan"},"etht-svc-end-point-name":"192.168.27.139:3","service-classification-type":"ietf-eth-tran-types:port-classification"}],"state":{"operational-state":"ietf-te-types:tunnel-state-up","provisioning-state":"ietf-te-types:lsp-state-up"}},{"te-topology-identifier":{"topology-id":"eth-native-topology","client-id":3373,"provider-id":3373},"etht-svc-name":"MGMT-VLAN-192-168-27-140","admin-status":"ietf-te-types:tunnel-state-up","etht-svc-type":"ietf-eth-tran-types:p2p-svc","etht-svc-end-points":[{"etht-svc-access-points":[{"access-node-id":"192.168.27.140","access-ltp-id":3,"access-point-id":"1"}],"outer-tag":{"vlan-value":100,"tag-type":"ietf-eth-tran-types:classify-c-vlan"},"etht-svc-end-point-name":"192.168.27.140:3","service-classification-type":"ietf-eth-tran-types:vlan-classification"}],"state":{"operational-state":"ietf-te-types:tunnel-state-up","provisioning-state":"ietf-te-types:lsp-state-up"}}],"globals":{}}} -- GitLab From 01e6cdb7d5023c1cc587de9ec1ae2adb741f43cb Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Mar 2023 09:39:43 +0000 Subject: [PATCH 75/77] Show Log Scripts: - Added flags to specify target container to retrieve log --- scripts/show_logs_compute.sh | 2 +- scripts/show_logs_device.sh | 2 +- scripts/show_logs_load_generator.sh | 2 +- scripts/show_logs_monitoring.sh | 2 +- scripts/show_logs_service.sh | 2 +- scripts/show_logs_slice.sh | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/show_logs_compute.sh b/scripts/show_logs_compute.sh index fc992eb43..f0c24b63a 100755 --- a/scripts/show_logs_compute.sh +++ b/scripts/show_logs_compute.sh @@ -24,4 +24,4 @@ export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} # Automated steps start here ######################################################################################################################## -kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/computeservice +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/computeservice -c server diff --git a/scripts/show_logs_device.sh b/scripts/show_logs_device.sh index 6a77c3815..e643f563a 100755 --- a/scripts/show_logs_device.sh +++ b/scripts/show_logs_device.sh @@ -24,4 +24,4 @@ export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} # Automated steps start here ######################################################################################################################## -kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/deviceservice +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/deviceservice -c server diff --git a/scripts/show_logs_load_generator.sh b/scripts/show_logs_load_generator.sh index d0f2527d7..51438f181 100755 --- a/scripts/show_logs_load_generator.sh +++ b/scripts/show_logs_load_generator.sh @@ -24,4 +24,4 @@ export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} # Automated steps start here ######################################################################################################################## -kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/load-generatorservice +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/load-generatorservice -c server diff --git a/scripts/show_logs_monitoring.sh b/scripts/show_logs_monitoring.sh index 1a152a322..61b0b5cc0 100755 --- a/scripts/show_logs_monitoring.sh +++ b/scripts/show_logs_monitoring.sh @@ -24,4 +24,4 @@ export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} # Automated steps start here ######################################################################################################################## -kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/monitoringservice server +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/monitoringservice -c server diff --git a/scripts/show_logs_service.sh b/scripts/show_logs_service.sh index 7ca1c1c2f..cc75e19c6 100755 --- a/scripts/show_logs_service.sh +++ b/scripts/show_logs_service.sh @@ -24,4 +24,4 @@ export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} # Automated steps start here ######################################################################################################################## -kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/serviceservice +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/serviceservice -c server diff --git a/scripts/show_logs_slice.sh b/scripts/show_logs_slice.sh index c71bc92ea..7fa8091cc 100755 --- a/scripts/show_logs_slice.sh +++ b/scripts/show_logs_slice.sh @@ -24,4 +24,4 @@ export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} # Automated steps start here ######################################################################################################################## -kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/sliceservice +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/sliceservice -c server -- GitLab From d9cc9e2116be76bdb48bb69b2e342b4f985041f7 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Mar 2023 09:40:30 +0000 Subject: [PATCH 76/77] PathComp component - FrontEnd: - Moved compute subservices test to tests folder --- .../frontend/tests/test_pathcomp}/__init__.py | 0 .../frontend/tests/test_pathcomp}/__main__.py | 2 +- .../frontend/tests/test_pathcomp}/data.py | 0 test_pathcomp/ComputeSubServices.py | 135 ------------------ test_pathcomp/ResourceGroups.py | 93 ------------ test_pathcomp/ServiceTypes.py | 53 ------- test_pathcomp/old_ComputeSubServices.py | 119 --------------- 7 files changed, 1 insertion(+), 401 deletions(-) rename {test_pathcomp => src/pathcomp/frontend/tests/test_pathcomp}/__init__.py (100%) rename {test_pathcomp => src/pathcomp/frontend/tests/test_pathcomp}/__main__.py (91%) rename {test_pathcomp => src/pathcomp/frontend/tests/test_pathcomp}/data.py (100%) delete mode 100644 test_pathcomp/ComputeSubServices.py delete mode 100644 test_pathcomp/ResourceGroups.py delete mode 100644 test_pathcomp/ServiceTypes.py delete mode 100644 test_pathcomp/old_ComputeSubServices.py diff --git a/test_pathcomp/__init__.py b/src/pathcomp/frontend/tests/test_pathcomp/__init__.py similarity index 100% rename from test_pathcomp/__init__.py rename to src/pathcomp/frontend/tests/test_pathcomp/__init__.py diff --git a/test_pathcomp/__main__.py b/src/pathcomp/frontend/tests/test_pathcomp/__main__.py similarity index 91% rename from test_pathcomp/__main__.py rename to src/pathcomp/frontend/tests/test_pathcomp/__main__.py index 6af584fe9..ba1cc4a2c 100644 --- a/test_pathcomp/__main__.py +++ b/src/pathcomp/frontend/tests/test_pathcomp/__main__.py @@ -15,8 +15,8 @@ import logging, sys from common.proto.context_pb2 import ServiceTypeEnum +from pathcomp.frontend.service.algorithms.tools.ComputeSubServices import convert_explicit_path_hops_to_connections from .data import path_hops, device_dict -from .ComputeSubServices import convert_explicit_path_hops_to_connections logging.basicConfig(level=logging.DEBUG) LOGGER = logging.getLogger(__name__) diff --git a/test_pathcomp/data.py b/src/pathcomp/frontend/tests/test_pathcomp/data.py similarity index 100% rename from test_pathcomp/data.py rename to src/pathcomp/frontend/tests/test_pathcomp/data.py diff --git a/test_pathcomp/ComputeSubServices.py b/test_pathcomp/ComputeSubServices.py deleted file mode 100644 index 40cb08576..000000000 --- a/test_pathcomp/ComputeSubServices.py +++ /dev/null @@ -1,135 +0,0 @@ -# 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. - -# Convert the path defined as explicit hops with ingress and egress endpoints per device into a set of connections and -# compute the dependencies among them. -# -# Example: -# o-- int DC1 eth1 -- 10/1 CS1 1/2 -- 1/2 R2 2/1 -- a7.. OLS 60.. -- 2/1 R3 1/1 -- 1/1 CS2 10/1 -- eth1 DC2 int --o -# APP PKT PKT CTRL PKT PKT APP -# -# path_hops = [ -# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, -# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, -# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, -# {'device': 'TN-OLS', 'ingress_ep': 'a7a80b23a703', 'egress_ep': '60519106029e'}, -# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, -# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'}, -# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} -# ] -# -# connections=[ -# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, [ -# {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'} -# ], []), -# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.SERVICETYPE_L2NM, [ -# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, -# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, -# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, -# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'} -# ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]), -# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), ServiceTypeEnum.SERVICETYPE_L2NM, [ -# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, -# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} -# ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) -# ] - -import logging, queue, uuid -from typing import Dict, List, Optional, Tuple -from common.DeviceTypes import DeviceTypeEnum -from common.proto.context_pb2 import Device, ServiceTypeEnum -from .ResourceGroups import IGNORED_DEVICE_TYPES, get_resource_classification -from .ServiceTypes import get_service_type - -LOGGER = logging.getLogger(__name__) - -def convert_explicit_path_hops_to_connections( - path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], - main_service_uuid : str, main_service_type : ServiceTypeEnum -) -> List[Tuple[str, int, List[str], List[str]]]: - - LOGGER.debug('path_hops={:s}'.format(str(path_hops))) - - connection_stack = queue.LifoQueue() - connections : List[Tuple[str, int, List[str], List[str]]] = list() - prv_device_uuid = None - prv_res_class : Tuple[Optional[int], Optional[DeviceTypeEnum], Optional[str]] = None, None, None - - for path_hop in path_hops: - device_uuid = path_hop['device'] - if prv_device_uuid == device_uuid: continue - device_tuple = device_dict.get(device_uuid) - if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) - _,grpc_device = device_tuple - - res_class = get_resource_classification(grpc_device, device_dict) - if res_class[1] in IGNORED_DEVICE_TYPES: continue - - if prv_res_class[0] is None: - # path ingress - connection_stack.put((main_service_uuid, main_service_type, [path_hop], [])) - elif prv_res_class[0] > res_class[0]: - # create underlying connection - connection_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] - service_type = get_service_type(res_class[1], prv_service_type) - connection_stack.put((connection_uuid, service_type, [path_hop], [])) - elif prv_res_class[0] == res_class[0]: - # same resource group kind - if prv_res_class[1] == res_class[1] and prv_res_class[2] == res_class[2]: - # same device type and device controller: connection continues - connection_stack.queue[-1][2].append(path_hop) - else: - # different device type or device controller: chain connections - connection = connection_stack.get() - connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) - - connection_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] - service_type = get_service_type(res_class[1], prv_service_type) - connection_stack.put((connection_uuid, service_type, [path_hop], [])) - elif prv_res_class[0] < res_class[0]: - # underlying connection ended - connection = connection_stack.get() - connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) - connection_stack.queue[-1][2].append(path_hop) - else: - raise Exception('Uncontrolled condition') - - prv_device_uuid = device_uuid - prv_res_class = res_class - - # path egress - connections.append(connection_stack.get()) - LOGGER.debug('connections={:s}'.format(str(connections))) - assert connection_stack.empty() - return connections - -def convert_explicit_path_hops_to_plain_connection( - path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum -) -> List[Tuple[str, int, List[str], List[str]]]: - - connection : Tuple[str, int, List[str], List[str]] = \ - (main_service_uuid, main_service_type, [], []) - - prv_device_uuid = None - for path_hop in path_hops: - device_uuid = path_hop['device'] - if prv_device_uuid == device_uuid: continue - connection[2].append(path_hop) - prv_device_uuid = device_uuid - - return [connection] diff --git a/test_pathcomp/ResourceGroups.py b/test_pathcomp/ResourceGroups.py deleted file mode 100644 index 17991ee33..000000000 --- a/test_pathcomp/ResourceGroups.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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 -from typing import Dict, Optional, Tuple -from common.DeviceTypes import DeviceTypeEnum -from common.proto.context_pb2 import Device -from common.tools.grpc.Tools import grpc_message_to_json_string - -DEVICE_TYPE_TO_DEEPNESS = { - DeviceTypeEnum.EMULATED_DATACENTER.value : 90, - DeviceTypeEnum.DATACENTER.value : 90, - DeviceTypeEnum.NETWORK.value : 90, - - DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value : 80, - DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : 70, - DeviceTypeEnum.PACKET_ROUTER.value : 70, - - DeviceTypeEnum.EMULATED_PACKET_SWITCH.value : 60, - DeviceTypeEnum.PACKET_SWITCH.value : 60, - DeviceTypeEnum.EMULATED_P4_SWITCH.value : 60, - DeviceTypeEnum.P4_SWITCH.value : 60, - - DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM.value : 40, - DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM.value : 40, - - DeviceTypeEnum.EMULATED_XR_CONSTELLATION.value : 40, - DeviceTypeEnum.XR_CONSTELLATION.value : 40, - - DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value : 30, - DeviceTypeEnum.OPEN_LINE_SYSTEM.value : 30, - - DeviceTypeEnum.EMULATED_PACKET_RADIO_ROUTER.value : 10, - DeviceTypeEnum.PACKET_RADIO_ROUTER.value : 10, - DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER.value : 10, - DeviceTypeEnum.OPTICAL_TRANSPONDER.value : 10, - DeviceTypeEnum.EMULATED_OPTICAL_ROADM.value : 10, - DeviceTypeEnum.OPTICAL_ROADM.value : 10, - - DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER.value : 0, -} - -IGNORED_DEVICE_TYPES = {DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER} - -def get_device_controller_uuid( - device : Device -) -> Optional[str]: - 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 - device_controller_id = json.loads(config_rule.custom.resource_value) - return device_controller_id['uuid'] - return None - -def _map_device_type(device : Device) -> DeviceTypeEnum: - device_type = DeviceTypeEnum._value2member_map_.get(device.device_type) # pylint: disable=no-member - if device_type is None: - MSG = 'Unsupported DeviceType({:s}) for Device({:s})' - raise Exception(MSG.format(str(device.device_type), grpc_message_to_json_string(device))) - return device_type - -def _map_resource_to_deepness(device_type : DeviceTypeEnum) -> int: - deepness = DEVICE_TYPE_TO_DEEPNESS.get(device_type.value) - if deepness is None: raise Exception('Unsupported DeviceType({:s})'.format(str(device_type.value))) - return deepness - -def get_device_type( - device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_controller_uuid : Optional[str] -) -> DeviceTypeEnum: - if device_controller_uuid is None: return _map_device_type(device) - device_controller_tuple = device_dict.get(device_controller_uuid) - if device_controller_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_controller_uuid))) - _,device = device_controller_tuple - return _map_device_type(device) - -def get_resource_classification( - device : Device, device_dict : Dict[str, Tuple[Dict, Device]] -) -> Tuple[int, DeviceTypeEnum, Optional[str]]: - device_controller_uuid = get_device_controller_uuid(device) - device_type = get_device_type(device, device_dict, device_controller_uuid) - resource_deepness = _map_resource_to_deepness(device_type) - return resource_deepness, device_type, device_controller_uuid diff --git a/test_pathcomp/ServiceTypes.py b/test_pathcomp/ServiceTypes.py deleted file mode 100644 index 463b8039b..000000000 --- a/test_pathcomp/ServiceTypes.py +++ /dev/null @@ -1,53 +0,0 @@ -# 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. - - -from common.DeviceTypes import DeviceTypeEnum -from common.proto.context_pb2 import ServiceTypeEnum - -PACKET_DEVICE_TYPES = { - DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, - DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER, - DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH, -} - -L2_DEVICE_TYPES = { - DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH, - DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM, - DeviceTypeEnum.PACKET_RADIO_ROUTER, DeviceTypeEnum.EMULATED_PACKET_RADIO_ROUTER, - DeviceTypeEnum.P4_SWITCH, DeviceTypeEnum.EMULATED_P4_SWITCH, -} - -OPTICAL_DEVICE_TYPES = { - DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM, - DeviceTypeEnum.XR_CONSTELLATION, DeviceTypeEnum.EMULATED_XR_CONSTELLATION, - DeviceTypeEnum.OPTICAL_ROADM, DeviceTypeEnum.EMULATED_OPTICAL_ROADM, - DeviceTypeEnum.OPTICAL_TRANSPONDER, DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER, -} - -SERVICE_TYPE_L2NM = {ServiceTypeEnum.SERVICETYPE_L2NM} -SERVICE_TYPE_L3NM = {ServiceTypeEnum.SERVICETYPE_L3NM} -SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} -SERVICE_TYPE_TAPI = {ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE} - -def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: - if device_type in PACKET_DEVICE_TYPES and prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type - if device_type in L2_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_L2NM - if device_type in OPTICAL_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE - - str_fields = ', '.join([ - 'device_type={:s}'.format(str(device_type)), - 'prv_service_type={:s}'.format(str(prv_service_type)), - ]) - raise Exception('Undefined Service Type for ({:s})'.format(str_fields)) diff --git a/test_pathcomp/old_ComputeSubServices.py b/test_pathcomp/old_ComputeSubServices.py deleted file mode 100644 index c1d3115d4..000000000 --- a/test_pathcomp/old_ComputeSubServices.py +++ /dev/null @@ -1,119 +0,0 @@ -# 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. - -# Convert the path defined as explicit hops with ingress and egress endpoints per device into a set of connections and -# compute the dependencies among them. -# -# Example: -# o-- int DC1 eth1 -- 10/1 CS1 1/2 -- 1/2 R2 2/1 -- a7.. OLS 60.. -- 2/1 R3 1/1 -- 1/1 CS2 10/1 -- eth1 DC2 int --o -# APP PKT PKT CTRL PKT PKT APP -# -# path_hops = [ -# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, -# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, -# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, -# {'device': 'TN-OLS', 'ingress_ep': 'a7a80b23a703', 'egress_ep': '60519106029e'}, -# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, -# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'}, -# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} -# ] -# -# connections=[ -# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, [ -# {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'} -# ], []), -# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.SERVICETYPE_L2NM, [ -# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, -# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, -# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, -# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'} -# ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]), -# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), ServiceTypeEnum.SERVICETYPE_L2NM, [ -# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, -# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} -# ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) -# ] - -import enum, json, logging, queue, uuid -from typing import Dict, List, Optional, Tuple -from common.DeviceTypes import DeviceTypeEnum -from common.proto.context_pb2 import Device, ServiceTypeEnum -from test_pathcomp.ResourceGroups import ResourceGroupKindEnum - - - -from .ConstantsMappings import DEVICE_TYPE_TO_LAYER, DeviceLayerEnum - -def convert_explicit_path_hops_to_connections( - path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], - main_service_uuid : str, main_service_type : ServiceTypeEnum -) -> List[Tuple[str, int, List[str], List[str]]]: - - connection_stack = queue.LifoQueue() - connections : List[Tuple[str, int, List[str], List[str]]] = list() - prv_device_uuid = None - prv_resource_group : Optional[Tuple[ResourceGroupKindEnum, DeviceTypeEnum, str]] = None - - for path_hop in path_hops: - device_uuid = path_hop['device'] - if prv_device_uuid == device_uuid: continue - device_tuple = device_dict.get(device_uuid) - if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) - json_device,_ = device_tuple - device_type = json_device['device_type'] - resource_group = DEVICE_TYPE_TO_LAYER.get(device_type) - if resource_group is None: raise Exception('Undefined Layer for DeviceType({:s})'.format(str(device_type))) - - if prv_resource_group is None: - # path ingress - connection_stack.put((main_service_uuid, main_service_type, [path_hop], [])) - elif prv_resource_group > resource_group: - # underlying connection begins - connection_uuid = str(uuid.uuid4()) - connection_stack.put((connection_uuid, resource_group, [path_hop], [])) - elif prv_resource_group == resource_group: - # same connection continues - connection_stack.queue[-1][2].append(path_hop) - elif prv_resource_group < resource_group: - # underlying connection ended - connection = connection_stack.get() - connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) - connection_stack.queue[-1][2].append(path_hop) - else: - raise Exception('Uncontrolled condition') - - prv_resource_group = resource_group - prv_device_uuid = device_uuid - - # path egress - connections.append(connection_stack.get()) - assert connection_stack.empty() - return connections - -def convert_explicit_path_hops_to_plain_connection( - path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum -) -> List[Tuple[str, int, List[str], List[str]]]: - - connection : Tuple[str, int, List[str], List[str]] = \ - (main_service_uuid, main_service_type, [], []) - - prv_device_uuid = None - for path_hop in path_hops: - device_uuid = path_hop['device'] - if prv_device_uuid == device_uuid: continue - connection[2].append(path_hop) - prv_device_uuid = device_uuid - - return [connection] -- GitLab From b9d1b959f2506a130515093df7eb694d518825ef Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 25 Mar 2023 09:47:03 +0000 Subject: [PATCH 77/77] Misc Scripts: - Remove unneeded scripts --- map_names.sh | 59 ---------------------------------------------------- 1 file changed, 59 deletions(-) delete mode 100755 map_names.sh diff --git a/map_names.sh b/map_names.sh deleted file mode 100755 index e3bcc5640..000000000 --- a/map_names.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/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. - - -sed -i 's#0dff8c06-873b-5799-ac54-c0452252bae1#R3#g' service.log -sed -i 's#1102e0b5-824b-57eb-86a1-d247e2deaf68#PE4#g' service.log -sed -i 's#29d766ca-d222-5257-bab3-6a060719270a#PE2#g' service.log -sed -i 's#68741528-2e94-5274-ab3c-fddcd8dc05ef#R1#g' service.log -sed -i 's#69a3a3f0-5237-5f9e-bc96-d450d0c6c02a#PE3#g' service.log -sed -i 's#6ab8fa38-ec20-5c32-8d9b-4fd86fce2555#OLS#g' service.log -sed -i 's#7faa13eb-903d-58f5-936b-1a1174fe98fd#PE1#g' service.log -sed -i 's#800d5bd4-a7a3-5a66-82ab-d399767ca3d8#DC2#g' service.log -sed -i 's#93c69975-3870-5892-955b-da0ff36eb884#MW1-2#g' service.log -sed -i 's#f185c8cf-37e5-51cc-9f71-076b775a574d#MW3-4#g' service.log -sed -i 's#a23cdc36-074d-5423-8abd-4a167a6e6fbc#TFS#g' service.log -sed -i 's#c944aaeb-bbdf-5f2d-b31c-8cc8903045b6#R2#g' service.log -sed -i 's#cda90d2f-e7b0-5837-8f2e-2fb29dd9b367#DC1#g' service.log -sed -i 's#79f4184c-d375-5e2c-a3df-1ae64537c95c#1/1#g' service.log -sed -i 's#93c853c2-429c-52e8-9ba9-454fcedb9090#1/2#g' service.log -sed -i 's#1fe2ee1a-fe92-57c9-afd9-260e6f0ecc54#mgmt#g' service.log -sed -i 's#cd378805-d73e-5681-8514-1d33e656c0e9#1/1#g' service.log -sed -i 's#e502e939-3ab8-5fee-8277-7fd1c1c0fa93#1/2#g' service.log -sed -i 's#780f6929-a863-5e6a-a046-3dac2e24bf58#1/1#g' service.log -sed -i 's#f1082088-a304-587b-a230-b8ce10e5a148#mgmt#g' service.log -sed -i 's#ffdfb0ce-1684-5d39-bad3-9ff1eb4ffbf8#1/2#g' service.log -sed -i 's#268b735d-c861-5319-88a4-2ea498f96a04#1/1#g' service.log -sed -i 's#62c0cba1-9ee8-5db5-82da-ce96d7e0f39f#1/3#g' service.log -sed -i 's#7d1bf45c-5ab2-525e-87a4-c0ddcd5c18e4#1/2#g' service.log -sed -i 's#2b11934b-dfd7-5267-87b9-7306a24e0182#1/1#g' service.log -sed -i 's#ca92338e-2038-5d74-8ef1-2b20a234a8b9#1/2#g' service.log -sed -i 's#f8955e74-4e93-5d43-a968-9626ee5c9b53#mgmt#g' service.log -sed -i 's#2b75d88d-095f-5752-a10a-1ff69df8008d#1/2#g' service.log -sed -i 's#bf0d75db-acf8-53cb-b6db-32d9dc0878c4#mgmt#g' service.log -sed -i 's#eeade85a-03df-55d2-bfc2-2af7267bbcf3#1/1#g' service.log -sed -i 's#06bb0b92-8783-5599-aa20-15bfbe241348#eth1#g' service.log -sed -i 's#6a6859c3-4a13-513c-a7dd-490c8b2931b1#eth2#g' service.log -sed -i 's#97f57787-cfec-5315-9718-7e850905f11a#int#g' service.log -sed -i 's#01251c49-6d35-5c70-8480-576991321d15#172.18.0.1:1#g' service.log -sed -i 's#023ac3a2-35bb-53cf-9173-a0b4f979af58#172.18.0.2:1#g' service.log -sed -i 's#1b4e8958-f1c2-5ef7-9297-f4e9756275f9#172.18.0.3:1#g' service.log -sed -i 's#f6cdc04b-1a12-537f-b0cc-daefdb89b61c#172.18.0.4:1#g' service.log -sed -i 's#85bb83b0-8d96-5e76-9959-97c35036d4f9#mgmt#g' service.log -sed -i 's#313fb1ed-5dee-5e49-884a-cbb3b1e00073#1/2#g' service.log -sed -i 's#79699f56-df25-5188-a389-04606c24fbfc#1/1#g' service.log -sed -i 's#37ab67ef-0064-54e3-ae9b-d40100953834#int#g' service.log -sed -i 's#55e88b6b-ccf7-538f-a062-a41219292ea1#eth1#g' service.log -sed -i 's#68b5972b-3630-53a8-ba9b-cc54dd31f8b8#eth2#g' service.log -- GitLab