diff --git a/proto/context.proto b/proto/context.proto index 4d61572df1db6c95b64e9ce1cbfdd9e0db111078..01e096233e364be8ad4e3810e7619e8f522e66e6 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -223,6 +223,9 @@ enum DeviceDriverEnum { DEVICEDRIVER_IETF_ACTN = 10; DEVICEDRIVER_OC = 11; DEVICEDRIVER_QKD = 12; + DEVICEDRIVER_IETF_L3VPN = 13; + DEVICEDRIVER_IETF_SLICE = 14; + DEVICEDRIVER_NCE = 15; } enum DeviceOperationalStatusEnum { diff --git a/scripts/run_tests_locally-nbi-ietf-slice.sh b/scripts/run_tests_locally-nbi-ietf-slice.sh new file mode 100755 index 0000000000000000000000000000000000000000..bf53f18b9a37f9248b072ae2c699b5874fa2c869 --- /dev/null +++ b/scripts/run_tests_locally-nbi-ietf-slice.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc + +# Run unitary tests and analyze coverage of code at same time +# helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0 +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + nbi/tests/test_slice_2.py diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py index eb315352b47bbe501f66868c0181a0d34cd6cfed..9a982d1eb71e8b139d2a86fe1a774154239c7147 100644 --- a/src/common/DeviceTypes.py +++ b/src/common/DeviceTypes.py @@ -38,6 +38,7 @@ class DeviceTypeEnum(Enum): CLIENT = 'client' DATACENTER = 'datacenter' IP_SDN_CONTROLLER = 'ip-sdn-controller' + NCE = 'nce' MICROWAVE_RADIO_SYSTEM = 'microwave-radio-system' OPEN_LINE_SYSTEM = 'open-line-system' OPTICAL_ROADM = 'optical-roadm' @@ -52,3 +53,4 @@ class DeviceTypeEnum(Enum): # ETSI TeraFlowSDN controller TERAFLOWSDN_CONTROLLER = 'teraflowsdn' + IETF_SLICE = 'ietf-slice' diff --git a/src/common/tools/context_queries/Slice.py b/src/common/tools/context_queries/Slice.py index c826c59ce79adbe1e399d674ffd46a421ddd9b5e..12bd8c03c68ab0baea5b4db91b7468e2eae7f1a9 100644 --- a/src/common/tools/context_queries/Slice.py +++ b/src/common/tools/context_queries/Slice.py @@ -12,12 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -import grpc, logging -from typing import Optional +import logging +import grpc +from typing import Optional, Tuple, Union +from uuid import UUID, uuid5 from common.Constants import DEFAULT_CONTEXT_NAME -from common.proto.context_pb2 import Slice, SliceFilter, SliceId +from common.method_wrappers.ServiceExceptions import InvalidArgumentsException +from common.proto.context_pb2 import ContextId, Slice, SliceFilter, SliceId +from common.method_wrappers.ServiceExceptions import InvalidArgumentsException +from common.proto.context_pb2 import ContextId, Slice, SliceFilter, SliceId from context.client.ContextClient import ContextClient + +NAMESPACE_TFS = UUID("200e3a1f-2223-534f-a100-758e29c37f40") + LOGGER = logging.getLogger(__name__) def get_slice_by_id( @@ -59,3 +67,96 @@ def get_slice_by_uuid( context_client, slice_id, rw_copy=rw_copy, include_endpoint_ids=include_endpoint_ids, include_constraints=include_constraints, include_service_ids=include_service_ids, include_subslice_ids=include_subslice_ids, include_config_rules=include_config_rules) + + +def get_uuid_from_string( + str_uuid_or_name: Union[str, UUID], prefix_for_name: Optional[str] = None +) -> str: + # if UUID given, assume it is already a valid UUID + if isinstance(str_uuid_or_name, UUID): + return str_uuid_or_name + if not isinstance(str_uuid_or_name, str): + MSG = "Parameter({:s}) cannot be used to produce a UUID" + raise Exception(MSG.format(str(repr(str_uuid_or_name)))) + try: + # try to parse as UUID + return str(UUID(str_uuid_or_name)) + except: # pylint: disable=bare-except + # produce a UUID within TFS namespace from parameter + if prefix_for_name is not None: + str_uuid_or_name = "{:s}/{:s}".format(prefix_for_name, str_uuid_or_name) + return str(uuid5(NAMESPACE_TFS, str_uuid_or_name)) + + +def context_get_uuid( + context_id: ContextId, + context_name: str = "", + allow_random: bool = False, + allow_default: bool = False, +) -> str: + context_uuid = context_id.context_uuid.uuid + + if len(context_uuid) > 0: + return get_uuid_from_string(context_uuid) + if len(context_name) > 0: + return get_uuid_from_string(context_name) + if allow_default: + return get_uuid_from_string(DEFAULT_CONTEXT_NAME) + + raise InvalidArgumentsException( + [ + ("context_id.context_uuid.uuid", context_uuid), + ("name", context_name), + ], + extra_details=["At least one is required to produce a Context UUID"], + ) + + +def slice_get_uuid(slice_id: SliceId) -> Tuple[str, str]: + context_uuid = context_get_uuid(slice_id.context_id, allow_random=False) + raw_slice_uuid = slice_id.slice_uuid.uuid + + if len(raw_slice_uuid) > 0: + return context_uuid, get_uuid_from_string( + raw_slice_uuid, prefix_for_name=context_uuid + ) + + raise InvalidArgumentsException( + [ + ("slice_id.slice_uuid.uuid", raw_slice_uuid), + ], + extra_details=["At least one is required to produce a Slice UUID"], + ) + +def get_slice_by_defualt_id( + context_client : ContextClient, default_slice_id : SliceId, context_uuid : str = DEFAULT_CONTEXT_NAME, + rw_copy : bool = False, include_endpoint_ids : bool = True, include_constraints : bool = True, + include_service_ids : bool = True, include_subslice_ids : bool = True, include_config_rules : bool = True +) -> Optional[Slice]: + context_uuid, slice_uuid = slice_get_uuid(default_slice_id) + LOGGER.debug(f'P60: {context_uuid} {slice_uuid}') + slice_id = SliceId() + slice_id.context_id.context_uuid.uuid = context_uuid # pylint: disable=no-member + slice_id.slice_uuid.uuid = slice_uuid # pylint: disable=no-member + return get_slice_by_id( + context_client, slice_id, rw_copy=rw_copy, include_endpoint_ids=include_endpoint_ids, + include_constraints=include_constraints, include_service_ids=include_service_ids, + include_subslice_ids=include_subslice_ids, include_config_rules=include_config_rules) + + +def get_slice_by_defualt_name( + context_client : ContextClient, slice_name : str, context_uuid : str = DEFAULT_CONTEXT_NAME, + rw_copy : bool = False, include_endpoint_ids : bool = True, include_constraints : bool = True, + include_service_ids : bool = True, include_subslice_ids : bool = True, include_config_rules : bool = True +) -> Optional[Slice]: + default_slice_id = SliceId() + default_slice_id.context_id.context_uuid.uuid = context_uuid # pylint: disable=no-member + default_slice_id.slice_uuid.uuid = slice_name # pylint: disable=no-member + context_uuid, slice_uuid = slice_get_uuid(default_slice_id) + slice_id = SliceId() + slice_id.context_id.context_uuid.uuid = context_uuid # pylint: disable=no-member + slice_id.slice_uuid.uuid = slice_uuid # pylint: disable=no-member + return get_slice_by_id( + context_client, slice_id, rw_copy=rw_copy, include_endpoint_ids=include_endpoint_ids, + include_constraints=include_constraints, include_service_ids=include_service_ids, + include_subslice_ids=include_subslice_ids, include_config_rules=include_config_rules) diff --git a/src/common/tools/descriptor/Tools.py b/src/common/tools/descriptor/Tools.py index c8807cef0d1357c732d34b11580d1f73e157501a..2ecd38ae146f5c5e3a04a84ad3bb05385554f659 100644 --- a/src/common/tools/descriptor/Tools.py +++ b/src/common/tools/descriptor/Tools.py @@ -115,6 +115,8 @@ CONTROLLER_DEVICE_TYPES = { DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM.value, DeviceTypeEnum.OPEN_LINE_SYSTEM.value, DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value, + DeviceTypeEnum.IETF_SLICE.value, + DeviceTypeEnum.NCE.value, } def split_controllers_and_network_devices(devices : List[Dict]) -> Tuple[List[Dict], List[Dict]]: diff --git a/src/common/tools/grpc/ConfigRules.py b/src/common/tools/grpc/ConfigRules.py index c8919273e65c6f6edb0f3e0ca6f060a5471e8f0e..0cf5165e9a15e1bcbaa4067db1e866ab01fbe33c 100644 --- a/src/common/tools/grpc/ConfigRules.py +++ b/src/common/tools/grpc/ConfigRules.py @@ -54,14 +54,13 @@ def update_config_rule_custom( config_rule.custom.resource_value = json.dumps(json_resource_value, sort_keys=True) -def copy_config_rules(source_config_rules, target_config_rules): +def copy_config_rules(source_config_rules, target_config_rules, raise_if_differs = True): for source_config_rule in source_config_rules: config_rule_kind = source_config_rule.WhichOneof('config_rule') if config_rule_kind == 'custom': custom = source_config_rule.custom resource_key = custom.resource_key resource_value = json.loads(custom.resource_value) - raise_if_differs = True fields = {name:(value, raise_if_differs) for name,value in resource_value.items()} update_config_rule_custom(target_config_rules, resource_key, fields) diff --git a/src/context/service/database/models/enums/DeviceDriver.py b/src/context/service/database/models/enums/DeviceDriver.py index 5342f788a7b273aa7f6ae3c5779774165cd852bc..fe0d83fb1886a42526b1c71304b7e3ecc2b0b7d7 100644 --- a/src/context/service/database/models/enums/DeviceDriver.py +++ b/src/context/service/database/models/enums/DeviceDriver.py @@ -33,6 +33,9 @@ class ORM_DeviceDriverEnum(enum.Enum): GNMI_OPENCONFIG = DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG OPTICAL_TFS = DeviceDriverEnum.DEVICEDRIVER_OPTICAL_TFS IETF_ACTN = DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN + IETF_L3VPN = DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN + NCE = DeviceDriverEnum.DEVICEDRIVER_NCE + IETF_SLICE = DeviceDriverEnum.DEVICEDRIVER_IETF_SLICE OC = DeviceDriverEnum.DEVICEDRIVER_OC QKD = DeviceDriverEnum.DEVICEDRIVER_QKD diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py index b99ee50ca8319ab96f9062a3c58c356fa2ae7ec7..e3102cdf523a4e0b551873bb8f0c423db00aebf0 100644 --- a/src/device/service/drivers/__init__.py +++ b/src/device/service/drivers/__init__.py @@ -81,6 +81,16 @@ DRIVERS.append( } ])) + +from .ietf_l3vpn.driver import IetfL3VpnDriver # pylint: disable=wrong-import-position +DRIVERS.append( + (IetfL3VpnDriver, [ + { + FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN, + } + ])) + from .ietf_actn.IetfActnDriver import IetfActnDriver # pylint: disable=wrong-import-position DRIVERS.append( (IetfActnDriver, [ @@ -90,6 +100,24 @@ DRIVERS.append( } ])) +from .ietf_slice.driver import IetfSliceDriver # pylint: disable=wrong-import-position +DRIVERS.append( + (IetfSliceDriver, [ + { + FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.IETF_SLICE, + FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_SLICE, + } + ])) + +from .nce.driver import NCEDriver # pylint: disable=wrong-import-position +DRIVERS.append( + (NCEDriver, [ + { + FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.NCE, + FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_NCE, + } + ])) + if LOAD_ALL_DEVICE_DRIVERS: from .openconfig.OpenConfigDriver import OpenConfigDriver # pylint: disable=wrong-import-position DRIVERS.append( diff --git a/src/device/service/drivers/emulated/Tools.py b/src/device/service/drivers/emulated/Tools.py index 9f2a105c0d9735f486f41fab5bc3069ec9327f65..2a6f9c95d351f447f7368584d0ad07889eaecd7c 100644 --- a/src/device/service/drivers/emulated/Tools.py +++ b/src/device/service/drivers/emulated/Tools.py @@ -82,6 +82,26 @@ def compose_resource_endpoint(endpoint_data : Dict[str, Any]) -> Optional[Tuple[ if 'location' in endpoint_data: endpoint_resource_value['location'] = endpoint_data['location'] + + if "site_location" in endpoint_data: + endpoint_resource_value["site_location"] = endpoint_data["site_location"] + + if "ce-ip" in endpoint_data: + endpoint_resource_value["ce-ip"] = endpoint_data["ce-ip"] + + if "address_ip" in endpoint_data: + endpoint_resource_value["address_ip"] = endpoint_data["address_ip"] + + if "address_prefix" in endpoint_data: + endpoint_resource_value["address_prefix"] = endpoint_data["address_prefix"] + + if "mtu" in endpoint_data: + endpoint_resource_value["mtu"] = endpoint_data["mtu"] + + if "ipv4_lan_prefixes" in endpoint_data: + endpoint_resource_value["ipv4_lan_prefixes"] = endpoint_data[ + "ipv4_lan_prefixes" + ] return endpoint_resource_key, endpoint_resource_value except: # pylint: disable=bare-except diff --git a/src/device/service/drivers/ietf_l3vpn/Constants.py b/src/device/service/drivers/ietf_l3vpn/Constants.py new file mode 100644 index 0000000000000000000000000000000000000000..df66eb16b3d78c1b388a086011ed6f6b75b8099f --- /dev/null +++ b/src/device/service/drivers/ietf_l3vpn/Constants.py @@ -0,0 +1,25 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 device.service.driver_api._Driver import ( + RESOURCE_ENDPOINTS, + RESOURCE_INTERFACES, + RESOURCE_NETWORK_INSTANCES, +) + +SPECIAL_RESOURCE_MAPPINGS = { + RESOURCE_ENDPOINTS: "/endpoints", + RESOURCE_INTERFACES: "/interfaces", + RESOURCE_NETWORK_INSTANCES: "/net-instances", +} diff --git a/src/device/service/drivers/ietf_l3vpn/TfsApiClient.py b/src/device/service/drivers/ietf_l3vpn/TfsApiClient.py new file mode 100644 index 0000000000000000000000000000000000000000..1ca965f8777aa23287ad379c8ac2cd0d92d9c28f --- /dev/null +++ b/src/device/service/drivers/ietf_l3vpn/TfsApiClient.py @@ -0,0 +1,187 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Dict, List, Optional + +import requests +from requests.auth import HTTPBasicAuth + +from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum + +GET_DEVICES_URL = "{:s}://{:s}:{:d}/tfs-api/devices" +GET_LINKS_URL = "{:s}://{:s}:{:d}/tfs-api/links" +L3VPN_URL = "{:s}://{:s}:{:d}/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" +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_532": 5, + "DEVICEDRIVER_XR": 6, + "DEVICEDRIVER_IETF_L2VPN": 7, + "DEVICEDRIVER_GNMI_OPENCONFIG": 8, + "DEVICEDRIVER_OPTICAL_TFS": 9, + "DEVICEDRIVER_IETF_ACTN": 10, + "DEVICEDRIVER_OC": 11, +} + +MSG_ERROR = "Could not retrieve devices in remote TeraFlowSDN instance({:s}). status_code={:s} reply={:s}" + +LOGGER = logging.getLogger(__name__) + + +class TfsApiClient: + def __init__( + self, + address: str, + port: int, + scheme: str = "http", + username: Optional[str] = None, + password: Optional[str] = None, + ) -> None: + self._devices_url = GET_DEVICES_URL.format(scheme, address, port) + self._links_url = GET_LINKS_URL.format(scheme, address, port) + self._l3vpn_url = L3VPN_URL.format(scheme, address, port) + self._auth = None + # ( + # HTTPBasicAuth(username, password) + # if username is not None and password is not None + # else None + # ) + + def get_devices_endpoints( + self, import_topology: ImportTopologyEnum = ImportTopologyEnum.DEVICES + ) -> List[Dict]: + LOGGER.debug("[get_devices_endpoints] begin") + LOGGER.debug( + "[get_devices_endpoints] import_topology={:s}".format(str(import_topology)) + ) + + reply = requests.get(self._devices_url, timeout=TIMEOUT, auth=self._auth) + if reply.status_code not in HTTP_OK_CODES: + msg = MSG_ERROR.format( + str(self._devices_url), str(reply.status_code), str(reply) + ) + LOGGER.error(msg) + raise Exception(msg) + + if import_topology == ImportTopologyEnum.DISABLED: + raise Exception( + "Unsupported import_topology mode: {:s}".format(str(import_topology)) + ) + + 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"] + 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)) + + if import_topology == ImportTopologyEnum.DEVICES: + LOGGER.debug("[get_devices_endpoints] devices only; returning") + return result + + reply = requests.get(self._links_url, timeout=TIMEOUT, auth=self._auth) + if reply.status_code not in HTTP_OK_CODES: + msg = MSG_ERROR.format( + str(self._links_url), str(reply.status_code), str(reply) + ) + LOGGER.error(msg) + raise Exception(msg) + + for json_link in reply.json()["links"]: + link_uuid: str = json_link["link_id"]["link_uuid"]["uuid"] + link_url = "/links/link[{:s}]".format(link_uuid) + link_endpoint_ids = [ + ( + json_endpoint_id["device_id"]["device_uuid"]["uuid"], + json_endpoint_id["endpoint_uuid"]["uuid"], + ) + for json_endpoint_id in json_link["link_endpoint_ids"] + ] + link_data = { + "uuid": json_link["link_id"]["link_uuid"]["uuid"], + "name": json_link["name"], + "endpoints": link_endpoint_ids, + } + result.append((link_url, link_data)) + + LOGGER.debug("[get_devices_endpoints] topology; returning") + return result + + def create_connectivity_service(self, l3vpn_data: dict) -> None: + try: + requests.post(self._l3vpn_url, json=l3vpn_data) + LOGGER.debug( + "[create_connectivity_service] l3vpn_data={:s}".format(str(l3vpn_data)) + ) + except requests.exceptions.ConnectionError: + raise Exception("faild to send post request to TFS L3VPN NBI") + + def update_connectivity_service(self, l3vpn_data: dict) -> None: + vpn_id = l3vpn_data['ietf-l3vpn-svc:l3vpn-svc']["vpn-services"]["vpn-service"][0]["vpn-id"] + url = self._l3vpn_url + f"/vpn-service={vpn_id}" + try: + requests.put(url, json=l3vpn_data) + LOGGER.debug( + "[update_connectivity_service] l3vpn_data={:s}".format(str(l3vpn_data)) + ) + except requests.exceptions.ConnectionError: + raise Exception("faild to send post request to TFS L3VPN NBI") + + def delete_connectivity_service(self, service_uuid: str) -> None: + url = self._l3vpn_url + f"/vpn-service={service_uuid}" + try: + requests.delete(url, auth=self._auth) + LOGGER.debug("[delete_connectivity_service] url={:s}".format(str(url))) + except requests.exceptions.ConnectionError: + raise Exception("faild to send delete request to TFS L3VPN NBI") diff --git a/src/device/service/drivers/ietf_l3vpn/Tools.py b/src/device/service/drivers/ietf_l3vpn/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..7caaa27a52cacf03d9822e933f0a6b74fd0e4db8 --- /dev/null +++ b/src/device/service/drivers/ietf_l3vpn/Tools.py @@ -0,0 +1,198 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Any, Dict, Optional, Tuple, TypedDict + +import requests + +from common.proto.kpi_sample_types_pb2 import KpiSampleType +from common.type_checkers.Checkers import chk_attribute, chk_string, chk_type +from device.service.driver_api._Driver import RESOURCE_ENDPOINTS + +from .Constants import SPECIAL_RESOURCE_MAPPINGS + + +class LANPrefixesDict(TypedDict): + lan: str + lan_tag: str + + +LOGGER = logging.getLogger(__name__) + +SITE_NETWORK_ACCESS_TYPE = "ietf-l3vpn-svc:multipoint" + + +def service_exists(wim_url: str, auth, service_uuid: str) -> bool: + try: + get_connectivity_service(wim_url, auth, service_uuid) + return True + except: # pylint: disable=bare-except + return False + + +def get_all_active_connectivity_services(wim_url: str, auth): + try: + LOGGER.info("Sending get all connectivity services") + servicepoint = f"{wim_url}/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" + response = requests.get(servicepoint, auth=auth) + + if response.status_code != requests.codes.ok: + raise Exception( + "Unable to get all connectivity services", + http_code=response.status_code, + ) + + return response + except requests.exceptions.ConnectionError: + raise Exception("Request Timeout", http_code=408) + + +def get_connectivity_service(wim_url, auth, service_uuid): + try: + LOGGER.info("Sending get connectivity service") + servicepoint = f"{wim_url}/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service={service_uuid}" + + response = requests.get(servicepoint) + + if response.status_code != requests.codes.ok: + raise Exception( + "Unable to get connectivity service{:s}".format(str(service_uuid)), + http_code=response.status_code, + ) + + return response + except requests.exceptions.ConnectionError: + raise Exception("Request Timeout", http_code=408) + + +def process_optional_string_field( + endpoint_data: Dict[str, Any], + field_name: str, + endpoint_resource_value: Dict[str, Any], +) -> None: + field_value = chk_attribute( + field_name, endpoint_data, "endpoint_data", default=None + ) + if field_value is None: + return + chk_string("endpoint_data.{:s}".format(field_name), field_value) + if len(field_value) > 0: + endpoint_resource_value[field_name] = field_value + + +def compose_resource_endpoint( + endpoint_data: Dict[str, Any], +) -> Optional[Tuple[str, Dict]]: + try: + # Check type of endpoint_data + chk_type("endpoint_data", endpoint_data, dict) + + # Check endpoint UUID (mandatory) + endpoint_uuid = chk_attribute("uuid", endpoint_data, "endpoint_data") + chk_string("endpoint_data.uuid", endpoint_uuid, min_length=1) + endpoint_resource_path = SPECIAL_RESOURCE_MAPPINGS.get(RESOURCE_ENDPOINTS) + endpoint_resource_key = "{:s}/endpoint[{:s}]".format( + endpoint_resource_path, endpoint_uuid + ) + endpoint_resource_value = {"uuid": endpoint_uuid} + + # Check endpoint optional string fields + process_optional_string_field(endpoint_data, "name", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "site_location", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "ce-ip", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "address_ip", endpoint_resource_value + ) + process_optional_string_field( + endpoint_data, "address_prefix", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "mtu", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "ipv4_lan_prefixes", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "type", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "context_uuid", endpoint_resource_value + ) + process_optional_string_field( + endpoint_data, "topology_uuid", endpoint_resource_value + ) + + # Check endpoint sample types (optional) + endpoint_sample_types = chk_attribute( + "sample_types", endpoint_data, "endpoint_data", default=[] + ) + chk_type("endpoint_data.sample_types", endpoint_sample_types, list) + sample_types = {} + sample_type_errors = [] + for i, endpoint_sample_type in enumerate(endpoint_sample_types): + field_name = "endpoint_data.sample_types[{:d}]".format(i) + try: + chk_type(field_name, endpoint_sample_type, (int, str)) + if isinstance(endpoint_sample_type, int): + metric_name = KpiSampleType.Name(endpoint_sample_type) + metric_id = endpoint_sample_type + elif isinstance(endpoint_sample_type, str): + metric_id = KpiSampleType.Value(endpoint_sample_type) + metric_name = endpoint_sample_type + else: + str_type = str(type(endpoint_sample_type)) + raise Exception("Bad format: {:s}".format(str_type)) # pylint: disable=broad-exception-raised + except Exception as e: # pylint: disable=broad-exception-caught + MSG = "Unsupported {:s}({:s}) : {:s}" + sample_type_errors.append( + MSG.format(field_name, str(endpoint_sample_type), str(e)) + ) + + metric_name = metric_name.lower().replace("kpisampletype_", "") + monitoring_resource_key = "{:s}/state/{:s}".format( + endpoint_resource_key, metric_name + ) + sample_types[metric_id] = monitoring_resource_key + + if len(sample_type_errors) > 0: + # pylint: disable=broad-exception-raised + raise Exception( + "Malformed Sample Types:\n{:s}".format("\n".join(sample_type_errors)) + ) + + if len(sample_types) > 0: + endpoint_resource_value["sample_types"] = sample_types + + if "site_location" in endpoint_data: + endpoint_resource_value["site_location"] = endpoint_data["site_location"] + + if "ce-ip" in endpoint_data: + endpoint_resource_value["ce-ip"] = endpoint_data["ce-ip"] + + if "address_ip" in endpoint_data: + endpoint_resource_value["address_ip"] = endpoint_data["address_ip"] + + if "address_prefix" in endpoint_data: + endpoint_resource_value["address_prefix"] = endpoint_data["address_prefix"] + + if "mtu" in endpoint_data: + endpoint_resource_value["mtu"] = endpoint_data["mtu"] + + if "ipv4_lan_prefixes" in endpoint_data: + endpoint_resource_value["ipv4_lan_prefixes"] = endpoint_data[ + "ipv4_lan_prefixes" + ] + + return endpoint_resource_key, endpoint_resource_value + except: # pylint: disable=bare-except + LOGGER.exception("Problem composing endpoint({:s})".format(str(endpoint_data))) + return None diff --git a/src/device/service/drivers/ietf_l3vpn/__init__.py b/src/device/service/drivers/ietf_l3vpn/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bbfc943b68af13a11e562abbc8680ade71db8f02 --- /dev/null +++ b/src/device/service/drivers/ietf_l3vpn/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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/device/service/drivers/ietf_l3vpn/driver.py b/src/device/service/drivers/ietf_l3vpn/driver.py new file mode 100644 index 0000000000000000000000000000000000000000..2aca83b6a645bf2e793b08841949813f0413a531 --- /dev/null +++ b/src/device/service/drivers/ietf_l3vpn/driver.py @@ -0,0 +1,309 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import logging +import re +import threading +from typing import Any, Iterator, List, Optional, Tuple, Union + +import anytree +import requests +from requests.auth import HTTPBasicAuth + +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.type_checkers.Checkers import chk_length, chk_string, chk_type +from device.service.driver_api._Driver import ( + RESOURCE_ENDPOINTS, + RESOURCE_SERVICES, + _Driver, +) +from device.service.driver_api.AnyTreeTools import ( + TreeNode, + dump_subtree, + get_subnode, + set_subnode_value, +) +from device.service.driver_api.ImportTopologyEnum import ( + ImportTopologyEnum, + get_import_topology, +) + +from .Constants import SPECIAL_RESOURCE_MAPPINGS +from .TfsApiClient import TfsApiClient +from .Tools import compose_resource_endpoint + +LOGGER = logging.getLogger(__name__) + + +ALL_RESOURCE_KEYS = [ + RESOURCE_ENDPOINTS, + RESOURCE_SERVICES, +] + +RE_GET_ENDPOINT_FROM_INTERFACE = re.compile(r"^\/interface\[([^\]]+)\].*") + +RE_IETF_L3VPN_DATA = re.compile(r"^\/service\[[^\]]+\]\/IETFL3VPN$") +RE_IETF_L3VPN_OPERATION = re.compile(r"^\/service\[[^\]]+\]\/IETFL3VPN\/operation$") + +DRIVER_NAME = "ietf_l3vpn" +METRICS_POOL = MetricsPool("Device", "Driver", labels={"driver": DRIVER_NAME}) + + +class IetfL3VpnDriver(_Driver): + def __init__(self, address: str, port: str, **settings) -> None: + super().__init__(DRIVER_NAME, address, int(port), **settings) + self.__lock = threading.Lock() + self.__started = threading.Event() + self.__terminate = threading.Event() + self.__running = TreeNode(".") + scheme = self.settings.get("scheme", "http") + username = self.settings.get("username") + password = self.settings.get("password") + self.tac = TfsApiClient( + self.address, + self.port, + scheme=scheme, + username=username, + password=password, + ) + self.__auth = None + # ( + # HTTPBasicAuth(username, password) + # if username is not None and password is not None + # else None + # ) + self.__tfs_nbi_root = "{:s}://{:s}:{:d}".format( + scheme, self.address, int(self.port) + ) + self.__timeout = int(self.settings.get("timeout", 120)) + self.__import_topology = get_import_topology( + self.settings, default=ImportTopologyEnum.DEVICES + ) + endpoints = self.settings.get("endpoints", []) + endpoint_resources = [] + for endpoint in endpoints: + endpoint_resource = compose_resource_endpoint(endpoint) + if endpoint_resource is None: + continue + endpoint_resources.append(endpoint_resource) + self._set_initial_config(endpoint_resources) + + def _set_initial_config( + self, resources: List[Tuple[str, Any]] + ) -> List[Union[bool, Exception]]: + chk_type("resources", resources, list) + if len(resources) == 0: + return [] + results = [] + resolver = anytree.Resolver(pathattr="name") + with self.__lock: + for i, resource in enumerate(resources): + str_resource_name = "resources[#{:d}]".format(i) + try: + chk_type(str_resource_name, resource, (list, tuple)) + chk_length(str_resource_name, resource, min_length=2, max_length=2) + resource_key, resource_value = resource + chk_string(str_resource_name, resource_key, allow_empty=False) + resource_path = resource_key.split("/") + except Exception as e: # pylint: disable=broad-except + LOGGER.exception( + "Exception validating {:s}: {:s}".format( + str_resource_name, str(resource_key) + ) + ) + results.append(e) # if validation fails, store the exception + continue + + try: + resource_value = json.loads(resource_value) + except: # pylint: disable=bare-except + pass + + set_subnode_value( + resolver, self.__running, resource_path, resource_value + ) + + results.append(True) + return results + + def Connect(self) -> bool: + url = ( + self.__tfs_nbi_root + "/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" + ) + with self.__lock: + if self.__started.is_set(): + return True + try: + # requests.get(url, timeout=self.__timeout, auth=self.__auth) + ... + except requests.exceptions.Timeout: + LOGGER.exception("Timeout connecting {:s}".format(url)) + return False + except Exception: # pylint: disable=broad-except + LOGGER.exception("Exception connecting {:s}".format(url)) + 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) + with self.__lock: + if len(resource_keys) == 0: + return dump_subtree(self.__running) + results = [] + resolver = anytree.Resolver(pathattr="name") + for i, resource_key in enumerate(resource_keys): + str_resource_name = "resource_key[#{:d}]".format(i) + try: + chk_string(str_resource_name, resource_key, allow_empty=False) + resource_key = SPECIAL_RESOURCE_MAPPINGS.get( + resource_key, resource_key + ) + resource_path = resource_key.split("/") + except Exception as e: # pylint: disable=broad-except + LOGGER.exception( + "Exception validating {:s}: {:s}".format( + str_resource_name, str(resource_key) + ) + ) + results.append( + (resource_key, e) + ) # if validation fails, store the exception + continue + + resource_node = get_subnode( + resolver, self.__running, resource_path, default=None + ) + # if not found, resource_node is None + if resource_node is None: + continue + results.extend(dump_subtree(resource_node)) + return results + 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: + resource_key, resource_value = resource + if RE_IETF_L3VPN_OPERATION.match(resource_key): + operation_type = json.loads(resource_value)["type"] + results.append((resource_key, True)) + break + else: + raise Exception("operation type not found in resources") + for resource in resources: + LOGGER.info("resource = {:s}".format(str(resource))) + resource_key, resource_value = resource + if not RE_IETF_L3VPN_DATA.match(resource_key): + continue + try: + resource_value = json.loads(resource_value) + + # if service_exists(self.__tfs_nbi_root, self.__auth, service_uuid): + # exc = NotImplementedError( + # "IETF L3VPN Service Update is still not supported" + # ) + # results.append((resource[0], exc)) + # continue + if operation_type == "create": + service_id = resource_value["ietf-l3vpn-svc:l3vpn-svc"][ + "vpn-services" + ]["vpn-service"][0]["vpn-id"] + self.tac.create_connectivity_service(resource_value) + elif operation_type == "update": + service_id = resource_value["ietf-l3vpn-svc:l3vpn-svc"][ + "vpn-services" + ]["vpn-service"][0]["vpn-id"] + self.tac.update_connectivity_service(resource_value) + else: + raise Exception("operation type not supported") + 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)) + 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))) + resource_key, resource_value = resource + if not RE_IETF_L3VPN_DATA.match(resource_key): + continue + try: + resource_value = json.loads(resource_value) + service_id = resource_value["id"] + + # if service_exists(self.__tfs_nbi_root, self.__auth, service_uuid): + self.tac.delete_connectivity_service(service_id) + 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)) + return results + + @metered_subclass_method(METRICS_POOL) + def SubscribeState( + self, subscriptions: List[Tuple[str, float, float]] + ) -> List[Union[bool, Exception]]: + # TODO: IETF L3VPN 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: IETF L3VPN 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: IETF L3VPN does not support monitoring by now + return [] diff --git a/src/device/service/drivers/ietf_slice/Constants.py b/src/device/service/drivers/ietf_slice/Constants.py new file mode 100644 index 0000000000000000000000000000000000000000..172c328aeaa5a2beafe4ced1b273cb3e7577e241 --- /dev/null +++ b/src/device/service/drivers/ietf_slice/Constants.py @@ -0,0 +1,25 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (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 device.service.driver_api._Driver import ( + RESOURCE_ENDPOINTS, + RESOURCE_INTERFACES, + RESOURCE_NETWORK_INSTANCES, +) + +SPECIAL_RESOURCE_MAPPINGS = { + RESOURCE_ENDPOINTS: "/endpoints", + RESOURCE_INTERFACES: "/interfaces", + RESOURCE_NETWORK_INSTANCES: "/net-instances", +} diff --git a/src/device/service/drivers/ietf_slice/Tools.py b/src/device/service/drivers/ietf_slice/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..bd976927e4b17c74264b3372ed1caeeb1bc96c61 --- /dev/null +++ b/src/device/service/drivers/ietf_slice/Tools.py @@ -0,0 +1,147 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Any, Dict, Optional, Tuple + +import requests + +from common.proto.kpi_sample_types_pb2 import KpiSampleType +from common.type_checkers.Checkers import chk_attribute, chk_string, chk_type +from device.service.driver_api._Driver import RESOURCE_ENDPOINTS + +from .Constants import SPECIAL_RESOURCE_MAPPINGS + +LOGGER = logging.getLogger(__name__) + + +def process_optional_string_field( + endpoint_data: Dict[str, Any], + field_name: str, + endpoint_resource_value: Dict[str, Any], +) -> None: + field_value = chk_attribute( + field_name, endpoint_data, "endpoint_data", default=None + ) + if field_value is None: + return + chk_string("endpoint_data.{:s}".format(field_name), field_value) + if len(field_value) > 0: + endpoint_resource_value[field_name] = field_value + + +def compose_resource_endpoint( + endpoint_data: Dict[str, Any], +) -> Optional[Tuple[str, Dict]]: + try: + # Check type of endpoint_data + chk_type("endpoint_data", endpoint_data, dict) + + # Check endpoint UUID (mandatory) + endpoint_uuid = chk_attribute("uuid", endpoint_data, "endpoint_data") + chk_string("endpoint_data.uuid", endpoint_uuid, min_length=1) + endpoint_resource_path = SPECIAL_RESOURCE_MAPPINGS.get(RESOURCE_ENDPOINTS) + endpoint_resource_key = "{:s}/endpoint[{:s}]".format( + endpoint_resource_path, endpoint_uuid + ) + endpoint_resource_value = {"uuid": endpoint_uuid} + + # Check endpoint optional string fields + process_optional_string_field(endpoint_data, "name", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "site_location", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "ce-ip", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "address_ip", endpoint_resource_value + ) + process_optional_string_field( + endpoint_data, "address_prefix", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "mtu", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "ipv4_lan_prefixes", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "type", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "context_uuid", endpoint_resource_value + ) + process_optional_string_field( + endpoint_data, "topology_uuid", endpoint_resource_value + ) + + # Check endpoint sample types (optional) + endpoint_sample_types = chk_attribute( + "sample_types", endpoint_data, "endpoint_data", default=[] + ) + chk_type("endpoint_data.sample_types", endpoint_sample_types, list) + sample_types = {} + sample_type_errors = [] + for i, endpoint_sample_type in enumerate(endpoint_sample_types): + field_name = "endpoint_data.sample_types[{:d}]".format(i) + try: + chk_type(field_name, endpoint_sample_type, (int, str)) + if isinstance(endpoint_sample_type, int): + metric_name = KpiSampleType.Name(endpoint_sample_type) + metric_id = endpoint_sample_type + elif isinstance(endpoint_sample_type, str): + metric_id = KpiSampleType.Value(endpoint_sample_type) + metric_name = endpoint_sample_type + else: + str_type = str(type(endpoint_sample_type)) + raise Exception("Bad format: {:s}".format(str_type)) # pylint: disable=broad-exception-raised + except Exception as e: # pylint: disable=broad-exception-caught + MSG = "Unsupported {:s}({:s}) : {:s}" + sample_type_errors.append( + MSG.format(field_name, str(endpoint_sample_type), str(e)) + ) + + metric_name = metric_name.lower().replace("kpisampletype_", "") + monitoring_resource_key = "{:s}/state/{:s}".format( + endpoint_resource_key, metric_name + ) + sample_types[metric_id] = monitoring_resource_key + + if len(sample_type_errors) > 0: + # pylint: disable=broad-exception-raised + raise Exception( + "Malformed Sample Types:\n{:s}".format("\n".join(sample_type_errors)) + ) + + if len(sample_types) > 0: + endpoint_resource_value["sample_types"] = sample_types + + if "site_location" in endpoint_data: + endpoint_resource_value["site_location"] = endpoint_data["site_location"] + + if "ce-ip" in endpoint_data: + endpoint_resource_value["ce-ip"] = endpoint_data["ce-ip"] + + if "address_ip" in endpoint_data: + endpoint_resource_value["address_ip"] = endpoint_data["address_ip"] + + if "address_prefix" in endpoint_data: + endpoint_resource_value["address_prefix"] = endpoint_data["address_prefix"] + + if "mtu" in endpoint_data: + endpoint_resource_value["mtu"] = endpoint_data["mtu"] + + if "ipv4_lan_prefixes" in endpoint_data: + endpoint_resource_value["ipv4_lan_prefixes"] = endpoint_data[ + "ipv4_lan_prefixes" + ] + + return endpoint_resource_key, endpoint_resource_value + except: # pylint: disable=bare-except + LOGGER.exception("Problem composing endpoint({:s})".format(str(endpoint_data))) + return None diff --git a/src/device/service/drivers/ietf_slice/__init__.py b/src/device/service/drivers/ietf_slice/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6242c89c7fa17bc5b6cc44328d8ce58438721d45 --- /dev/null +++ b/src/device/service/drivers/ietf_slice/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (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/device/service/drivers/ietf_slice/driver.py b/src/device/service/drivers/ietf_slice/driver.py new file mode 100644 index 0000000000000000000000000000000000000000..e8b6e7d0effac1f7a3eb05c7aabd2d0a39125b65 --- /dev/null +++ b/src/device/service/drivers/ietf_slice/driver.py @@ -0,0 +1,306 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import logging +import re +import threading +from typing import Any, Iterator, List, Optional, Tuple, Union + +import anytree +import requests +from requests.auth import HTTPBasicAuth + +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.type_checkers.Checkers import chk_length, chk_string, chk_type +from device.service.driver_api._Driver import ( + RESOURCE_ENDPOINTS, + RESOURCE_SERVICES, + _Driver, +) +from device.service.driver_api.AnyTreeTools import ( + TreeNode, + dump_subtree, + get_subnode, + set_subnode_value, +) +from device.service.driver_api.ImportTopologyEnum import ( + ImportTopologyEnum, + get_import_topology, +) + +from .Constants import SPECIAL_RESOURCE_MAPPINGS +from .tfs_slice_nbi_client import TfsApiClient +from .Tools import compose_resource_endpoint + +LOGGER = logging.getLogger(__name__) + + +ALL_RESOURCE_KEYS = [ + RESOURCE_ENDPOINTS, + RESOURCE_SERVICES, +] + +RE_IETF_SLICE_DATA = re.compile(r"^\/service\[[^\]]+\]\/IETFSlice$") +RE_IETF_SLICE_OPERATION = re.compile(r"^\/service\[[^\]]+\]\/IETFSlice\/operation$") + +DRIVER_NAME = "ietf_slice" +METRICS_POOL = MetricsPool("Device", "Driver", labels={"driver": DRIVER_NAME}) + + +class IetfSliceDriver(_Driver): + def __init__(self, address: str, port: str, **settings) -> None: + super().__init__(DRIVER_NAME, address, int(port), **settings) + self.__lock = threading.Lock() + self.__started = threading.Event() + self.__terminate = threading.Event() + self.__running = TreeNode(".") + scheme = self.settings.get("scheme", "http") + username = self.settings.get("username") + password = self.settings.get("password") + self.tac = TfsApiClient( + self.address, + self.port, + scheme=scheme, + username=username, + password=password, + ) + self.__auth = None + # ( + # HTTPBasicAuth(username, password) + # if username is not None and password is not None + # else None + # ) + self.__tfs_nbi_root = "{:s}://{:s}:{:d}".format( + scheme, self.address, int(self.port) + ) + self.__timeout = int(self.settings.get("timeout", 120)) + self.__import_topology = get_import_topology( + self.settings, default=ImportTopologyEnum.DEVICES + ) + endpoints = self.settings.get("endpoints", []) + endpoint_resources = [] + for endpoint in endpoints: + endpoint_resource = compose_resource_endpoint(endpoint) + if endpoint_resource is None: + continue + endpoint_resources.append(endpoint_resource) + self._set_initial_config(endpoint_resources) + + def _set_initial_config( + self, resources: List[Tuple[str, Any]] + ) -> List[Union[bool, Exception]]: + chk_type("resources", resources, list) + if len(resources) == 0: + return [] + results = [] + resolver = anytree.Resolver(pathattr="name") + with self.__lock: + for i, resource in enumerate(resources): + str_resource_name = "resources[#{:d}]".format(i) + try: + chk_type(str_resource_name, resource, (list, tuple)) + chk_length(str_resource_name, resource, min_length=2, max_length=2) + resource_key, resource_value = resource + chk_string(str_resource_name, resource_key, allow_empty=False) + resource_path = resource_key.split("/") + except Exception as e: # pylint: disable=broad-except + LOGGER.exception( + "Exception validating {:s}: {:s}".format( + str_resource_name, str(resource_key) + ) + ) + results.append(e) # if validation fails, store the exception + continue + + try: + resource_value = json.loads(resource_value) + except: # pylint: disable=bare-except + pass + + set_subnode_value( + resolver, self.__running, resource_path, resource_value + ) + + results.append(True) + return results + + def Connect(self) -> bool: + url = self.__tfs_nbi_root + "/restconf/data/ietf-network-slice-service:ietf-nss" + with self.__lock: + if self.__started.is_set(): + return True + try: + # requests.get(url, timeout=self.__timeout) + ... + except requests.exceptions.Timeout: + LOGGER.exception("Timeout connecting {:s}".format(url)) + return False + except Exception: # pylint: disable=broad-except + LOGGER.exception("Exception connecting {:s}".format(url)) + 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) + with self.__lock: + if len(resource_keys) == 0: + return dump_subtree(self.__running) + results = [] + resolver = anytree.Resolver(pathattr="name") + for i, resource_key in enumerate(resource_keys): + str_resource_name = "resource_key[#{:d}]".format(i) + try: + chk_string(str_resource_name, resource_key, allow_empty=False) + resource_key = SPECIAL_RESOURCE_MAPPINGS.get( + resource_key, resource_key + ) + resource_path = resource_key.split("/") + except Exception as e: # pylint: disable=broad-except + LOGGER.exception( + "Exception validating {:s}: {:s}".format( + str_resource_name, str(resource_key) + ) + ) + results.append( + (resource_key, e) + ) # if validation fails, store the exception + continue + resource_node = get_subnode( + resolver, self.__running, resource_path, default=None + ) + # if not found, resource_node is None + if resource_node is None: + continue + results.extend(dump_subtree(resource_node)) + 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: + resource_key, resource_value = resource + if RE_IETF_SLICE_OPERATION.match(resource_key): + operation_type = json.loads(resource_value)["type"] + results.append((resource_key, True)) + break + else: + raise Exception("operation type not found in resources") + for resource in resources: + LOGGER.info("resource = {:s}".format(str(resource))) + resource_key, resource_value = resource + if not RE_IETF_SLICE_DATA.match(resource_key): + continue + try: + resource_value = json.loads(resource_value) + + slice_name = resource_value["network-slice-services"][ + "slice-service" + ][0]["id"] + + if operation_type == "create": + self.tac.create_slice(resource_value) + + elif operation_type == "update": + connection_groups = resource_value["network-slice-services"][ + "slice-service" + ][0]["connection-groups"]["connection-group"] + + if len(connection_groups) != 1: + raise Exception("only one connection group is supported") + + connection_group = connection_groups[0] + + self.tac.update_slice( + slice_name, connection_group["id"], connection_group + ) + + elif operation_type == "delete": + self.tac.delete_slice(slice_name) + + 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)) + 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))) + resource_key, resource_value = resource + try: + 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)) + return results + + @metered_subclass_method(METRICS_POOL) + def SubscribeState( + self, subscriptions: List[Tuple[str, float, float]] + ) -> List[Union[bool, Exception]]: + # TODO: IETF Slice 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: IETF Slice 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: IETF Slice does not support monitoring by now + return [] diff --git a/src/device/service/drivers/ietf_slice/tfs_slice_nbi_client.py b/src/device/service/drivers/ietf_slice/tfs_slice_nbi_client.py new file mode 100644 index 0000000000000000000000000000000000000000..596e3d903d6e17dda96be42d776aae2f9068f7bd --- /dev/null +++ b/src/device/service/drivers/ietf_slice/tfs_slice_nbi_client.py @@ -0,0 +1,76 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Optional + +import requests +from requests.auth import HTTPBasicAuth + +IETF_SLICE_URL = "{:s}://{:s}:{:d}/restconf/data/ietf-network-slice-service" +TIMEOUT = 30 + +LOGGER = logging.getLogger(__name__) + +HEADERS = {"Content-Type": "application/json"} + + +class TfsApiClient: + def __init__( + self, + address: str, + port: int, + scheme: str = "http", + username: Optional[str] = None, + password: Optional[str] = None, + ) -> None: + self._slice_url = IETF_SLICE_URL.format(scheme, address, port) + self._auth = None + # ( + # HTTPBasicAuth(username, password) + # if username is not None and password is not None + # else None + # ) + + def create_slice(self, slice_data: dict) -> None: + url = self._slice_url + ":network-slice-services" + try: + requests.post(url, json=slice_data, headers=HEADERS) + LOGGER.info(f"IETF Slice Post to {url}: {slice_data}") + except requests.exceptions.ConnectionError: + raise Exception("faild to send post request to TFS IETF Slice NBI") + + def update_slice( + self, + slice_name: str, + connection_group_id: str, + updated_connection_group_data: dict, + ) -> None: + url = ( + self._slice_url + + f":network-slice-services/slice-service={slice_name}/connection-groups/connection-group={connection_group_id}" + ) + try: + requests.put(url, json=updated_connection_group_data, headers=HEADERS) + LOGGER.info(f"IETF Slice Put to {url}: {updated_connection_group_data}") + except requests.exceptions.ConnectionError: + raise Exception("faild to send update request to TFS IETF Slice NBI") + + def delete_slice(self, slice_name: str) -> None: + url = self._slice_url + f":network-slice-services/slice-service={slice_name}" + try: + requests.delete(url) + LOGGER.info(f"IETF Slice Delete to {url}") + except requests.exceptions.ConnectionError: + raise Exception("faild to send delete request to TFS IETF Slice NBI") diff --git a/src/device/service/drivers/nce/Constants.py b/src/device/service/drivers/nce/Constants.py new file mode 100644 index 0000000000000000000000000000000000000000..172c328aeaa5a2beafe4ced1b273cb3e7577e241 --- /dev/null +++ b/src/device/service/drivers/nce/Constants.py @@ -0,0 +1,25 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (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 device.service.driver_api._Driver import ( + RESOURCE_ENDPOINTS, + RESOURCE_INTERFACES, + RESOURCE_NETWORK_INSTANCES, +) + +SPECIAL_RESOURCE_MAPPINGS = { + RESOURCE_ENDPOINTS: "/endpoints", + RESOURCE_INTERFACES: "/interfaces", + RESOURCE_NETWORK_INSTANCES: "/net-instances", +} diff --git a/src/device/service/drivers/nce/Tools.py b/src/device/service/drivers/nce/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..f9b2f24a8df494d04d749624ea6b2e5b986944fc --- /dev/null +++ b/src/device/service/drivers/nce/Tools.py @@ -0,0 +1,145 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Any, Dict, Optional, Tuple + +from common.proto.kpi_sample_types_pb2 import KpiSampleType +from common.type_checkers.Checkers import chk_attribute, chk_string, chk_type +from device.service.driver_api._Driver import RESOURCE_ENDPOINTS + +from .Constants import SPECIAL_RESOURCE_MAPPINGS + +LOGGER = logging.getLogger(__name__) + + +def process_optional_string_field( + endpoint_data: Dict[str, Any], + field_name: str, + endpoint_resource_value: Dict[str, Any], +) -> None: + field_value = chk_attribute( + field_name, endpoint_data, "endpoint_data", default=None + ) + if field_value is None: + return + chk_string("endpoint_data.{:s}".format(field_name), field_value) + if len(field_value) > 0: + endpoint_resource_value[field_name] = field_value + + +def compose_resource_endpoint( + endpoint_data: Dict[str, Any], +) -> Optional[Tuple[str, Dict]]: + try: + # Check type of endpoint_data + chk_type("endpoint_data", endpoint_data, dict) + + # Check endpoint UUID (mandatory) + endpoint_uuid = chk_attribute("uuid", endpoint_data, "endpoint_data") + chk_string("endpoint_data.uuid", endpoint_uuid, min_length=1) + endpoint_resource_path = SPECIAL_RESOURCE_MAPPINGS.get(RESOURCE_ENDPOINTS) + endpoint_resource_key = "{:s}/endpoint[{:s}]".format( + endpoint_resource_path, endpoint_uuid + ) + endpoint_resource_value = {"uuid": endpoint_uuid} + + # Check endpoint optional string fields + process_optional_string_field(endpoint_data, "name", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "site_location", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "ce-ip", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "address_ip", endpoint_resource_value + ) + process_optional_string_field( + endpoint_data, "address_prefix", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "mtu", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "ipv4_lan_prefixes", endpoint_resource_value + ) + process_optional_string_field(endpoint_data, "type", endpoint_resource_value) + process_optional_string_field( + endpoint_data, "context_uuid", endpoint_resource_value + ) + process_optional_string_field( + endpoint_data, "topology_uuid", endpoint_resource_value + ) + + # Check endpoint sample types (optional) + endpoint_sample_types = chk_attribute( + "sample_types", endpoint_data, "endpoint_data", default=[] + ) + chk_type("endpoint_data.sample_types", endpoint_sample_types, list) + sample_types = {} + sample_type_errors = [] + for i, endpoint_sample_type in enumerate(endpoint_sample_types): + field_name = "endpoint_data.sample_types[{:d}]".format(i) + try: + chk_type(field_name, endpoint_sample_type, (int, str)) + if isinstance(endpoint_sample_type, int): + metric_name = KpiSampleType.Name(endpoint_sample_type) + metric_id = endpoint_sample_type + elif isinstance(endpoint_sample_type, str): + metric_id = KpiSampleType.Value(endpoint_sample_type) + metric_name = endpoint_sample_type + else: + str_type = str(type(endpoint_sample_type)) + raise Exception("Bad format: {:s}".format(str_type)) # pylint: disable=broad-exception-raised + except Exception as e: # pylint: disable=broad-exception-caught + MSG = "Unsupported {:s}({:s}) : {:s}" + sample_type_errors.append( + MSG.format(field_name, str(endpoint_sample_type), str(e)) + ) + + metric_name = metric_name.lower().replace("kpisampletype_", "") + monitoring_resource_key = "{:s}/state/{:s}".format( + endpoint_resource_key, metric_name + ) + sample_types[metric_id] = monitoring_resource_key + + if len(sample_type_errors) > 0: + # pylint: disable=broad-exception-raised + raise Exception( + "Malformed Sample Types:\n{:s}".format("\n".join(sample_type_errors)) + ) + + if len(sample_types) > 0: + endpoint_resource_value["sample_types"] = sample_types + + if "site_location" in endpoint_data: + endpoint_resource_value["site_location"] = endpoint_data["site_location"] + + if "ce-ip" in endpoint_data: + endpoint_resource_value["ce-ip"] = endpoint_data["ce-ip"] + + if "address_ip" in endpoint_data: + endpoint_resource_value["address_ip"] = endpoint_data["address_ip"] + + if "address_prefix" in endpoint_data: + endpoint_resource_value["address_prefix"] = endpoint_data["address_prefix"] + + if "mtu" in endpoint_data: + endpoint_resource_value["mtu"] = endpoint_data["mtu"] + + if "ipv4_lan_prefixes" in endpoint_data: + endpoint_resource_value["ipv4_lan_prefixes"] = endpoint_data[ + "ipv4_lan_prefixes" + ] + + return endpoint_resource_key, endpoint_resource_value + except: # pylint: disable=bare-except + LOGGER.exception("Problem composing endpoint({:s})".format(str(endpoint_data))) + return None diff --git a/src/device/service/drivers/nce/__init__.py b/src/device/service/drivers/nce/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6242c89c7fa17bc5b6cc44328d8ce58438721d45 --- /dev/null +++ b/src/device/service/drivers/nce/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (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/device/service/drivers/nce/driver.py b/src/device/service/drivers/nce/driver.py new file mode 100644 index 0000000000000000000000000000000000000000..4ac1a2b1c604a6ebe5028688dc39f545e0e1cee6 --- /dev/null +++ b/src/device/service/drivers/nce/driver.py @@ -0,0 +1,278 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import logging +import re +import threading +from typing import Any, Iterator, List, Optional, Tuple, Union + +import anytree +import requests +from requests.auth import HTTPBasicAuth + +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.type_checkers.Checkers import chk_length, chk_string, chk_type +from device.service.driver_api._Driver import _Driver +from device.service.driver_api.AnyTreeTools import ( + TreeNode, + dump_subtree, + get_subnode, + set_subnode_value, +) +from device.service.driver_api.ImportTopologyEnum import ( + ImportTopologyEnum, + get_import_topology, +) + +from .Constants import SPECIAL_RESOURCE_MAPPINGS +from .nce_fan_client import NCEClient +from .Tools import compose_resource_endpoint + +LOGGER = logging.getLogger(__name__) + + +RE_NCE_APP_FLOW_DATA = re.compile(r"^\/service\[[^\]]+\]\/AppFlow$") +RE_NCE_APP_FLOW_OPERATION = re.compile(r"^\/service\[[^\]]+\]\/AppFlow\/operation$") + +DRIVER_NAME = "nce" +METRICS_POOL = MetricsPool("Device", "Driver", labels={"driver": DRIVER_NAME}) + + +class NCEDriver(_Driver): + def __init__(self, address: str, port: str, **settings) -> None: + super().__init__(DRIVER_NAME, address, int(port), **settings) + self.__lock = threading.Lock() + self.__started = threading.Event() + self.__terminate = threading.Event() + self.__running = TreeNode(".") + scheme = self.settings.get("scheme", "http") + username = self.settings.get("username") + password = self.settings.get("password") + self.nce = NCEClient( + self.address, + self.port, + scheme=scheme, + username=username, + password=password, + ) + self.__auth = None + # ( + # HTTPBasicAuth(username, password) + # if username is not None and password is not None + # else None + # ) + self.__tfs_nbi_root = "{:s}://{:s}:{:d}".format( + scheme, self.address, int(self.port) + ) + self.__timeout = int(self.settings.get("timeout", 120)) + self.__import_topology = get_import_topology( + self.settings, default=ImportTopologyEnum.DEVICES + ) + endpoints = self.settings.get("endpoints", []) + endpoint_resources = [] + for endpoint in endpoints: + endpoint_resource = compose_resource_endpoint(endpoint) + if endpoint_resource is None: + continue + endpoint_resources.append(endpoint_resource) + self._set_initial_config(endpoint_resources) + + def _set_initial_config( + self, resources: List[Tuple[str, Any]] + ) -> List[Union[bool, Exception]]: + chk_type("resources", resources, list) + if len(resources) == 0: + return [] + results = [] + resolver = anytree.Resolver(pathattr="name") + with self.__lock: + for i, resource in enumerate(resources): + str_resource_name = "resources[#{:d}]".format(i) + try: + chk_type(str_resource_name, resource, (list, tuple)) + chk_length(str_resource_name, resource, min_length=2, max_length=2) + resource_key, resource_value = resource + chk_string(str_resource_name, resource_key, allow_empty=False) + resource_path = resource_key.split("/") + except Exception as e: # pylint: disable=broad-except + LOGGER.exception( + "Exception validating {:s}: {:s}".format( + str_resource_name, str(resource_key) + ) + ) + results.append(e) # if validation fails, store the exception + continue + + try: + resource_value = json.loads(resource_value) + except: # pylint: disable=bare-except + pass + + set_subnode_value( + resolver, self.__running, resource_path, resource_value + ) + + results.append(True) + return results + + def Connect(self) -> bool: + with self.__lock: + if self.__started.is_set(): + return True + try: + ... + except requests.exceptions.Timeout: + return False + except Exception: # pylint: disable=broad-except + 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) + with self.__lock: + if len(resource_keys) == 0: + return dump_subtree(self.__running) + results = [] + resolver = anytree.Resolver(pathattr="name") + for i, resource_key in enumerate(resource_keys): + str_resource_name = "resource_key[#{:d}]".format(i) + try: + chk_string(str_resource_name, resource_key, allow_empty=False) + resource_key = SPECIAL_RESOURCE_MAPPINGS.get( + resource_key, resource_key + ) + resource_path = resource_key.split("/") + except Exception as e: # pylint: disable=broad-except + LOGGER.exception( + "Exception validating {:s}: {:s}".format( + str_resource_name, str(resource_key) + ) + ) + results.append( + (resource_key, e) + ) # if validation fails, store the exception + continue + + resource_node = get_subnode( + resolver, self.__running, resource_path, default=None + ) + # if not found, resource_node is None + if resource_node is None: + continue + results.extend(dump_subtree(resource_node)) + return results + 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: + resource_key, resource_value = resource + LOGGER.debug("resource = {:s}".format(str(resource))) + if RE_NCE_APP_FLOW_OPERATION.match(resource_key): + operation_type = json.loads(resource_value)["type"] + results.append((resource_key, True)) + break + else: + raise Exception("operation type not found in resources") + for resource in resources: + LOGGER.info("resource = {:s}".format(str(resource))) + resource_key, resource_value = resource + if not RE_NCE_APP_FLOW_DATA.match(resource_key): + continue + try: + resource_value = json.loads(resource_value) + if operation_type == "create": + + self.nce.create_app_flow(resource_value) + elif operation_type == "delete": + + app_flow_name = resource_value["huawei-nce-app-flow:app-flows"][ + "app-flow" + ][0]["app-name"] + self.nce.delete_app_flow(app_flow_name) + 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)) + 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))) + resource_key, resource_value = resource + try: + 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)) + return results + + @metered_subclass_method(METRICS_POOL) + def SubscribeState( + self, subscriptions: List[Tuple[str, float, float]] + ) -> List[Union[bool, Exception]]: + # TODO: IETF L3VPN 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: IETF L3VPN 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: IETF L3VPN does not support monitoring by now + return [] diff --git a/src/device/service/drivers/nce/nce_fan_client.py b/src/device/service/drivers/nce/nce_fan_client.py new file mode 100644 index 0000000000000000000000000000000000000000..1c3523427ab5818b650a5eeecafc098b1c55662e --- /dev/null +++ b/src/device/service/drivers/nce/nce_fan_client.py @@ -0,0 +1,94 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Optional + +import requests +from requests.auth import HTTPBasicAuth + +LOGGER = logging.getLogger(__name__) + +NCE_FAN_URL = "{:s}://{:s}:{:d}/restconf/v1/data" +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_532": 5, + "DEVICEDRIVER_XR": 6, + "DEVICEDRIVER_IETF_L2VPN": 7, + "DEVICEDRIVER_GNMI_OPENCONFIG": 8, + "DEVICEDRIVER_OPTICAL_TFS": 9, + "DEVICEDRIVER_IETF_ACTN": 10, + "DEVICEDRIVER_OC": 11, +} + +HEADERS = {'Content-Type': 'application/json'} + +class NCEClient: + def __init__( + self, + address: str, + port: int, + scheme: str = "http", + username: Optional[str] = None, + password: Optional[str] = None, + ) -> None: + self._nce_fan_url = NCE_FAN_URL.format(scheme, address, port) + self._auth = None + + def create_app_flow(self, app_flow_data: dict) -> None: + try: + app_data = app_flow_data["huawei-nce-app-flow:app-flows"]["applications"] + app_url = self._nce_fan_url + "/app-flows/apps" + LOGGER.info(f'Creating app: {app_data} URL: {app_url}') + requests.post(app_url, json=app_data, headers=HEADERS) + + app_flow_data = { + "app-flow": app_flow_data["huawei-nce-app-flow:app-flows"]["app-flow"] + } + app_flow_url = self._nce_fan_url + "/app-flows" + LOGGER.info(f'Creating app flow: {app_flow_data} URL: {app_flow_url}') + requests.post(app_flow_url, json=app_flow_data, headers=HEADERS) + except requests.exceptions.ConnectionError: + raise Exception("faild to send post requests to NCE FAN") + + def delete_app_flow(self, app_flow_name: str) -> None: + try: + app_url = self._nce_fan_url + f"/app-flows/apps/application={app_flow_name}" + LOGGER.info(f'Deleting app: {app_flow_name} URL: {app_url}') + requests.delete(app_url) + + app_flow_url = self._nce_fan_url + f"/app-flows/app-flow={app_flow_name}" + LOGGER.info(f'Deleting app flow: {app_flow_name} URL: {app_flow_url}') + requests.delete(app_flow_url) + except requests.exceptions.ConnectionError: + raise Exception("faild to send delete request to NCE FAN") diff --git a/src/device/tests/test_unitary_ietf_l3vpn.py b/src/device/tests/test_unitary_ietf_l3vpn.py new file mode 100644 index 0000000000000000000000000000000000000000..728ca691332c8abee7b5d6f5ad6c151240e540ed --- /dev/null +++ b/src/device/tests/test_unitary_ietf_l3vpn.py @@ -0,0 +1,345 @@ +import json +from json import dumps + +import requests + +from device.service.drivers.ietf_l3vpn.driver import IetfL3VpnDriver +from device.service.Tools import RESOURCE_ENDPOINTS + +settings = { + "endpoints": [ + { + "uuid": "access-pe", + "name": "access-pe", + "type": "copper", + "ce-ip": "1.1.1.1", + "address_ip": "3.3.2.1", + "address_prefix": 24, + "location": "access", + "mtu": 1500, + "ipv4_lan_prefixes": [ + {"lan": "128.32.10.0/24", "lan_tag": 10}, + {"lan": "128.32.20.0/24", "lan_tag": 20}, + ], + }, + { + "uuid": "cloud-pe", + "name": "cloud-pe", + "type": "copper", + "ce-ip": "1.1.1.1", + "address_ip": "3.3.2.1", + "address_prefix": 24, + "location": "cloud", + "mtu": 1500, + "ipv4_lan_prefixes": [{"lan": "172.1.101.0/24", "lan_tag": 101}], + }, + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": False, +} + +post_request_data = [] +get_request_data = [] + + +def mock_post(*args, **kwargs): + post_request_data.append((args, kwargs)) + + +def mock_get(*args, **kwargs): + get_request_data.append((args, kwargs)) + + +driver = IetfL3VpnDriver(address="1.2.3.4", port=0, **settings) + + +def test_connect(monkeypatch): + global post_request_data + global get_request_data + post_request_data = [] + get_request_data = [] + monkeypatch.setattr(requests, "post", mock_post) + monkeypatch.setattr(requests, "get", mock_get) + + driver.Connect() + assert not post_request_data + assert len(get_request_data) == 1 + assert get_request_data[0][0] == ( + "http://1.2.3.4:0/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services", + ) + assert list(get_request_data[0][1].keys()) == ["timeout", "verify", "auth"] + + +def test_GetConfig(monkeypatch): + global post_request_data + global get_request_data + post_request_data = [] + get_request_data = [] + monkeypatch.setattr(requests, "post", mock_post) + monkeypatch.setattr(requests, "get", mock_get) + + resources_to_get = [RESOURCE_ENDPOINTS] + result_GetConfig = driver.GetConfig(resources_to_get) + assert result_GetConfig == [ + ( + "/endpoints/endpoint[access-pe]", + { + "uuid": "access-pe", + "name": "access-pe", + "type": "copper", + "location": "access", + "ce-ip": "1.1.1.1", + "address_ip": "3.3.2.1", + "address_prefix": 24, + "mtu": 1500, + "ipv4_lan_prefixes": [ + {"lan": "128.32.10.0/24", "lan_tag": 10}, + {"lan": "128.32.20.0/24", "lan_tag": 20}, + ], + }, + ), + ( + "/endpoints/endpoint[cloud-pe]", + { + "uuid": "cloud-pe", + "name": "cloud-pe", + "type": "copper", + "location": "cloud", + "ce-ip": "1.1.1.1", + "address_ip": "3.3.2.1", + "address_prefix": 24, + "mtu": 1500, + "ipv4_lan_prefixes": [{"lan": "172.1.101.0/24", "lan_tag": 101}], + }, + ), + ] + + +def test_SetConfig(monkeypatch): + global post_request_data + global get_request_data + post_request_data = [] + get_request_data = [] + monkeypatch.setattr(requests, "post", mock_post) + monkeypatch.setattr(requests, "get", mock_get) + + resources = [ + ( + "/services/service[vpn_A]", + json.dumps( + { + "uuid": "vpn_A", + "src_device_name": "ip-net-controller", + "src_endpoint_name": settings["endpoints"][0]["name"], + "src_site_location": settings["endpoints"][0]["location"], + "src_ipv4_lan_prefixes": settings["endpoints"][0][ + "ipv4_lan_prefixes" + ], + "src_ce_address": settings["endpoints"][0]["ce-ip"], + "src_pe_address": settings["endpoints"][0]["address_ip"], + "src_ce_pe_network_prefix": settings["endpoints"][0][ + "address_prefix" + ], + "src_mtu": settings["endpoints"][0]["mtu"], + "dst_device_name": "ip-net-controller", + "dst_endpoint_name": settings["endpoints"][1]["name"], + "dst_site_location": settings["endpoints"][1]["location"], + "dst_ipv4_lan_prefixes": settings["endpoints"][1][ + "ipv4_lan_prefixes" + ], + "dst_ce_address": settings["endpoints"][1]["ce-ip"], + "dst_pe_address": settings["endpoints"][1]["address_ip"], + "dst_ce_pe_network_prefix": settings["endpoints"][1][ + "address_prefix" + ], + "dst_mtu": settings["endpoints"][1]["mtu"], + } + ), + ) + ] + result_SetConfig = driver.SetConfig(resources) + assert result_SetConfig == [("/services/service[vpn_A]", True)] + assert len(get_request_data) == 1 + assert get_request_data[0][0] == ( + "http://1.2.3.4:0/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service=vpn_A", + ) + assert len(post_request_data) == 1 + assert post_request_data[0][0] == ( + "http://1.2.3.4:0/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-services", + ) + assert post_request_data[0][1]["json"] == { + "ietf-l3vpn-svc:l3vpn-svc": { + "vpn-services": {"vpn-service": [{"vpn-id": "vpn_A"}]}, + "sites": { + "site": [ + { + "site-id": "site_access", + "management": {"type": "ietf-l3vpn-svc:customer-managed"}, + "locations": {"location": [{"location-id": "access"}]}, + "devices": { + "device": [ + { + "device-id": "ip-net-controller", + "location": "access", + } + ] + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "128.32.10.0/24", + "lan-tag": 10, + "next-hop": "3.3.2.1", + }, + { + "lan": "128.32.20.0/24", + "lan-tag": 20, + "next-hop": "3.3.2.1", + }, + ] + } + }, + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "access-pe", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "ip-net-controller", + "vpn-attachment": { + "vpn-id": "vpn_A", + "site-role": "ietf-l3vpn-svc:hub-role", + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "3.3.2.1", + "customer-address": "1.1.1.1", + "prefix-length": 24, + }, + } + }, + "service": { + "svc-mtu": 1500, + "svc-input-bandwidth": 1000000000, + "svc-output-bandwidth": 1000000000, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": "src_qos_profile", + "direction": ( + "ietf-l3vpn-svc:both", + ), + "latency": { + "latency-boundary": 10 + }, + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + } + ] + } + } + }, + }, + } + ] + }, + }, + { + "site-id": "site_cloud", + "management": {"type": "ietf-l3vpn-svc:customer-managed"}, + "locations": {"location": [{"location-id": "cloud"}]}, + "devices": { + "device": [ + { + "device-id": "ip-net-controller", + "location": "cloud", + } + ] + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.101.0/24", + "lan-tag": 101, + "next-hop": "3.3.2.1", + } + ] + } + }, + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "cloud-pe", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "ip-net-controller", + "vpn-attachment": { + "vpn-id": "vpn_A", + "site-role": "ietf-l3vpn-svc:spoke-role", + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "3.3.2.1", + "customer-address": "1.1.1.1", + "prefix-length": 24, + }, + } + }, + "service": { + "svc-mtu": 1500, + "svc-input-bandwidth": 1000000000, + "svc-output-bandwidth": 1000000000, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": "dst_qos_profile", + "direction": ( + "ietf-l3vpn-svc:both", + ), + "latency": { + "latency-boundary": 10 + }, + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + } + ] + } + } + }, + }, + } + ] + }, + }, + ] + }, + } + } diff --git a/src/nbi/service/__main__.py b/src/nbi/service/__main__.py index 71df0517aa688c106d8d0fe544f5f6b1af5a58f3..1d470f4eac30795e2272c9145baf947f3c982ba5 100644 --- a/src/nbi/service/__main__.py +++ b/src/nbi/service/__main__.py @@ -31,6 +31,7 @@ from .rest_server.nbi_plugins.ietf_network_slice import register_ietf_nss from .rest_server.nbi_plugins.ietf_acl import register_ietf_acl from .rest_server.nbi_plugins.qkd_app import register_qkd_app from .rest_server.nbi_plugins.tfs_api import register_tfs_api +from .rest_server.nbi_plugins import register_restconf from .context_subscription import register_context_subscription terminate = threading.Event() @@ -79,6 +80,7 @@ def main(): register_ietf_acl(rest_server) register_qkd_app(rest_server) register_tfs_api(rest_server) + register_restconf(rest_server) rest_server.start() register_context_subscription() diff --git a/src/nbi/service/rest_server/nbi_plugins/__init__.py b/src/nbi/service/rest_server/nbi_plugins/__init__.py index 53d5157f750bfb085125cbd33faff1cec5924e14..9b5d7920db49bab6d456aba186e6500142aad5b2 100644 --- a/src/nbi/service/rest_server/nbi_plugins/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/__init__.py @@ -12,3 +12,27 @@ # See the License for the specific language governing permissions and # limitations under the License. +from flask.json import jsonify +from flask_restful import Resource + +from nbi.service.rest_server.RestServer import RestServer + +from .tools.HttpStatusCodes import HTTP_CREATED + +URL_PREFIX = "/restconf/data" + + +class BaseServer(Resource): + def post(self): + response = jsonify({}) + response.status_code = HTTP_CREATED + return response + + +def _add_resource(rest_server: RestServer, resource: Resource, *urls, **kwargs): + urls = [(URL_PREFIX + url) for url in urls] + rest_server.add_resource(resource, *urls, **kwargs) + + +def register_restconf(rest_server: RestServer): + _add_resource(rest_server, BaseServer, "") diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Service.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Service.py index 97364dff8606f1af48bab362b94b968561792411..bf3f8aabca1c04260d32a4163ec2686931f520fc 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Service.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Service.py @@ -26,7 +26,7 @@ from ..tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK LOGGER = logging.getLogger(__name__) class L3VPN_Service(Resource): - @HTTP_AUTH.login_required + # @HTTP_AUTH.login_required def get(self, vpn_id : str): LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id))) LOGGER.debug('Request: {:s}'.format(str(request))) @@ -52,7 +52,7 @@ class L3VPN_Service(Resource): response.status_code = HTTP_SERVERERROR return response - @HTTP_AUTH.login_required + # @HTTP_AUTH.login_required def delete(self, vpn_id : str): LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id))) LOGGER.debug('Request: {:s}'.format(str(request))) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py index 98d950952702d5cf1df8aa29edc50683e56a296e..47f6d5726225350976a67eeaacda64ceb32f0d7f 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py @@ -26,11 +26,11 @@ from .YangValidator import YangValidator LOGGER = logging.getLogger(__name__) class L3VPN_Services(Resource): - @HTTP_AUTH.login_required + # @HTTP_AUTH.login_required def get(self): return {} - @HTTP_AUTH.login_required + # @HTTP_AUTH.login_required def post(self): if not request.is_json: raise UnsupportedMediaType('JSON payload is required') request_data : Dict = request.json diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py index e25270d3601ffa1cfdffa68a98305e3646602001..8f6dc86609f8c0e4d51fa02c64ab7c59a377395a 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py @@ -1,4 +1,4 @@ -# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. @@ -13,47 +13,61 @@ # limitations under the License. import logging + from flask.json import jsonify from flask_restful import Resource + from common.proto.context_pb2 import SliceStatusEnum from common.tools.context_queries.Slice import get_slice_by_uuid from common.tools.grpc.Tools import grpc_message_to_json from context.client.ContextClient import ContextClient from slice.client.SliceClient import SliceClient + from ..tools.Authentication import HTTP_AUTH -from ..tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR +from ..tools.HttpStatusCodes import ( + HTTP_GATEWAYTIMEOUT, + HTTP_NOCONTENT, + HTTP_OK, + HTTP_SERVERERROR, +) LOGGER = logging.getLogger(__name__) + class NSS_Service(Resource): - @HTTP_AUTH.login_required - def get(self, slice_id : str): - LOGGER.debug('GET Slice ID: {:s}'.format(str(slice_id))) + # @HTTP_AUTH.login_required + def get(self, slice_id: str): + LOGGER.debug("GET Slice ID: {:s}".format(str(slice_id))) try: context_client = ContextClient() target = get_slice_by_uuid(context_client, slice_id, rw_copy=True) if target is None: - raise Exception('Slice({:s}) not found in database'.format(str(slice_id))) + raise Exception( + "Slice({:s}) not found in database".format(str(slice_id)) + ) - if target.slice_id.slice_uuid.uuid != slice_id: # pylint: disable=no-member - raise Exception('Slice retrieval failed. Wrong Slice Id was returned') + if target.slice_id.slice_uuid.uuid != slice_id: # pylint: disable=no-member + raise Exception("Slice retrieval failed. Wrong Slice Id was returned") slice_ready_status = SliceStatusEnum.SLICESTATUS_ACTIVE - slice_status = target.slice_status.slice_status # pylint: disable=no-member + slice_status = target.slice_status.slice_status # pylint: disable=no-member response = jsonify(grpc_message_to_json(target)) - response.status_code = HTTP_OK if slice_status == slice_ready_status else HTTP_GATEWAYTIMEOUT + response.status_code = ( + HTTP_OK if slice_status == slice_ready_status else HTTP_GATEWAYTIMEOUT + ) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Something went wrong Retrieving Slice({:s})'.format(str(slice_id))) - response = jsonify({'error': str(e)}) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception( + "Something went wrong Retrieving Slice({:s})".format(str(slice_id)) + ) + response = jsonify({"error": str(e)}) response.status_code = HTTP_SERVERERROR return response - - @HTTP_AUTH.login_required - def delete(self, slice_id : str): - LOGGER.debug('DELETE Slice ID: {:s}'.format(str(slice_id))) + # @HTTP_AUTH.login_required + def delete(self, slice_id: str): + LOGGER.debug("DELETE Slice ID: {:s}".format(str(slice_id))) try: context_client = ContextClient() target = get_slice_by_uuid(context_client, slice_id) @@ -62,17 +76,25 @@ class NSS_Service(Resource): response.status_code = HTTP_OK if target is None: - LOGGER.warning('Slice({:s}) not found in database. Nothing done.'.format(str(slice_id))) + LOGGER.warning( + "Slice({:s}) not found in database. Nothing done.".format( + str(slice_id) + ) + ) response.status_code = HTTP_NOCONTENT else: - if target.slice_id.slice_uuid.uuid != slice_id: # pylint: disable=no-member - raise Exception('Slice retrieval failed. Wrong Slice Id was returned') + if target.slice_id.slice_uuid.uuid != slice_id and target.name != slice_id: # pylint: disable=no-member + raise Exception( + "Slice retrieval failed. Wrong Slice Id was returned" + ) slice_client = SliceClient() slice_client.DeleteSlice(target.slice_id) LOGGER.debug(f"Slice({slice_id}) successfully deleted") except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Something went wrong Deleting Slice({:s})'.format(str(slice_id))) - response = jsonify({'error': str(e)}) + LOGGER.exception( + "Something went wrong Deleting Slice({:s})".format(str(slice_id)) + ) + response = jsonify({"error": str(e)}) response.status_code = HTTP_SERVERERROR - return response \ No newline at end of file + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service_Match_Criteria.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service_Match_Criteria.py new file mode 100644 index 0000000000000000000000000000000000000000..3e1c9f73f97f0df8a2b271cb34a4852e983e77a1 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service_Match_Criteria.py @@ -0,0 +1,59 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Dict + +from flask import request +from flask.json import jsonify +from flask_restful import Resource +from werkzeug.exceptions import UnsupportedMediaType + +from context.client.ContextClient import ContextClient +from slice.client.SliceClient import SliceClient + +from ..tools.Authentication import HTTP_AUTH +from ..tools.HttpStatusCodes import ( + HTTP_CREATED, +) +from .ietf_slice_handler import IETFSliceHandler + +LOGGER = logging.getLogger(__name__) + + +class NSS_Service_Match_Criteria(Resource): + # @HTTP_AUTH.login_required + def get(self): + response = jsonify({"message": "All went well!"}) + # TODO Return list of current network-slice-services + return response + + # @HTTP_AUTH.login_required + def post(self, slice_id: str, sdp_id: str): + if not request.is_json: + raise UnsupportedMediaType("JSON payload is required") + request_data: Dict = request.json + context_client = ContextClient() + slice_request = IETFSliceHandler.create_match_criteria( + request_data, slice_id, sdp_id, context_client + ) + slice_client = SliceClient() + slice_client.UpdateSlice(slice_request) + slice_request = IETFSliceHandler.copy_candidate_ietf_slice_data_to_running( + slice_id, context_client + ) + _ = context_client.SetSlice(slice_request) + + response = jsonify({}) + response.status_code = HTTP_CREATED + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service_Match_Criterion.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service_Match_Criterion.py new file mode 100644 index 0000000000000000000000000000000000000000..8fb8adfd98e461a43f2dc24121b84a59aaabbd6b --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service_Match_Criterion.py @@ -0,0 +1,48 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import logging + +from flask.json import jsonify +from flask_restful import Resource + +from context.client.ContextClient import ContextClient + +from ..tools.Authentication import HTTP_AUTH +from ..tools.HttpStatusCodes import ( + HTTP_CREATED, +) +from .ietf_slice_handler import IETFSliceHandler + +LOGGER = logging.getLogger(__name__) + + +class NSS_Service_Match_Criterion(Resource): + # @HTTP_AUTH.login_required + def get(self): + response = jsonify({"message": "All went well!"}) + # TODO Return list of current network-slice-services + return response + + # @HTTP_AUTH.login_required + def delete(self, slice_id: str, sdp_id: str, match_criterion_id: str): + context_client = ContextClient() + slice_request = IETFSliceHandler.delete_match_criteria( + slice_id, sdp_id, int(match_criterion_id), context_client + ) + context_client = ContextClient() + _ = context_client.SetSlice(slice_request) + + response = jsonify({}) + response.status_code = HTTP_CREATED + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py index 11a73141d6bd05db851d3019903e5a6db2f5c2d6..8398917a2c5d8c553efe59551962271d91759e9e 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py @@ -1,4 +1,4 @@ -# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. @@ -11,112 +11,44 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import json import logging -import ssl -import uuid from typing import Dict + +from flask import request from flask.json import jsonify from flask_restful import Resource -from flask import request - -from common.Constants import DEFAULT_CONTEXT_NAME -from common.proto.context_pb2 import Slice, SliceStatusEnum, EndPointId, Constraint -from common.tools.grpc.Tools import grpc_message_to_json -from ..tools.Authentication import HTTP_AUTH -from ..tools.HttpStatusCodes import HTTP_BADREQUEST, HTTP_OK, HTTP_CREATED, HTTP_SERVERERROR from werkzeug.exceptions import UnsupportedMediaType +from context.client.ContextClient import ContextClient from slice.client.SliceClient import SliceClient -from .bindings import load_json_data -from .bindings.network_slice_services import NetworkSliceServices + +from ..tools.HttpStatusCodes import HTTP_CREATED, HTTP_OK +from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) + class NSS_Services(Resource): - @HTTP_AUTH.login_required - def get(self): - response = jsonify({"message": "All went well!"}) - # TODO Return list of current network-slice-services + # @HTTP_AUTH.login_required + def get(self): + context_client = ContextClient() + ietf_slices = IETFSliceHandler.get_all_ietf_slices(context_client) + response = jsonify(ietf_slices) + response.status_code = HTTP_OK return response - @HTTP_AUTH.login_required + # @HTTP_AUTH.login_required def post(self): if not request.is_json: - raise UnsupportedMediaType('JSON payload is required') - request_data = json.dumps(request.json) + raise UnsupportedMediaType("JSON payload is required") + request_data: Dict = request.json + context_client = ContextClient() + slice_request = IETFSliceHandler.create_slice_service( + request_data, context_client + ) + slice_client = SliceClient() + slice_client.CreateSlice(slice_request) + response = jsonify({}) response.status_code = HTTP_CREATED - - slices: NetworkSliceServices = load_json_data(request_data, NetworkSliceServices)[0] - for ietf_slice in slices.slice_service: - slice_request: Slice = Slice() - # Meta information - # TODO implement name and owner based on "tags" - slice_request.slice_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME - slice_request.slice_id.slice_uuid.uuid = ietf_slice.service_id() - # TODO map with admin status of IETF Slice - slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED - - list_endpoints = [] - for sdp in ietf_slice.sdps().sdp: - endpoint = EndPointId() - endpoint.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME - endpoint.device_id.device_uuid.uuid = sdp.node_id() - endpoint.endpoint_uuid.uuid = sdp.sdp_id() - list_endpoints.append(endpoint) - slice_request.slice_endpoint_ids.extend(list_endpoints) - - # TODO Map connectivity_groups and connectivity constructs to real connections - LOGGER.debug(f"Connection groups detected: {len(ietf_slice.connection_groups().connection_group())}") - list_constraints = [] - for cg in ietf_slice.connection_groups().connection_group: - for cc in cg.connectivity_construct: - if cc.slo_sle_policy.custom: - with cc.slo_sle_policy.custom as slo: - for metric_bound in slo.service_slo_sle_policy().metric_bounds().metric_bound: - metric_type = str(metric_bound.metric_type()).casefold() - if metric_type == "service-slo-two-way-bandwidth": # TODO fix to two way! - constraint = Constraint() - metric_unit = metric_bound.metric_unit().casefold() - capacity = float(metric_bound.bound()) # Assuming capacity already in Gbps - if metric_unit == "mbps": - capacity /= 1E3 - elif metric_unit != "gbps": - LOGGER.warning(f"Invalided metric unit ({metric_bound.metric_unit()}), must be Mbps or Gbps") - response.status_code = HTTP_SERVERERROR - return response - constraint.sla_capacity.capacity_gbps = capacity - list_constraints.append(constraint) - - elif metric_type == "service-slo-one-way-delay": - if metric_bound.metric_unit().casefold() == "ms": - latency = int(metric_bound.bound()) - else: - LOGGER.warning(f"Invalided metric unit ({metric_bound.metric_unit()}), must be \"ms\" ") - response.status_code = HTTP_SERVERERROR - return response - constraint = Constraint() - constraint.sla_latency.e2e_latency_ms = latency - list_constraints.append(constraint) - - elif metric_type == "service-slo-availability": - availability = float(metric_bound.bound()) - if availability > 100.0 or availability < 0.0: - raise Exception(f'Slice SLO availability ({availability}) must be constrained [0,100]') - constraint = Constraint() - constraint.sla_availability.availability = availability - # TODO not really necessary, remove after OFC2023 - constraint.sla_availability.num_disjoint_paths = 0 - constraint.sla_availability.all_active = False - list_constraints.append(constraint) - - slice_request.slice_constraints.extend(list_constraints) - LOGGER.debug(grpc_message_to_json(slice_request)) # TODO remove - # TODO adding owner, needs to be recoded after updating the bindings - owner = request.json["data"]["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["service-tags"][0]["value"] - slice_request.slice_owner.owner_string = owner - slice_request.slice_owner.owner_uuid.uuid = str(uuid.uuid5(uuid.NAMESPACE_DNS, owner)) - slice_client = SliceClient() - slice_client.CreateSlice(slice_request) return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_Connection_Group.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_Connection_Group.py new file mode 100644 index 0000000000000000000000000000000000000000..0309c6ac475dec59d3219be79792fdef81e3d330 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_Connection_Group.py @@ -0,0 +1,75 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Dict + +from flask import request +from flask.json import jsonify +from flask_restful import Resource +from werkzeug.exceptions import UnsupportedMediaType + +from context.client.ContextClient import ContextClient +from slice.client.SliceClient import SliceClient + +from ..tools.Authentication import HTTP_AUTH +from ..tools.HttpStatusCodes import HTTP_CREATED +from .ietf_slice_handler import IETFSliceHandler + +LOGGER = logging.getLogger(__name__) + + +class NSS_Service_Connection_Group(Resource): + # @HTTP_AUTH.login_required + def get(self): + response = jsonify({"message": "All went well!"}) + # TODO Return list of current network-slice-services + return response + + # @HTTP_AUTH.login_required + def put(self, slice_id: str, connection_group_id: str): + if not request.is_json: + raise UnsupportedMediaType("JSON payload is required") + request_data: Dict = request.json + + context_client = ContextClient() + slice_request = IETFSliceHandler.update_connection_group( + slice_id, request_data, context_client + ) + slice_client = SliceClient() + slice_client.UpdateSlice(slice_request) + slice_request = IETFSliceHandler.copy_candidate_ietf_slice_data_to_running( + slice_id, context_client + ) + _ = context_client.SetSlice(slice_request) + + response = jsonify({}) + response.status_code = HTTP_CREATED + return response + + # @HTTP_AUTH.login_required + def delete(self, slice_id: str, connection_group_id: str): + context_client = ContextClient() + slice_request = IETFSliceHandler.delete_connection_group( + slice_id, connection_group_id, context_client + ) + slice_client = SliceClient() + slice_client.UpdateSlice(slice_request) + slice_request = IETFSliceHandler.copy_candidate_ietf_slice_data_to_running( + slice_id, context_client + ) + _ = context_client.SetSlice(slice_request) + + response = jsonify({}) + response.status_code = HTTP_CREATED + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_Connection_Groups.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_Connection_Groups.py new file mode 100644 index 0000000000000000000000000000000000000000..bee8349ef1949f0eb7f633ca4cbe6de6de1abbd6 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_Connection_Groups.py @@ -0,0 +1,52 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Dict + +from flask import request +from flask.json import jsonify +from flask_restful import Resource +from werkzeug.exceptions import UnsupportedMediaType + +from context.client.ContextClient import ContextClient + +from ..tools.Authentication import HTTP_AUTH +from ..tools.HttpStatusCodes import HTTP_CREATED +from .ietf_slice_handler import IETFSliceHandler + +LOGGER = logging.getLogger(__name__) + + +class NSS_Service_Connection_Groups(Resource): + # @HTTP_AUTH.login_required + def get(self): + response = jsonify({"message": "All went well!"}) + # TODO Return list of current network-slice-services + return response + + # @HTTP_AUTH.login_required + def post(self, slice_id: str): + if not request.is_json: + raise UnsupportedMediaType("JSON payload is required") + request_data: Dict = request.json + + context_client = ContextClient() + slice_request = IETFSliceHandler.create_connection_group( + request_data, slice_id, context_client + ) + _ = context_client.SetSlice(slice_request) + + response = jsonify({}) + response.status_code = HTTP_CREATED + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_SDP.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_SDP.py new file mode 100644 index 0000000000000000000000000000000000000000..f1d04a858907d016dc39a5a15a0d2771e25310af --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_SDP.py @@ -0,0 +1,45 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Dict + +from flask import request +from flask.json import jsonify +from flask_restful import Resource +from werkzeug.exceptions import UnsupportedMediaType + +from context.client.ContextClient import ContextClient + +from ..tools.HttpStatusCodes import HTTP_OK +from .ietf_slice_handler import IETFSliceHandler + +LOGGER = logging.getLogger(__name__) + + +class NSS_Service_SDP(Resource): + # @HTTP_AUTH.login_required + def get(self): + response = jsonify({"message": "All went well!"}) + # TODO Return list of current network-slice-services + return response + + # @HTTP_AUTH.login_required + def delete(self, slice_id: str, sdp_id: str): + context_client = ContextClient() + slice_request = IETFSliceHandler.delete_sdp(slice_id, sdp_id, context_client) + _ = context_client.SetSlice(slice_request) + + response = jsonify({}) + response.status_code = HTTP_OK + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_SDPs.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_SDPs.py new file mode 100644 index 0000000000000000000000000000000000000000..8a3fb8c4210b790dd25f9c0f8467b79ef5a86bc9 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_SDPs.py @@ -0,0 +1,51 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 typing import Dict + +from flask import request +from flask.json import jsonify +from flask_restful import Resource +from werkzeug.exceptions import UnsupportedMediaType + +from context.client.ContextClient import ContextClient + +from ..tools.HttpStatusCodes import HTTP_CREATED +from .ietf_slice_handler import IETFSliceHandler + +LOGGER = logging.getLogger(__name__) + + +class NSS_Service_SDPs(Resource): + # @HTTP_AUTH.login_required + def get(self): + response = jsonify({"message": "All went well!"}) + # TODO Return list of current network-slice-services + return response + + # @HTTP_AUTH.login_required + def post(self, slice_id: str): + if not request.is_json: + raise UnsupportedMediaType("JSON payload is required") + request_data: Dict = request.json + + context_client = ContextClient() + slice_request = IETFSliceHandler.create_sdp( + request_data, slice_id, context_client + ) + _ = context_client.SetSlice(slice_request) + + response = jsonify({}) + response.status_code = HTTP_CREATED + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/YangValidator.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/YangValidator.py new file mode 100644 index 0000000000000000000000000000000000000000..77071f7f7a72ad7d46e34f118a87dc2280a2a24c --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/YangValidator.py @@ -0,0 +1,36 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 libyang, os +from typing import Dict, Optional + +YANG_DIR = os.path.join(os.path.dirname(__file__), 'yang') + +class YangValidator: + def __init__(self, module_name : str) -> None: + self._yang_context = libyang.Context(YANG_DIR) + self._yang_module = self._yang_context.load_module(module_name) + self._yang_module.feature_enable_all() + + def parse_to_dict(self, message : Dict) -> Dict: + dnode : Optional[libyang.DNode] = self._yang_module.parse_data_dict( + message, validate_present=True, validate=True, strict=True + ) + if dnode is None: raise Exception('Unable to parse Message({:s})'.format(str(message))) + message = dnode.print_dict() + dnode.free() + return message + + def destroy(self) -> None: + self._yang_context.destroy() diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/__init__.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/__init__.py index e900c27e96aafc248e5db1bec303cb25b5a0f2d7..db76b3b911c971df8ab81b1710f4ac80e4ccb3ad 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. @@ -16,16 +16,60 @@ # Ref: https://datatracker.ietf.org/doc/draft-ietf-teas-ietf-network-slice-nbi-yang/ from flask_restful import Resource + from nbi.service.rest_server.RestServer import RestServer -from .NSS_Services import NSS_Services + from .NSS_Service import NSS_Service +from .NSS_Service_Match_Criteria import NSS_Service_Match_Criteria +from .NSS_Service_Match_Criterion import NSS_Service_Match_Criterion +from .NSS_Services import NSS_Services +from .NSS_Services_Connection_Group import NSS_Service_Connection_Group +from .NSS_Services_Connection_Groups import NSS_Service_Connection_Groups +from .NSS_Services_SDP import NSS_Service_SDP +from .NSS_Services_SDPs import NSS_Service_SDPs + +URL_PREFIX = "/restconf/data/ietf-network-slice-service" -URL_PREFIX = '/restconf/data/ietf-network-slice-service:ietf-nss' -def _add_resource(rest_server : RestServer, resource : Resource, *urls, **kwargs): +def _add_resource(rest_server: RestServer, resource: Resource, *urls, **kwargs): urls = [(URL_PREFIX + url) for url in urls] rest_server.add_resource(resource, *urls, **kwargs) -def register_ietf_nss(rest_server : RestServer): - _add_resource(rest_server, NSS_Services, '/network-slice-services') - _add_resource(rest_server, NSS_Service, '/network-slice-services/slice-service=<string:slice_id>') + +def register_ietf_nss(rest_server: RestServer): + _add_resource(rest_server, NSS_Services, ":network-slice-services") + _add_resource( + rest_server, + NSS_Service, + ":network-slice-services/slice-service=<string:slice_id>", + ) + _add_resource( + rest_server, + NSS_Service_SDPs, + ":network-slice-services/slice-service=<string:slice_id>/sdps", + ) + _add_resource( + rest_server, + NSS_Service_SDP, + ":network-slice-services/slice-service=<string:slice_id>/sdps/sdp=<string:sdp_id>", + ) + _add_resource( + rest_server, + NSS_Service_Connection_Groups, + ":network-slice-services/slice-service=<string:slice_id>/connection-groups", + ) + _add_resource( + rest_server, + NSS_Service_Connection_Group, + ":network-slice-services/slice-service=<string:slice_id>/connection-groups/connection-group=<string:connection_group_id>", + ) + _add_resource( + rest_server, + NSS_Service_Match_Criteria, + ":network-slice-services/slice-service=<string:slice_id>/sdps/sdp=<string:sdp_id>/service-match-criteria", + ) + _add_resource( + rest_server, + NSS_Service_Match_Criterion, + ":network-slice-services/slice-service=<string:slice_id>/sdps/sdp=<string:sdp_id>/service-match-criteria/match-criterion=<string:match_criterion_id>", + ) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/ietf_slice_handler.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/ietf_slice_handler.py new file mode 100644 index 0000000000000000000000000000000000000000..80ce4c6b78d446ff1e08a750f236e0c143e1ba57 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/ietf_slice_handler.py @@ -0,0 +1,640 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import logging +import uuid +from typing import Dict, List, Optional + +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import ( + ConfigRule, + Constraint, + DeviceId, + Device, + Empty, + EndPointId, + ServiceConfig, + Slice, + SliceStatusEnum, +) +from common.tools.context_queries.Slice import get_slice_by_defualt_name +from common.tools.grpc.ConfigRules import update_config_rule_custom +from common.tools.object_factory.Device import json_device_id +from common.DeviceTypes import DeviceTypeEnum +from context.client import ContextClient + +from .YangValidator import YangValidator + +LOGGER = logging.getLogger(__name__) + + +RUNNING_RESOURCE_KEY = "running_ietf_slice" +CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" +ADDRESS_PREFIX = 24 +RAISE_IF_DIFFERS = False + + +def validate_ietf_slice_data(request_data: Dict) -> None: + """ + Validate the provided IETF slice data against the YANG model. + """ + yang_validator = YangValidator("ietf-network-slice-service") + _ = yang_validator.parse_to_dict(request_data) + yang_validator.destroy() + + +def get_custom_config_rule( + service_config: ServiceConfig, resource_key: str +) -> Optional[ConfigRule]: + """ + Retrieve the custom config rule with the given resource_key from a ServiceConfig. + """ + for cr in service_config.config_rules: + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key == resource_key + ): + return cr + return None + + +def get_ietf_data_from_config(slice_request: Slice, resource_key: str) -> Dict: + """ + Retrieve the IETF data (as a Python dict) from a slice's config rule for the specified resource_key. + Raises an exception if not found. + """ + config_rule = get_custom_config_rule(slice_request.slice_config, resource_key) + if not config_rule: + raise Exception(f"IETF data not found for resource_key: {resource_key}") + return json.loads(config_rule.custom.resource_value) + + +def update_ietf_data_in_config( + slice_request: Slice, resource_key: str, ietf_data: Dict +) -> None: + """ + Update the slice config rule (identified by resource_key) with the provided IETF data. + """ + fields = {name: (value, RAISE_IF_DIFFERS) for name, value in ietf_data.items()} + update_config_rule_custom( + slice_request.slice_config.config_rules, resource_key, fields + ) + + +def build_constraints_from_connection_group(connection_group: dict) -> List[Constraint]: + """ + Build a list of Constraints from the 'metric-bound' data in a connection group. + """ + constraints = [] + metric_bounds = connection_group["connectivity-construct"][0][ + "service-slo-sle-policy" + ]["slo-policy"]["metric-bound"] + + for metric in metric_bounds: + metric_type = metric["metric-type"] + if metric_type == "ietf-nss:one-way-delay-maximum": + bound_value = float(metric["bound"]) + constraint = Constraint() + constraint.sla_latency.e2e_latency_ms = bound_value + constraints.append(constraint) + elif metric_type == "ietf-nss:one-way-bandwidth": + bound_value = float(metric["bound"]) + constraint = Constraint() + # Convert from Mbps to Gbps if needed + constraint.sla_capacity.capacity_gbps = bound_value / 1.0e3 + constraints.append(constraint) + + return constraints + + +def get_endpoint_controller_type( + endpoint: EndPointId, context_client: ContextClient +) -> str: + """ + Retrieve the device type of an endpoint's controller device, if any; otherwise returns an empty string. + """ + endpoint_device: Device = context_client.GetDevice(endpoint.device_id) + if endpoint_device.controller_id == DeviceId(): + return "" + controller = context_client.GetDevice(endpoint_device.controller_id) + if controller is None: + controller_uuid = endpoint_device.controller_id.device_uuid.uuid + raise Exception(f"Controller device {controller_uuid} not found") + return controller.device_type + + +def sort_endpoints( + endpoints_list: List[EndPointId], + sdps: List, + connection_group: Dict, + context_client: ContextClient, +) -> List[EndPointId]: + """ + Sort the endpoints_list based on controller type: + - If the first endpoint is an NCE, keep order. + - If the last endpoint is an NCE, reverse order. + - Otherwise, use the 'p2p-sender-sdp' from the connection group to decide. + """ + if not endpoints_list: + return endpoints_list + + first_ep = endpoints_list[0] + last_ep = endpoints_list[-1] + first_controller_type = get_endpoint_controller_type(first_ep, context_client) + last_controller_type = get_endpoint_controller_type(last_ep, context_client) + + if first_controller_type == DeviceTypeEnum.NCE.value: + return endpoints_list + elif last_controller_type == DeviceTypeEnum.NCE.value: + return endpoints_list[::-1] + + src_sdp_id = connection_group["connectivity-construct"][0]["p2p-sender-sdp"] + sdp_id_name_mapping = {sdp["id"]: sdp["node-id"] for sdp in sdps} + if endpoints_list[0].device_id.device_uuid.uuid == sdp_id_name_mapping[src_sdp_id]: + return endpoints_list + return endpoints_list[::-1] + + +def replace_ont_endpoint_with_emu_dc( + endpoint_list: List[EndPointId], context_client: ContextClient +) -> List[EndPointId]: + """ + Replace an ONT endpoint in endpoint_list with an 'emu-datacenter' endpoint if found. + One endpoint must be managed (controller_id != empty), the other must be unmanaged. + """ + if len(endpoint_list) != 2: + raise Exception( + "Expecting exactly two endpoints to handle ONT -> emu-dc replacement" + ) + + link_list = context_client.ListLinks(Empty()) + links = list(link_list.links) + devices_list = context_client.ListDevices(Empty()) + devices = devices_list.devices + + uuid_name_map = {d.device_id.device_uuid.uuid: d.name for d in devices} + uuid_device_map = {d.device_id.device_uuid.uuid: d for d in devices} + name_device_map = {d.name: d for d in devices} + + endpoint_id_1, endpoint_id_2 = endpoint_list + device_uuid_1 = endpoint_id_1.device_id.device_uuid.uuid + device_uuid_2 = endpoint_id_2.device_id.device_uuid.uuid + + device_1 = name_device_map.get(device_uuid_1) + device_2 = name_device_map.get(device_uuid_2) + + if not device_1 or not device_2: + raise Exception("One or both devices not found in name_device_map") + + # Check if the first endpoint is managed + if device_1.controller_id != DeviceId(): + for link in links: + link_endpoints = list(link.link_endpoint_ids) + link_ep_1, link_ep_2 = link_endpoints + if ( + device_uuid_1 == uuid_name_map.get(link_ep_1.device_id.device_uuid.uuid) + and uuid_device_map[link_ep_2.device_id.device_uuid.uuid].device_type + == "emu-datacenter" + ): + endpoint_list[0] = link_ep_2 + break + # Otherwise, check if the second endpoint is managed + elif device_2.controller_id != DeviceId(): + for link in links: + link_endpoints = list(link.link_endpoint_ids) + link_ep_1, link_ep_2 = link_endpoints + if ( + device_uuid_2 == uuid_name_map.get(link_ep_1.device_id.device_uuid.uuid) + and uuid_device_map[link_ep_2.device_id.device_uuid.uuid].device_type + == "emu-datacenter" + ): + endpoint_list[1] = link_ep_2 + break + else: + raise Exception( + "One endpoint should be managed by a controller and the other should not be" + ) + + return endpoint_list + + +class IETFSliceHandler: + @staticmethod + def get_all_ietf_slices(context_client: ContextClient) -> Dict: + """ + Retrieve all IETF slices from the (single) context. Expects exactly one context in the system. + """ + existing_context_ids = context_client.ListContextIds(Empty()) + context_ids = list(existing_context_ids.context_ids) + if len(context_ids) != 1: + raise Exception("Number of contexts should be exactly 1") + + slices_list = context_client.ListSlices(context_ids[0]) + slices = slices_list.slices + + ietf_slices = {"network-slice-services": {"slice-service": []}} + for slc in slices: + candidate_cr = get_custom_config_rule( + slc.slice_config, CANDIDATE_RESOURCE_KEY + ) + if not candidate_cr: + # Skip slices that don't have the candidate_ietf_slice data + continue + candidate_ietf_data = json.loads(candidate_cr.custom.resource_value) + ietf_slices["network-slice-services"]["slice-service"].append( + candidate_ietf_data["network-slice-services"]["slice-service"][0] + ) + return ietf_slices + + @staticmethod + def create_slice_service( + request_data: dict, context_client: ContextClient + ) -> Slice: + """ + Create a new slice service from the provided IETF data, applying validations and constructing a Slice object. + """ + # Ensure the top-level key is "network-slice-services" + if "network-slice-services" not in request_data: + request_data = {"network-slice-services": request_data} + + validate_ietf_slice_data(request_data) + slice_service = request_data["network-slice-services"]["slice-service"][0] + + slice_id = slice_service["id"] + sdps = slice_service["sdps"]["sdp"] + if len(sdps) != 2: + raise Exception("Number of SDPs should be exactly 2") + + connection_groups = slice_service["connection-groups"]["connection-group"] + slice_request = Slice() + slice_request.slice_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME + slice_request.slice_id.slice_uuid.uuid = slice_id + slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED + + list_endpoints = [] + endpoint_config_rules = [] + connection_group_ids = set() + + # Build endpoints from SDPs + for sdp in sdps: + attachment_circuits = sdp["attachment-circuits"]["attachment-circuit"] + if len(attachment_circuits) != 1: + raise Exception("Each SDP must have exactly 1 attachment-circuit") + + endpoint = EndPointId() + endpoint.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME + device_uuid = sdp["node-id"] + endpoint.device_id.device_uuid.uuid = device_uuid + endpoint_uuid = attachment_circuits[0]["ac-tp-id"] + endpoint.endpoint_uuid.uuid = endpoint_uuid + list_endpoints.append(endpoint) + + # Keep track of connection-group-id from each SDP + connection_group_ids.add( + sdp["service-match-criteria"]["match-criterion"][0][ + "target-connection-group-id" + ] + ) + + # Endpoint-specific config rule fields + endpoint_config_rule_fields = { + "address_ip": (endpoint_uuid, RAISE_IF_DIFFERS), + "address_prefix": (ADDRESS_PREFIX, RAISE_IF_DIFFERS), + } + endpoint_config_rules.append( + ( + f"/device[{device_uuid}]/endpoint[{endpoint_uuid}]/settings", + endpoint_config_rule_fields, + ) + ) + + if len(connection_group_ids) != 1: + raise Exception("SDPs do not share a common connection-group-id") + + # Build constraints from the matching connection group + unique_cg_id = connection_group_ids.pop() + found_cg = next( + (cg for cg in connection_groups if cg["id"] == unique_cg_id), None + ) + if not found_cg: + raise Exception("The connection group referenced by the SDPs was not found") + + list_constraints = build_constraints_from_connection_group(found_cg) + + # Sort endpoints and optionally replace the ONT endpoint + list_endpoints = sort_endpoints(list_endpoints, sdps, found_cg, context_client) + list_endpoints = replace_ont_endpoint_with_emu_dc( + list_endpoints, context_client + ) + + slice_request.slice_endpoint_ids.extend(list_endpoints) + slice_request.slice_constraints.extend(list_constraints) + + # Set slice owner + slice_request.slice_owner.owner_string = slice_id + slice_request.slice_owner.owner_uuid.uuid = str( + uuid.uuid5(uuid.NAMESPACE_DNS, slice_id) + ) + + # Update slice config with IETF data (both running and candidate) + ietf_slice_fields = { + name: (value, RAISE_IF_DIFFERS) for name, value in request_data.items() + } + update_config_rule_custom( + slice_request.slice_config.config_rules, + RUNNING_RESOURCE_KEY, + ietf_slice_fields, + ) + update_config_rule_custom( + slice_request.slice_config.config_rules, + CANDIDATE_RESOURCE_KEY, + ietf_slice_fields, + ) + + # Update endpoint config rules + for ep_cr_key, ep_cr_fields in endpoint_config_rules: + update_config_rule_custom( + slice_request.slice_config.config_rules, ep_cr_key, ep_cr_fields + ) + + return slice_request + + @staticmethod + def create_sdp( + request_data: dict, slice_uuid: str, context_client: ContextClient + ) -> Slice: + """ + Add a new SDP to an existing slice, updating the candidate IETF data. + """ + sdps = request_data["sdp"] + if len(sdps) != 1: + raise Exception("Number of SDPs to create must be exactly 1") + + new_sdp = sdps[0] + slice_request = get_slice_by_defualt_name( + context_client, slice_uuid, rw_copy=False + ) + ietf_data = get_ietf_data_from_config(slice_request, CANDIDATE_RESOURCE_KEY) + + slice_service = ietf_data["network-slice-services"]["slice-service"][0] + slice_sdps = slice_service["sdps"]["sdp"] + slice_sdps.append(new_sdp) + + # Save updated IETF data + update_ietf_data_in_config(slice_request, CANDIDATE_RESOURCE_KEY, ietf_data) + return slice_request + + @staticmethod + def delete_sdp( + slice_uuid: str, sdp_id: str, context_client: ContextClient + ) -> Slice: + """ + Delete the specified SDP from an existing slice's candidate IETF data. + """ + slice_request = get_slice_by_defualt_name( + context_client, slice_uuid, rw_copy=False + ) + ietf_data = get_ietf_data_from_config(slice_request, CANDIDATE_RESOURCE_KEY) + + slice_service = ietf_data["network-slice-services"]["slice-service"][0] + slice_sdps = slice_service["sdps"]["sdp"] + + # Find and remove the matching SDP + sdp_idx = next( + (i for i, sdp in enumerate(slice_sdps) if sdp["id"] == sdp_id), None + ) + if sdp_idx is None: + raise Exception(f"SDP with id '{sdp_id}' not found in slice '{slice_uuid}'") + slice_sdps.pop(sdp_idx) + + update_ietf_data_in_config(slice_request, CANDIDATE_RESOURCE_KEY, ietf_data) + return slice_request + + @staticmethod + def create_connection_group( + request_data: dict, slice_id: str, context_client: ContextClient + ) -> Slice: + """ + Add a new connection group to an existing slice's candidate IETF data. + """ + connection_groups = request_data["connection-group"] + if len(connection_groups) != 1: + raise Exception("Number of connection groups to create must be exactly 1") + + new_connection_group = connection_groups[0] + slice_request = get_slice_by_defualt_name( + context_client, slice_id, rw_copy=False + ) + ietf_data = get_ietf_data_from_config(slice_request, CANDIDATE_RESOURCE_KEY) + + slice_service = ietf_data["network-slice-services"]["slice-service"][0] + slice_connection_groups = slice_service["connection-groups"]["connection-group"] + slice_connection_groups.append(new_connection_group) + + # Validate the updated data, then save + validate_ietf_slice_data(ietf_data) + update_ietf_data_in_config(slice_request, CANDIDATE_RESOURCE_KEY, ietf_data) + return slice_request + + @staticmethod + def update_connection_group( + slice_name: str, + updated_connection_group: dict, + context_client: ContextClient, + ) -> Slice: + """ + Update an existing connection group in the candidate IETF data. + """ + slice_request = get_slice_by_defualt_name( + context_client, slice_name, rw_copy=False + ) + candidate_ietf_data = get_ietf_data_from_config( + slice_request, CANDIDATE_RESOURCE_KEY + ) + + slice_service = candidate_ietf_data["network-slice-services"]["slice-service"][ + 0 + ] + slice_connection_groups = slice_service["connection-groups"]["connection-group"] + + cg_id = updated_connection_group["id"] + cg_idx = next( + (i for i, cg in enumerate(slice_connection_groups) if cg["id"] == cg_id), + None, + ) + if cg_idx is None: + raise Exception(f"Connection group with id '{cg_id}' not found") + + slice_connection_groups[cg_idx] = updated_connection_group + update_ietf_data_in_config( + slice_request, CANDIDATE_RESOURCE_KEY, candidate_ietf_data + ) + + slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED + return slice_request + + @staticmethod + def delete_connection_group( + slice_uuid: str, connection_group_id: str, context_client: ContextClient + ) -> Slice: + """ + Remove an existing connection group from the candidate IETF data of a slice. + """ + slice_request = get_slice_by_defualt_name( + context_client, slice_uuid, rw_copy=False + ) + candidate_ietf_data = get_ietf_data_from_config( + slice_request, CANDIDATE_RESOURCE_KEY + ) + + slice_service = candidate_ietf_data["network-slice-services"]["slice-service"][ + 0 + ] + slice_connection_groups = slice_service["connection-groups"]["connection-group"] + + cg_idx = next( + ( + i + for i, cg in enumerate(slice_connection_groups) + if cg["id"] == connection_group_id + ), + None, + ) + if cg_idx is None: + raise Exception( + f"Connection group with id '{connection_group_id}' not found" + ) + + slice_connection_groups.pop(cg_idx) + update_ietf_data_in_config( + slice_request, CANDIDATE_RESOURCE_KEY, candidate_ietf_data + ) + + slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED + return slice_request + + @staticmethod + def create_match_criteria( + request_data: dict, slice_name: str, sdp_id: str, context_client: ContextClient + ) -> Slice: + """ + Create a new match-criterion for the specified SDP in a slice's candidate IETF data. + """ + match_criteria = request_data["match-criterion"] + if len(match_criteria) != 1: + raise Exception( + "Number of match-criterion entries to create must be exactly 1" + ) + + new_match_criterion = match_criteria[0] + target_connection_group_id = new_match_criterion["target-connection-group-id"] + + slice_request = get_slice_by_defualt_name( + context_client, slice_name, rw_copy=False + ) + ietf_data = get_ietf_data_from_config(slice_request, CANDIDATE_RESOURCE_KEY) + + slice_service = ietf_data["network-slice-services"]["slice-service"][0] + connection_groups = slice_service["connection-groups"]["connection-group"] + sdps = slice_service["sdps"]["sdp"] + + # Find the referenced connection group + found_cg = next( + (cg for cg in connection_groups if cg["id"] == target_connection_group_id), + None, + ) + if not found_cg: + raise Exception( + f"Connection group '{target_connection_group_id}' not found" + ) + + # Build constraints from that connection group + list_constraints = build_constraints_from_connection_group(found_cg) + + # Add match-criterion to the relevant SDP + sdp_to_update = next((s for s in sdps if s["id"] == sdp_id), None) + if not sdp_to_update: + raise Exception(f"SDP '{sdp_id}' not found") + + sdp_to_update["service-match-criteria"]["match-criterion"].append( + new_match_criterion + ) + + # Update constraints at the slice level as needed + del slice_request.slice_constraints[:] + slice_request.slice_constraints.extend(list_constraints) + slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED + + update_ietf_data_in_config(slice_request, CANDIDATE_RESOURCE_KEY, ietf_data) + return slice_request + + @staticmethod + def delete_match_criteria( + slice_uuid: str, + sdp_id: str, + match_criterion_id: int, + context_client: ContextClient, + ) -> Slice: + """ + Delete the specified match-criterion from an SDP in the slice's candidate IETF data. + """ + slice_request = get_slice_by_defualt_name( + context_client, slice_uuid, rw_copy=False + ) + ietf_data = get_ietf_data_from_config(slice_request, CANDIDATE_RESOURCE_KEY) + + slice_service = ietf_data["network-slice-services"]["slice-service"][0] + sdps = slice_service["sdps"]["sdp"] + + # Find and modify the specified SDP + sdp_to_update = next((s for s in sdps if s["id"] == sdp_id), None) + if not sdp_to_update: + raise Exception(f"SDP '{sdp_id}' not found in slice '{slice_uuid}'") + + match_criteria = sdp_to_update["service-match-criteria"]["match-criterion"] + mc_index = next( + ( + i + for i, m in enumerate(match_criteria) + if m["index"] == match_criterion_id + ), + None, + ) + if mc_index is None: + raise Exception( + f"No match-criterion with index '{match_criterion_id}' found in SDP '{sdp_id}'" + ) + + match_criteria.pop(mc_index) + update_ietf_data_in_config(slice_request, CANDIDATE_RESOURCE_KEY, ietf_data) + return slice_request + + @staticmethod + def copy_candidate_ietf_slice_data_to_running( + slice_uuid: str, context_client: ContextClient + ) -> Slice: + """ + Copy candidate IETF slice data to the running IETF slice data for a given slice. + """ + slice_request = get_slice_by_defualt_name( + context_client, slice_uuid, rw_copy=False + ) + candidate_ietf_data = get_ietf_data_from_config( + slice_request, CANDIDATE_RESOURCE_KEY + ) + update_ietf_data_in_config( + slice_request, RUNNING_RESOURCE_KEY, candidate_ietf_data + ) + return slice_request diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ac-common@2023-11-13.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ac-common@2023-11-13.yang new file mode 100644 index 0000000000000000000000000000000000000000..170e70fff67242b380fc984d815e3d83557eca06 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ac-common@2023-11-13.yang @@ -0,0 +1,1651 @@ +module ietf-ac-common { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-ac-common"; + prefix ac-common; + + import ietf-vpn-common { + prefix vpn-common; + reference + "RFC 9181: A Common YANG Data Model for Layer 2 and Layer 3 + VPNs"; + } + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types, Section 4"; + } + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types, Section 3"; + } + import ietf-key-chain { + prefix key-chain; + reference + "RFC 8177: YANG Data Model for Key Chains"; + } + + organization + "IETF OPSAWG (Operations and Management Area Working Group)"; + contact + "WG Web: <https://datatracker.ietf.org/wg/opsawg/> + WG List: <mailto:opsawg@ietf.org> + + Editor: Mohamed Boucadair + <mailto:mohamed.boucadair@orange.com> + Author: Richard Roberts + <mailto:rroberts@juniper.net> + Author: Oscar Gonzalez de Dios + <mailto:oscar.gonzalezdedios@telefonica.com> + Author: Samier Barguil + <mailto:ssamier.barguil_giraldo@nokia.com> + Author: Bo Wu + <mailto:lana.wubo@huawei.com>"; + description + "This YANG module defines a common attachment circuit (AC) + YANG model. + + Copyright (c) 2024 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Revised BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see the + RFC itself for full legal notices."; + + revision 2023-11-13 { + description + "Initial revision."; + reference + "RFC XXXX: A Common YANG Data Model for Attachment Circuits"; + } + + /****************************Features************************/ + + feature layer2-ac { + description + "Indicates support of Layer 2 ACs."; + } + + feature layer3-ac { + description + "Indicates support of Layer 3 ACs."; + } + + feature server-assigned-reference { + description + "This feature indicates support for server-generated references + and use of such references to access related resources."; + } + + /****************************Identities************************/ + // IP address allocation types + + identity address-allocation-type { + description + "Base identity for address allocation type in the AC."; + } + + identity provider-dhcp { + base address-allocation-type; + description + "The provider's network provides a DHCP service to the + customer."; + } + + identity provider-dhcp-relay { + base address-allocation-type; + description + "The provider's network provides a DHCP relay service to the + customer."; + } + + identity provider-dhcp-slaac { + if-feature "vpn-common:ipv6"; + base address-allocation-type; + description + "The provider's network provides a DHCP service to the customer + as well as IPv6 Stateless Address Autoconfiguration (SLAAC)."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration"; + } + + identity static-address { + base address-allocation-type; + description + "The provider's network provides static IP addressing to the + customer."; + } + + identity slaac { + if-feature "vpn-common:ipv6"; + base address-allocation-type; + description + "The provider's network uses IPv6 SLAAC to provide addressing + to the customer."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration"; + } + + identity dynamic-infra { + base address-allocation-type; + description + "The IP address is dynamically allocated by the hosting + infrastrcture."; + } + + // next-hop actions + + identity local-defined-next-hop { + description + "Base identity of local defined next hops."; + } + + identity discard { + base local-defined-next-hop; + description + "Indicates an action to discard traffic for the corresponding + destination. For example, this can be used to black-hole + traffic."; + } + + identity local-link { + base local-defined-next-hop; + description + "Treat traffic towards addresses within the specified next-hop + prefix as though they are connected to a local link."; + } + + // Layer 2 tunnel types + + identity l2-tunnel-type { + description + "Base identity for Layer 2 tunnel selection for an AC."; + } + + identity pseudowire { + base l2-tunnel-type; + description + "Pseudowire tunnel termination for the AC."; + } + + identity vpls { + base l2-tunnel-type; + description + "Virtual Private LAN Service (VPLS) tunnel termination for + the AC."; + } + + identity vxlan { + base l2-tunnel-type; + description + "Virtual eXtensible Local Area Network (VXLAN) tunnel + termination for the AC."; + } + + // Layer 3 tunnel types + + identity l3-tunnel-type { + description + "Base identity for Layer 3 tunnel selection for an AC."; + } + + identity ip-in-ip { + base l3-tunnel-type; + description + "IP in IP Tunneling."; + } + + identity ipsec { + base l3-tunnel-type; + description + "IP Security (IPsec)."; + } + + identity gre { + base l3-tunnel-type; + description + "Generic Routing Encapsulation (GRE)."; + } + + // Tagging precedence + + identity precedence-type { + description + "Redundancy type. The service can be created with primary and + secondary tagging."; + } + + identity primary { + base precedence-type; + description + "Identifies the main attachment circuit."; + } + + identity secondary { + base precedence-type; + description + "Identifies the secondary attachment circuit."; + } + // AC Type + + identity role { + description + "Base identity for the network role of an AC."; + } + + identity uni { + base role; + description + "User-to-Network Interface (UNI)."; + } + + identity nni { + base role; + description + "Network-to-Network Interface (NNI)."; + } + + identity public-nni { + base role; + description + "Public peering."; + } + + // More Admin status types + + identity awaiting-validation { + base vpn-common:administrative-status; + description + "This administrative status reflects that a request is + pending an adiministrator approval."; + } + + identity awaiting-processing { + base vpn-common:administrative-status; + description + "This administrative status reflects that a request was + approved and validated, but is awaiting more processing + before activation."; + } + + identity admin-prohibited { + base vpn-common:administrative-status; + description + "This administrative status reflects that a request cannot + be handled because of administrative policies."; + } + identity rejected { + base vpn-common:administrative-status; + description + "This administrative status reflects that a request was + rejected because, e.g., there are no sufficient resources + or other reasons not covered by the other status types."; + } + + identity bgp-role { + description + "Used to indicate BGP role when establishing a BGP session."; + reference + "RFC 9234: Route Leak Prevention and Detection Using + Roles in UPDATE and OPEN Messages, Section 4"; + } + + identity provider { + base bgp-role; + description + "The local AS is a transit provider of the remote AS."; + } + + identity client { + base bgp-role; + description + "The local AS is a transit provider of the remote AS."; + } + + identity rs { + base bgp-role; + description + "The local AS is a Route Server (RS)."; + } + + identity rs-client { + base bgp-role; + description + "The local AS is a client of an RS and the RS is the + remote AS."; + } + + identity peer { + base bgp-role; + description + "The local and remote ASes have a peering relationship."; + } + + /****************************Typedefs************************/ + typedef predefined-next-hop { + type identityref { + base local-defined-next-hop; + } + description + "Predefined next-hop designation for locally generated + routes."; + } + + typedef area-address { + type string { + pattern '[0-9A-Fa-f]{2}(\.[0-9A-Fa-f]{4}){0,6}'; + } + description + "This type defines the area address format."; + } + + /************************Reusable groupings********************/ + /**** Service Status ****/ + + grouping service-status { + description + "Service status grouping."; + container status { + description + "Service status."; + container admin-status { + description + "Administrative service status."; + leaf status { + type identityref { + base vpn-common:administrative-status; + } + description + "Administrative service status."; + } + leaf last-change { + type yang:date-and-time; + config false; + description + "Indicates the actual date and time of the service + status change."; + } + } + container oper-status { + config false; + description + "Operational service status."; + uses vpn-common:oper-status-timestamp; + } + } + } + + /**** A set of profiles ****/ + + grouping ac-profile-cfg { + description + "Grouping for AC profile configuration."; + container valid-provider-identifiers { + description + "Container for valid provider profile identifiers. + The profiles only have significance within the service + provider's administrative domain."; + list encryption-profile-identifier { + key "id"; + description + "List of encryption profile identifiers."; + leaf id { + type string; + description + "Identification of the encryption profile to be used."; + } + } + list qos-profile-identifier { + key "id"; + description + "List of QoS profile identifiers."; + leaf id { + type string; + description + "Identification of the QoS profile to be used."; + } + } + list failure-detection-profile-identifier { + key "id"; + description + "List of BFD profile identifiers."; + leaf id { + type string; + description + "Identification of the a failure detection (e.g., BFD) + profile to be used."; + } + } + list forwarding-profile-identifier { + key "id"; + description + "List of forwarding profile identifiers."; + leaf id { + type string; + description + "Identification of the forwarding profile to be used."; + } + } + list routing-profile-identifier { + key "id"; + description + "List of routing profile identifiers."; + leaf id { + type string; + description + "Identification of the routing profile to be used by + the routing protocols over an AC."; + } + } + nacm:default-deny-write; + } + } + + /**** Operational instructions ****/ + + grouping op-instructions { + description + "Scheduling instructions."; + leaf requested-start { + type yang:date-and-time; + description + "Indicates the requested date and time when the service is + expected to be active."; + } + leaf requested-stop { + type yang:date-and-time; + description + "Indicates the requested date and time when the service is + expected to be disabled."; + } + leaf actual-start { + type yang:date-and-time; + config false; + description + "Indicates the actual date and time when the service + actually was enabled."; + } + leaf actual-stop { + type yang:date-and-time; + config false; + description + "Indicates the actual date and time when the service + actually was disabled."; + } + } + + /**** Layer 2 encapsulations ****/ + // Dot1q + + grouping dot1q { + description + "Defines a grouping for tagged interfaces."; + leaf tag-type { + type identityref { + base vpn-common:tag-type; + } + description + "Tag type."; + } + leaf cvlan-id { + type uint16 { + range "1..4094"; + } + description + "VLAN identifier."; + } + } + + // priority-tagged + + grouping priority-tagged { + description + "Priority tagged."; + leaf tag-type { + type identityref { + base vpn-common:tag-type; + } + description + "Tag type."; + } + } + + // QinQ + + grouping qinq { + description + "Includes QinQ parameters."; + leaf tag-type { + type identityref { + base vpn-common:tag-type; + } + description + "Tag type."; + } + leaf svlan-id { + type uint16 { + range "1..4094"; + } + description + "Service VLAN (S-VLAN) identifier."; + } + leaf cvlan-id { + type uint16 { + range "1..4094"; + } + description + "Customer VLAN (C-VLAN) identifier."; + } + } + + /**** Layer 2 tunnel services ****/ + // pseudowire (PW) + + grouping pseudowire { + description + "Includes pseudowire termination parameters."; + leaf vcid { + type uint32; + description + "Indicates a PW or virtual circuit (VC) identifier."; + } + leaf far-end { + type union { + type uint32; + type inet:ip-address; + } + description + "Neighbor reference."; + reference + "RFC 8077: Pseudowire Setup and Maintenance Using the Label + Distribution Protocol (LDP), Section 6.1"; + } + } + // VPLS + + grouping vpls { + description + "VPLS termination parameters."; + leaf vcid { + type uint32; + description + "VC identifier."; + } + leaf-list far-end { + type union { + type uint32; + type inet:ip-address; + } + description + "Neighbor reference."; + } + } + + // VXLAN + + grouping vxlan { + description + "VXLAN termination parameters."; + leaf vni-id { + type uint32; + description + "VXLAN Network Identifier (VNI)."; + } + leaf peer-mode { + type identityref { + base vpn-common:vxlan-peer-mode; + } + description + "Specifies the VXLAN access mode. By default, + the peer mode is set to 'static-mode'."; + } + leaf-list peer-ip-address { + type inet:ip-address; + description + "List of a peer's IP addresses."; + } + } + + // Layer 2 Tunnel service + + grouping l2-tunnel-service { + description + "Defines a Layer 2 tunnel termination."; + leaf type { + type identityref { + base l2-tunnel-type; + } + description + "Selects the tunnel termination type for an AC."; + } + container pseudowire { + when "derived-from-or-self(../type, 'ac-common:pseudowire')" { + description + "Only applies when the Layer 2 service type is + 'pseudowire'."; + } + description + "Includes pseudowire termination parameters."; + uses pseudowire; + } + container vpls { + when "derived-from-or-self(../type, 'ac-common:vpls')" { + description + "Only applies when the Layer 2 service type is 'vpls'."; + } + description + "VPLS termination parameters."; + uses vpls; + } + container vxlan { + when "derived-from-or-self(../type, 'ac-common:vxlan')" { + description + "Only applies when the Layer 2 service type is 'vxlan'."; + } + description + "VXLAN termination parameters."; + uses vxlan; + } + } + + /**** Layer 3 connection *****/ + // IPv4 allocation type + + grouping ipv4-allocation-type { + description + "IPv4-specific parameters."; + leaf prefix-length { + type uint8 { + range "0..32"; + } + description + "Subnet prefix length expressed in bits. It is applied to + both local and customer addresses."; + } + leaf address-allocation-type { + type identityref { + base address-allocation-type; + } + must "not(derived-from-or-self(current(), 'ac-common:slaac') " + + "or derived-from-or-self(current(), " + + "'ac-common:provider-dhcp-slaac'))" { + error-message "SLAAC is only applicable to IPv6."; + } + description + "Defines how IPv4 addresses are allocated to the peer site."; + } + } + + // IPv6 allocation type + + grouping ipv6-allocation-type { + description + "IPv6-specific parameters."; + leaf prefix-length { + type uint8 { + range "0..128"; + } + description + "Subnet prefix length expressed in bits. It is applied to + both local and customer addresses."; + } + leaf address-allocation-type { + type identityref { + base address-allocation-type; + } + description + "Defines how IPv6 addresses are allocated to the peer site."; + } + } + + // Basic parameters for IPv4 connection + + grouping ipv4-connection-basic { + description + "Basic set fof IPv4-specific parameters for the connection."; + uses ipv4-allocation-type; + choice allocation-type { + description + "Choice of the IPv4 address allocation."; + case dynamic { + description + "When the addresses are allocated by DHCP or other dynamic + means local to the infrastructure."; + choice provider-dhcp { + description + "Parameters related to DHCP-allocated addresses. IP + addresses are allocated by DHCP, that is provided by + the operator."; + leaf dhcp-service-type { + type enumeration { + enum server { + description + "Local DHCP server."; + } + enum relay { + description + "Local DHCP relay. DHCP requests are relayed to + a provider's server."; + } + } + description + "Indicates the type of DHCP service to be enabled on + an AC."; + } + } + choice dhcp-relay { + description + "The DHCP relay is provided by the operator."; + container customer-dhcp-servers { + description + "Container for a list of the customer's DHCP servers."; + leaf-list server-ip-address { + type inet:ipv4-address; + description + "IPv4 addresses of the customer's DHCP server."; + } + } + } + } + } + } + + // Basic parameters for IPv6 connection + + grouping ipv6-connection-basic { + description + "Basic set fof IPv6-specific parameters for the connection."; + uses ipv6-allocation-type; + choice allocation-type { + description + "Choice of the IPv6 address allocation."; + case dynamic { + description + "When the addresses are allocated by DHCP or other dynamic + means local to the infrastructure."; + choice provider-dhcp { + description + "Parameters related to DHCP-allocated addresses. + IP addresses are allocated by DHCP, that is provided + by the operator."; + leaf dhcp-service-type { + type enumeration { + enum server { + description + "Local DHCP server."; + } + enum relay { + description + "Local DHCP relay. DHCP requests are relayed to a + provider's server."; + } + } + description + "Indicates the type of DHCP service to be enabled on + the AC."; + } + } + choice dhcp-relay { + description + "The DHCP relay is provided by the operator."; + container customer-dhcp-servers { + description + "Container for a list of the customer's DHCP servers."; + leaf-list server-ip-address { + type inet:ipv6-address; + description + "IPv6 addresses of the customer's DHCP server."; + } + } + } + } + } + } + // Full parameters for the IPv4 connection + + grouping ipv4-connection { + description + "IPv4-specific parameters."; + leaf local-address { + type inet:ipv4-address; + description + "The IP address used at the provider's interface."; + } + leaf virtual-address { + type inet:ipv4-address; + description + "This addresss may be used for redundancy purposes."; + } + uses ipv4-allocation-type; + choice allocation-type { + description + "Choice of the IPv4 address allocation."; + case dynamic { + description + "When the addresses are allocated by DHCP or other + dynamic means local to the infrastructure."; + choice address-assign { + description + "A choice for how IPv4 addresses are assigned."; + case number { + leaf number-of-dynamic-address { + type uint16; + description + "Specifies the number of IP addresses to be assigned + to the customer on the AC."; + } + } + case explicit { + container customer-addresses { + description + "Container for customer addresses to be allocated + using DHCP."; + list address-pool { + key "pool-id"; + description + "Describes IP addresses to be dyncamically + allocated. + + When only 'start-address' is present, it + represents a single address. + + When both 'start-address' and 'end-address' are + specified, it implies a range inclusive of both + addresses."; + leaf pool-id { + type string; + description + "A pool identifier for the address range from + 'start-address' to 'end-address'."; + } + leaf start-address { + type inet:ipv4-address; + mandatory true; + description + "Indicates the first address in the pool."; + } + leaf end-address { + type inet:ipv4-address; + description + "Indicates the last address in the pool."; + } + } + } + } + } + choice provider-dhcp { + description + "Parameters related to DHCP-allocated addresses. IP + addresses are allocated by DHCP, which is provided by + the operator."; + leaf dhcp-service-type { + type enumeration { + enum server { + description + "Local DHCP server."; + } + enum relay { + description + "Local DHCP relay. DHCP requests are relayed to + a provider's server."; + } + } + description + "Indicates the type of DHCP service to be enabled on + this AC."; + } + } + choice dhcp-relay { + description + "The DHCP relay is provided by the operator."; + container customer-dhcp-servers { + description + "Container for a list of the customer's DHCP servers."; + leaf-list server-ip-address { + type inet:ipv4-address; + description + "IPv4 addresses of the customer's DHCP server."; + } + } + } + } + case static-addresses { + description + "Lists the IPv4 addresses that are used."; + list address { + key "address-id"; + ordered-by user; + description + "Lists the IPv4 addresses that are used. The first + address of the list is the primary address of the + connection."; + leaf address-id { + type string; + description + "An identifier of the static IPv4 address."; + } + leaf customer-address { + type inet:ipv4-address; + description + "An IPv4 address of the customer side."; + } + } + } + } + } + + // Full parameters for the IPv6 connection + + grouping ipv6-connection { + description + "IPv6-specific parameters."; + leaf local-address { + type inet:ipv6-address; + description + "IPv6 address of the provider side."; + } + leaf virtual-address { + type inet:ipv6-address; + description + "This addresss may be used for redundancy purposes."; + } + uses ipv6-allocation-type; + choice allocation-type { + description + "Choice of the IPv6 address allocation."; + case dynamic { + description + "When the addresses are allocated by DHCP or other + dynamic means local to the infrastructure."; + choice address-assign { + description + "A choice for how IPv6 addresses are assigned."; + case number { + leaf number-of-dynamic-address { + type uint16; + description + "Specifies the number of IP addresses to be + assigned to the customer on this access."; + } + } + case explicit { + container customer-addresses { + description + "Container for customer addresses to be allocated + using DHCP."; + list address-pool { + key "pool-id"; + description + "Describes IP addresses to be dyncamically + allocated. + + When only 'start-address' is present, it + represents a single address. + + When both 'start-address' and 'end-address' are + specified, it implies a range inclusive of both + addresses."; + leaf pool-id { + type string; + description + "A pool identifier for the address range from + 'start-address' to 'end-address'."; + } + leaf start-address { + type inet:ipv6-address; + mandatory true; + description + "Indicates the first address in the pool."; + } + leaf end-address { + type inet:ipv6-address; + description + "Indicates the last address in the pool."; + } + } + } + } + } + choice provider-dhcp { + description + "Parameters related to DHCP-allocated addresses. + IP addresses are allocated by DHCP, which is provided + by the operator."; + leaf dhcp-service-type { + type enumeration { + enum server { + description + "Local DHCP server."; + } + enum relay { + description + "Local DHCP relay. DHCP requests are relayed + to a provider's server."; + } + } + description + "Indicates the type of DHCP service to + be enabled on this access."; + } + } + choice dhcp-relay { + description + "The DHCP relay is provided by the operator."; + container customer-dhcp-servers { + description + "Container for a list of the customer's DHCP servers."; + leaf-list server-ip-address { + type inet:ipv6-address; + description + "IPv6 addresses of the customer's DHCP server."; + } + } + } + } + case static-addresses { + description + "Lists the IPv6 addresses that are used."; + list address { + key "address-id"; + ordered-by user; + description + "Lists the IPv6 addresses that are used. The first + address of the list is the primary IP address of + the connection."; + leaf address-id { + type string; + description + "An identifier of the static IPv6 address."; + } + leaf customer-address { + type inet:ipv6-address; + description + "An IPv6 address of the customer side."; + } + } + } + } + } + + /**** Routing ****/ + // Routing authentication + + grouping bgp-authentication { + description + "Grouping for BGP authentication parameters."; + container authentication { + description + "Container for BGP authentication parameters."; + leaf enabled { + type boolean; + description + "Enables or disables authentication."; + } + container keying-material { + when "../enabled = 'true'"; + description + "Container for describing how a BGP routing session is to + be secured on an AC."; + choice option { + description + "Choice of authentication options."; + case ao { + description + "Uses the TCP Authentication Option (TCP-AO)."; + reference + "RFC 5925: The TCP Authentication Option"; + leaf enable-ao { + type boolean; + description + "Enables the TCP-AO."; + } + leaf ao-keychain { + type key-chain:key-chain-ref; + description + "Reference to the TCP-AO key chain."; + reference + "RFC 8177: YANG Data Model for Key Chains"; + } + } + case md5 { + description + "Uses MD5 to secure the session."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks + (VPNs), Section 13.2"; + leaf md5-keychain { + type key-chain:key-chain-ref; + description + "Reference to the MD5 key chain."; + reference + "RFC 8177: YANG Data Model for Key Chains"; + } + } + case explicit { + leaf key-id { + type uint32; + description + "Key identifier."; + } + leaf key { + type string; + description + "BGP authentication key. + + This model only supports the subset of keys that + are representable as ASCII strings."; + } + leaf crypto-algorithm { + type identityref { + base key-chain:crypto-algorithm; + } + description + "Indicates the cryptographic algorithm associated + with the key."; + } + } + } + } + } + } + + grouping ospf-authentication { + description + "Authentication configuration."; + container authentication { + description + "Container for OSPF authentication parameters."; + leaf enabled { + type boolean; + description + "Enables or disables authentication."; + } + container keying-material { + when "../enabled = 'true'"; + description + "Container for describing how an OSPF session is to be + secured for this AC."; + choice option { + description + "Options for OSPF authentication."; + case auth-key-chain { + leaf key-chain { + type key-chain:key-chain-ref; + description + "Name of the key chain."; + } + } + case auth-key-explicit { + leaf key-id { + type uint32; + description + "Key identifier."; + } + leaf key { + type string; + description + "OSPF authentication key. + This model only supports the subset of keys that + are representable as ASCII strings."; + } + leaf crypto-algorithm { + type identityref { + base key-chain:crypto-algorithm; + } + description + "Indicates the cryptographic algorithm associated + with the key."; + } + } + } + } + } + } + + grouping isis-authentication { + description + "IS-IS authentication configuration."; + container authentication { + description + "Container for IS-IS authentication parameters."; + leaf enabled { + type boolean; + description + "Enables or disables authentication."; + } + container keying-material { + when "../enabled = 'true'"; + description + "Container for describing how an IS-IS session is secured + over an AC."; + choice option { + description + "Options for IS-IS authentication."; + case auth-key-chain { + leaf key-chain { + type key-chain:key-chain-ref; + description + "Name of the key chain."; + } + } + case auth-key-explicit { + leaf key-id { + type uint32; + description + "Key identifier."; + } + leaf key { + type string; + description + "IS-IS authentication key. + + This model only supports the subset of keys that + are representable as ASCII strings."; + } + leaf crypto-algorithm { + type identityref { + base key-chain:crypto-algorithm; + } + description + "Indicates the cryptographic algorithm associated + with the key."; + } + } + } + } + } + } + + grouping rip-authentication { + description + "RIP authentication configuration."; + container authentication { + description + "Container for RIP authentication parameters."; + leaf enabled { + type boolean; + description + "Enables or disables authentication."; + } + container keying-material { + when "../enabled = 'true'"; + description + "Container for describing how a RIP session is to be + secured on this AC."; + choice option { + description + "Specifies the authentication + scheme."; + case auth-key-chain { + leaf key-chain { + type key-chain:key-chain-ref; + description + "Name of the key chain."; + } + } + case auth-key-explicit { + leaf key { + type string; + description + "RIP authentication key. + + This model only supports the subset of keys that + are representable as ASCII strings."; + } + leaf crypto-algorithm { + type identityref { + base key-chain:crypto-algorithm; + } + description + "Indicates the cryptographic algorithm associated + with the key."; + } + } + } + } + } + } + + // Basic routing parameters + + grouping bgp-peer-group-without-name { + description + "Identifies a BGP peer-group configured on the local system."; + leaf local-as { + type inet:as-number; + description + "Indicates a local AS Number (ASN). This ASN is exposed + to a customer so that it knows which ASN to use + to set up a BGP session."; + } + leaf peer-as { + type inet:as-number; + description + "Indicates the customer's ASN when the customer + requests BGP routing."; + } + leaf address-family { + type identityref { + base vpn-common:address-family; + } + description + "This node contains the address families to be activated. + 'dual-stack' means that both IPv4 and IPv6 will be + activated."; + } + leaf role { + type identityref { + base ac-common:bgp-role; + } + description + "Specifies the BGP role (provider, customer, peer, etc.)."; + reference + "RFC 9234: Route Leak Prevention and Detection Using + Roles in UPDATE and OPEN Messages, Section 4"; + } + } + + grouping bgp-peer-group-with-name { + description + "Identifies a BGP peer-group configured on the local system - + identified by a peer-group name."; + leaf name { + type string; + description + "Name of the BGP peer-group."; + } + uses bgp-peer-group-without-name; + } + + grouping ospf-basic { + description + "Configuration specific to OSPF."; + leaf address-family { + type identityref { + base vpn-common:address-family; + } + description + "Indicates whether IPv4, IPv6, or both are to be activated."; + } + leaf area-id { + type yang:dotted-quad; + mandatory true; + description + "Area ID."; + reference + "RFC 4577: OSPF as the Provider/Customer Edge Protocol + for BGP/MPLS IP Virtual Private Networks + (VPNs), Section 4.2.3 + RFC 6565: OSPFv3 as a Provider Edge to Customer Edge + (PE-CE) Routing Protocol, Section 4.2"; + } + leaf metric { + type uint16; + description + "Metric of the AC. It is used in the routing state + calculation and path selection."; + } + } + + grouping isis-basic { + description + "Basic configuration specific to IS-IS."; + leaf address-family { + type identityref { + base vpn-common:address-family; + } + description + "Indicates whether IPv4, IPv6, or both are to be activated."; + } + leaf area-address { + type area-address; + mandatory true; + description + "Area address."; + } + } + + // Static routing + + grouping ipv4-static-rtg-entry { + description + "Paramters to configure a specific IPv4 static routing entry."; + leaf lan { + type inet:ipv4-prefix; + description + "LAN prefix."; + } + leaf lan-tag { + type string; + description + "Internal tag to be used in service policies."; + } + leaf next-hop { + type union { + type inet:ip-address; + type predefined-next-hop; + } + description + "The next hop that is to be used for the static route. + This may be specified as an IP address or a + predefined next-hop type (e.g., 'discard' or + 'local-link')."; + } + leaf metric { + type uint32; + description + "Indicates the metric associated with the static route."; + } + } + + grouping ipv4-static-rtg { + description + "Configuration specific to IPv4 static routing."; + list ipv4-lan-prefixes { + if-feature "vpn-common:ipv4"; + key "lan next-hop"; + description + "List of LAN prefixes for the site."; + uses ipv4-static-rtg-entry; + uses ac-common:service-status; + } + } + + grouping ipv6-static-rtg-entry { + description + "Paramters to configure a specific IPv6 static routing entry."; + leaf lan { + type inet:ipv6-prefix; + description + "LAN prefixes."; + } + leaf lan-tag { + type string; + description + "Internal tag to be used in service (e.g., VPN) policies."; + } + leaf next-hop { + type union { + type inet:ip-address; + type predefined-next-hop; + } + description + "The next hop that is to be used for the static route. + This may be specified as an IP address or a predefined + next-hop type (e.g., 'discard' or 'local-link')."; + } + leaf metric { + type uint32; + description + "Indicates the metric associated with the static route."; + } + } + + grouping ipv6-static-rtg { + description + "Configuration specific to IPv6 static routing."; + list ipv6-lan-prefixes { + if-feature "vpn-common:ipv6"; + key "lan next-hop"; + description + "List of LAN prefixes for the site."; + uses ipv6-static-rtg-entry; + uses ac-common:service-status; + } + } + + // OAM + + grouping bfd { + description + "A grouping for basic BFD."; + leaf holdtime { + type uint32; + units "milliseconds"; + description + "Expected BFD holdtime. + The customer may impose some fixed values + for the holdtime period if the provider allows + the customer to use this function. + If the provider doesn't allow the customer to + use this function, fixed values will not be set."; + reference + "RFC 5880: Bidirectional Forwarding Detection (BFD), + Section 6.8.18"; + } + } + + // redundancy + + grouping redundancy-group { + description + "A grouping for redundancy group."; + list group { + key "group-id"; + description + "List of group-ids."; + leaf group-id { + type string; + description + "Indicates the group-id to which the AC belongs."; + } + leaf precedence { + type identityref { + base ac-common:precedence-type; + } + description + "Defines redundancy of an AC."; + } + } + } + + // QoS + + grouping bandwidth-parameters { + description + "A grouping for bandwidth parameters."; + leaf cir { + type uint64; + units "bps"; + description + "Committed Information Rate (CIR). The maximum number of bits + that a port can receive or send during one second over + an interface."; + } + leaf cbs { + type uint64; + units "bytes"; + description + "Committed Burst Size (CBS). CBS controls the bursty nature + of the traffic. Traffic that does not use the configured + CIR accumulates credits until the credits reach the + configured CBS."; + } + leaf eir { + type uint64; + units "bps"; + description + "Excess Information Rate (EIR), i.e., excess frame delivery + allowed not subject to a Service Level Agreement (SLA). + The traffic rate can be limited by EIR."; + } + leaf ebs { + type uint64; + units "bytes"; + description + "Excess Burst Size (EBS). The bandwidth available for burst + traffic from the EBS is subject to the amount of bandwidth + that is accumulated during periods when traffic allocated + by the EIR policy is not used."; + } + leaf pir { + type uint64; + units "bps"; + description + "Peak Information Rate (PIR), i.e., maximum frame delivery + allowed. It is equal to or less than sum of CIR and EIR."; + } + leaf pbs { + type uint64; + units "bytes"; + description + "Peak Burst Size (PBS)."; + } + } + + grouping bandwidth-per-type { + description + "Grouping for bandwidth per type."; + list bandwidth { + key "bw-type"; + description + "List for bandwidth per type data nodes."; + leaf bw-type { + type identityref { + base vpn-common:bw-type; + } + description + "Indicates the bandwidth type."; + } + choice type { + description + "Choice based upon bandwidth type."; + case per-cos { + description + "Bandwidth per CoS."; + list cos { + key "cos-id"; + description + "List of Class of Services."; + leaf cos-id { + type uint8; + description + "Identifier of the CoS, indicated by a Differentiated + Services Code Point (DSCP) or a CE-CLAN CoS (802.1p) + value in the service frame."; + reference + "IEEE Std 802.1Q: Bridges and Bridged Networks"; + } + uses bandwidth-parameters; + } + } + case other { + description + "Other bandwidth types."; + uses bandwidth-parameters; + } + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ac-svc@2024-08-06.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ac-svc@2024-08-06.yang new file mode 100644 index 0000000000000000000000000000000000000000..a5790644f1f7600cfad42663f2e7c7ed13134ac1 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ac-svc@2024-08-06.yang @@ -0,0 +1,1252 @@ +module ietf-ac-svc { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-ac-svc"; + prefix ac-svc; + + import ietf-ac-common { + prefix ac-common; + reference + "RFC CCCC: A Common YANG Data Model for Attachment Circuits"; + } + import ietf-vpn-common { + prefix vpn-common; + reference + "RFC 9181: A Common YANG Data Model for Layer 2 and Layer 3 + VPNs"; + } + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types, Section 4"; + } + import ietf-key-chain { + prefix key-chain; + reference + "RFC 8177: YANG Data Model for Key Chains"; + } + + organization + "IETF OPSAWG (Operations and Management Area Working Group)"; + contact + "WG Web: <https://datatracker.ietf.org/wg/opsawg/> + WG List: <mailto:opsawg@ietf.org> + + Editor: Mohamed Boucadair + <mailto:mohamed.boucadair@orange.com> + Author: Richard Roberts + <mailto:rroberts@juniper.net> + Author: Oscar Gonzalez de Dios + <mailto:oscar.gonzalezdedios@telefonica.com> + Author: Samier Barguil + <mailto:ssamier.barguil_giraldo@nokia.com> + Author: Bo Wu + <mailto:lana.wubo@huawei.com>"; + description + "This YANG module defines a YANG model for exposing + attachment circuits as a service (ACaaS). + + Copyright (c) 2024 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Revised BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see the + RFC itself for full legal notices."; + + revision 2024-08-06 { + description + "Initial revision."; + reference + "RFC XXXX: YANG Data Models for Bearers and 'Attachment + Circuits'-as-a-Service (ACaaS)"; + } + + /* A set of typedefs to ease referencing cross-modules */ + + typedef attachment-circuit-reference { + type leafref { + path "/ac-svc:attachment-circuits/ac-svc:ac/ac-svc:name"; + } + description + "Defines a reference to an attachment circuit that can be used + by other modules."; + } + + typedef ac-group-reference { + type leafref { + path "/ac-svc:attachment-circuits/ac-svc:ac-group-profile" + + "/ac-svc:name"; + } + description + "Defines a reference to an attachment circuit profile."; + } + + typedef encryption-profile-reference { + type leafref { + path + "/ac-svc:specific-provisioning-profiles" + + "/ac-svc:valid-provider-identifiers" + + "/ac-svc:encryption-profile-identifier/ac-svc:id"; + } + description + "Defines a reference to an encryption profile."; + } + + typedef qos-profile-reference { + type leafref { + path + "/ac-svc:specific-provisioning-profiles" + + "/ac-svc:valid-provider-identifiers" + + "/ac-svc:qos-profile-identifier/ac-svc:id"; + } + description + "Defines a reference to a QoS profile."; + } + + typedef failure-detection-profile-reference { + type leafref { + path + "/ac-svc:specific-provisioning-profiles" + + "/ac-svc:valid-provider-identifiers" + + "/ac-svc:failure-detection-profile-identifier" + + "/ac-svc:id"; + } + description + "Defines a reference to a BFD profile."; + } + + typedef forwarding-profile-reference { + type leafref { + path + "/ac-svc:specific-provisioning-profiles" + + "/ac-svc:valid-provider-identifiers" + + "/ac-svc:forwarding-profile-identifier/ac-svc:id"; + } + description + "Defines a reference to a forwarding profile."; + } + + typedef routing-profile-reference { + type leafref { + path + "/ac-svc:specific-provisioning-profiles" + + "/ac-svc:valid-provider-identifiers" + + "/ac-svc:routing-profile-identifier/ac-svc:id"; + } + description + "Defines a reference to a routing profile."; + } + + typedef service-profile-reference { + type leafref { + path + "/ac-svc:service-provisioning-profiles" + + "/ac-svc:service-profile-identifier" + + "/ac-svc:id"; + } + description + "Defines a reference to a service profile."; + } + + /******************** Reusable groupings ********************/ + // Basic Layer 2 connection + + grouping l2-connection-basic { + description + "Defines Layer 2 protocols and parameters that can be + factorized when provisioning Layer 2 connectivity + among multiple ACs."; + container encapsulation { + description + "Container for Layer 2 encapsulation."; + leaf type { + type identityref { + base vpn-common:encapsulation-type; + } + description + "Encapsulation type."; + } + container dot1q { + when "derived-from-or-self(../type, 'vpn-common:dot1q')" { + description + "Only applies when the type of the tagged interface + is 'dot1q'."; + } + description + "Tagged interface."; + uses ac-common:dot1q; + } + container qinq { + when "derived-from-or-self(../type, 'vpn-common:qinq')" { + description + "Only applies when the type of the tagged interface + is 'qinq'."; + } + description + "Includes QinQ parameters."; + uses ac-common:qinq; + } + } + } + + // Full Layer 2 connection + + grouping l2-connection { + description + "Defines Layer 2 protocols and parameters that are used to + enable AC connectivity."; + container encapsulation { + description + "Container for Layer 2 encapsulation."; + leaf type { + type identityref { + base vpn-common:encapsulation-type; + } + description + "Indicates the encapsulation type."; + } + container dot1q { + when "derived-from-or-self(../type, 'vpn-common:dot1q')" { + description + "Only applies when the type of the tagged interface + is 'dot1q'."; + } + description + "Tagged interface."; + uses ac-common:dot1q; + } + container priority-tagged { + when "derived-from-or-self(../type, " + + "'vpn-common:priority-tagged')" { + description + "Only applies when the type of the tagged interface is + 'priority-tagged'."; + } + description + "Priority-tagged interface."; + uses ac-common:priority-tagged; + } + container qinq { + when "derived-from-or-self(../type, 'vpn-common:qinq')" { + description + "Only applies when the type of the tagged interface + is 'qinq'."; + } + description + "Includes QinQ parameters."; + uses ac-common:qinq; + } + } + choice l2-service { + description + "The Layer 2 connectivity service can be provided by + indicating a pointer to an L2VPN or by specifying a + Layer 2 tunnel service."; + container l2-tunnel-service { + description + "Defines a Layer 2 tunnel termination. + It is only applicable when a tunnel is required."; + uses ac-common:l2-tunnel-service; + } + case l2vpn { + leaf l2vpn-id { + type vpn-common:vpn-id; + description + "Indicates the L2VPN service associated with an + Integrated Routing and Bridging (IRB) interface."; + } + } + } + leaf bearer-reference { + if-feature "ac-common:server-assigned-reference"; + type string; + description + "This is an internal reference for the service provider + to identify the bearer associated with this AC."; + } + } + + // Basic IP connection + + grouping ip-connection-basic { + description + "Defines basic IP connection parameters."; + container ipv4 { + if-feature "vpn-common:ipv4"; + description + "IPv4-specific parameters."; + uses ac-common:ipv4-connection-basic; + } + container ipv6 { + if-feature "vpn-common:ipv6"; + description + "IPv6-specific parameters."; + uses ac-common:ipv6-connection-basic; + } + } + + // Full IP connection + + grouping ip-connection { + description + "Defines IP connection parameters."; + container ipv4 { + if-feature "vpn-common:ipv4"; + description + "IPv4-specific parameters."; + uses ac-common:ipv4-connection { + augment ac-svc:allocation-type/static-addresses/address { + leaf failure-detection-profile { + if-feature "vpn-common:bfd"; + type failure-detection-profile-reference; + description + "Points to a failure detection profile."; + } + description + "Adds a failure detection profile."; + } + } + } + container ipv6 { + if-feature "vpn-common:ipv6"; + description + "IPv6-specific parameters."; + uses ac-common:ipv6-connection { + augment ac-svc:allocation-type/static-addresses/address { + leaf failure-detection-profile { + if-feature "vpn-common:bfd"; + type failure-detection-profile-reference; + description + "Points to a failure detection profile."; + } + description + "Adds a failure detection profile."; + } + } + } + choice l3-service { + description + "The Layer 3 connectivity service can be provided by + specifying a Layer 3 tunnel service."; + container l3-tunnel-service { + description + "Defines a Layer 3 tunnel termination. + It is only applicable when a tunnel is required."; + leaf type { + type identityref { + base ac-common:l3-tunnel-type; + } + description + "Selects the tunnel termination type for an AC."; + } + } + } + } + + // Routing protocol list + + grouping routing-protocol-list { + description + "List of routing protocols used on the AC."; + leaf type { + type identityref { + base vpn-common:routing-protocol-type; + } + description + "Type of routing protocol."; + } + list routing-profiles { + key "id"; + description + "Routing profiles."; + leaf id { + type routing-profile-reference; + description + "Reference to the routing profile to be used."; + } + leaf type { + type identityref { + base vpn-common:ie-type; + } + description + "Import, export, or both."; + } + } + } + + // Static routing with BFD + + grouping ipv4-static-rtg-with-bfd { + description + "Configuration specific to IPv4 static routing with + failure protection (e.g., BFD)."; + list ipv4-lan-prefix { + if-feature "vpn-common:ipv4"; + key "lan next-hop"; + description + "List of LAN prefixes for the site."; + uses ac-common:ipv4-static-rtg-entry; + leaf failure-detection-profile { + if-feature "vpn-common:bfd"; + type failure-detection-profile-reference; + description + "Points to a failure detection profile."; + } + uses ac-common:service-status; + } + } + + grouping ipv6-static-rtg-with-bfd { + description + "Configuration specific to IPv6 static routing with + failure protection (e.g., BFD)."; + list ipv6-lan-prefix { + if-feature "vpn-common:ipv6"; + key "lan next-hop"; + description + "List of LAN prefixes for the site."; + uses ac-common:ipv6-static-rtg-entry; + leaf failure-detection-profile { + if-feature "vpn-common:bfd"; + type failure-detection-profile-reference; + description + "Points to a failure detection profile."; + } + uses ac-common:service-status; + } + } + + // BGP Service + + grouping bgp-neighbor-without-name { + description + "A grouping with generic parameters for configuring a BGP + neighbor."; + leaf remote-address { + type inet:ip-address; + description + "The remote IP address of this entry's BGP peer. This is + a customer IP address. + + If this leaf is not present, this means that the primary + customer IP address is used as remote IP address."; + } + leaf local-address { + type inet:ip-address; + description + "The provider's IP address that will be used to establish + the BGP session."; + } + uses ac-common:bgp-peer-group-without-name; + container bgp-max-prefix { + description + "A container for the maximum number of BGP prefixes + allowed in the BGP session."; + leaf max-prefix { + type uint32; + description + "Indicates the maximum number of BGP prefixes allowed + in the BGP session. + + It allows control of how many prefixes can be received + from a neighbor."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4), + Section 8.2.2"; + } + } + uses ac-common:bgp-authentication; + uses ac-common:op-instructions; + uses ac-common:service-status; + } + + grouping bgp-neighbor-with-name { + description + "A grouping with generic parameters for configuring a BGP + neighbor with an identifier."; + leaf id { + type string; + description + "An identifier that uniquely identifies a neighbor."; + } + uses ac-svc:bgp-neighbor-without-name; + } + + grouping bgp-neighbor-with-server-reference { + description + "A grouping with generic parameters for configuring a BGP + neighbor with a reference generated by the provider."; + leaf server-reference { + if-feature "ac-common:server-assigned-reference"; + type string; + config false; + description + "This is an internal reference for the service provider + to identify the BGP session."; + } + uses ac-svc:bgp-neighbor-without-name; + } + + grouping bgp-neighbor-with-name-server-reference { + description + "A grouping with generic parameters for configuring a BGP + neighbor with an identifier and a reference generated by + the provider."; + leaf id { + type string; + description + "An identifier that uniquely identifiers a neighbor."; + } + uses ac-svc:bgp-neighbor-with-server-reference; + } + + grouping bgp-svc { + description + "Configuration specific to BGP."; + container peer-groups { + description + "Configuration for BGP peer-groups"; + list peer-group { + key "name"; + description + "List of BGP peer-groups configured on the local + system - uniquely identified by peer-group + name."; + uses ac-common:bgp-peer-group-with-name; + leaf local-address { + type inet:ip-address; + description + "The provider's local IP address that will be used to + establish the BGP session."; + } + container bgp-max-prefix { + description + "A container for the maximum number of BGP prefixes + allowed in the BGP session."; + leaf max-prefix { + type uint32; + description + "Indicates the maximum number of BGP prefixes allowed + in the BGP session. + + It allows control of how many prefixes can be received + from a neighbor."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4), + Section 8.2.2"; + } + } + uses ac-common:bgp-authentication; + } + } + list neighbor { + key "id"; + description + "List of BGP neighbors."; + uses ac-svc:bgp-neighbor-with-name-server-reference; + leaf peer-group { + type leafref { + path "../../peer-groups/peer-group/name"; + } + description + "The peer-group with which this neighbor is associated."; + } + leaf failure-detection-profile { + if-feature "vpn-common:bfd"; + type failure-detection-profile-reference; + description + "Points to a failure detection profile."; + } + } + } + + // OSPF Service + + grouping ospf-svc { + description + "Service configuration specific to OSPF."; + uses ac-common:ospf-basic; + uses ac-common:ospf-authentication; + uses ac-common:service-status; + } + + // IS-IS Service + + grouping isis-svc { + description + "Service configuration specific to IS-IS."; + uses ac-common:isis-basic; + uses ac-common:isis-authentication; + uses ac-common:service-status; + } + + // RIP Service + + grouping rip-svc { + description + "Service configuration specific to RIP routing."; + leaf address-family { + type identityref { + base vpn-common:address-family; + } + description + "Indicates whether IPv4, IPv6, or both address families + are to be activated."; + } + uses ac-common:rip-authentication; + uses ac-common:service-status; + } + + // VRRP Service + + grouping vrrp-svc { + description + "Service configuration specific to VRRP."; + reference + "RFC 9568: Virtual Router Redundancy Protocol (VRRP) + Version 3 for IPv4 and IPv6"; + leaf address-family { + type identityref { + base vpn-common:address-family; + } + description + "Indicates whether IPv4, IPv6, or both + address families are to be enabled."; + } + uses ac-common:service-status; + } + + // Basic routing parameters + + grouping routing-basic { + description + "Defines basic parameters for routing protocols."; + list routing-protocol { + key "id"; + description + "List of routing protocols used on the AC."; + leaf id { + type string; + description + "Unique identifier for the routing protocol."; + } + uses routing-protocol-list; + container bgp { + when + "derived-from-or-self(../type, 'vpn-common:bgp-routing')" { + description + "Only applies when the protocol is BGP."; + } + if-feature "vpn-common:rtg-bgp"; + description + "Configuration specific to BGP."; + container peer-groups { + description + "Configuration for BGP peer-groups"; + list peer-group { + key "name"; + description + "List of BGP peer-groups configured on the local + system - uniquely identified by peer-group + name."; + uses ac-common:bgp-peer-group-with-name; + } + } + } + container ospf { + when "derived-from-or-self(../type, " + + "'vpn-common:ospf-routing')" { + description + "Only applies when the protocol is OSPF."; + } + if-feature "vpn-common:rtg-ospf"; + description + "Configuration specific to OSPF."; + uses ac-common:ospf-basic; + } + container isis { + when "derived-from-or-self(../type, " + + "'vpn-common:isis-routing')" { + description + "Only applies when the protocol is IS-IS."; + } + if-feature "vpn-common:rtg-isis"; + description + "Configuration specific to IS-IS."; + uses ac-common:isis-basic; + } + container rip { + when "derived-from-or-self(../type, " + + "'vpn-common:rip-routing')" { + description + "Only applies when the protocol is RIP. + For IPv4, the model assumes that RIP + version 2 is used."; + } + if-feature "vpn-common:rtg-rip"; + description + "Configuration specific to RIP routing."; + leaf address-family { + type identityref { + base vpn-common:address-family; + } + description + "Indicates whether IPv4, IPv6, or both + address families are to be activated."; + } + } + container vrrp { + when "derived-from-or-self(../type, " + + "'vpn-common:vrrp-routing')" { + description + "Only applies when the protocol is the + Virtual Router Redundancy Protocol (VRRP)."; + } + if-feature "vpn-common:rtg-vrrp"; + description + "Configuration specific to VRRP."; + leaf address-family { + type identityref { + base vpn-common:address-family; + } + description + "Indicates whether IPv4, IPv6, or both address families + are to be enabled."; + } + } + } + } + + // Full routing parameters + + grouping routing { + description + "Defines routing protocols."; + list routing-protocol { + key "id"; + description + "List of routing protocols used on the AC."; + leaf id { + type string; + description + "Unique identifier for the routing protocol."; + } + uses routing-protocol-list; + container static { + when "derived-from-or-self(../type, " + + "'vpn-common:static-routing')" { + description + "Only applies when the protocol is static routing + protocol."; + } + description + "Configuration specific to static routing."; + container cascaded-lan-prefixes { + description + "LAN prefixes from the customer."; + uses ipv4-static-rtg-with-bfd; + uses ipv6-static-rtg-with-bfd; + } + } + container bgp { + when "derived-from-or-self(../type, " + + "'vpn-common:bgp-routing')" { + description + "Only applies when the protocol is BGP."; + } + if-feature "vpn-common:rtg-bgp"; + description + "Configuration specific to BGP."; + uses bgp-svc; + } + container ospf { + when "derived-from-or-self(../type, " + + "'vpn-common:ospf-routing')" { + description + "Only applies when the protocol is OSPF."; + } + if-feature "vpn-common:rtg-ospf"; + description + "Configuration specific to OSPF."; + uses ospf-svc; + } + container isis { + when "derived-from-or-self(../type, " + + "'vpn-common:isis-routing')" { + description + "Only applies when the protocol is IS-IS."; + } + if-feature "vpn-common:rtg-isis"; + description + "Configuration specific to IS-IS."; + uses isis-svc; + } + container rip { + when "derived-from-or-self(../type, " + + "'vpn-common:rip-routing')" { + description + "Only applies when the protocol is RIP. + For IPv4, the model assumes that RIP version 2 is + used."; + } + if-feature "vpn-common:rtg-rip"; + description + "Configuration specific to RIP routing."; + uses rip-svc; + } + container vrrp { + when "derived-from-or-self(../type, " + + "'vpn-common:vrrp-routing')" { + description + "Only applies when the protocol is the Virtual Router + Redundancy Protocol (VRRP)."; + } + if-feature "vpn-common:rtg-vrrp"; + description + "Configuration specific to VRRP."; + uses vrrp-svc; + } + } + } + + // Encryption choice + + grouping encryption-choice { + description + "Container for the encryption profile."; + choice profile { + description + "Choice for the encryption profile."; + case provider-profile { + leaf provider-profile { + type encryption-profile-reference; + description + "Reference to a provider encryption profile."; + } + } + case customer-profile { + leaf customer-key-chain { + type key-chain:key-chain-ref; + description + "Customer-supplied key chain."; + } + } + } + } + + // Basic security parameters + + grouping ac-security-basic { + description + "AC-specific security parameters."; + container encryption { + if-feature "vpn-common:encryption"; + description + "Container for AC security encryption."; + leaf enabled { + type boolean; + description + "If set to 'true', traffic encryption on the connection + is required. Otherwise, it is disabled."; + } + leaf layer { + when "../enabled = 'true'" { + description + "Included only when encryption is enabled."; + } + type enumeration { + enum layer2 { + description + "Encryption occurs at Layer 2."; + } + enum layer3 { + description + "Encryption occurs at Layer 3. + For example, IPsec may be used when a customer + requests Layer 3 encryption."; + } + } + description + "Indicates the layer on which encryption is applied."; + } + } + container encryption-profile { + when "../encryption/enabled = 'true'" { + description + "Indicates the layer on which encryption is enabled."; + } + description + "Container for the encryption profile."; + uses encryption-choice; + } + } + + // Bandwith parameters + + grouping bandwidth { + description + "Container for bandwidth."; + container svc-pe-to-ce-bandwidth { + if-feature "vpn-common:inbound-bw"; + description + "From the customer site's perspective, the inbound + bandwidth of the AC or download bandwidth from the + service provider to the site."; + uses ac-common:bandwidth-per-type; + } + container svc-ce-to-pe-bandwidth { + if-feature "vpn-common:outbound-bw"; + description + "From the customer site's perspective, the outbound + bandwidth of the AC or upload bandwidth from + the CE to the PE."; + uses ac-common:bandwidth-per-type; + } + } + + // Basic AC parameters + + grouping ac-basic { + description + "Grouping for basic parameters for an attachment circuit."; + leaf name { + type string; + description + "A name that uniquely identifies the AC."; + } + container l2-connection { + if-feature "ac-common:layer2-ac"; + description + "Defines Layer 2 protocols and parameters that are required + to enable AC connectivity."; + uses l2-connection-basic; + } + container ip-connection { + if-feature "ac-common:layer3-ac"; + description + "Defines IP connection parameters."; + uses ip-connection-basic; + } + container routing-protocols { + description + "Defines routing protocols."; + uses routing-basic; + } + container oam { + description + "Defines the Operations, Administration, and Maintenance + (OAM) mechanisms used."; + container bfd { + if-feature "vpn-common:bfd"; + description + "Container for BFD."; + uses ac-common:bfd; + } + } + container security { + description + "AC-specific security parameters."; + uses ac-security-basic; + } + container service { + description + "AC-specific bandwith parameters."; + leaf mtu { + type uint32; + units "bytes"; + description + "Layer 2 MTU."; + } + uses bandwidth; + } + } + + // Full AC parameters + + grouping ac { + description + "Grouping for an attachment circuit."; + leaf name { + type string; + description + "A name of the AC. Data models that need to reference + an attachment circuit should use + attachment-circuit-reference."; + } + leaf-list service-profile { + type service-profile-reference; + description + "A reference to a service profile."; + } + container l2-connection { + if-feature "ac-common:layer2-ac"; + description + "Defines Layer 2 protocols and parameters that are required + to enable AC connectivity."; + uses l2-connection; + } + container ip-connection { + if-feature "ac-common:layer3-ac"; + description + "Defines IP connection parameters."; + uses ip-connection; + } + container routing-protocols { + description + "Defines routing protocols."; + uses routing; + } + container oam { + description + "Defines the OAM mechanisms used."; + container bfd { + if-feature "vpn-common:bfd"; + description + "Container for BFD."; + list session { + key "id"; + description + "List of BFD sessions."; + leaf id { + type string; + description + "A unique identifer for the BFD session."; + } + leaf local-address { + type inet:ip-address; + description + "Provider's IP address of the BFD session."; + } + leaf remote-address { + type inet:ip-address; + description + "Customer's IP address of the BFD session."; + } + leaf profile { + type failure-detection-profile-reference; + description + "Points to a BFD profile."; + } + uses ac-common:bfd; + uses ac-common:service-status; + } + } + } + container security { + description + "AC-specific security parameters."; + uses ac-security-basic; + } + container service { + description + "AC-specific bandwith parameters."; + leaf mtu { + type uint32; + units "bytes"; + description + "Layer 2 MTU."; + } + uses bandwidth; + container qos { + if-feature "vpn-common:qos"; + description + "QoS configuration."; + container qos-profiles { + description + "QoS profile configuration."; + list qos-profile { + key "profile"; + description + "Points to a QoS profile."; + leaf profile { + type qos-profile-reference; + description + "QoS profile to be used."; + } + leaf direction { + type identityref { + base vpn-common:qos-profile-direction; + } + description + "The direction to which the QoS profile + is applied."; + } + } + } + } + container access-control-list { + description + "Container for the Access Control List (ACL)."; + container acl-profiles { + description + "ACL profile configuration."; + list acl-profile { + key "profile"; + description + "Points to an ACL profile."; + leaf profile { + type forwarding-profile-reference; + description + "Forwarding profile to be used."; + } + } + } + } + } + } + + // Parent and Child ACs + + grouping ac-hierarchy { + description + "Container for parent and child AC references."; + leaf-list parent-ref { + type ac-svc:attachment-circuit-reference; + description + "Specifies a parent AC that is inherited by an AC. + In contexts where dynamic terminating points are + bound to the same AC, a parent AC with stable + information is created with a set of child ACs + to track dynamic AC information."; + } + leaf-list child-ref { + type ac-svc:attachment-circuit-reference; + config false; + description + "Specifies a child AC that relies upon a parent AC."; + } + } + + /******************** Main AC containers ********************/ + + container specific-provisioning-profiles { + description + "Contains a set of valid profiles to reference for an AC."; + uses ac-common:ac-profile-cfg; + } + container service-provisioning-profiles { + description + "Contains a set of valid profiles to reference for an AC."; + list service-profile-identifier { + key "id"; + description + "List of generic service profile identifiers."; + leaf id { + type string; + description + "Identification of the service profile to be used. + The profile only has significance within the service + provider's administrative domain."; + } + } + nacm:default-deny-write; + } + container attachment-circuits { + description + "Main container for the attachment circuits."; + list ac-group-profile { + key "name"; + description + "Maintains a list of profiles that are shared among + a set of ACs."; + uses ac; + } + container placement-constraints { + description + "Diversity constraint type."; + uses vpn-common:placement-constraints; + } + leaf customer-name { + type string; + description + "Indicates the name of the customer that requested these + ACs."; + } + uses ac-common:op-instructions; + list ac { + key "name"; + description + "Global provisioning of attachment circuits."; + leaf customer-name { + type string; + description + "Indicates the name of the customer that requested this + AC."; + } + leaf description { + type string; + description + "Associates a description with an AC."; + } + leaf test-only { + type empty; + description + "When present, this indicates that this is a feasibility + check request. No resources are commited for such AC + requests."; + } + uses ac-common:op-instructions; + leaf role { + type identityref { + base ac-common:role; + } + description + "Indicates whether this AC is used as UNI, NNI, etc."; + } + leaf-list peer-sap-id { + type string; + description + "One or more peer SAPs can be indicated."; + } + leaf-list group-profile-ref { + type ac-group-reference; + description + "A reference to an AC profile."; + } + uses ac-hierarchy; + uses ac-common:redundancy-group; + list service-ref { + key "service-type service-id"; + config false; + description + "Reports the set of services that are bound to the AC."; + leaf service-type { + type identityref { + base vpn-common:service-type; + } + description + "Indicates the service type (e.g., L3VPN or Network Slice + Service)."; + reference + "RFC 9408: A YANG Network Data Model for Service + Attachment Points (SAPs), Section 5"; + } + leaf service-id { + type string; + description + "Indicates an identifier of a service instance + of a given type that uses the AC."; + } + } + leaf server-reference { + if-feature "ac-common:server-assigned-reference"; + type string; + config false; + description + "Reports an internal reference for the service provider + to identify the AC."; + } + uses ac; + } + } +} \ No newline at end of file diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ethertypes@2019-03-04.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ethertypes@2019-03-04.yang new file mode 100644 index 0000000000000000000000000000000000000000..fd055074aeba5c277bbefdce0b81ebd24d0d3551 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-ethertypes@2019-03-04.yang @@ -0,0 +1,381 @@ +module ietf-ethertypes { + namespace "urn:ietf:params:xml:ns:yang:ietf-ethertypes"; + prefix ethertypes; + + organization + "IETF NETMOD (Network Modeling) Working Group."; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + Editor: Mahesh Jethanandani + <mjethanandani@gmail.com>"; + + description + "This module contains common definitions for the + Ethertype used by different modules. It is a + placeholder module, till such time that IEEE + starts a project to define these Ethertypes + and publishes a standard. + + At that time, this module can be deprecated. + + Copyright (c) 2019 IETF Trust and the persons identified as + the document authors. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD + License set forth in Section 4.c of the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8519; see + the RFC itself for full legal notices."; + + revision 2019-03-04 { + description + "Initial revision."; + reference + "RFC 8519: YANG Data Model for Network Access Control + Lists (ACLs)."; + } + + typedef ethertype { + type union { + type uint16; + type enumeration { + enum ipv4 { + value 2048; + description + "Internet Protocol version 4 (IPv4) with a + hex value of 0x0800."; + reference + "RFC 791: Internet Protocol."; + } + enum arp { + value 2054; + description + "Address Resolution Protocol (ARP) with a + hex value of 0x0806."; + reference + "RFC 826: An Ethernet Address Resolution Protocol: Or + Converting Network Protocol Addresses to 48.bit + Ethernet Address for Transmission on Ethernet + Hardware."; + } + enum wlan { + value 2114; + description + "Wake-on-LAN. Hex value of 0x0842."; + } + enum trill { + value 8947; + description + "Transparent Interconnection of Lots of Links. + Hex value of 0x22F3."; + reference + "RFC 6325: Routing Bridges (RBridges): Base Protocol + Specification."; + } + enum srp { + value 8938; + description + "Stream Reservation Protocol. Hex value of + 0x22EA."; + reference + "IEEE 801.1Q-2011."; + } + enum decnet { + value 24579; + description + "DECnet Phase IV. Hex value of 0x6003."; + } + enum rarp { + value 32821; + description + "Reverse Address Resolution Protocol. + Hex value 0x8035."; + reference + "RFC 903: A Reverse Address Resolution Protocol."; + } + enum appletalk { + value 32923; + description + "Appletalk (Ethertalk). Hex value of 0x809B."; + } + enum aarp { + value 33011; + description + "Appletalk Address Resolution Protocol. Hex value + of 0x80F3."; + } + enum vlan { + value 33024; + description + "VLAN-tagged frame (IEEE 802.1Q) and Shortest Path + Bridging IEEE 802.1aq with Network-Network + Interface (NNI) compatibility. Hex value of + 0x8100."; + reference + "IEEE 802.1Q."; + } + enum ipx { + value 33079; + description + "Internetwork Packet Exchange (IPX). Hex value + of 0x8137."; + } + enum qnx { + value 33284; + description + "QNX Qnet. Hex value of 0x8204."; + } + enum ipv6 { + value 34525; + description + "Internet Protocol Version 6 (IPv6). Hex value + of 0x86DD."; + reference + "RFC 8200: Internet Protocol, Version 6 (IPv6) + Specification + RFC 8201: Path MTU Discovery for IP version 6."; + } + enum efc { + value 34824; + description + "Ethernet flow control using pause frames. + Hex value of 0x8808."; + reference + "IEEE 802.1Qbb."; + } + enum esp { + value 34825; + description + "Ethernet Slow Protocol. Hex value of 0x8809."; + reference + "IEEE 802.3-2015."; + } + enum cobranet { + value 34841; + description + "CobraNet. Hex value of 0x8819."; + } + enum mpls-unicast { + value 34887; + description + "Multiprotocol Label Switching (MPLS) unicast traffic. + Hex value of 0x8847."; + reference + "RFC 3031: Multiprotocol Label Switching Architecture."; + } + enum mpls-multicast { + value 34888; + description + "MPLS multicast traffic. Hex value of 0x8848."; + reference + "RFC 3031: Multiprotocol Label Switching Architecture."; + } + enum pppoe-discovery { + value 34915; + description + "Point-to-Point Protocol over Ethernet. Used during + the discovery process. Hex value of 0x8863."; + reference + "RFC 2516: A Method for Transmitting PPP Over Ethernet + (PPPoE)."; + } + enum pppoe-session { + value 34916; + description + "Point-to-Point Protocol over Ethernet. Used during + session stage. Hex value of 0x8864."; + reference + "RFC 2516: A Method for Transmitting PPP Over Ethernet + (PPPoE)."; + } + enum intel-ans { + value 34925; + description + "Intel Advanced Networking Services. Hex value of + 0x886D."; + } + enum jumbo-frames { + value 34928; + description + "Jumbo frames or Ethernet frames with more than + 1500 bytes of payload, up to 9000 bytes."; + } + enum homeplug { + value 34939; + description + "Family name for the various power line + communications. Hex value of 0x887B."; + } + enum eap { + value 34958; + description + "Ethernet Access Protocol (EAP) over LAN. Hex value + of 0x888E."; + reference + "IEEE 802.1X."; + } + enum profinet { + value 34962; + description + "PROcess FIeld Net (PROFINET). Hex value of 0x8892."; + } + enum hyperscsi { + value 34970; + description + "Small Computer System Interface (SCSI) over Ethernet. + Hex value of 0x889A."; + } + enum aoe { + value 34978; + description + "Advanced Technology Advancement (ATA) over Ethernet. + Hex value of 0x88A2."; + } + enum ethercat { + value 34980; + description + "Ethernet for Control Automation Technology (EtherCAT). + Hex value of 0x88A4."; + } + enum provider-bridging { + value 34984; + description + "Provider Bridging (802.1ad) and Shortest Path Bridging + (801.1aq). Hex value of 0x88A8."; + reference + "IEEE 802.1ad and IEEE 802.1aq)."; + } + enum ethernet-powerlink { + value 34987; + description + "Ethernet Powerlink. Hex value of 0x88AB."; + } + enum goose { + value 35000; + description + "Generic Object Oriented Substation Event (GOOSE). + Hex value of 0x88B8."; + reference + "IEC/ISO 8802-2 and 8802-3."; + } + enum gse { + value 35001; + description + "Generic Substation Events. Hex value of 88B9."; + reference + "IEC 61850."; + } + enum sv { + value 35002; + description + "Sampled Value Transmission. Hex value of 0x88BA."; + reference + "IEC 61850."; + } + enum lldp { + value 35020; + description + "Link Layer Discovery Protocol (LLDP). Hex value of + 0x88CC."; + reference + "IEEE 802.1AB."; + } + enum sercos { + value 35021; + description + "Sercos Interface. Hex value of 0x88CD."; + } + enum wsmp { + value 35036; + description + "WAVE Short Message Protocol (WSMP). Hex value of + 0x88DC."; + } + enum homeplug-av-mme { + value 35041; + description + "HomePlug AV Mobile Management Entity (MME). Hex value + of 88E1."; + } + enum mrp { + value 35043; + description + "Media Redundancy Protocol (MRP). Hex value of + 0x88E3."; + reference + "IEC 62439-2."; + } + enum macsec { + value 35045; + description + "MAC Security. Hex value of 0x88E5."; + reference + "IEEE 802.1AE."; + } + enum pbb { + value 35047; + description + "Provider Backbone Bridges (PBB). Hex value of + 0x88E7."; + reference + "IEEE 802.1ah."; + } + enum cfm { + value 35074; + description + "Connectivity Fault Management (CFM). Hex value of + 0x8902."; + reference + "IEEE 802.1ag."; + } + enum fcoe { + value 35078; + description + "Fiber Channel over Ethernet (FCoE). Hex value of + 0x8906."; + reference + "T11 FC-BB-5."; + } + enum fcoe-ip { + value 35092; + description + "FCoE Initialization Protocol. Hex value of 0x8914."; + } + enum roce { + value 35093; + description + "RDMA over Converged Ethernet (RoCE). Hex value of + 0x8915."; + } + enum tte { + value 35101; + description + "TTEthernet Protocol Control Frame (TTE). Hex value + of 0x891D."; + reference + "SAE AS6802."; + } + enum hsr { + value 35119; + description + "High-availability Seamless Redundancy (HSR). Hex + value of 0x892F."; + reference + "IEC 62439-3:2016."; + } + } + } + description + "The uint16 type placeholder is defined to enable + users to manage their own ethertypes not + covered by the module. Otherwise, the module contains + enum definitions for the more commonly used ethertypes."; + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-geo-location@2022-02-11.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-geo-location@2022-02-11.yang new file mode 100644 index 0000000000000000000000000000000000000000..b815446f819ac2dcba50e63e5781e8a1be9b59c6 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-geo-location@2022-02-11.yang @@ -0,0 +1,278 @@ +module ietf-geo-location { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-geo-location"; + prefix geo; + import ietf-yang-types { + prefix yang; + reference "RFC 6991: Common YANG Data Types"; + } + + organization + "IETF NETMOD Working Group (NETMOD)"; + contact + "WG Web: <https://datatracker.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + Editor: Christian Hopps + <mailto:chopps@chopps.org>"; + + description + "This module defines a grouping of a container object for + specifying a location on or around an astronomical object (e.g., + 'earth'). + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL + NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', + 'MAY', and 'OPTIONAL' in this document are to be interpreted as + described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, + they appear in all capitals, as shown here. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, is permitted pursuant to, + and subject to the license terms contained in, the + Revised BSD License set forth in Section 4.c of the + IETF Trust's Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 9179 + (https://www.rfc-editor.org/info/rfc9179); see the RFC itself + for full legal notices."; + + revision 2022-02-11 { + description + "Initial Revision"; + reference + "RFC 9179: A YANG Grouping for Geographic Locations"; + } + + feature alternate-systems { + description + "This feature means the device supports specifying locations + using alternate systems for reference frames."; + } + + grouping geo-location { + description + "Grouping to identify a location on an astronomical object."; + + container geo-location { + description + "A location on an astronomical body (e.g., 'earth') + somewhere in a universe."; + + container reference-frame { + description + "The Frame of Reference for the location values."; + + leaf alternate-system { + if-feature "alternate-systems"; + type string; + description + "The system in which the astronomical body and + geodetic-datum is defined. Normally, this value is not + present and the system is the natural universe; however, + when present, this value allows for specifying alternate + systems (e.g., virtual realities). An alternate-system + modifies the definition (but not the type) of the other + values in the reference frame."; + } + leaf astronomical-body { + type string { + pattern '[ -@\[-\^_-~]*'; + } + default "earth"; + description + "An astronomical body as named by the International + Astronomical Union (IAU) or according to the alternate + system if specified. Examples include 'sun' (our star), + 'earth' (our planet), 'moon' (our moon), 'enceladus' (a + moon of Saturn), 'ceres' (an asteroid), and + '67p/churyumov-gerasimenko (a comet). The ASCII value + SHOULD have uppercase converted to lowercase and not + include control characters (i.e., values 32..64, and + 91..126). Any preceding 'the' in the name SHOULD NOT be + included."; + reference + "https://www.iau.org/"; + } + container geodetic-system { + description + "The geodetic system of the location data."; + leaf geodetic-datum { + type string { + pattern '[ -@\[-\^_-~]*'; + } + description + "A geodetic-datum defining the meaning of latitude, + longitude, and height. The default when the + astronomical body is 'earth' is 'wgs-84', which is + used by the Global Positioning System (GPS). The + ASCII value SHOULD have uppercase converted to + lowercase and not include control characters + (i.e., values 32..64, and 91..126). The IANA registry + further restricts the value by converting all spaces + (' ') to dashes ('-'). + The specification for the geodetic-datum indicates + how accurately it models the astronomical body in + question, both for the 'horizontal' + latitude/longitude coordinates and for height + coordinates."; + reference + "RFC 9179: A YANG Grouping for Geographic Locations, + Section 6.1"; + } + leaf coord-accuracy { + type decimal64 { + fraction-digits 6; + } + description + "The accuracy of the latitude/longitude pair for + ellipsoidal coordinates, or the X, Y, and Z components + for Cartesian coordinates. When coord-accuracy is + specified, it indicates how precisely the coordinates + in the associated list of locations have been + determined with respect to the coordinate system + defined by the geodetic-datum. For example, there + might be uncertainty due to measurement error if an + experimental measurement was made to determine each + location."; + } + leaf height-accuracy { + type decimal64 { + fraction-digits 6; + } + units "meters"; + description + "The accuracy of the height value for ellipsoidal + coordinates; this value is not used with Cartesian + coordinates. When height-accuracy is specified, it + indicates how precisely the heights in the + associated list of locations have been determined + with respect to the coordinate system defined by the + geodetic-datum. For example, there might be + uncertainty due to measurement error if an + experimental measurement was made to determine each + location."; + } + } + } + choice location { + description + "The location data either in latitude/longitude or + Cartesian values"; + case ellipsoid { + leaf latitude { + type decimal64 { + fraction-digits 16; + } + units "decimal degrees"; + description + "The latitude value on the astronomical body. The + definition and precision of this measurement is + indicated by the reference-frame."; + } + leaf longitude { + type decimal64 { + fraction-digits 16; + } + units "decimal degrees"; + description + "The longitude value on the astronomical body. The + definition and precision of this measurement is + indicated by the reference-frame."; + } + leaf height { + type decimal64 { + fraction-digits 6; + } + units "meters"; + description + "Height from a reference 0 value. The precision and + '0' value is defined by the reference-frame."; + } + } + case cartesian { + leaf x { + type decimal64 { + fraction-digits 6; + } + units "meters"; + description + "The X value as defined by the reference-frame."; + } + leaf y { + type decimal64 { + fraction-digits 6; + } + units "meters"; + description + "The Y value as defined by the reference-frame."; + } + leaf z { + type decimal64 { + fraction-digits 6; + } + units "meters"; + description + "The Z value as defined by the reference-frame."; + } + } + } + container velocity { + description + "If the object is in motion, the velocity vector describes + this motion at the time given by the timestamp. For a + formula to convert these values to speed and heading, see + RFC 9179."; + reference + "RFC 9179: A YANG Grouping for Geographic Locations"; + + leaf v-north { + type decimal64 { + fraction-digits 12; + } + units "meters per second"; + description + "v-north is the rate of change (i.e., speed) towards + true north as defined by the geodetic-system."; + } + + leaf v-east { + type decimal64 { + fraction-digits 12; + } + units "meters per second"; + description + "v-east is the rate of change (i.e., speed) perpendicular + to the right of true north as defined by + the geodetic-system."; + } + + leaf v-up { + type decimal64 { + fraction-digits 12; + } + units "meters per second"; + description + "v-up is the rate of change (i.e., speed) away from the + center of mass."; + } + } + leaf timestamp { + type yang:date-and-time; + description + "Reference time when location was recorded."; + } + leaf valid-until { + type yang:date-and-time; + description + "The timestamp for which this geo-location is valid until. + If unspecified, the geo-location has no specific + expiration time."; + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-inet-types@2024-10-21.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-inet-types@2024-10-21.yang new file mode 100644 index 0000000000000000000000000000000000000000..78c5201baf73f339d3ac409083b1f8ea13682faf --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-inet-types@2024-10-21.yang @@ -0,0 +1,657 @@ +module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF Network Modeling (NETMOD) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + Editor: Juergen Schoenwaelder + <mailto:jschoenwaelder@constructor.university>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL + NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', + 'MAY', and 'OPTIONAL' in this document are to be interpreted as + described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, + they appear in all capitals, as shown here. + + Copyright (c) 2024 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Revised BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; + see the RFC itself for full legal notices."; + + revision 2024-10-21 { + description + "This revision adds the following new data types: + - inet:ip-address-and-prefix + - inet:ipv4-address-and-prefix + - inet:ipv6-address-and-prefix + - inet:protocol-number + - inet:host-name + - inet:email-address + - inet:ip-address-link-local + - inet:ipv4-address-link-local + - inet:ipv6-address-link-local + The inet:host union was changed to use inet:host-name instead + of inet:domain-name. Several pattern statements have been + improved."; + reference + "RFC XXXX: Common YANG Data Types"; + } + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - inet:ip-address-no-zone + - inet:ipv4-address-no-zone + - inet:ipv6-address-no-zone"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of types related to protocol fields ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet + protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 8200."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 8200: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The ipv6-flow-label type represents the flow identifier or + Flow Label in an IPv6 packet header that may be used to + discriminate traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 8200: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport-layer protocol such as UDP, TCP, DCCP, or + SCTP. + + Port numbers are assigned by IANA. The current list of + all assignments is available from <https://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 9293: Transmission Control Protocol (TCP) + RFC 9260: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef protocol-number { + type uint8; + description + "The protocol-number type represents an 8-bit Internet + protocol number, carried in the 'protocol' field of the + IPv4 header or in the 'next header' field of the IPv6 + header. If IPv6 extension headers are present, then the + protocol number type represents the upper layer protocol + number, i.e., the number of the last 'next header' field + of the IPv6 extension headers. + + Protocol numbers are assigned by IANA. The current list of + all assignments is available from <https://www.iana.org/>."; + reference + "RFC 791: Internet Protocol + RFC 8200: Internet Protocol, Version 6 (IPv6) Specification"; + } + + /*** collection of types related to autonomous systems ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASes. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4001: Textual Conventions for Internet Network Addresses + RFC 6793: BGP Support for Four-Octet Autonomous System (AS) + Number Space"; + } + + /*** collection of types related to IP addresses and hostnames ***/ + + typedef ip-address { + type union { + type ipv4-address; + type ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representation + implies the IP version. This type supports scoped addresses + by allowing zone identifiers in the address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%.+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. If a system uses zone names + that are not represented in UTF-8, then an implementation + needs to use some mechanism to transform the local name + into UTF-8. The definition of such a mechanism is outside + the scope of this document. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[A-Za-z0-9][A-Za-z0-9\-\._~/]*)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + If a system uses zone names that are not represented in + UTF-8, then an implementation needs to use some mechanism + to transform the local name into UTF-8. The definition of + such a mechanism is outside the scope of this document. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the textual + representation defined in Section 4 of RFC 5952. The + canonical format for the zone index is the numerical + format as described in Section 11.2 of RFC 4007."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-address-no-zone { + type union { + type ipv4-address-no-zone; + type ipv6-address-no-zone; + } + description + "The ip-address-no-zone type represents an IP address and is + IP version neutral. The format of the textual representation + implies the IP version. This type does not support scoped + addresses since it does not allow zone identifiers in the + address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address-no-zone { + type ipv4-address { + pattern '[0-9\.]*'; + } + description + "An IPv4 address without a zone index. This type, derived + from the type ipv4-address, may be used in situations where + the zone is known from the context and no zone index is + needed."; + } + + typedef ipv6-address-no-zone { + type ipv6-address { + pattern '[0-9a-fA-F:\.]*'; + } + description + "An IPv6 address without a zone index. This type, derived + from the type ipv6-address, may be used in situations where + the zone is known from the context and no zone index is + needed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-address-link-local { + type union { + type ipv4-address-link-local; + type ipv6-address-link-local; + } + description + "The ip-address-link-local type represents a link-local IP + address and is IP version neutral. The format of the textual + representation implies the IP version."; + } + + typedef ipv4-address-link-local { + type ipv4-address { + pattern '169\.254\..*'; + } + description + "A link-local IPv4 address in the prefix 169.254.0.0/16 as + defined in section 2.1. of RFC 3927."; + reference + "RFC 3927: Dynamic Configuration of IPv4 Link-Local Addresses"; + } + + typedef ipv6-address-link-local { + type ipv6-address { + pattern '[fF][eE]80:.*'; + } + description + "A link-local IPv6 address in the prefix fe80::/10 as defined + in section 2.5.6. of RFC 4291."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + typedef ip-prefix { + type union { + type ipv4-prefix; + type ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix. + + The definition of ipv4-prefix does not require that bits, + which are not part of the prefix, are set to zero. However, + implementations have to return values in canonical format, + which requires non-prefix bits to be set to zero. This means + that 192.0.2.1/24 must be accepted as a valid value but it + will be converted into the canonical format 192.0.2.0/24."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, the IPv6 address is represented + as defined in Section 4 of RFC 5952. + + The definition of ipv6-prefix does not require that bits, + which are not part of the prefix, are set to zero. However, + implementations have to return values in canonical format, + which requires non-prefix bits to be set to zero. This means + that 2001:db8::1/64 must be accepted as a valid value but it + will be converted into the canonical format 2001:db8::/64."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-address-and-prefix { + type union { + type ipv4-address-and-prefix; + type ipv6-address-and-prefix; + } + description + "The ip-address-and-prefix type represents an IP address and + prefix and is IP version neutral. The format of the textual + representations implies the IP version."; + } + + typedef ipv4-address-and-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-address-and-prefix type represents an IPv4 + address and an associated IPv4 prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0."; + } + + typedef ipv6-address-and-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-address-and-prefix type represents an IPv6 + address and an associated IPv6 prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format requires that the IPv6 address is + represented as defined in Section 4 of RFC 5952."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + length "1..253"; + pattern + '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. This + type does not support wildcards (see RFC 4592) or + classless in-addr.arpa delegations (see RFC 2317). + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. Note that Internet host names have a + stricter syntax (described in RFC 952) than the DNS + recommendations in RFCs 1034 and 1123. Schema nodes + representing host names should use the host-name type + instead of the domain-type. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitly or may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be A-labels as per RFC 5890."; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2317: Classless IN-ADDR.ARPA delegation + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 4592: The Role of Wildcards in the Domain Name System + RFC 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework + RFC 9499: DNS Terminology"; + } + + typedef host-name { + type domain-name { + length "2..max"; + pattern '[a-zA-Z0-9\-\.]+'; + } + description + "The host-name type represents (fully qualified) host names. + Host names must be at least two characters long (see RFC 952) + and they are restricted to labels consisting of letters, digits + and hyphens separated by dots (see RFC1123 and RFC 952)."; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1123: Requirements for Internet Hosts -- Application + and Support"; + } + + typedef host { + type union { + type ip-address; + type host-name; + } + description + "The host type represents either an IP address or a (fully + qualified) host name."; + } + + typedef uri { + type string { + pattern '[a-z][a-z0-9+.-]*:.*'; + } + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by the rule 'URI' in RFC 3986. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits within a percent-encoded triplet, which are + normalized to uppercase as described in Section 6.2.2.1 + of RFC 3986. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + typedef email-address { + type string { + pattern '.+@.+'; + } + description + "The email-address type represents an internationalized + email address. + + The email address format is defined by the addr-spec + ABNF rule in RFC 5322 section 3.4.1. This format has + been extended by RFC 6532 to support internationalized + email addresses. Implementations MUST support the + internationalization extensions of RFC 6532. Support + of the obsolete obs-local-part, obs-domain, and + obs-qtext parts of RFC 5322 is not required. + + The domain part may use both A-labels and U-labels + (see RFC 5890). The canonical format of the domain part + uses lowercase characters and U-labels (RFC 5890) where + applicable."; + reference + "RFC 5322: Internet Message Format + RFC 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework + RFC 6531: SMTP Extension for Internationalized Email"; + } + +} \ No newline at end of file diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-key-chain@2017-06-15.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-key-chain@2017-06-15.yang new file mode 100644 index 0000000000000000000000000000000000000000..445d1994a5ac57366078b198200a9e143d4ccda8 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-key-chain@2017-06-15.yang @@ -0,0 +1,382 @@ +module ietf-key-chain { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-key-chain"; + prefix key-chain; + + import ietf-yang-types { + prefix yang; + } + import ietf-netconf-acm { + prefix nacm; + } + + organization + "IETF RTGWG - Routing Area Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/group/rtgwg> + WG List: <mailto:rtgwg@ietf.org> + + Editor: Acee Lindem + <mailto:acee@cisco.com> + Yingzhen Qu + <mailto:yingzhen.qu@huawei.com> + Derek Yeung + <mailto:derek@arrcus.com> + Ing-Wher Chen + <mailto:Ing-Wher_Chen@jabail.com> + Jeffrey Zhang + <mailto:zzhang@juniper.net>"; + + description + "This YANG module defines the generic configuration + data for key chains. It is intended that the module + will be extended by vendors to define vendor-specific + key chain configuration parameters. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8177; + see the RFC itself for full legal notices."; + + reference "RFC 8177"; + + revision 2017-06-15 { + description + "Initial RFC Revision"; + reference "RFC 8177: YANG Data Model for Key Chains"; + } + + feature hex-key-string { + description + "Support hexadecimal key string."; + } + + feature accept-tolerance { + description + "Support the tolerance or acceptance limit."; + } + + feature independent-send-accept-lifetime { + description + "Support for independent send and accept key lifetimes."; + } + + feature crypto-hmac-sha-1-12 { + description + "Support for TCP HMAC-SHA-1 12-byte digest hack."; + } + + feature cleartext { + description + "Support for cleartext algorithm. Usage is + NOT RECOMMENDED."; + } + + feature aes-cmac-prf-128 { + description + "Support for AES Cipher-based Message Authentication + Code Pseudorandom Function."; + } + + feature aes-key-wrap { + description + "Support for Advanced Encryption Standard (AES) Key Wrap."; + } + + feature replay-protection-only { + description + "Provide replay protection without any authentication + as required by protocols such as Bidirectional + Forwarding Detection (BFD)."; + } + identity crypto-algorithm { + description + "Base identity of cryptographic algorithm options."; + } + + identity hmac-sha-1-12 { + base crypto-algorithm; + if-feature "crypto-hmac-sha-1-12"; + description + "The HMAC-SHA1-12 algorithm."; + } + + identity aes-cmac-prf-128 { + base crypto-algorithm; + if-feature "aes-cmac-prf-128"; + description + "The AES-CMAC-PRF-128 algorithm - required by + RFC 5926 for TCP-AO key derivation functions."; + } + + identity md5 { + base crypto-algorithm; + description + "The MD5 algorithm."; + } + + identity sha-1 { + base crypto-algorithm; + description + "The SHA-1 algorithm."; + } + + identity hmac-sha-1 { + base crypto-algorithm; + description + "HMAC-SHA-1 authentication algorithm."; + } + + identity hmac-sha-256 { + base crypto-algorithm; + description + "HMAC-SHA-256 authentication algorithm."; + } + + identity hmac-sha-384 { + base crypto-algorithm; + description + "HMAC-SHA-384 authentication algorithm."; + } + + identity hmac-sha-512 { + base crypto-algorithm; + description + "HMAC-SHA-512 authentication algorithm."; + } + + identity cleartext { + base crypto-algorithm; + if-feature "cleartext"; + description + "cleartext."; + } + + identity replay-protection-only { + base crypto-algorithm; + if-feature "replay-protection-only"; + description + "Provide replay protection without any authentication as + required by protocols such as Bidirectional Forwarding + Detection (BFD)."; + } + + typedef key-chain-ref { + type leafref { + path + "/key-chain:key-chains/key-chain:key-chain/key-chain:name"; + } + description + "This type is used by data models that need to reference + configured key chains."; + } + + grouping lifetime { + description + "Key lifetime specification."; + choice lifetime { + default "always"; + description + "Options for specifying key accept or send lifetimes"; + case always { + leaf always { + type empty; + description + "Indicates key lifetime is always valid."; + } + } + case start-end-time { + leaf start-date-time { + type yang:date-and-time; + description + "Start time."; + } + choice end-time { + default "infinite"; + description + "End-time setting."; + case infinite { + leaf no-end-time { + type empty; + description + "Indicates key lifetime end-time is infinite."; + } + } + case duration { + leaf duration { + type uint32 { + range "1..2147483646"; + } + units "seconds"; + description + "Key lifetime duration, in seconds"; + } + } + case end-date-time { + leaf end-date-time { + type yang:date-and-time; + description + "End time."; + } + } + } + } + } + } + + container key-chains { + description + "All configured key-chains on the device."; + list key-chain { + key "name"; + description + "List of key-chains."; + leaf name { + type string; + description + "Name of the key-chain."; + } + leaf description { + type string; + description + "A description of the key-chain"; + } + container accept-tolerance { + if-feature "accept-tolerance"; + description + "Tolerance for key lifetime acceptance (seconds)."; + leaf duration { + type uint32; + units "seconds"; + default "0"; + description + "Tolerance range, in seconds."; + } + } + leaf last-modified-timestamp { + type yang:date-and-time; + config false; + description + "Timestamp of the most recent update to the key-chain"; + } + list key { + key "key-id"; + description + "Single key in key chain."; + leaf key-id { + type uint64; + description + "Numeric value uniquely identifying the key"; + } + container lifetime { + description + "Specify a key's lifetime."; + choice lifetime { + description + "Options for specification of send and accept + lifetimes."; + case send-and-accept-lifetime { + description + "Send and accept key have the same lifetime."; + container send-accept-lifetime { + description + "Single lifetime specification for both + send and accept lifetimes."; + uses lifetime; + } + } + case independent-send-accept-lifetime { + if-feature "independent-send-accept-lifetime"; + description + "Independent send and accept key lifetimes."; + container send-lifetime { + description + "Separate lifetime specification for send + lifetime."; + uses lifetime; + } + container accept-lifetime { + description + "Separate lifetime specification for accept + lifetime."; + uses lifetime; + } + } + } + } + leaf crypto-algorithm { + type identityref { + base crypto-algorithm; + } + mandatory true; + description + "Cryptographic algorithm associated with key."; + } + container key-string { + description + "The key string."; + nacm:default-deny-all; + choice key-string-style { + description + "Key string styles"; + case keystring { + leaf keystring { + type string; + description + "Key string in ASCII format."; + } + } + case hexadecimal { + if-feature "hex-key-string"; + leaf hexadecimal-string { + type yang:hex-string; + description + "Key in hexadecimal string format. When compared + to ASCII, specification in hexadecimal affords + greater key entropy with the same number of + internal key-string octets. Additionally, it + discourages usage of well-known words or + numbers."; + } + } + } + } + leaf send-lifetime-active { + type boolean; + config false; + description + "Indicates if the send lifetime of the + key-chain key is currently active."; + } + leaf accept-lifetime-active { + type boolean; + config false; + description + "Indicates if the accept lifetime of the + key-chain key is currently active."; + } + } + } + container aes-key-wrap { + if-feature "aes-key-wrap"; + description + "AES Key Wrap encryption for key-chain key-strings. The + encrypted key-strings are encoded as hexadecimal key + strings using the hex-key-string leaf."; + leaf enable { + type boolean; + default "false"; + description + "Enable AES Key Wrap encryption."; + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-netconf-acm@2018-02-14.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-netconf-acm@2018-02-14.yang new file mode 100644 index 0000000000000000000000000000000000000000..bf4855faf0508a152471f6c6c8f756581b8ebb96 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-netconf-acm@2018-02-14.yang @@ -0,0 +1,464 @@ +module ietf-netconf-acm { + + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"; + + prefix nacm; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + description + "Network Configuration Access Control Model. + + Copyright (c) 2012 - 2018 IETF Trust and the persons + identified as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD + License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8341; see + the RFC itself for full legal notices."; + + revision "2018-02-14" { + description + "Added support for YANG 1.1 actions and notifications tied to + data nodes. Clarified how NACM extensions can be used by + other data models."; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + revision "2012-02-22" { + description + "Initial version."; + reference + "RFC 6536: Network Configuration Protocol (NETCONF) + Access Control Model"; + } + + /* + * Extension statements + */ + + extension default-deny-write { + description + "Used to indicate that the data model node + represents a sensitive security system parameter. + + If present, the NETCONF server will only allow the designated + 'recovery session' to have write access to the node. An + explicit access control rule is required for all other users. + + If the NACM module is used, then it must be enabled (i.e., + /nacm/enable-nacm object equals 'true'), or this extension + is ignored. + + The 'default-deny-write' extension MAY appear within a data + definition statement. It is ignored otherwise."; + } + + extension default-deny-all { + description + "Used to indicate that the data model node + controls a very sensitive security system parameter. + + If present, the NETCONF server will only allow the designated + 'recovery session' to have read, write, or execute access to + the node. An explicit access control rule is required for all + other users. + + If the NACM module is used, then it must be enabled (i.e., + /nacm/enable-nacm object equals 'true'), or this extension + is ignored. + + The 'default-deny-all' extension MAY appear within a data + definition statement, 'rpc' statement, or 'notification' + statement. It is ignored otherwise."; + } + + /* + * Derived types + */ + + typedef user-name-type { + type string { + length "1..max"; + } + description + "General-purpose username string."; + } + + typedef matchall-string-type { + type string { + pattern '\*'; + } + description + "The string containing a single asterisk '*' is used + to conceptually represent all possible values + for the particular leaf using this data type."; + } + + typedef access-operations-type { + type bits { + bit create { + description + "Any protocol operation that creates a + new data node."; + } + bit read { + description + "Any protocol operation or notification that + returns the value of a data node."; + } + bit update { + description + "Any protocol operation that alters an existing + data node."; + } + bit delete { + description + "Any protocol operation that removes a data node."; + } + bit exec { + description + "Execution access to the specified protocol operation."; + } + } + description + "Access operation."; + } + + typedef group-name-type { + type string { + length "1..max"; + pattern '[^\*].*'; + } + description + "Name of administrative group to which + users can be assigned."; + } + + typedef action-type { + type enumeration { + enum permit { + description + "Requested action is permitted."; + } + enum deny { + description + "Requested action is denied."; + } + } + description + "Action taken by the server when a particular + rule matches."; + } + + typedef node-instance-identifier { + type yang:xpath1.0; + description + "Path expression used to represent a special + data node, action, or notification instance-identifier + string. + + A node-instance-identifier value is an + unrestricted YANG instance-identifier expression. + All the same rules as an instance-identifier apply, + except that predicates for keys are optional. If a key + predicate is missing, then the node-instance-identifier + represents all possible server instances for that key. + + This XML Path Language (XPath) expression is evaluated in the + following context: + + o The set of namespace declarations are those in scope on + the leaf element where this type is used. + + o The set of variable bindings contains one variable, + 'USER', which contains the name of the user of the + current session. + + o The function library is the core function library, but + note that due to the syntax restrictions of an + instance-identifier, no functions are allowed. + + o The context node is the root node in the data tree. + + The accessible tree includes actions and notifications tied + to data nodes."; + } + + /* + * Data definition statements + */ + + container nacm { + nacm:default-deny-all; + + description + "Parameters for NETCONF access control model."; + + leaf enable-nacm { + type boolean; + default "true"; + description + "Enables or disables all NETCONF access control + enforcement. If 'true', then enforcement + is enabled. If 'false', then enforcement + is disabled."; + } + + leaf read-default { + type action-type; + default "permit"; + description + "Controls whether read access is granted if + no appropriate rule is found for a + particular read request."; + } + + leaf write-default { + type action-type; + default "deny"; + description + "Controls whether create, update, or delete access + is granted if no appropriate rule is found for a + particular write request."; + } + + leaf exec-default { + type action-type; + default "permit"; + description + "Controls whether exec access is granted if no appropriate + rule is found for a particular protocol operation request."; + } + + leaf enable-external-groups { + type boolean; + default "true"; + description + "Controls whether the server uses the groups reported by the + NETCONF transport layer when it assigns the user to a set of + NACM groups. If this leaf has the value 'false', any group + names reported by the transport layer are ignored by the + server."; + } + + leaf denied-operations { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request was denied."; + } + + leaf denied-data-writes { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request to alter + a configuration datastore was denied."; + } + + leaf denied-notifications { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that + a notification was dropped for a subscription because + access to the event type was denied."; + } + + container groups { + description + "NETCONF access control groups."; + + list group { + key name; + + description + "One NACM group entry. This list will only contain + configured entries, not any entries learned from + any transport protocols."; + + leaf name { + type group-name-type; + description + "Group name associated with this entry."; + } + + leaf-list user-name { + type user-name-type; + description + "Each entry identifies the username of + a member of the group associated with + this entry."; + } + } + } + + list rule-list { + key name; + ordered-by user; + description + "An ordered collection of access control rules."; + + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule-list."; + } + leaf-list group { + type union { + type matchall-string-type; + type group-name-type; + } + description + "List of administrative groups that will be + assigned the associated access rights + defined by the 'rule' list. + + The string '*' indicates that all groups apply to the + entry."; + } + + list rule { + key name; + ordered-by user; + description + "One access control rule. + + Rules are processed in user-defined order until a match is + found. A rule matches if 'module-name', 'rule-type', and + 'access-operations' match the request. If a rule + matches, the 'action' leaf determines whether or not + access is granted."; + + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule."; + } + + leaf module-name { + type union { + type matchall-string-type; + type string; + } + default "*"; + description + "Name of the module associated with this rule. + + This leaf matches if it has the value '*' or if the + object being accessed is defined in the module with the + specified module name."; + } + choice rule-type { + description + "This choice matches if all leafs present in the rule + match the request. If no leafs are present, the + choice matches all requests."; + case protocol-operation { + leaf rpc-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if + its value equals the requested protocol operation + name."; + } + } + case notification { + leaf notification-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if its + value equals the requested notification name."; + } + } + + case data-node { + leaf path { + type node-instance-identifier; + mandatory true; + description + "Data node instance-identifier associated with the + data node, action, or notification controlled by + this rule. + + Configuration data or state data + instance-identifiers start with a top-level + data node. A complete instance-identifier is + required for this type of path value. + + The special value '/' refers to all possible + datastore contents."; + } + } + } + + leaf access-operations { + type union { + type matchall-string-type; + type access-operations-type; + } + default "*"; + description + "Access operations associated with this rule. + + This leaf matches if it has the value '*' or if the + bit corresponding to the requested operation is set."; + } + + leaf action { + type action-type; + mandatory true; + description + "The access control action associated with the + rule. If a rule has been determined to match a + particular request, then this object is used + to determine whether to permit or deny the + request."; + } + + leaf comment { + type string; + description + "A textual description of the access rule."; + } + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/ietf-network-slice-service.txt b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-slice-service.txt similarity index 100% rename from src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/ietf-network-slice-service.txt rename to src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-slice-service.txt diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-slice-service@2024-08-28.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-slice-service@2024-08-28.yang new file mode 100644 index 0000000000000000000000000000000000000000..d72dd1ed38c6b098c70ab824f98e8029aef7d137 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-slice-service@2024-08-28.yang @@ -0,0 +1,1375 @@ +module ietf-network-slice-service { + yang-version 1.1; + namespace + "urn:ietf:params:xml:ns:yang:ietf-network-slice-service"; + prefix ietf-nss; + + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Types"; + } + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-geo-location { + prefix geo; + reference + "RFC 9179: A YANG Grouping for Geographic Locations"; + } + import ietf-vpn-common { + prefix vpn-common; + reference + "RFC 9181: A Common YANG Data Model for Layer 2 and Layer 3 + VPNs"; + } + import ietf-network { + prefix nw; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + import ietf-network-topology { + prefix nt; + reference + "RFC 8345: A YANG Data Model for Network + Topologies, Section 6.2"; + } + import ietf-ac-common { + prefix ac-common; + reference + "RFC BBBB: A Common YANG Data Model for Attachment Circuits"; + } + import ietf-ac-svc { + prefix ac-svc; + reference + "RFC CCCC: YANG Data Models for Bearers and 'Attachment + Circuits'-as-a-Service (ACaaS)"; + } + import ietf-te-types { + prefix te-types; + reference + "RFC DDDD: Common YANG Types for Traffic Engineering"; + } + import ietf-te-packet-types { + prefix te-packet-types; + reference + "RFC DDDD: Common YANG Data Types for Traffic Engineering"; + } + + organization + "IETF Traffic Engineering Architecture and Signaling (TEAS) + Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/teas/> + WG List: <mailto:teas@ietf.org> + + Editor: Bo Wu + <lana.wubo@huawei.com> + Editor: Dhruv Dhody + <dhruv.ietf@gmail.com> + Editor: Reza Rokui + <rrokui@ciena.com> + Editor: Tarek Saad + <tsaad@cisco.com> + Editor: John Mullooly + <jmullool@cisco.com>"; + description + "This YANG module defines a service model for the RFC 9543 + Network Slice Service. + + Copyright (c) 2024 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Revised BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC AAAA; see the + RFC itself for full legal notices."; + + revision 2024-08-28 { + description + "Initial revision."; + reference + "RFC AAAA: A YANG Data Model for the RFC 9543 Network Slice + Service"; + } + + /* Identities */ + + identity service-tag-type { + description + "Base identity of Network Slice Service tag type, which is + used for management purposes, such as classification + (e.g., customer names) and policy constraints + (e.g., Layer 2 or Layer 3 technology realization)."; + } + + identity customer { + base service-tag-type; + description + "The Network Slice Service customer name tag type, + e.g., adding tags with 'customer name' when multiple actual + customers use the same Network Slice Service."; + } + + identity service { + base service-tag-type; + description + "The Network Slice Service tag type, which can indicate the + technical constraints used during service realization, + for example, Layer 2 or Layer 3 technologies."; + } + + identity opaque { + base service-tag-type; + description + "An opaque type, which can be used for future use, + such as filtering of services."; + } + + identity attachment-circuit-tag-type { + description + "Base identity for the attachment circuit tag type."; + } + + identity vlan-id { + base attachment-circuit-tag-type; + description + "Identity for VLAN ID tag type, 802.1Q dot1Q."; + reference + "IEEE Std 802.1Q: IEEE Standard for Local and Metropolitan + Area Networks--Bridges and Bridged + Networks"; + } + + identity cvlan-id { + base attachment-circuit-tag-type; + description + "Identity for C-VLAN ID tag type, 802.1ad QinQ VLAN IDs."; + reference + "IEEE Std 802.1ad: IEEE Standard for Local and Metropolitan + Area Networks---Virtual Bridged Local + Area Networks---Amendment 4: Provider + Bridges"; + } + + identity svlan-id { + base attachment-circuit-tag-type; + description + "Identity for S-VLAN ID tag type, 802.1ad QinQ VLAN IDs."; + reference + "IEEE Std 802.1ad: IEEE Standard for Local and Metropolitan + Area Networks---Virtual Bridged Local + Area Networks---Amendment 4: Provider + Bridges"; + } + + identity ip-address-mask { + base attachment-circuit-tag-type; + description + "Identity for IP address mask tag type."; + } + + identity service-isolation-type { + description + "Base identity for Network Slice Service isolation type."; + } + + identity traffic-isolation { + base service-isolation-type; + description + "Specify the requirement for separating the traffic of the + customer's Network Slice Service from other services, + which may be provided by the service provider using VPN + technologies, such as L3VPN, L2VPN, EVPN, etc."; + } + + identity service-security-type { + description + "Base identity for Network Slice Service security type."; + } + + identity authentication { + base service-security-type; + description + "Indicates that the Slice Service requires authentication."; + } + + identity integrity { + base service-security-type; + description + "Indicates that the Slice Service requires data integrity."; + } + + identity encryption { + base service-security-type; + description + "Indicates that the Slice Service requires data encryption."; + } + + identity point-to-point { + base vpn-common:vpn-topology; + description + "Identity for point-to-point Network Slice + Service connectivity."; + } + + identity point-to-multipoint { + base vpn-common:vpn-topology; + description + "Identity for point-to-multipoint Network Slice + Service connectivity."; + } + + identity multipoint-to-multipoint { + base vpn-common:vpn-topology; + description + "Identity for multipoint-to-multipoint Network Slice + Service connectivity."; + } + + identity multipoint-to-point { + base vpn-common:vpn-topology; + description + "Identity for multipoint-to-point Network Slice + Service connectivity."; + } + + identity sender-role { + base vpn-common:role; + description + "Indicates that an SDP is acting as a sender."; + } + + identity receiver-role { + base vpn-common:role; + description + "Indicates that an SDP is acting as a receiver."; + } + + identity service-slo-metric-type { + description + "Base identity for Network Slice Service SLO metric type."; + } + + identity one-way-bandwidth { + base service-slo-metric-type; + description + "SLO bandwidth metric. Minimum guaranteed bandwidth between + two SDPs at any time and is measured unidirectionally."; + } + + identity two-way-bandwidth { + base service-slo-metric-type; + description + "SLO bandwidth metric. Minimum guaranteed bandwidth between + two SDPs at any time."; + } + + identity shared-bandwidth { + base service-slo-metric-type; + description + "The shared SLO bandwidth bound. It is the limit on the + bandwidth that can be shared amongst a group of + connectivity constructs of a Slice Service."; + } + + identity one-way-delay-maximum { + base service-slo-metric-type; + description + "The SLO objective of this metric is the upper bound of network + delay when transmitting between two SDPs."; + reference + "RFC 7679: A One-Way Delay Metric for IP Performance + Metrics (IPPM)"; + } + + identity one-way-delay-percentile { + base service-slo-metric-type; + description + "The SLO objective of this metric is percentile objective of + network delay when transmitting between two SDPs. + The metric is defined in RFC7679."; + reference + "RFC 7679: A One-Way Delay Metric for IP Performance + Metrics (IPPM)"; + } + + identity two-way-delay-maximum { + base service-slo-metric-type; + description + "SLO two-way delay is the upper bound of network delay when + transmitting between two SDPs"; + reference + "RFC 2681: A Round-trip Delay Metric for IPPM"; + } + + identity two-way-delay-percentile { + base service-slo-metric-type; + description + "The SLO objective of this metric is the percentile + objective of network delay when the traffic transmitting + between two SDPs."; + reference + "RFC 2681: A Round-trip Delay Metric for IPPM"; + } + + identity one-way-delay-variation-maximum { + base service-slo-metric-type; + description + "The SLO objective of this metric is maximum bound of the + difference in the one-way delay between sequential packets + between two SDPs."; + reference + "RFC 3393: IP Packet Delay Variation Metric for IP Performance + Metrics (IPPM)"; + } + + identity one-way-delay-variation-percentile { + base service-slo-metric-type; + description + "The SLO objective of this metric is the percentile objective + in the one-way delay between sequential packets between two + SDPs."; + reference + "RFC 3393: IP Packet Delay Variation Metric for IP Performance + Metrics (IPPM)"; + } + + identity two-way-delay-variation-maximum { + base service-slo-metric-type; + description + "SLO two-way delay variation is the difference in the + round-trip delay between sequential packets between two + SDPs."; + reference + "RFC 5481: Packet Delay Variation Applicability Statement"; + } + + identity two-way-delay-variation-percentile { + base service-slo-metric-type; + description + "The SLO objective of this metric is the percentile objective + in the round-trip delay between sequential packets between + two SDPs."; + reference + "RFC 5481: Packet Delay Variation Applicability Statement"; + } + + identity one-way-packet-loss { + base service-slo-metric-type; + description + "This metric type refers to the ratio of packets dropped + to packets transmitted between two SDPs in one-way."; + reference + "RFC 7680: A One-Way Loss Metric for IP Performance + Metrics (IPPM)"; + } + + identity two-way-packet-loss { + base service-slo-metric-type; + description + "This metric type refers to the ratio of packets dropped + to packets transmitted between two SDPs in two-way."; + reference + "RFC 7680: A One-Way Loss Metric for IP Performance + Metrics (IPPM)"; + } + + identity availability-type { + description + "Base identity for availability."; + } + + identity six-nines { + base availability-type; + description + "Specifies the availability level: 99.9999%"; + } + + identity five-nines { + base availability-type; + description + "Specifies the availability level: 99.999%"; + } + + identity four-nines { + base availability-type; + description + "Specifies the availability level: 99.99%"; + } + + identity three-nines { + base availability-type; + description + "Specifies the availability level: 99.9%"; + } + + identity two-nines { + base availability-type; + description + "Specifies the availability level: 99%"; + } + + identity service-match-type { + description + "Base identity for Network Slice Service traffic + match type."; + } + identity phy-interface { + base service-match-type; + description + "Uses the physical interface as match criteria for + Slice Service traffic."; + } + + identity vlan { + base service-match-type; + description + "Uses the VLAN ID as match criteria for the Slice Service + traffic."; + } + + identity label { + base service-match-type; + description + "Uses the MPLS label as match criteria for the Slice Service + traffic."; + } + + identity source-ip-prefix { + base service-match-type; + description + "Uses source IP prefix as match criteria for the Slice Service + traffic. Examples of 'value' of this match type are + '192.0.2.0/24' and '2001:db8::1/64'."; + } + + identity destination-ip-prefix { + base service-match-type; + description + "Uses destination IP prefix as match criteria for the Slice + Service traffic. Examples of 'value' of this match type are + '203.0.113.1/32' and '2001:db8::2/128'."; + } + + identity dscp { + base service-match-type; + description + "Uses DSCP field in the IP packet header as match criteria + for the Slice Service traffic."; + } + + identity acl { + base service-match-type; + description + "Uses Access Control List (ACL) as match criteria + for the Slice Service traffic."; + reference + "RFC 8519: YANG Data Model for Network Access Control + Lists (ACLs)"; + } + + identity any { + base service-match-type; + description + "Matches any Slice Service traffic."; + } + + identity source-tcp-port { + base service-match-type; + description + "Uses source TCP port as match criteria for the Slice Service + traffic. Examples of 'value' of this match type are + '8080' and '22'."; + } + + identity destination-tcp-port { + base service-match-type; + description + "Uses destination TCP port as match criteria for the Slice + Service traffic. Examples of 'value' of this match type are + '8080' and '22'."; + } + + identity source-udp-port { + base service-match-type; + description + "Uses source UDP port as match criteria for the Slice Service + traffic. Examples of 'value' of this match type are + '53', '67' and '68'."; + } + +identity destination-udp-port { + base service-match-type; + description + "Uses destination UDP port as match criteria for the Slice + Service traffic. Examples of 'value' of this match type are + '53', '67' and '68'."; +} + + identity slo-sle-policy-override { + description + "Base identity for SLO/SLE policy override options."; + } + + identity full-override { + base slo-sle-policy-override; + description + "The SLO/SLE policy defined at the child level overrides a + parent SLO/SLE policy, which means that no SLO/SLEs are + inherited from parent if a child SLO/SLE policy exists."; + } + + identity partial-override { + base slo-sle-policy-override; + description + "The SLO/SLE policy defined at the child level updates the + parent SLO/SLE policy. For example, if a specific SLO is + defined at the child level, that specific SLO overrides + the one inherited from a parent SLO/SLE policy, while all + other SLOs in the parent SLO-SLE policy still apply."; + } + + /* Typedef */ + + typedef percentage { + type decimal64 { + fraction-digits 5; + range "0..100"; + } + description + "Percentage to 5 decimal places."; + } + + typedef percentile { + type decimal64 { + fraction-digits 3; + range "0..100"; + } + description + "The percentile is a value between 0 and 100 + to 3 decimal places, e.g., 10.000, 99.900,99.990, etc. + For example, for a given one-way delay measurement, + if the percentile is set to 95.000 and the 95th percentile + one-way delay is 2 milliseconds, then the 95 percent of + the sample value is less than or equal to 2 milliseconds."; + } + + typedef slice-template-ref { + type leafref { + path "/ietf-nss:network-slice-services" + + "/ietf-nss:slo-sle-templates" + + "/ietf-nss:slo-sle-template" + + "/ietf-nss:id"; + } + description + "This type is used by data models that need to reference + Network Slice templates."; + } + + typedef slice-service-ref { + type leafref { + path + "/ietf-nss:network-slice-services/ietf-nss:slice-service" + + "/ietf-nss:id"; + } + description + "Defines a reference to a slice service that can be used + by other modules."; + } + + /* Groupings */ + + grouping service-slos { + description + "A reusable grouping for directly measurable objectives of + a Slice Service."; + container slo-policy { + description + "Contains the SLO policy."; + list metric-bound { + key "metric-type"; + description + "List of Slice Service metric bounds."; + leaf metric-type { + type identityref { + base service-slo-metric-type; + } + description + "Identifies SLO metric type of the Slice Service."; + } + leaf metric-unit { + type string; + mandatory true; + description + "The metric unit of the parameter. For example, + for time units, where the options are hours, minutes, + seconds, milliseconds, microseconds, and nanoseconds; + for bandwidth units, where the options are bps, Kbps, + Mbps, Gbps; for the packet loss rate unit, + the options can be a percentage."; + } + leaf value-description { + type string; + description + "The description of the provided value."; + } + leaf percentile-value { + type percentile; + description + "The percentile value of the metric type."; + } + leaf bound { + type uint64; + description + "The bound on the Slice Service connection metric. + When set to zero, this indicates an unbounded + upper limit for the specific metric-type."; + } + } + leaf availability { + type identityref { + base availability-type; + } + description + "Service availability level"; + } + leaf mtu { + type uint32; + units "bytes"; + description + "Specifies the maximum length of Layer 2 data + packets of the Slice Service. + If the customer sends packets that are longer than the + requested service MTU, the network may discard them + (or for IPv4, fragment them). + This service MTU takes precedence over the MTUs of + all attachment circuits (ACs). The value needs to be + less than or equal to the minimum MTU value of + all ACs in the SDPs."; + } + } + } + + grouping service-sles { + description + "A reusable grouping for indirectly measurable objectives of + a Slice Service."; + container sle-policy { + description + "Contains the SLE policy."; + leaf-list security { + type identityref { + base service-security-type; + } + description + "The security functions that the customer requests + the operator to apply to traffic between the two SDPs."; + } + leaf-list isolation { + type identityref { + base service-isolation-type; + } + description + "The Slice Service isolation requirement."; + } + leaf max-occupancy-level { + type uint8 { + range "1..100"; + } + description + "The maximal occupancy level specifies the number of flows + to be admitted and optionally a maximum number of + countable resource units (e.g., IP or MAC addresses) + a Network Slice Service can consume."; + } + container path-constraints { + description + "Container for the policy of path constraints + applicable to the Slice Service."; + container service-functions { + description + "Container for the policy of service function + applicable to the Slice Service."; + } + container diversity { + description + "Container for the policy of disjointness + applicable to the Slice Service."; + leaf diversity-type { + type te-types:te-path-disjointness; + description + "The type of disjointness on Slice Service, i.e., + across all connectivity constructs."; + } + } + } + } + } + + grouping slice-service-template { + description + "A reusable grouping for Slice Service templates."; + container slo-sle-templates { + description + "Contains a set of Slice Service templates."; + list slo-sle-template { + key "id"; + description + "List for SLO and SLE template identifiers."; + leaf id { + type string; + description + "Identification of the Service Level Objective (SLO) + and Service Level Expectation (SLE) template to be used. + Local administration meaning."; + } + leaf description { + type string; + description + "Describes the SLO and SLE policy template."; + } + leaf template-ref { + type slice-template-ref; + description + "The reference to a standard template. When set it + indicates the base template over which further + SLO/SLE policy changes are made."; + } + uses service-slos; + uses service-sles; + } + } + } + + grouping service-slo-sle-policy { + description + "Slice service policy grouping."; + choice slo-sle-policy { + description + "Choice for SLO and SLE policy template. + Can be standard template or customized template."; + case standard { + description + "Standard SLO template."; + leaf slo-sle-template { + type slice-template-ref; + description + "Standard SLO and SLE template to be used."; + } + } + case custom { + description + "Customized SLO and SLE template."; + container service-slo-sle-policy { + description + "Contains the SLO and SLE policy."; + leaf description { + type string; + description + "Describes the SLO and SLE policy."; + } + uses service-slos; + uses service-sles; + } + } + } + } + + grouping service-qos { + description + "Grouping for the Slice Service QoS policy."; + container incoming-qos-policy { + description + "The QoS policy imposed on ingress direction of the traffic, + from the customer network or from another provider's + network."; + leaf qos-policy-name { + type string; + description + "The name of the QoS policy that is applied to the + attachment circuit. The name can reference a QoS + profile that is pre-provisioned on the device."; + } + container rate-limits { + description + "Container for the asymmetric traffic control."; + uses ac-common:bandwidth-parameters; + container classes { + description + "Container for service class bandwidth control."; + list cos { + key "cos-id"; + description + "List of Class of Services."; + leaf cos-id { + type uint8; + description + "Identifier of the CoS, indicated by + a Differentiated Services Code Point + (DSCP) or a CE-CLAN CoS (802.1p) + value in the service frame."; + reference + "IEEE Std 802.1Q: Bridges and Bridged + Networks"; + } + uses ac-common:bandwidth-parameters; + } + } + } + } + container outgoing-qos-policy { + description + "The QoS policy imposed on egress direction of the traffic, + towards the customer network or towards another + provider's network."; + leaf qos-policy-name { + type string; + description + "The name of the QoS policy that is applied to the + attachment circuit. The name can reference a QoS + profile that is pre-provisioned on the device."; + } + container rate-limits { + description + "The rate-limit imposed on outgoing traffic."; + uses ac-common:bandwidth-parameters; + container classes { + description + "Container for classes."; + list cos { + key "cos-id"; + description + "List of Class of Services."; + leaf cos-id { + type uint8; + description + "Identifier of the CoS, indicated by + a Differentiated Services Code Point + (DSCP) or a CE-CLAN CoS (802.1p) + value in the service frame."; + reference + "IEEE Std 802.1Q: Bridges and Bridged + Networks"; + } + uses ac-common:bandwidth-parameters; + } + } + } + } + } + + grouping service-slo-sle-policy-override { + description + "Slice Service policy override grouping."; + leaf service-slo-sle-policy-override { + type identityref { + base slo-sle-policy-override; + } + description + "SLO/SLE policy override option."; + } + } + + grouping connectivity-construct-monitoring-metrics { + description + "Grouping for connectivity construct monitoring metrics."; + uses + te-packet-types:one-way-performance-metrics-gauge-packet; + uses + te-packet-types:two-way-performance-metrics-gauge-packet; + } + /* Main Network Slice Services Container */ + + container network-slice-services { + description + "Contains a list of Network Slice Services"; + uses slice-service-template; + list slice-service { + key "id"; + description + "A Slice Service is identified by a service id."; + leaf id { + type string; + description + "A unique Slice Service identifier within an NSC."; + } + leaf description { + type string; + description + "Textual description of the Slice Service."; + } + container service-tags { + description + "Container for a list of service tags for management + purposes, such as policy constraints + (e.g., Layer 2 or Layer 3 technology realization), + classification (e.g., customer names, opaque values)."; + list tag-type { + key "tag-type"; + description + "The service tag list."; + leaf tag-type { + type identityref { + base service-tag-type; + } + description + "Slice Service tag type, e.g., realization technology + constraints, customer name, or other customer-defined + opaque types."; + } + leaf-list value { + type string; + description + "The tag values, e.g., 5G customer names when multiple + customers share the same Slice Service in 5G scenario, + or Slice realization technology (such as Layer 2 or + Layer 3)."; + } + } + } + uses service-slo-sle-policy; + leaf compute-only { + type empty; + description + "When present, this is a feasibility check. That is, no + resources are reserved in the network."; + } + uses ac-common:service-status; + container sdps { + description + "Slice Service SDPs."; + list sdp { + key "id"; + min-elements 2; + description + "List of SDPs in this Slice Service."; + leaf id { + type string; + description + "The unique identifier of the SDP within the scope of + an NSC."; + } + leaf description { + type string; + description + "Provides a description of the SDP."; + } + uses geo:geo-location; + leaf node-id { + type string; + description + "A unique identifier of an edge node of the SDP + within the scope of the NSC."; + } + leaf-list sdp-ip-address { + type inet:ip-address; + description + "IPv4 or IPv6 address of the SDP."; + } + leaf tp-ref { + type leafref { + path + "/nw:networks/nw:network[nw:network-id=" + + "current()/../../../custom-topology/network-ref]/" + + "nw:node/nt:termination-point/nt:tp-id"; + } + description + "A reference to Termination Point (TP) in the custom + topology"; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + container service-match-criteria { + description + "Describes the Slice Service match criteria."; + list match-criterion { + key "index"; + description + "List of the Slice Service traffic match criteria."; + leaf index { + type uint32; + description + "The identifier of a match criteria."; + } + list match-type { + key "type"; + description + "List of the Slice Service traffic match types."; + leaf type { + type identityref { + base service-match-type; + } + mandatory true; + description + "Indicates the match type of the entry in the + list of the Slice Service match criteria."; + } + leaf-list value { + type string; + description + "Provides a value for the Slice Service match + criteria, e.g., IP prefix, VLAN ID, or + ACL name."; + } + } + leaf target-connection-group-id { + type leafref { + path + "../../../../../ietf-nss:connection-groups" + + "/ietf-nss:connection-group" + + "/ietf-nss:id"; + } + mandatory true; + description + "Reference to the Slice Service connection group."; + } + leaf connection-group-sdp-role { + type identityref { + base vpn-common:role; + } + description + "Specifies the role of SDP in the connection group + When the service connection type is MP2MP, + such as hub and spoke service connection type. + In addition, this helps to create connectivity + construct automatically, rather than explicitly + specifying each one."; + } + leaf target-connectivity-construct-id { + type leafref { + path + "../../../../../ietf-nss:connection-groups" + + "/ietf-nss:connection-group[ietf-nss:id=" + + "current()/../target-connection-group-id]" + + "/ietf-nss:connectivity-construct/ietf-nss:id"; + } + description + "Reference to a Network Slice connection + construct."; + } + } + } + uses service-qos; + container sdp-peering { + description + "Describes SDP peering attributes."; + leaf-list peer-sap-id { + type string; + description + "Indicates the reference to the remote endpoints of + the attachment circuits. This information can be + used for correlation purposes, such as identifying + SAPs of provider equipments when requesting + a service with CE based SDP attributes."; + reference + "RFC 9408: A YANG Network Data Model for Service + Attachment Points (SAPs)"; + } + container protocols { + description + "Serves as an augmentation target. + Protocols can be augmented into this container, + e.g., BGP, static routing."; + } + } + leaf-list ac-svc-ref { + type ac-svc:attachment-circuit-reference; + description + "A reference to the ACs that have been created before + the slice creation."; + reference + "RFC CCCC: YANG Data Models for Bearers and + 'Attachment Circuits'-as-a-Service (ACaaS)"; + } + leaf ce-mode { + type boolean; + description + "Indicates that SDP is on the CE."; + } + container attachment-circuits { + description + "List of attachment circuits."; + list attachment-circuit { + key "id"; + description + "The Network Slice Service SDP attachment circuit + related parameters."; + leaf id { + type string; + description + "The identifier of attachment circuit."; + } + leaf description { + type string; + description + "The attachment circuit's description."; + } + leaf ac-svc-ref { + type ac-svc:attachment-circuit-reference; + description + "A reference to the AC service that has been + created before the slice creation."; + reference + "RFC CCCC: YANG Data Models for Bearers and + 'Attachment Circuits'-as-a-Service (ACaaS)"; + } + leaf ac-node-id { + type string; + description + "The attachment circuit node ID in the case of + multi-homing."; + } + leaf ac-tp-id { + type string; + description + "The termination port ID of the + attachment circuit."; + } + leaf ac-ipv4-address { + type inet:ipv4-address; + description + "The IPv4 address of the AC."; + } + leaf ac-ipv4-prefix-length { + type uint8; + description + "The IPv4 subnet prefix length expressed in bits."; + } + leaf ac-ipv6-address { + type inet:ipv6-address; + description + "The IPv6 address of the AC."; + } + leaf ac-ipv6-prefix-length { + type uint8; + description + "The IPv6 subnet prefix length expressed in bits."; + } + leaf mtu { + type uint32; + units "bytes"; + description + "Maximum size of the Slice Service Layer 2 data + packet that can traverse an SDP."; + } + container ac-tags { + description + "Container for the attachment circuit tags."; + list ac-tag { + key "tag-type"; + description + "The attachment circuit tag list."; + leaf tag-type { + type identityref { + base attachment-circuit-tag-type; + } + description + "The attachment circuit tag type."; + } + leaf-list value { + type string; + description + "The attachment circuit tag values. + For example, the tag may indicate + multiple VLAN identifiers."; + } + } + } + uses service-qos; + container sdp-peering { + description + "Describes SDP peering attributes."; + leaf peer-sap-id { + type string; + description + "Indicates a reference to the remote endpoints + of an attachment circuit. This information can + be used for correlation purposes, such as + identifying a service attachment point (SAP) + of a provider equipment when requesting a + service with CE based SDP attributes."; + reference + "RFC 9408: A YANG Network Data Model for + Service Attachment Points (SAPs)"; + } + container protocols { + description + "Serves as an augmentation target. + Protocols can be augmented into this container, + e.g., BGP or static routing."; + } + } + uses ac-common:service-status; + } + } + uses ac-common:service-status; + container sdp-monitoring { + config false; + description + "Container for SDP monitoring metrics."; + leaf incoming-bw-value { + type yang:gauge64; + units "bps"; + description + "Indicates the absolute value of the incoming + bandwidth at an SDP from the customer network or + from another provider's network."; + } + leaf incoming-bw-percent { + type percentage; + units "percent"; + description + "Indicates a percentage of the incoming bandwidth + at an SDP from the customer network or + from another provider's network."; + } + leaf outgoing-bw-value { + type yang:gauge64; + units "bps"; + description + "Indicates the absolute value of the outgoing + bandwidth at an SDP towards the customer network or + towards another provider's network."; + } + leaf outgoing-bw-percent { + type percentage; + units "percent"; + description + "Indicates a percentage of the outgoing bandwidth + at an SDP towards the customer network or towards + another provider's network."; + } + } + } + } + container connection-groups { + description + "Contains connection groups."; + list connection-group { + key "id"; + description + "List of connection groups."; + leaf id { + type string; + description + "The connection group identifier."; + } + leaf connectivity-type { + type identityref { + base vpn-common:vpn-topology; + } + description + "Connection group connectivity type."; + } + uses service-slo-sle-policy; + /* Per connection group SLO/SLE policy + * overrides the per Slice SLO/SLE policy. + */ + uses service-slo-sle-policy-override; + list connectivity-construct { + key "id"; + description + "List of connectivity constructs."; + leaf id { + type string; + description + "The connectivity construct identifier."; + } + choice type { + default "p2p"; + description + "Choice for connectivity construct type."; + case p2p { + description + "P2P connectivity construct."; + leaf p2p-sender-sdp { + type leafref { + path "../../../../sdps/sdp/id"; + } + description + "Reference to a sender SDP."; + } + leaf p2p-receiver-sdp { + type leafref { + path "../../../../sdps/sdp/id"; + } + description + "Reference to a receiver SDP."; + } + } + case p2mp { + description + "P2MP connectivity construct."; + leaf p2mp-sender-sdp { + type leafref { + path "../../../../sdps/sdp/id"; + } + description + "Reference to a sender SDP."; + } + leaf-list p2mp-receiver-sdp { + type leafref { + path "../../../../sdps/sdp/id"; + } + description + "Reference to a receiver SDP."; + } + } + case a2a { + description + "A2A connectivity construct."; + list a2a-sdp { + key "sdp-id"; + description + "List of included A2A SDPs."; + leaf sdp-id { + type leafref { + path "../../../../../sdps/sdp/id"; + } + description + "Reference to an SDP."; + } + uses service-slo-sle-policy; + } + } + } + uses service-slo-sle-policy; + /* Per connectivity construct SLO/SLE policy + * overrides the per slice SLO/SLE policy. + */ + uses service-slo-sle-policy-override; + uses ac-common:service-status; + container connectivity-construct-monitoring { + config false; + description + "SLO status per connectivity construct."; + uses connectivity-construct-monitoring-metrics; + } + } + container connection-group-monitoring { + config false; + description + "SLO status per connection group."; + uses connectivity-construct-monitoring-metrics; + } + } + } + container custom-topology { + description + "Serves as an augmentation target. + Container for custom topology, which is indicated by the + referenced topology predefined, e.g., an abstract RFC8345 + topology."; + uses nw:network-ref; + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-slice@2022-03-04.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-slice@2022-03-04.yang new file mode 100644 index 0000000000000000000000000000000000000000..b1ead4bf025c59065d01172f309af188c0ee2f75 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-slice@2022-03-04.yang @@ -0,0 +1,1130 @@ +module ietf-network-slice { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-network-slice"; + prefix ietf-ns; + + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Types."; + } + import ietf-te-types { + prefix te-types; + reference + "RFC 8776: Common YANG Data Types for Traffic Engineering."; + } + import ietf-te-packet-types { + prefix te-packet-types; + reference + "RFC 8776: Common YANG Data Types for Traffic Engineering."; + } + + organization + "IETF Traffic Engineering Architecture and Signaling (TEAS) + Working Group"; + contact + "WG Web: <https://tools.ietf.org/wg/teas/> + WG List: <mailto:teas@ietf.org> + + Editor: Bo Wu + <lana.wubo@huawei.com> + Editor: Dhruv Dhody + <dhruv.ietf@gmail.com> + Editor: Reza Rokui + <reza.rokui@nokia.com> + Editor: Tarek Saad + <tsaad@juniper.net> + Author: Liuyan Han + <hanliuyan@chinamobile.com>"; + description + "This module contains a YANG module for the IETF Network Slice. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Revised BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see the + RFC itself for full legal notices."; + + revision 2022-03-04 { + description + "initial version."; + reference + "RFC XXXX: A Yang Data Model for IETF Network Slice Operation"; + } + + /* Features */ + /* Identities */ + + identity ns-tag-type { + description + "Base identity for IETF Network Slice tag type."; + } + + identity ns-tag-customer { + base ns-tag-type; + description + "The IETF Network Slice customer ID tag type."; + } + + identity ns-tag-service { + base ns-tag-type; + description + "The IETF Network Slice service tag type."; + } + + identity ns-tag-opaque { + base ns-tag-type; + description + "The IETF Network Slice opaque tag type."; + } + + identity network-access-tag-type { + description + "Base identity for the network access tag type."; + } + + identity network-access-tag-vlan-id { + base network-access-tag-type; + description + "The network access interface VLAN ID tag type."; + } + + identity network-access-tag-ip-mask { + base network-access-tag-type; + description + "The network access tag IP mask."; + } + + identity network-access-tag-opaque { + base network-access-tag-type; + description + "The network access opaque tag type."; + } + + identity ns-isolation-type { + description + "Base identity for IETF Network slice isolation level."; + } + + identity ns-isolation-shared { + base ns-isolation-type; + description + "Shared resources (e.g. queues) are associated with the Network + Slice traffic. Hence, the IETF network slice traffic can be + impacted by effects of other services traffic sharing + the same resources."; + } + + identity ns-isolation-dedicated { + base ns-isolation-type; + description + "Dedicated resources (e.g. queues) are associated with the + Network Slice traffic. Hence, the IETF network slice traffic + is isolated from other servceis traffic sharing the same + resources."; + } + + identity ns-security-type { + description + "Base identity for for IETF Network security level."; + } + + identity ns-security-authenticate { + base ns-security-type; + description + "IETF Network Slice requires authentication."; + } + + identity ns-security-integrity { + base ns-security-type; + description + "IETF Network Slice requires data integrity."; + } + + identity ns-security-encryption { + base ns-security-type; + description + "IETF Network Slice requires data encryption."; + } + + identity ns-connectivity-type { + description + "Base identity for IETF Network Slice connectivity."; + } + + identity point-to-point { + base ns-connectivity-type; + description + "Identity for point-to-point IETF Network Slice connectivity."; + } + + identity point-to-multipoint { + base ns-connectivity-type; + description + "Identity for point-to-multipoint IETF Network Slice + connectivity."; + } + + identity multipoint-to-multipoint { + base ns-connectivity-type; + description + "Identity for multipoint-to-multipoint IETF Network Slice + connectivity."; + } + + identity any-to-any { + base ns-connectivity-type; + description + "Identity for any-to-any IETF Network Slice connectivity."; + } + + identity hub-spoke { + base ns-connectivity-type; + description + "Identity for Hub-and-Spoke IETF Network Slice connectivity."; + } + + identity custom { + base ns-connectivity-type; + description + "Identity of a custom NS topology where Hubs can act as + Spoke for certain parts of the network or Spokes as Hubs."; + } + + identity endpoint-role { + description + "Base identity of a NSE role in an IETF Network Slice topology."; + } + + identity any-to-any-role { + base endpoint-role; + description + "Identity of any-to-any NS."; + } + + identity spoke-role { + base endpoint-role; + description + "A NSE is acting as a Spoke."; + } + + identity hub-role { + base endpoint-role; + description + "A NSE is acting as a Hub."; + } + + identity ns-slo-metric-type { + description + "Base identity for IETF Network Slice SLO metric type."; + } + + identity ns-slo-one-way-bandwidth { + base ns-slo-metric-type; + description + "SLO bandwidth metric. Minimum guaranteed bandwidth between + two endpoints at any time and is measured unidirectionally."; + } + + identity ns-slo-two-way-bandwidth { + base ns-slo-metric-type; + description + "SLO bandwidth metric. Minimum guaranteed bandwidth between + two endpoints at any time."; + } + + identity ns-slo-shared-bandwidth { + base ns-slo-metric-type; + description + "The shared SLO bandwidth bound. It is the limit on the + bandwidth that can be shared amongst a group of connections + of an IETF Network Slice."; + } + + identity ns-slo-one-way-delay { + base ns-slo-metric-type; + description + "SLO one-way-delay is the upper bound of network delay when + transmitting between two endpoints. The metric is defined in + RFC7679."; + } + + identity ns-slo-two-way-delay { + base ns-slo-metric-type; + description + "SLO two-way delay is the upper bound of network delay when + transmitting between two endpoints. The metric is defined in + RFC2681."; + } + identity ns-slo-one-way-delay-variation { + base ns-slo-metric-type; + description + "SLO one-way delay variation is defined by RFC3393, is the + difference in the one-way delay between sequential packets + between two endpoints."; + } + + identity ns-slo-two-way-delay-variation { + base ns-slo-metric-type; + description + "SLO two-way delay variation is defined by RFC5481, is the + difference in the round-trip delay between sequential packets + between two endpoints."; + } + + identity ns-slo-one-way-packet-loss { + base ns-slo-metric-type; + description + "SLO loss metric. The ratio of packets dropped to packets + transmitted between two endpoints in one-way + over a period of time as specified in RFC7680."; + } + + identity ns-slo-two-way-packet-loss { + base ns-slo-metric-type; + description + "SLO loss metric. The ratio of packets dropped to packets + transmitted between two endpoints in two-way + over a period of time as specified in RFC7680."; + } + + identity ns-slo-availability { + base ns-slo-metric-type; + description + "SLO availability level."; + } + + identity ns-match-type { + description + "Base identity for IETF Network Slice traffic match type."; + } + + identity ns-phy-interface-match { + base ns-match-type; + description + "Use the physical interface as match criteria for the IETF + Network Slice traffic."; + } + + identity ns-vlan-match { + base ns-match-type; + description + "Use the VLAN ID as match criteria for the IETF Network Slice + traffic."; + } + + identity ns-label-match { + base ns-match-type; + description + "Use the MPLS label as match criteria for the IETF Network + Slice traffic."; + } + + identity peering-protocol-type { + description + "Base identity for NSE peering protocol type."; + } + + identity peering-protocol-bgp { + base peering-protocol-type; + description + "Use BGP as protocol for NSE peering with customer device."; + } + + identity peering-static-routing { + base peering-protocol-type; + description + "Use static routing for NSE peering with customer device."; + } + + /* + * Identity for availability-type + */ + + identity availability-type { + description + "Base identity from which specific availability types are + derived."; + } + + identity level-1 { + base availability-type; + description + "level 1: 99.9999%"; + } + identity level-2 { + base availability-type; + description + "level 2: 99.999%"; + } + + identity level-3 { + base availability-type; + description + "level 3: 99.99%"; + } + + identity level-4 { + base availability-type; + description + "level 4: 99.9%"; + } + + identity level-5 { + base availability-type; + description + "level 5: 99%"; + } + + /* typedef */ + + typedef operational-type { + type enumeration { + enum up { + value 0; + description + "Operational status UP."; + } + enum down { + value 1; + description + "Operational status DOWN."; + } + enum unknown { + value 2; + description + "Operational status UNKNOWN."; + } + } + description + "This is a read-only attribute used to determine the + status of a particular element."; + } + typedef ns-monitoring-type { + type enumeration { + enum one-way { + description + "Represents one-way measurments monitoring type."; + } + enum two-way { + description + "represents two-way measurements monitoring type."; + } + } + description + "An enumerated type for monitoring on a IETF Network Slice + connection."; + } + + /* Groupings */ + + grouping status-params { + description + "A grouping used to join operational and administrative status."; + container status { + description + "A container for the administrative and operational state."; + leaf admin-enabled { + type boolean; + description + "The administrative status."; + } + leaf oper-status { + type operational-type; + config false; + description + "The operational status."; + } + } + } + + grouping ns-match-criteria { + description + "A grouping for the IETF Network Slice match definition."; + container ns-match-criteria { + description + "Describes the IETF Network Slice match criteria."; + list ns-match-criterion { + key "index"; + description + "List of the IETF Network Slice traffic match criteria."; + leaf index { + type uint32; + description + "The entry index."; + } + leaf match-type { + type identityref { + base ns-match-type; + } + description + "Identifies an entry in the list of the IETF Network Slice + match criteria."; + } + list values { + key "index"; + description + "List of match criteria values."; + leaf index { + type uint8; + description + "Index of an entry in the list."; + } + leaf value { + type string; + description + "Describes the IETF Network Slice match criteria, e.g. + IP address, VLAN, etc."; + } + } + leaf target-ns-connection-group-id { + type leafref { + path "/network-slices/network-slice" + + "/ns-connection-groups/ns-connection-group" + + "/ns-connection-group-id"; + } + description + "reference to a Network Slice connection group."; + } + } + } + } + + grouping ns-sles { + description + "Indirectly Measurable Objectives of a IETF Network + Slice."; + leaf-list security { + type identityref { + base ns-security-type; + } + description + "The IETF Network Slice security SLE(s)"; + } + leaf isolation { + type identityref { + base ns-isolation-type; + } + default "ns-isolation-shared"; + description + "The IETF Network Slice isolation SLE requirement."; + } + leaf max-occupancy-level { + type uint8 { + range "1..100"; + } + description + "The maximal occupancy level specifies the number of flows to + be admitted."; + } + leaf mtu { + type uint16; + units "bytes"; + mandatory true; + description + "The MTU specifies the maximum length in octets of data + packets that can be transmitted by the NS. The value needs + to be less than or equal to the minimum MTU value of + all 'ep-network-access-points' in the NSEs of the NS."; + } + container steering-constraints { + description + "Container for the policy of steering constraints + applicable to IETF Network Slice."; + container path-constraints { + description + "Container for the policy of path constraints + applicable to IETF Network Slice."; + } + container service-function { + description + "Container for the policy of service function + applicable to IETF Network Slice."; + } + } + } + + grouping ns-metric-bounds { + description + "IETF Network Slice metric bounds grouping."; + container ns-metric-bounds { + description + "IETF Network Slice metric bounds container."; + list ns-metric-bound { + key "metric-type"; + description + "List of IETF Network Slice metric bounds."; + leaf metric-type { + type identityref { + base ns-slo-metric-type; + } + description + "Identifies an entry in the list of metric type + bounds for the IETF Network Slice."; + } + leaf metric-unit { + type string; + mandatory true; + description + "The metric unit of the parameter. For example, + s, ms, ns, and so on."; + } + leaf value-description { + type string; + description + "The description of previous value."; + } + leaf bound { + type uint64; + default "0"; + description + "The Bound on the Network Slice connection metric. A + zero indicate an unbounded upper limit for the + specific metric-type."; + } + } + } + } + + grouping ep-peering { + description + "A grouping for the IETF Network Slice Endpoint peering."; + container ep-peering { + description + "Describes NSE peering attributes."; + list protocol { + key "protocol-type"; + description + "List of the NSE peering protocol."; + leaf protocol-type { + type identityref { + base peering-protocol-type; + } + description + "Identifies an entry in the list of NSE peering + protocol type."; + } + list attribute { + key "index"; + description + "List of protocol attribute."; + leaf index { + type uint8; + description + "Index of an entry in the list."; + } + leaf attribute-description { + type string; + description + "The description of the attribute."; + } + leaf value { + type string; + description + "Describes the value of protocol attribute, e.g. + nexthop address, peer address, etc."; + } + } + } + } + } + + grouping ep-network-access-points { + description + "Grouping for the endpoint network access definition."; + container ep-network-access-points { + description + "List of network access points."; + list ep-network-access-point { + key "network-access-id"; + description + "The IETF Network Slice network access points + related parameters."; + leaf network-access-id { + type string; + description + "Uniquely identifier a network access point."; + } + leaf network-access-description { + type string; + description + "The network access point description."; + } + leaf network-access-node-id { + type string; + description + "The network access point node ID in the case of + multi-homing."; + } + leaf network-access-tp-id { + type string; + description + "The termination port ID of the EP network access + point."; + } + leaf network-access-tp-ip-address { + type inet:ip-address; + description + "The IP address of the EP network access point."; + } + leaf network-access-tp-ip-prefix-length { + type uint8; + description + "The subnet prefix length expressed in bits."; + } + leaf network-access-qos-policy-name { + type string; + description + "The name of the QoS policy that is applied to the + network access point. The name can reference a QoS + profile that is pre-provisioned on the device."; + } + leaf mtu { + type uint16; + units "bytes"; + mandatory true; + description + "Maximum size in octets of a data packet that + can traverse a NSE network access point."; + } + container network-access-tags { + description + "Container for the network access tags."; + list network-access-tag { + key "index"; + description + "The network access point tags list."; + leaf index { + type uint32; + description + "The entry index."; + } + leaf network-access-tag-type { + type identityref { + base network-access-tag-type; + } + description + "The network access point tag type."; + } + leaf network-access-tag-value { + type string; + description + "The network access point tag value."; + } + } + } + /* Per ep-network-access-point rate limits */ + uses ns-match-criteria; + uses ep-peering; + uses ns-rate-limit; + } + } + } + + grouping ep-monitoring-metrics { + description + "Grouping for the NS endpoint monitoring metrics."; + container ep-monitoring { + config false; + description + "Container for NS endpoint monitoring metrics."; + leaf incoming-utilized-bandwidth { + type te-types:te-bandwidth; + description + "Incoming bandwidth utilization at an endpoint."; + } + leaf incoming-bw-utilization { + type decimal64 { + fraction-digits 5; + range "0..100"; + } + units "percent"; + mandatory true; + description + "To be used to define the bandwidth utilization + as a percentage of the available bandwidth."; + } + leaf outgoing-utilized-bandwidth { + type te-types:te-bandwidth; + description + "Outoing bandwidth utilization at an endpoint."; + } + leaf outgoing-bw-utilization { + type decimal64 { + fraction-digits 5; + range "0..100"; + } + units "percent"; + mandatory true; + description + "To be used to define the bandwidth utilization + as a percentage of the available bandwidth."; + } + } + } + + grouping ns-connection-monitoring-metrics { + description + "Grouping for NS connection monitoring metrics."; + uses te-packet-types:one-way-performance-metrics-packet; + uses te-packet-types:two-way-performance-metrics-packet; + } + + grouping geolocation-container { + description + "A grouping containing a GPS location."; + container location { + description + "A container containing a GPS location."; + leaf altitude { + type int64; + units "millimeter"; + description + "Distance above the sea level."; + } + leaf latitude { + type decimal64 { + fraction-digits 8; + range "-90..90"; + } + description + "Relative position north or south on the Earth's surface."; + } + leaf longitude { + type decimal64 { + fraction-digits 8; + range "-180..180"; + } + description + "Angular distance east or west on the Earth's surface."; + } + } + // gps-location + } + + // geolocation-container + + grouping bw-rate-limits { + description + "Bandwidth rate limits grouping."; + reference + "RFC 7640: Traffic Management Benchmarking"; + leaf cir { + type uint64; + units "bps"; + description + "Committed Information Rate. The maximum number of bits + that a port can receive or send during one-second over an + interface."; + } + leaf cbs { + type uint64; + units "bytes"; + description + "Committed Burst Size. CBS controls the bursty nature + of the traffic. Traffic that does not use the configured + CIR accumulates credits until the credits reach the + configured CBS."; + } + leaf eir { + type uint64; + units "bps"; + description + "Excess Information Rate, i.e., excess frame delivery + allowed not subject to SLA. The traffic rate can be + limited by EIR."; + } + leaf ebs { + type uint64; + units "bytes"; + description + "Excess Burst Size. The bandwidth available for burst + traffic from the EBS is subject to the amount of + bandwidth that is accumulated during periods when + traffic allocated by the EIR policy is not used."; + } + leaf pir { + type uint64; + units "bps"; + description + "Peak Information Rate, i.e., maximum frame delivery + allowed. It is equal to or less than sum of CIR and EIR."; + } + leaf pbs { + type uint64; + units "bytes"; + description + "Peak Burst Size."; + } + } + + grouping ns-rate-limit { + description + "The rate limits grouping."; + container incoming-rate-limits { + description + "Container for the asymmetric traffic control."; + uses bw-rate-limits; + } + container outgoing-rate-limits { + description + "The rate-limit imposed on outgoing traffic."; + uses bw-rate-limits; + } + } + + grouping endpoint { + description + "IETF Network Slice endpoint related information"; + leaf ep-id { + type string; + description + "Unique identifier for the referred IETF Network + Slice endpoint."; + } + leaf ep-description { + type string; + description + "Give more description of the Network Slice endpoint."; + } + uses geolocation-container; + leaf node-id { + type string; + description + "Uniquely identifies an edge node within the IETF slice + network."; + } + leaf ep-ip { + type inet:ip-address; + description + "The IP address of the endpoint."; + } + uses ns-match-criteria; + uses ep-peering; + uses ep-network-access-points; + uses ns-rate-limit; + /* Per NSE rate limits */ + uses status-params; + uses ep-monitoring-metrics; + } + + //ns-endpoint + + grouping ns-connection { + description + "The network slice connection grouping."; + list ns-connection { + key "ns-connection-id"; + description + "List of Network Slice connections."; + leaf ns-connection-id { + type uint32; + description + "The Network Slice connection identifier."; + } + leaf ns-connectivity-type { + type identityref { + base ns-connectivity-type; + } + default "point-to-point"; + description + "Network Slice connection construct type."; + } + leaf-list src-nse { + type leafref { + path "/network-slices/network-slice" + + "/ns-endpoints/ns-endpoint/ep-id"; + } + description + "reference to source Network Slice endpoint."; + } + leaf-list dest-nse { + type leafref { + path "/network-slices/network-slice" + + "/ns-endpoints/ns-endpoint/ep-id"; + } + description + "reference to source Network Slice endpoint."; + } + uses ns-slo-sle-policy; + /* Per connection ns-slo-sle-policy overrides + * the per network slice ns-slo-sle-policy. + */ + container ns-connection-monitoring { + config false; + description + "SLO status Per NS connection."; + uses ns-connection-monitoring-metrics; + } + } + } + + //ns-connection + + grouping ns-connection-group { + description + "The Network Slice connection group is described in this + container."; + leaf ns-connection-group-id { + type string; + description + "The Network Slice connection group identifier."; + } + uses ns-slo-sle-policy; + uses ns-connection; + /* Per connection ns-slo-sle-policy overrides + * the per network slice ns-slo-sle-policy. + */ + container ns-connection-group-monitoring { + config false; + description + "SLO status Per NS connection."; + uses ns-connection-monitoring-metrics; + } + } + + //ns-connection-group + + grouping slice-template { + description + "Grouping for slice-templates."; + container ns-slo-sle-templates { + description + "Contains a set of network slice templates to + reference in the IETF network slice."; + list ns-slo-sle-template { + key "id"; + leaf id { + type string; + description + "Identification of the Service Level Objective (SLO) + and Service Level Expectation (SLE) template to be used. + Local administration meaning."; + } + leaf template-description { + type string; + description + "Description of the SLO & SLE policy template."; + } + description + "List for SLO and SLE template identifiers."; + } + } + } + + /* Configuration data nodes */ + + grouping ns-slo-sle-policy { + description + "Network Slice policy grouping."; + choice ns-slo-sle-policy { + description + "Choice for SLO and SLE policy template. + Can be standard template or customized template."; + case standard { + description + "Standard SLO template."; + leaf slo-sle-template { + type leafref { + path "/network-slices" + + "/ns-slo-sle-templates/ns-slo-sle-template/id"; + } + description + "Standard SLO and SLE template to be used."; + } + } + case custom { + description + "Customized SLO template."; + container slo-sle-policy { + description + "Contains the SLO policy."; + leaf policy-description { + type string; + description + "Description of the SLO policy."; + } + uses ns-metric-bounds; + uses ns-sles; + } + } + } + } + + container network-slices { + description + "Containes a list of IETF network slice"; + uses slice-template; + list network-slice { + key "ns-id"; + description + "A network-slice is identified by a ns-id."; + leaf ns-id { + type string; + description + "A unique network-slice identifier across an IETF NSC."; + } + leaf ns-description { + type string; + description + "Give more description of the network slice."; + } + container ns-tags { + description + "Container for the list of IETF Network Slice tags."; + list ns-tag { + key "index"; + description + "IETF Network Slice tag list."; + leaf index { + type uint32; + description + "The entry index."; + } + leaf ns-tag-type { + type identityref { + base ns-tag-type; + } + description + "The IETF Network Slice tag type."; + } + leaf ns-tag-value { + type string; + description + "The IETF Network Slice tag value."; + } + } + } + uses ns-slo-sle-policy; + uses status-params; + container ns-endpoints { + description + "NS Endpoints."; + list ns-endpoint { + key "ep-id"; + uses endpoint; + description + "List of endpoints in this slice."; + } + } + container ns-connection-groups { + description + "Contains NS connections group."; + list ns-connection-group { + key "ns-connection-group-id"; + description + "List of Network Slice connections."; + uses ns-connection-group; + } + } + } + //ietf-network-slice list + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-topology@2018-02-26.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-topology@2018-02-26.yang new file mode 100644 index 0000000000000000000000000000000000000000..1ec944d791db1da1b8236c6069f10d65b1b6f97f --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network-topology@2018-02-26.yang @@ -0,0 +1,294 @@ +module ietf-network-topology { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-network-topology"; + prefix nt; + + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-network { + prefix nw; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + + organization + "IETF I2RS (Interface to the Routing System) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/i2rs/> + WG List: <mailto:i2rs@ietf.org> + + Editor: Alexander Clemm + <mailto:ludwig@clemm.org> + + Editor: Jan Medved + <mailto:jmedved@cisco.com> + + Editor: Robert Varga + <mailto:robert.varga@pantheon.tech> + + Editor: Nitin Bahadur + <mailto:nitin_bahadur@yahoo.com> + + Editor: Hariharan Ananthakrishnan + <mailto:hari@packetdesign.com> + + Editor: Xufeng Liu + <mailto:xufeng.liu.ietf@gmail.com>"; + + description + "This module defines a common base model for a network topology, + augmenting the base network data model with links to connect + nodes, as well as termination points to terminate links + on nodes. + + Copyright (c) 2018 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8345; + see the RFC itself for full legal notices."; + + revision 2018-02-26 { + description + "Initial revision."; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + + typedef link-id { + type inet:uri; + description + "An identifier for a link in a topology. The precise + structure of the link-id will be up to the implementation. + The identifier SHOULD be chosen such that the same link in a + real network topology will always be identified through the + same identifier, even if the data model is instantiated in + separate datastores. An implementation MAY choose to capture + semantics in the identifier -- for example, to indicate the + type of link and/or the type of topology of which the link is + a part."; + } + + typedef tp-id { + type inet:uri; + description + "An identifier for termination points on a node. The precise + structure of the tp-id will be up to the implementation. + The identifier SHOULD be chosen such that the same termination + point in a real network topology will always be identified + through the same identifier, even if the data model is + instantiated in separate datastores. An implementation MAY + choose to capture semantics in the identifier -- for example, + to indicate the type of termination point and/or the type of + node that contains the termination point."; + } + + grouping link-ref { + description + "This grouping can be used to reference a link in a specific + network. Although it is not used in this module, it is + defined here for the convenience of augmenting modules."; + leaf link-ref { + type leafref { + path "/nw:networks/nw:network[nw:network-id=current()/../"+ + "network-ref]/nt:link/nt:link-id"; + require-instance false; + } + description + "A type for an absolute reference to a link instance. + (This type should not be used for relative references. + In such a case, a relative path should be used instead.)"; + } + uses nw:network-ref; + } + + grouping tp-ref { + description + "This grouping can be used to reference a termination point + in a specific node. Although it is not used in this module, + it is defined here for the convenience of augmenting + modules."; + leaf tp-ref { + type leafref { + path "/nw:networks/nw:network[nw:network-id=current()/../"+ + "network-ref]/nw:node[nw:node-id=current()/../"+ + "node-ref]/nt:termination-point/nt:tp-id"; + require-instance false; + } + description + "A type for an absolute reference to a termination point. + (This type should not be used for relative references. + In such a case, a relative path should be used instead.)"; + } + uses nw:node-ref; + } + + augment "/nw:networks/nw:network" { + description + "Add links to the network data model."; + list link { + key "link-id"; + description + "A network link connects a local (source) node and + a remote (destination) node via a set of the respective + node's termination points. It is possible to have several + links between the same source and destination nodes. + Likewise, a link could potentially be re-homed between + termination points. Therefore, in order to ensure that we + would always know to distinguish between links, every link + is identified by a dedicated link identifier. Note that a + link models a point-to-point link, not a multipoint link."; + leaf link-id { + type link-id; + description + "The identifier of a link in the topology. + A link is specific to a topology to which it belongs."; + } + container source { + description + "This container holds the logical source of a particular + link."; + leaf source-node { + type leafref { + path "../../../nw:node/nw:node-id"; + require-instance false; + } + description + "Source node identifier. Must be in the same topology."; + } + leaf source-tp { + type leafref { + path "../../../nw:node[nw:node-id=current()/../"+ + "source-node]/termination-point/tp-id"; + require-instance false; + } + description + "This termination point is located within the source node + and terminates the link."; + } + } + + container destination { + description + "This container holds the logical destination of a + particular link."; + leaf dest-node { + type leafref { + path "../../../nw:node/nw:node-id"; + require-instance false; + } + description + "Destination node identifier. Must be in the same + network."; + } + leaf dest-tp { + type leafref { + path "../../../nw:node[nw:node-id=current()/../"+ + "dest-node]/termination-point/tp-id"; + require-instance false; + } + description + "This termination point is located within the + destination node and terminates the link."; + } + } + list supporting-link { + key "network-ref link-ref"; + description + "Identifies the link or links on which this link depends."; + leaf network-ref { + type leafref { + path "../../../nw:supporting-network/nw:network-ref"; + require-instance false; + } + description + "This leaf identifies in which underlay topology + the supporting link is present."; + } + + leaf link-ref { + type leafref { + path "/nw:networks/nw:network[nw:network-id=current()/"+ + "../network-ref]/link/link-id"; + require-instance false; + } + description + "This leaf identifies a link that is a part + of this link's underlay. Reference loops in which + a link identifies itself as its underlay, either + directly or transitively, are not allowed."; + } + } + } + } + augment "/nw:networks/nw:network/nw:node" { + description + "Augments termination points that terminate links. + Termination points can ultimately be mapped to interfaces."; + list termination-point { + key "tp-id"; + description + "A termination point can terminate a link. + Depending on the type of topology, a termination point + could, for example, refer to a port or an interface."; + leaf tp-id { + type tp-id; + description + "Termination point identifier."; + } + list supporting-termination-point { + key "network-ref node-ref tp-ref"; + description + "This list identifies any termination points on which a + given termination point depends or onto which it maps. + Those termination points will themselves be contained + in a supporting node. This dependency information can be + inferred from the dependencies between links. Therefore, + this item is not separately configurable. Hence, no + corresponding constraint needs to be articulated. + The corresponding information is simply provided by the + implementing system."; + + leaf network-ref { + type leafref { + path "../../../nw:supporting-node/nw:network-ref"; + require-instance false; + } + description + "This leaf identifies in which topology the + supporting termination point is present."; + } + leaf node-ref { + type leafref { + path "../../../nw:supporting-node/nw:node-ref"; + require-instance false; + } + description + "This leaf identifies in which node the supporting + termination point is present."; + } + leaf tp-ref { + type leafref { + path "/nw:networks/nw:network[nw:network-id=current()/"+ + "../network-ref]/nw:node[nw:node-id=current()/../"+ + "node-ref]/termination-point/tp-id"; + require-instance false; + } + description + "Reference to the underlay node (the underlay node must + be in a different topology)."; + } + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network@2018-02-26.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network@2018-02-26.yang new file mode 100644 index 0000000000000000000000000000000000000000..6a03d7e41614cc8dc017cfb4d5aacfb4ca60bc2c --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-network@2018-02-26.yang @@ -0,0 +1,192 @@ +module ietf-network { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-network"; + prefix nw; + + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + + organization + "IETF I2RS (Interface to the Routing System) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/i2rs/> + WG List: <mailto:i2rs@ietf.org> + + Editor: Alexander Clemm + <mailto:ludwig@clemm.org> + + Editor: Jan Medved + <mailto:jmedved@cisco.com> + + Editor: Robert Varga + <mailto:robert.varga@pantheon.tech> + + Editor: Nitin Bahadur + <mailto:nitin_bahadur@yahoo.com> + + Editor: Hariharan Ananthakrishnan + <mailto:hari@packetdesign.com> + + Editor: Xufeng Liu + <mailto:xufeng.liu.ietf@gmail.com>"; + description + "This module defines a common base data model for a collection + of nodes in a network. Node definitions are further used + in network topologies and inventories. + + Copyright (c) 2018 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8345; + see the RFC itself for full legal notices."; + + revision 2018-02-26 { + description + "Initial revision."; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + + typedef node-id { + type inet:uri; + description + "Identifier for a node. The precise structure of the node-id + will be up to the implementation. For example, some + implementations MAY pick a URI that includes the network-id + as part of the path. The identifier SHOULD be chosen + such that the same node in a real network topology will + always be identified through the same identifier, even if + the data model is instantiated in separate datastores. An + implementation MAY choose to capture semantics in the + identifier -- for example, to indicate the type of node."; + } + + typedef network-id { + type inet:uri; + description + "Identifier for a network. The precise structure of the + network-id will be up to the implementation. The identifier + SHOULD be chosen such that the same network will always be + identified through the same identifier, even if the data model + is instantiated in separate datastores. An implementation MAY + choose to capture semantics in the identifier -- for example, + to indicate the type of network."; + } + + grouping network-ref { + description + "Contains the information necessary to reference a network -- + for example, an underlay network."; + leaf network-ref { + type leafref { + path "/nw:networks/nw:network/nw:network-id"; + require-instance false; + } + description + "Used to reference a network -- for example, an underlay + network."; + } + } + + grouping node-ref { + description + "Contains the information necessary to reference a node."; + leaf node-ref { + type leafref { + path "/nw:networks/nw:network[nw:network-id=current()/../"+ + "network-ref]/nw:node/nw:node-id"; + require-instance false; + } + description + "Used to reference a node. + Nodes are identified relative to the network that + contains them."; + } + uses network-ref; + } + + container networks { + description + "Serves as a top-level container for a list of networks."; + list network { + key "network-id"; + description + "Describes a network. + A network typically contains an inventory of nodes, + topological information (augmented through the + network-topology data model), and layering information."; + leaf network-id { + type network-id; + description + "Identifies a network."; + } + container network-types { + description + "Serves as an augmentation target. + The network type is indicated through corresponding + presence containers augmented into this container."; + } + list supporting-network { + key "network-ref"; + description + "An underlay network, used to represent layered network + topologies."; + leaf network-ref { + type leafref { + path "/nw:networks/nw:network/nw:network-id"; + require-instance false; + } + description + "References the underlay network."; + } + } + + list node { + key "node-id"; + description + "The inventory of nodes of this network."; + leaf node-id { + type node-id; + description + "Uniquely identifies a node within the containing + network."; + } + list supporting-node { + key "network-ref node-ref"; + description + "Represents another node that is in an underlay network + and that supports this node. Used to represent layering + structure."; + leaf network-ref { + type leafref { + path "../../../nw:supporting-network/nw:network-ref"; + require-instance false; + } + description + "References the underlay network of which the + underlay node is a part."; + } + leaf node-ref { + type leafref { + path "/nw:networks/nw:network/nw:node/nw:node-id"; + require-instance false; + } + description + "References the underlay node itself."; + } + } + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-packet-fields@2019-03-04.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-packet-fields@2019-03-04.yang new file mode 100644 index 0000000000000000000000000000000000000000..2fb797bd87bf4ed825f83ec788df707b94c5f68b --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-packet-fields@2019-03-04.yang @@ -0,0 +1,576 @@ +module ietf-packet-fields { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-packet-fields"; + prefix packet-fields; + + import ietf-inet-types { + prefix inet; + reference + "RFC 6991 - Common YANG Data Types."; + } + + import ietf-yang-types { + prefix yang; + reference + "RFC 6991 - Common YANG Data Types."; + } + + import ietf-ethertypes { + prefix eth; + reference + "RFC 8519 - YANG Data Model for Network Access Control + Lists (ACLs)."; + } + + organization + "IETF NETMOD (Network Modeling) Working Group."; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netmod/> + WG List: netmod@ietf.org + + Editor: Mahesh Jethanandani + mjethanandani@gmail.com + Editor: Lisa Huang + huangyi_99@yahoo.com + Editor: Sonal Agarwal + sagarwal12@gmail.com + Editor: Dana Blair + dana@blairhome.com"; + + description + "This YANG module defines groupings that are used by + the ietf-access-control-list YANG module. Their usage + is not limited to ietf-access-control-list and can be + used anywhere as applicable. + + Copyright (c) 2019 IETF Trust and the persons identified as + the document authors. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD + License set forth in Section 4.c of the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8519; see + the RFC itself for full legal notices."; + + revision 2019-03-04 { + description + "Initial version."; + reference + "RFC 8519: YANG Data Model for Network Access Control + Lists (ACLs)."; + } + + /* + * Typedefs + */ + typedef operator { + type enumeration { + enum lte { + description + "Less than or equal to."; + } + enum gte { + description + "Greater than or equal to."; + } + enum eq { + description + "Equal to."; + } + enum neq { + description + "Not equal to."; + } + } + description + "The source and destination port range definitions + can be further qualified using an operator. An + operator is needed only if the lower-port is specified + and the upper-port is not specified. The operator + therefore further qualifies the lower-port only."; + } + + /* + * Groupings + */ + grouping port-range-or-operator { + choice port-range-or-operator { + case range { + leaf lower-port { + type inet:port-number; + must '. <= ../upper-port' { + error-message + "The lower-port must be less than or equal to + the upper-port."; + } + mandatory true; + description + "Lower boundary for a port."; + } + leaf upper-port { + type inet:port-number; + mandatory true; + description + "Upper boundary for a port."; + } + } + case operator { + leaf operator { + type operator; + default "eq"; + description + "Operator to be applied on the port below."; + } + leaf port { + type inet:port-number; + mandatory true; + description + "Port number along with the operator on which to + match."; + } + } + description + "Choice of specifying a port range or a single + port along with an operator."; + } + description + "Grouping for port definitions in the form of a + choice statement."; + } + + grouping acl-ip-header-fields { + description + "IP header fields common to IPv4 and IPv6"; + reference + "RFC 791: Internet Protocol."; + + leaf dscp { + type inet:dscp; + description + "Differentiated Services Code Point."; + reference + "RFC 2474: Definition of the Differentiated Services + Field (DS Field) in the IPv4 and IPv6 + Headers."; + } + + leaf ecn { + type uint8 { + range "0..3"; + } + description + "Explicit Congestion Notification."; + reference + "RFC 3168: The Addition of Explicit Congestion + Notification (ECN) to IP."; + } + + leaf length { + type uint16; + description + "In the IPv4 header field, this field is known as the Total + Length. Total Length is the length of the datagram, measured + in octets, including internet header and data. + + In the IPv6 header field, this field is known as the Payload + Length, which is the length of the IPv6 payload, i.e., the rest + of the packet following the IPv6 header, in octets."; + reference + "RFC 791: Internet Protocol + RFC 8200: Internet Protocol, Version 6 (IPv6) Specification."; + } + leaf ttl { + type uint8; + description + "This field indicates the maximum time the datagram is allowed + to remain in the internet system. If this field contains the + value zero, then the datagram must be dropped. + + In IPv6, this field is known as the Hop Limit."; + reference + "RFC 791: Internet Protocol + RFC 8200: Internet Protocol, Version 6 (IPv6) Specification."; + } + leaf protocol { + type uint8; + description + "Internet Protocol number. Refers to the protocol of the + payload. In IPv6, this field is known as 'next-header', + and if extension headers are present, the protocol is + present in the 'upper-layer' header."; + reference + "RFC 791: Internet Protocol + RFC 8200: Internet Protocol, Version 6 (IPv6) Specification."; + } + } + + grouping acl-ipv4-header-fields { + description + "Fields in the IPv4 header."; + leaf ihl { + type uint8 { + range "5..60"; + } + description + "In an IPv4 header field, the Internet Header Length (IHL) is + the length of the internet header in 32-bit words and + thus points to the beginning of the data. Note that the + minimum value for a correct header is 5."; + } + leaf flags { + type bits { + bit reserved { + position 0; + description + "Reserved. Must be zero."; + } + bit fragment { + position 1; + description + "Setting the value to 0 indicates may fragment, while + setting the value to 1 indicates do not fragment."; + } + bit more { + position 2; + description + "Setting the value to 0 indicates this is the last fragment, + and setting the value to 1 indicates more fragments are + coming."; + } + } + description + "Bit definitions for the Flags field in the IPv4 header."; + } + leaf offset { + type uint16 { + range "20..65535"; + } + description + "The fragment offset is measured in units of 8 octets (64 bits). + The first fragment has offset zero. The length is 13 bits"; + } + leaf identification { + type uint16; + description + "An identifying value assigned by the sender to aid in + assembling the fragments of a datagram."; + } + + choice destination-network { + case destination-ipv4-network { + leaf destination-ipv4-network { + type inet:ipv4-prefix; + description + "Destination IPv4 address prefix."; + } + } + description + "Choice of specifying a destination IPv4 address or + referring to a group of IPv4 destination addresses."; + } + + choice source-network { + case source-ipv4-network { + leaf source-ipv4-network { + type inet:ipv4-prefix; + description + "Source IPv4 address prefix."; + } + } + description + "Choice of specifying a source IPv4 address or + referring to a group of IPv4 source addresses."; + } + } + + grouping acl-ipv6-header-fields { + description + "Fields in the IPv6 header."; + + choice destination-network { + case destination-ipv6-network { + leaf destination-ipv6-network { + type inet:ipv6-prefix; + description + "Destination IPv6 address prefix."; + } + } + description + "Choice of specifying a destination IPv6 address + or referring to a group of IPv6 destination + addresses."; + } + + choice source-network { + case source-ipv6-network { + leaf source-ipv6-network { + type inet:ipv6-prefix; + description + "Source IPv6 address prefix."; + } + } + description + "Choice of specifying a source IPv6 address or + referring to a group of IPv6 source addresses."; + } + + leaf flow-label { + type inet:ipv6-flow-label; + description + "IPv6 Flow label."; + } + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation."; + } + + grouping acl-eth-header-fields { + description + "Fields in the Ethernet header."; + leaf destination-mac-address { + type yang:mac-address; + description + "Destination IEEE 802 Media Access Control (MAC) + address."; + } + leaf destination-mac-address-mask { + type yang:mac-address; + description + "Destination IEEE 802 MAC address mask."; + } + leaf source-mac-address { + type yang:mac-address; + description + "Source IEEE 802 MAC address."; + } + leaf source-mac-address-mask { + type yang:mac-address; + description + "Source IEEE 802 MAC address mask."; + } + leaf ethertype { + type eth:ethertype; + description + "The Ethernet Type (or Length) value represented + in the canonical order defined by IEEE 802. + The canonical representation uses lowercase + characters."; + reference + "IEEE 802-2014, Clause 9.2."; + } + reference + "IEEE 802: IEEE Standard for Local and Metropolitan + Area Networks: Overview and Architecture."; + } + + grouping acl-tcp-header-fields { + description + "Collection of TCP header fields that can be used to + set up a match filter."; + leaf sequence-number { + type uint32; + description + "Sequence number that appears in the packet."; + } + leaf acknowledgement-number { + type uint32; + description + "The acknowledgement number that appears in the + packet."; + } + leaf data-offset { + type uint8 { + range "5..15"; + } + description + "Specifies the size of the TCP header in 32-bit + words. The minimum size header is 5 words and + the maximum is 15 words; thus, this gives a + minimum size of 20 bytes and a maximum of 60 + bytes, allowing for up to 40 bytes of options + in the header."; + } + leaf reserved { + type uint8; + description + "Reserved for future use."; + } + leaf flags { + type bits { + bit cwr { + position 1; + description + "The Congestion Window Reduced (CWR) flag is set + by the sending host to indicate that it received + a TCP segment with the ECN-Echo (ECE) flag set + and had responded in the congestion control + mechanism."; + reference + "RFC 3168: The Addition of Explicit Congestion + Notification (ECN) to IP."; + } + bit ece { + position 2; + description + "ECN-Echo has a dual role, depending on the value + of the SYN flag. It indicates the following: if + the SYN flag is set (1), the TCP peer is ECN + capable, and if the SYN flag is clear (0), a packet + with the Congestion Experienced flag set (ECN=11) + in the IP header was received during normal + transmission (added to the header by RFC 3168). + This serves as an indication of network congestion + (or impending congestion) to the TCP sender."; + reference + "RFC 3168: The Addition of Explicit Congestion + Notification (ECN) to IP."; + } + bit urg { + position 3; + description + "Indicates that the Urgent Pointer field is significant."; + } + bit ack { + position 4; + description + "Indicates that the Acknowledgement field is significant. + All packets after the initial SYN packet sent by the + client should have this flag set."; + } + bit psh { + position 5; + description + "Push function. Asks to push the buffered data to the + receiving application."; + } + bit rst { + position 6; + description + "Reset the connection."; + } + bit syn { + position 7; + description + "Synchronize sequence numbers. Only the first packet + sent from each end should have this flag set. Some + other flags and fields change meaning based on this + flag, and some are only valid for when it is set, + and others when it is clear."; + } + bit fin { + position 8; + description + "Last package from the sender."; + } + } + description + "Also known as Control Bits. Contains nine 1-bit flags."; + reference + "RFC 793: Transmission Control Protocol."; + } + leaf window-size { + type uint16; + units "bytes"; + description + "The size of the receive window, which specifies + the number of window size units beyond the segment + identified by the sequence number in the Acknowledgement + field that the sender of this segment is currently + willing to receive."; + } + leaf urgent-pointer { + type uint16; + description + "This field is an offset from the sequence number + indicating the last urgent data byte."; + } + leaf options { + type binary { + length "1..40"; + } + description + "The length of this field is determined by the + Data Offset field. Options have up to three + fields: Option-Kind (1 byte), Option-Length + (1 byte), and Option-Data (variable). The Option-Kind + field indicates the type of option and is the + only field that is not optional. Depending on + what kind of option we are dealing with, + the next two fields may be set: the Option-Length + field indicates the total length of the option, + and the Option-Data field contains the value of + the option, if applicable."; + } + } + + grouping acl-udp-header-fields { + description + "Collection of UDP header fields that can be used + to set up a match filter."; + leaf length { + type uint16; + description + "A field that specifies the length in bytes of + the UDP header and UDP data. The minimum + length is 8 bytes because that is the length of + the header. The field size sets a theoretical + limit of 65,535 bytes (8-byte header plus 65,527 + bytes of data) for a UDP datagram. However, the + actual limit for the data length, which is + imposed by the underlying IPv4 protocol, is + 65,507 bytes (65,535 minus 8-byte UDP header + minus 20-byte IP header). + + In IPv6 jumbograms, it is possible to have + UDP packets of a size greater than 65,535 bytes. + RFC 2675 specifies that the Length field is set + to zero if the length of the UDP header plus + UDP data is greater than 65,535."; + } + } + + grouping acl-icmp-header-fields { + description + "Collection of ICMP header fields that can be + used to set up a match filter."; + leaf type { + type uint8; + description + "Also known as control messages."; + reference + "RFC 792: Internet Control Message Protocol + RFC 4443: Internet Control Message Protocol (ICMPv6) + for Internet Protocol Version 6 (IPv6) + Specification."; + } + leaf code { + type uint8; + description + "ICMP subtype. Also known as control messages."; + reference + "RFC 792: Internet Control Message Protocol + RFC 4443: Internet Control Message Protocol (ICMPv6) + for Internet Protocol Version 6 (IPv6) + Specification."; + } + leaf rest-of-header { + type binary; + description + "Unbounded in length, the contents vary based on the + ICMP type and code. Also referred to as 'Message Body' + in ICMPv6."; + reference + "RFC 792: Internet Control Message Protocol + RFC 4443: Internet Control Message Protocol (ICMPv6) + for Internet Protocol Version 6 (IPv6) + Specification."; + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-routing-types@2017-12-04.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-routing-types@2017-12-04.yang new file mode 100644 index 0000000000000000000000000000000000000000..24319c155fb104e20bee79e5b257317b01323197 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-routing-types@2017-12-04.yang @@ -0,0 +1,771 @@ +module ietf-routing-types { + namespace "urn:ietf:params:xml:ns:yang:ietf-routing-types"; + prefix rt-types; + + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + + organization + "IETF RTGWG - Routing Area Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/rtgwg/> + WG List: <mailto:rtgwg@ietf.org> + + Editors: Xufeng Liu + <mailto:Xufeng_Liu@jabail.com> + Yingzhen Qu + <mailto:yingzhen.qu@huawei.com> + Acee Lindem + <mailto:acee@cisco.com> + Christian Hopps + <mailto:chopps@chopps.org> + Lou Berger + <mailto:lberger@labn.com>"; + + description + "This module contains a collection of YANG data types + considered generally useful for routing protocols. + + Copyright (c) 2017 IETF Trust and the persons + identified as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8294; see + the RFC itself for full legal notices."; + revision 2017-12-04 { + description "Initial revision."; + reference + "RFC 8294: Common YANG Data Types for the Routing Area. + Section 3."; + } + + /*** Identities related to MPLS/GMPLS ***/ + + identity mpls-label-special-purpose-value { + description + "Base identity for deriving identities describing + special-purpose Multiprotocol Label Switching (MPLS) label + values."; + reference + "RFC 7274: Allocating and Retiring Special-Purpose MPLS + Labels."; + } + + identity ipv4-explicit-null-label { + base mpls-label-special-purpose-value; + description + "This identity represents the IPv4 Explicit NULL Label."; + reference + "RFC 3032: MPLS Label Stack Encoding. Section 2.1."; + } + + identity router-alert-label { + base mpls-label-special-purpose-value; + description + "This identity represents the Router Alert Label."; + reference + "RFC 3032: MPLS Label Stack Encoding. Section 2.1."; + } + + identity ipv6-explicit-null-label { + base mpls-label-special-purpose-value; + description + "This identity represents the IPv6 Explicit NULL Label."; + reference + "RFC 3032: MPLS Label Stack Encoding. Section 2.1."; + } + + identity implicit-null-label { + base mpls-label-special-purpose-value; + description + "This identity represents the Implicit NULL Label."; + reference + "RFC 3032: MPLS Label Stack Encoding. Section 2.1."; + } + + identity entropy-label-indicator { + base mpls-label-special-purpose-value; + description + "This identity represents the Entropy Label Indicator."; + reference + "RFC 6790: The Use of Entropy Labels in MPLS Forwarding. + Sections 3 and 10.1."; + } + + identity gal-label { + base mpls-label-special-purpose-value; + description + "This identity represents the Generic Associated Channel + (G-ACh) Label (GAL)."; + reference + "RFC 5586: MPLS Generic Associated Channel. + Sections 4 and 10."; + } + + identity oam-alert-label { + base mpls-label-special-purpose-value; + description + "This identity represents the OAM Alert Label."; + reference + "RFC 3429: Assignment of the 'OAM Alert Label' for + Multiprotocol Label Switching Architecture (MPLS) + Operation and Maintenance (OAM) Functions. + Sections 3 and 6."; + } + + identity extension-label { + base mpls-label-special-purpose-value; + description + "This identity represents the Extension Label."; + reference + "RFC 7274: Allocating and Retiring Special-Purpose MPLS + Labels. Sections 3.1 and 5."; + } + + /*** Collection of types related to routing ***/ + + typedef router-id { + type yang:dotted-quad; + description + "A 32-bit number in the dotted-quad format assigned to each + router. This number uniquely identifies the router within + an Autonomous System."; + } + + /*** Collection of types related to VPNs ***/ + + typedef route-target { + type string { + pattern + '(0:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))|' + + '(1:((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(2:(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(6(:[a-fA-F0-9]{2}){6})|' + + '(([3-57-9a-fA-F]|[1-9a-fA-F][0-9a-fA-F]{1,3}):' + + '[0-9a-fA-F]{1,12})'; + } + + description + "A Route Target is an 8-octet BGP extended community + initially identifying a set of sites in a BGP VPN + (RFC 4364). However, it has since taken on a more general + role in BGP route filtering. A Route Target consists of two + or three fields: a 2-octet Type field, an administrator + field, and, optionally, an assigned number field. + + According to the data formats for types 0, 1, 2, and 6 as + defined in RFC 4360, RFC 5668, and RFC 7432, the encoding + pattern is defined as: + + 0:2-octet-asn:4-octet-number + 1:4-octet-ipv4addr:2-octet-number + 2:4-octet-asn:2-octet-number + 6:6-octet-mac-address + + Additionally, a generic pattern is defined for future + Route Target types: + + 2-octet-other-hex-number:6-octet-hex-number + + Some valid examples are 0:100:100, 1:1.1.1.1:100, + 2:1234567890:203, and 6:26:00:08:92:78:00."; + reference + "RFC 4360: BGP Extended Communities Attribute. + RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 5668: 4-Octet AS Specific BGP Extended Community. + RFC 7432: BGP MPLS-Based Ethernet VPN."; + } + + typedef ipv6-route-target { + type string { + pattern + '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])))' + + ':' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + pattern '((([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?))' + + ':' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + } + description + "An IPv6 Route Target is a 20-octet BGP IPv6 Address + Specific Extended Community serving the same function + as a standard 8-octet Route Target, except that it only + allows an IPv6 address as the global administrator. + The format is <ipv6-address:2-octet-number>. + + Two valid examples are 2001:db8::1:6544 and + 2001:db8::5eb1:791:6b37:17958."; + reference + "RFC 5701: IPv6 Address Specific BGP Extended Community + Attribute."; + } + + typedef route-target-type { + type enumeration { + enum import { + value 0; + description + "The Route Target applies to route import."; + } + enum export { + value 1; + description + "The Route Target applies to route export."; + } + + enum both { + value 2; + description + "The Route Target applies to both route import and + route export."; + } + } + description + "Indicates the role a Route Target takes in route filtering."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs)."; + } + + typedef route-distinguisher { + type string { + pattern + '(0:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))|' + + '(1:((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(2:(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(6(:[a-fA-F0-9]{2}){6})|' + + '(([3-57-9a-fA-F]|[1-9a-fA-F][0-9a-fA-F]{1,3}):' + + '[0-9a-fA-F]{1,12})'; + } + + description + "A Route Distinguisher is an 8-octet value used to + distinguish routes from different BGP VPNs (RFC 4364). + A Route Distinguisher will have the same format as a + Route Target as per RFC 4360 and will consist of + two or three fields: a 2-octet Type field, an administrator + field, and, optionally, an assigned number field. + + According to the data formats for types 0, 1, 2, and 6 as + defined in RFC 4360, RFC 5668, and RFC 7432, the encoding + pattern is defined as: + + 0:2-octet-asn:4-octet-number + 1:4-octet-ipv4addr:2-octet-number + 2:4-octet-asn:2-octet-number + 6:6-octet-mac-address + + Additionally, a generic pattern is defined for future + route discriminator types: + + 2-octet-other-hex-number:6-octet-hex-number + + Some valid examples are 0:100:100, 1:1.1.1.1:100, + 2:1234567890:203, and 6:26:00:08:92:78:00."; + reference + "RFC 4360: BGP Extended Communities Attribute. + RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 5668: 4-Octet AS Specific BGP Extended Community. + RFC 7432: BGP MPLS-Based Ethernet VPN."; + } + + typedef route-origin { + type string { + pattern + '(0:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))|' + + '(1:((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(2:(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(6(:[a-fA-F0-9]{2}){6})|' + + '(([3-57-9a-fA-F]|[1-9a-fA-F][0-9a-fA-F]{1,3}):' + + '[0-9a-fA-F]{1,12})'; + } + description + "A Route Origin is an 8-octet BGP extended community + identifying the set of sites where the BGP route + originated (RFC 4364). A Route Origin will have the same + format as a Route Target as per RFC 4360 and will consist + of two or three fields: a 2-octet Type field, an + administrator field, and, optionally, an assigned number + field. + + According to the data formats for types 0, 1, 2, and 6 as + defined in RFC 4360, RFC 5668, and RFC 7432, the encoding + pattern is defined as: + + 0:2-octet-asn:4-octet-number + 1:4-octet-ipv4addr:2-octet-number + 2:4-octet-asn:2-octet-number + 6:6-octet-mac-address + Additionally, a generic pattern is defined for future + Route Origin types: + + 2-octet-other-hex-number:6-octet-hex-number + + Some valid examples are 0:100:100, 1:1.1.1.1:100, + 2:1234567890:203, and 6:26:00:08:92:78:00."; + reference + "RFC 4360: BGP Extended Communities Attribute. + RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 5668: 4-Octet AS Specific BGP Extended Community. + RFC 7432: BGP MPLS-Based Ethernet VPN."; + } + + typedef ipv6-route-origin { + type string { + pattern + '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])))' + + ':' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + pattern '((([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?))' + + ':' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + } + description + "An IPv6 Route Origin is a 20-octet BGP IPv6 Address + Specific Extended Community serving the same function + as a standard 8-octet route, except that it only allows + an IPv6 address as the global administrator. The format + is <ipv6-address:2-octet-number>. + + Two valid examples are 2001:db8::1:6544 and + 2001:db8::5eb1:791:6b37:17958."; + reference + "RFC 5701: IPv6 Address Specific BGP Extended Community + Attribute."; + } + + /*** Collection of types common to multicast ***/ + + typedef ipv4-multicast-group-address { + type inet:ipv4-address { + pattern '(2((2[4-9])|(3[0-9]))\.).*'; + } + description + "This type represents an IPv4 multicast group address, + which is in the range of 224.0.0.0 to 239.255.255.255."; + reference + "RFC 1112: Host Extensions for IP Multicasting."; + } + + typedef ipv6-multicast-group-address { + type inet:ipv6-address { + pattern '(([fF]{2}[0-9a-fA-F]{2}):).*'; + } + description + "This type represents an IPv6 multicast group address, + which is in the range of ff00::/8."; + reference + "RFC 4291: IP Version 6 Addressing Architecture. Section 2.7. + RFC 7346: IPv6 Multicast Address Scopes."; + } + + typedef ip-multicast-group-address { + type union { + type ipv4-multicast-group-address; + type ipv6-multicast-group-address; + } + description + "This type represents a version-neutral IP multicast group + address. The format of the textual representation implies + the IP version."; + } + + typedef ipv4-multicast-source-address { + type union { + type enumeration { + enum * { + description + "Any source address."; + } + } + type inet:ipv4-address; + } + description + "Multicast source IPv4 address type."; + } + + typedef ipv6-multicast-source-address { + type union { + type enumeration { + enum * { + description + "Any source address."; + } + } + type inet:ipv6-address; + } + description + "Multicast source IPv6 address type."; + } + + /*** Collection of types common to protocols ***/ + + typedef bandwidth-ieee-float32 { + type string { + pattern + '0[xX](0((\.0?)?[pP](\+)?0?|(\.0?))|' + + '1(\.([0-9a-fA-F]{0,5}[02468aAcCeE]?)?)?[pP](\+)?(12[0-7]|' + + '1[01][0-9]|0?[0-9]?[0-9])?)'; + } + description + "Bandwidth in IEEE 754 floating-point 32-bit binary format: + (-1)**(S) * 2**(Exponent-127) * (1 + Fraction), + where Exponent uses 8 bits and Fraction uses 23 bits. + The units are octets per second. + The encoding format is the external hexadecimal-significant + character sequences specified in IEEE 754 and ISO/IEC C99. + The format is restricted to be normalized, non-negative, and + non-fraction: 0x1.hhhhhhp{+}d, 0X1.HHHHHHP{+}D, or 0x0p0, + where 'h' and 'H' are hexadecimal digits and 'd' and 'D' are + integers in the range of [0..127]. + When six hexadecimal digits are used for 'hhhhhh' or + 'HHHHHH', the least significant digit must be an even + number. 'x' and 'X' indicate hexadecimal; 'p' and 'P' + indicate a power of two. Some examples are 0x0p0, 0x1p10, + and 0x1.abcde2p+20."; + reference + "IEEE Std 754-2008: IEEE Standard for Floating-Point + Arithmetic. + ISO/IEC C99: Information technology - Programming + Languages - C."; + } + + typedef link-access-type { + type enumeration { + enum broadcast { + description + "Specify broadcast multi-access network."; + } + enum non-broadcast-multiaccess { + description + "Specify Non-Broadcast Multi-Access (NBMA) network."; + } + enum point-to-multipoint { + description + "Specify point-to-multipoint network."; + } + enum point-to-point { + description + "Specify point-to-point network."; + } + } + description + "Link access type."; + } + + typedef timer-multiplier { + type uint8; + description + "The number of timer value intervals that should be + interpreted as a failure."; + } + + typedef timer-value-seconds16 { + type union { + type uint16 { + range "1..65535"; + } + type enumeration { + enum infinity { + description + "The timer is set to infinity."; + } + enum not-set { + description + "The timer is not set."; + } + } + } + units "seconds"; + description + "Timer value type, in seconds (16-bit range)."; + } + + typedef timer-value-seconds32 { + type union { + type uint32 { + range "1..4294967295"; + } + type enumeration { + enum infinity { + description + "The timer is set to infinity."; + } + enum not-set { + description + "The timer is not set."; + } + } + } + units "seconds"; + description + "Timer value type, in seconds (32-bit range)."; + } + + typedef timer-value-milliseconds { + type union { + type uint32 { + range "1..4294967295"; + } + type enumeration { + enum infinity { + description + "The timer is set to infinity."; + } + enum not-set { + description + "The timer is not set."; + } + } + } + units "milliseconds"; + description + "Timer value type, in milliseconds."; + } + + typedef percentage { + type uint8 { + range "0..100"; + } + description + "Integer indicating a percentage value."; + } + + typedef timeticks64 { + type uint64; + description + "This type is based on the timeticks type defined in + RFC 6991, but with 64-bit width. It represents the time, + modulo 2^64, in hundredths of a second between two epochs."; + reference + "RFC 6991: Common YANG Data Types."; + } + + typedef uint24 { + type uint32 { + range "0..16777215"; + } + description + "24-bit unsigned integer."; + } + + /*** Collection of types related to MPLS/GMPLS ***/ + + typedef generalized-label { + type binary; + description + "Generalized Label. Nodes sending and receiving the + Generalized Label are aware of the link-specific + label context and type."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description. Section 3.2."; + } + + typedef mpls-label-special-purpose { + type identityref { + base mpls-label-special-purpose-value; + } + description + "This type represents the special-purpose MPLS label values."; + reference + "RFC 3032: MPLS Label Stack Encoding. + RFC 7274: Allocating and Retiring Special-Purpose MPLS + Labels."; + } + + typedef mpls-label-general-use { + type uint32 { + range "16..1048575"; + } + description + "The 20-bit label value in an MPLS label stack as specified + in RFC 3032. This label value does not include the + encodings of Traffic Class and TTL (Time to Live). + The label range specified by this type is for general use, + with special-purpose MPLS label values excluded."; + reference + "RFC 3032: MPLS Label Stack Encoding."; + } + + typedef mpls-label { + type union { + type mpls-label-special-purpose; + type mpls-label-general-use; + } + description + "The 20-bit label value in an MPLS label stack as specified + in RFC 3032. This label value does not include the + encodings of Traffic Class and TTL."; + reference + "RFC 3032: MPLS Label Stack Encoding."; + } + + /*** Groupings **/ + + grouping mpls-label-stack { + description + "This grouping specifies an MPLS label stack. The label + stack is encoded as a list of label stack entries. The + list key is an identifier that indicates the relative + ordering of each entry, with the lowest-value identifier + corresponding to the top of the label stack."; + container mpls-label-stack { + description + "Container for a list of MPLS label stack entries."; + list entry { + key "id"; + description + "List of MPLS label stack entries."; + leaf id { + type uint8; + description + "Identifies the entry in a sequence of MPLS label + stack entries. An entry with a smaller identifier + value precedes an entry with a larger identifier + value in the label stack. The value of this ID has + no semantic meaning other than relative ordering + and referencing the entry."; + } + leaf label { + type rt-types:mpls-label; + description + "Label value."; + } + + leaf ttl { + type uint8; + description + "Time to Live (TTL)."; + reference + "RFC 3032: MPLS Label Stack Encoding."; + } + leaf traffic-class { + type uint8 { + range "0..7"; + } + description + "Traffic Class (TC)."; + reference + "RFC 5462: Multiprotocol Label Switching (MPLS) Label + Stack Entry: 'EXP' Field Renamed to 'Traffic Class' + Field."; + } + } + } + } + + grouping vpn-route-targets { + description + "A grouping that specifies Route Target import-export rules + used in BGP-enabled VPNs."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 4664: Framework for Layer 2 Virtual Private Networks + (L2VPNs)."; + list vpn-target { + key "route-target"; + description + "List of Route Targets."; + leaf route-target { + type rt-types:route-target; + description + "Route Target value."; + } + leaf route-target-type { + type rt-types:route-target-type; + mandatory true; + description + "Import/export type of the Route Target."; + } + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-te-packet-types@2024-10-30.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-te-packet-types@2024-10-30.yang new file mode 100644 index 0000000000000000000000000000000000000000..70bead463820694c7f88c977f8ef28df0bb3db7a --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-te-packet-types@2024-10-30.yang @@ -0,0 +1,806 @@ +module ietf-te-packet-types { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-te-packet-types"; + prefix te-packet-types; + + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + + import ietf-te-types { + prefix te-types; + reference + "RFC XXXX: Common YANG Data Types for Traffic Engineering"; + } + // RFC Editor: replace XXXX with actual RFC number + // and remove this note + + organization + "IETF Traffic Engineering Architecture and Signaling (TEAS) + Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/teas/> + WG List: <mailto:teas@ietf.org> + + Editor: Tarek Saad + <mailto:tsaad.net@gmail.com> + + Editor: Rakesh Gandhi + <mailto:rgandhi@cisco.com> + + Editor: Vishnu Pavan Beeram + <mailto:vbeeram@juniper.net> + + Editor: Xufeng Liu + <mailto:xufeng.liu.ietf@gmail.com> + + Editor: Igor Bryskin + <mailto:i_bryskin@yahoo.com>"; + description + "This YANG module contains a collection of generally useful YANG + data type definitions specific to Packet Traffic Enginnering + (TE). + + The model fully conforms to the Network Management Datastore + Architecture (NMDA). + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL + NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', + 'MAY', and 'OPTIONAL' in this document are to be interpreted as + described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, + they appear in all capitals, as shown here. + + Copyright (c) 2024 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Revised BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX + (https://www.rfc-editor.org/info/rfcXXXX); see the RFC itself + for full legal notices."; + revision 2024-10-30 { + description + "This revision adds the following new identities: + - bandwidth-profile-type; + - link-metric-delay-variation; + - link-metric-loss; + - path-metric-delay-variation; + - path-metric-loss. + + This revision adds the following new groupings: + - bandwidth-profile-parameters; + - te-packet-path-bandwidth; + - te-packet-link-bandwidth. + + This revision provides also few editorial changes."; + reference + "RFC XXXX: Common YANG Data Types for Traffic Engineering"; + } + // RFC Editor: replace XXXX with actual RFC number, update date + // information and remove this note + + revision 2020-06-10 { + description + "Latest revision of TE MPLS types."; + reference + "RFC 8776: Common YANG Data Types for Traffic Engineering"; + } + + /* + * Identities + */ + + identity bandwidth-profile-type { + description + "Bandwidth Profile Types"; + } + + identity mef-10 { + base bandwidth-profile-type; + description + "MEF 10 Bandwidth Profile"; + reference + "MEF 10.3: Ethernet Services Attributes Phase 3"; + } + + identity rfc-2697 { + base bandwidth-profile-type; + description + "RFC 2697 Bandwidth Profile"; + reference + "RFC 2697: A Single Rate Three Color Marker"; + } + + identity rfc-2698 { + base bandwidth-profile-type; + description + "RFC 2698 Bandwidth Profile"; + reference + "RFC 2698: A Two Rate Three Color Marker"; + } + + // Derived identities from te-types:link-metric-type + + identity link-metric-delay-variation { + base te-types:link-metric-type; + description + "The Unidirectional Delay Variation Metric, + measured in units of microseconds."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions, + Section 4.3 + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions, + Section 4.3"; + } + + identity link-metric-loss { + base te-types:link-metric-type; + description + "The Unidirectional Link Loss Metric, + measured in units of 0.000003%."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions, + Section 4.4 + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions, + Section 4.4"; + } + + // Derived identities from te-types:link-metric-type + + identity path-metric-delay-variation { + base te-types:path-metric-type; + description + "The Path Delay Variation Metric, + measured in units of microseconds."; + reference + "RFC 8233: Extensions to the Path Computation Element + Communication Protocol (PCEP) to Compute + Service-Aware Label Switched Paths (LSPs), + Section 3.1.2"; + } + + identity path-metric-loss { + base te-types:path-metric-type; + description + "The Path Loss Metric, measured in units of 0.000003%."; + reference + "RFC 8233: Extensions to the Path Computation Element + Communication Protocol (PCEP) to Compute + Service-Aware Label Switched Paths (LSPs), + Section 3.1.3"; + } + + /* + * Typedefs + */ + + typedef te-bandwidth-requested-type { + type enumeration { + enum specified-value { + description + "Bandwidth value is explicitly specified."; + } + enum specified-profile { + description + "Bandwidth profile is explicitly specified."; + } + enum auto { + description + "Bandwidth is automatically computed."; + } + } + description + "Enumerated type for specifying whether bandwidth is + explicitly specified or automatically computed."; + } + + typedef te-class-type { + type uint8; + description + "Diffserv-TE Class-Type. Defines a set of Traffic Trunks + crossing a link that is governed by a specific set of + bandwidth constraints. Class-Type is used for the purposes + of link bandwidth allocation, constraint-based routing, and + admission control."; + reference + "RFC 4124: Protocol Extensions for Support of Diffserv-aware + MPLS Traffic Engineering"; + } + + typedef bc-type { + type uint8 { + range "0..7"; + } + description + "Diffserv-TE bandwidth constraints as defined in RFC 4124."; + reference + "RFC 4124: Protocol Extensions for Support of Diffserv-aware + MPLS Traffic Engineering"; + } + + typedef bandwidth-kbps { + type uint64; + units "Kbps"; + description + "Bandwidth values, expressed in kilobits per second."; + } + + typedef bandwidth-mbps { + type uint64; + units "Mbps"; + description + "Bandwidth values, expressed in megabits per second."; + } + + typedef bandwidth-gbps { + type uint64; + units "Gbps"; + description + "Bandwidth values, expressed in gigabits per second."; + } + + identity backup-protection-type { + description + "Base identity for the backup protection type."; + } + + identity backup-protection-link { + base backup-protection-type; + description + "Backup provides link protection only."; + } + + identity backup-protection-node-link { + base backup-protection-type; + description + "Backup offers node (preferred) or link protection."; + } + + identity bc-model-type { + description + "Base identity for the Diffserv-TE Bandwidth Constraints + Model type."; + reference + "RFC 4124: Protocol Extensions for Support of Diffserv-aware + MPLS Traffic Engineering"; + } + + identity bc-model-rdm { + base bc-model-type; + description + "Russian Dolls Bandwidth Constraints Model type."; + reference + "RFC 4127: Russian Dolls Bandwidth Constraints Model for + Diffserv-aware MPLS Traffic Engineering"; + } + + identity bc-model-mam { + base bc-model-type; + description + "Maximum Allocation Bandwidth Constraints Model type."; + reference + "RFC 4125: Maximum Allocation Bandwidth Constraints Model for + Diffserv-aware MPLS Traffic Engineering"; + } + + identity bc-model-mar { + base bc-model-type; + description + "Maximum Allocation with Reservation Bandwidth Constraints + Model type."; + reference + "RFC 4126: Max Allocation with Reservation Bandwidth + Constraints Model for Diffserv-aware MPLS Traffic + Engineering & Performance Comparisons"; + } + + /* + * Groupings + */ + + grouping performance-metrics-attributes-packet { + description + "Contains PM attributes."; + uses te-types:performance-metrics-attributes { + augment "performance-metrics-one-way" { + leaf one-way-min-delay { + type uint32 { + range "0..16777215"; + } + description + "One-way minimum delay or latency in microseconds."; + } + leaf one-way-min-delay-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "One-way minimum delay or latency normality."; + } + leaf one-way-max-delay { + type uint32 { + range "0..16777215"; + } + description + "One-way maximum delay or latency in microseconds."; + } + leaf one-way-max-delay-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "One-way maximum delay or latency normality."; + } + leaf one-way-delay-variation { + type uint32 { + range "0..16777215"; + } + description + "One-way delay variation in microseconds."; + reference + "RFC 5481: Packet Delay Variation Applicability + Statement, Section 4.2"; + } + leaf one-way-delay-variation-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "One-way delay variation normality."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions + RFC 7823: Performance-Based Path Selection for + Explicitly Routed Label Switched Paths (LSPs) + Using TE Metric Extensions"; + } + leaf one-way-packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + description + "One-way packet loss as a percentage of the total traffic + sent over a configurable interval. The finest precision + is 0.000003%, where the maximum is 50.331642%."; + reference + "RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions, Section 4.4"; + } + leaf one-way-packet-loss-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "Packet loss normality."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions + RFC 7823: Performance-Based Path Selection for + Explicitly Routed Label Switched Paths (LSPs) + Using TE Metric Extensions"; + } + description + "PM one-way packet-specific augmentation for a generic PM + grouping."; + } + augment "performance-metrics-two-way" { + leaf two-way-min-delay { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "Two-way minimum delay or latency in microseconds."; + } + leaf two-way-min-delay-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "Two-way minimum delay or latency normality."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions + RFC 7823: Performance-Based Path Selection for + Explicitly Routed Label Switched Paths (LSPs) + Using TE Metric Extensions"; + } + leaf two-way-max-delay { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "Two-way maximum delay or latency in microseconds."; + } + leaf two-way-max-delay-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "Two-way maximum delay or latency normality."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions + RFC 7823: Performance-Based Path Selection for + Explicitly Routed Label Switched Paths (LSPs) + Using TE Metric Extensions"; + } + leaf two-way-delay-variation { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "Two-way delay variation in microseconds."; + reference + "RFC 5481: Packet Delay Variation Applicability + Statement, Section 4.2"; + } + leaf two-way-delay-variation-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "Two-way delay variation normality."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions + RFC 7823: Performance-Based Path Selection for + Explicitly Routed Label Switched Paths (LSPs) + Using TE Metric Extensions"; + } + leaf two-way-packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + default "0"; + description + "Two-way packet loss as a percentage of the total traffic + sent over a configurable interval. The finest precision + is 0.000003%."; + } + leaf two-way-packet-loss-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "Two-way packet loss normality."; + } + description + "PM two-way packet-specific augmentation for a generic PM + grouping."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions + RFC 7823: Performance-Based Path Selection for Explicitly + Routed Label Switched Paths (LSPs) Using TE + Metric Extensions"; + } + } + } + + grouping one-way-performance-metrics-packet { + description + "One-way packet PM throttle grouping."; + leaf one-way-min-delay { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "One-way minimum delay or latency in microseconds."; + } + leaf one-way-max-delay { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "One-way maximum delay or latency in microseconds."; + } + leaf one-way-delay-variation { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "One-way delay variation in microseconds."; + } + leaf one-way-packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + default "0"; + description + "One-way packet loss as a percentage of the total traffic + sent over a configurable interval. The finest precision is + 0.000003%."; + } + } + + grouping one-way-performance-metrics-gauge-packet { + description + "One-way packet PM throttle grouping. + + This grouping is used to report the same metrics defined in + the one-way-performance-metrics-packet grouping, using gauges + instead of uint32 data types and referencing IPPM RFCs + instead of IGP-TE RFCs."; + leaf one-way-min-delay { + type yang:gauge64; + description + "One-way minimum delay or latency in microseconds."; + } + leaf one-way-max-delay { + type yang:gauge64; + description + "One-way maximum delay or latency in microseconds."; + reference + "RFC 7679: A One-Way Delay Metric for IP Performance + Metrics (IPPM)"; + } + leaf one-way-delay-variation { + type yang:gauge64; + description + "One-way delay variation in microseconds."; + reference + "RFC 3393: IP Packet Delay Variation Metric for IP + Performance Metrics (IPPM)"; + } + leaf one-way-packet-loss { + type decimal64 { + fraction-digits 5; + range "0..100"; + } + description + "The ratio of packets dropped to packets transmitted between + two endpoints."; + reference + "RFC 7680: A One-Way Loss Metric for IP Performance + Metrics (IPPM)"; + } + } + + grouping two-way-performance-metrics-packet { + description + "Two-way packet PM throttle grouping."; + leaf two-way-min-delay { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "Two-way minimum delay or latency in microseconds."; + } + leaf two-way-max-delay { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "Two-way maximum delay or latency in microseconds."; + } + leaf two-way-delay-variation { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "Two-way delay variation in microseconds."; + } + leaf two-way-packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + default "0"; + description + "Two-way packet loss as a percentage of the total traffic + sent over a configurable interval. The finest precision is + 0.000003%."; + } + } + + grouping two-way-performance-metrics-gauge-packet { + description + "Two-way packet PM throttle grouping. + + This grouping is used to report the same metrics defined in + the two-way-performance-metrics-packet grouping, using gauges + instead of uint32 data types and referencing IPPM RFCs + instead of IGP-TE RFCs."; + leaf two-way-min-delay { + type yang:gauge64; + description + "Two-way minimum delay or latency in microseconds."; + reference + "RFC 2681: A Round-trip Delay Metric for IPPM"; + } + leaf two-way-max-delay { + type yang:gauge64; + description + "Two-way maximum delay or latency in microseconds."; + reference + "RFC 2681: A Round-trip Delay Metric for IPPM"; + } + leaf two-way-delay-variation { + type yang:gauge64; + description + "Two-way delay variation in microseconds."; + reference + "RFC 5481: Packet Delay Variation Applicability Statement"; + } + leaf two-way-packet-loss { + type decimal64 { + fraction-digits 5; + range "0..100"; + } + description + "The ratio of packets dropped to packets transmitted between + two endpoints."; + } + } + + grouping performance-metrics-throttle-container-packet { + description + "Packet PM threshold grouping."; + uses te-types:performance-metrics-throttle-container { + augment "throttle/threshold-out" { + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + description + "PM threshold-out packet augmentation for a + generic grouping."; + } + augment "throttle/threshold-in" { + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + description + "PM threshold-in packet augmentation for a + generic grouping."; + } + augment "throttle/threshold-accelerated-advertisement" { + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + description + "PM accelerated advertisement packet augmentation for a + generic grouping."; + } + } + } + + grouping bandwidth-profile-parameters { + description + "Common parameters to define bandwidth profiles in packet + networks."; + leaf cir { + type uint64; + units "bits/second"; + description + "Committed Information Rate (CIR)."; + } + leaf cbs { + type uint64; + units "bytes"; + description + "Committed Burst Size (CBS)."; + } + leaf eir { + type uint64; + units "bits/second"; + description + "Excess Information Rate (EIR)."; + } + leaf ebs { + type uint64; + units "bytes"; + description + "Excess Burst Size (EBS)."; + } + leaf pir { + type uint64; + units "bits/second"; + description + "Peak Information Rate (PIR)."; + } + leaf pbs { + type uint64; + units "bytes"; + description + "Peak Burst Size (PBS)."; + } + } + + grouping te-packet-path-bandwidth { + description + "Bandwidth attributes for TE Packet paths."; + container packet-bandwidth { + description + "Bandwidth attributes for TE Packet paths."; + leaf specification-type { + type te-bandwidth-requested-type; + description + "The bandwidth specification type, either explicitly + specified or automatically computed."; + } + leaf set-bandwidth { + when "../specification-type = 'specified-value'" { + description + "When the bandwidth value is explicitly specified."; + } + type bandwidth-kbps; + description + "Set the bandwidth value explicitly, e.g., using offline + calculation."; + } + container bandwidth-profile { + when "../specification-type = 'specified-profile'" { + description + "When the bandwidth profile is explicitly specified."; + } + description + "Set the bandwidth profile attributes explicitly."; + leaf bandwidth-profile-name { + type string; + description + "Name of Bandwidth Profile."; + } + leaf bandwidth-profile-type { + type identityref { + base bandwidth-profile-type; + } + description + "Type of Bandwidth Profile."; + } + uses bandwidth-profile-parameters; + } + leaf class-type { + type te-types:te-ds-class; + description + "The Class-Type of traffic transported by the LSP."; + reference + "RFC 4124: Protocol Extensions for Support of + Diffserv-aware MPLS Traffic Engineering, + Section 4.3.1"; + } + leaf signaled-bandwidth { + type te-packet-types:bandwidth-kbps; + config false; + description + "The currently signaled bandwidth of the LSP. + + In the case where the bandwidth is specified + explicitly, then this will match the value of the + set-bandwidth leaf. + + In the cases where the bandwidth is dynamically + computed by the system, the current value of the + bandwidth should be reflected."; + } + } + } + + grouping te-packet-link-bandwidth { + description + "Bandwidth attributes for Packet TE links."; + leaf packet-bandwidth { + type uint64; + units "bits/second"; + description + "Bandwidth value for Packet TE links."; + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-te-types@2024-10-30.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-te-types@2024-10-30.yang new file mode 100644 index 0000000000000000000000000000000000000000..5d9ae16f4bb43b5389217771a9b3f83d177449ca --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-te-types@2024-10-30.yang @@ -0,0 +1,4399 @@ +module ietf-te-types { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-te-types"; + prefix te-types; + + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-routing-types { + prefix rt-types; + reference + "RFC 8294: Common YANG Data Types for the Routing Area"; + } + + import ietf-network { + prefix "nw"; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + + import ietf-network-topology { + prefix "nt"; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + + organization + "IETF Traffic Engineering Architecture and Signaling (TEAS) + Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/teas/> + WG List: <mailto:teas@ietf.org> + + Editor: Tarek Saad + <mailto:tsaad.net@gmail.com> + + Editor: Rakesh Gandhi + <mailto:rgandhi@cisco.com> + + Editor: Vishnu Pavan Beeram + <mailto:vbeeram@juniper.net> + + Editor: Xufeng Liu + <mailto:xufeng.liu.ietf@gmail.com> + + Editor: Igor Bryskin + <mailto:i_bryskin@yahoo.com>"; + description + "This YANG module contains a collection of generally useful + YANG data type definitions specific to TE. The model fully + conforms to the Network Management Datastore Architecture + (NMDA). + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL + NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', + 'MAY', and 'OPTIONAL' in this document are to be interpreted as + described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, + they appear in all capitals, as shown here. + + Copyright (c) 2024 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Revised BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX + (https://www.rfc-editor.org/info/rfcXXXX); see the RFC itself + for full legal notices."; + revision 2024-10-30 { + description + "This revision adds the following new identities: + - lsp-provisioning-error-reason; + - association-type-diversity; + - tunnel-admin-state-auto; + - lsp-restoration-restore-none; + - restoration-scheme-rerouting; + - path-metric-optimization-type; + - link-path-metric-type; + - link-metric-type and its derived identities; + - path-computation-error-reason and its derived identities; + - protocol-origin-type and its derived identities; + - svec-objective-function-type and its derived identities; + - svec-metric-type and its derived identities. + + This revision adds the following new data types: + - path-type. + + This revision adds the following new groupings: + - encoding-and-switching-type; + - te-generic-node-id. + + This revision updates the following identities: + - objective-function-type; + - action-exercise; + - path-metric-type; + - path-metric-te; + - path-metric-igp; + - path-metric-hop; + - path-metric-delay-average; + - path-metric-delay-minimum; + - path-metric-residual-bandwidth; + - path-metric-optimize-includes; + - path-metric-optimize-excludes; + - te-optimization-criterion. + + This revision updates the following data types: + - te-node-id. + + This revision updates the following groupings: + - explicit-route-hop: + - adds the following leaves: + - node-id-uri; + - link-tp-id-uri; + - updates the following leaves: + - node-id; + - link-tp-id; + - record-route-state: + - adds the following leaves: + - node-id-uri; + - link-tp-id-uri; + - updates the following leaves: + - node-id; + - link-tp-id; + - optimization-metric-entry: + - updates the following leaves: + - metric-type; + - tunnel-constraints; + - adds the following leaves: + - network-id; + - path-constraints-route-objects: + - updates the following containers: + - explicit-route-objects-always; + - generic-path-metric-bounds: + - updates the following leaves: + - metric-type; + - generic-path-optimization + - adds the following leaves: + - tiebreaker; + - deprecate the following containers: + - tiebreakers. + + This revision obsoletes the following identities: + - of-minimize-agg-bandwidth-consumption; + - of-minimize-load-most-loaded-link; + - of-minimize-cost-path-set; + - lsp-protection-reroute-extra; + - lsp-protection-reroute. + + This revision provides also few editorial changes."; + reference + "RFC XXXX: Common YANG Data Types for Traffic Engineering"; + } + // RFC Editor: replace XXXX with actual RFC number, update date + // information and remove this note + + revision 2020-06-10 { + description + "Initial Version of TE types."; + reference + "RFC 8776: Common YANG Data Types for Traffic Engineering"; + } + + /** + * Typedefs + */ + + typedef admin-group { + type yang:hex-string { + /* 01:02:03:04 */ + length "1..11"; + } + description + "Administrative group / resource class / color representation + in 'hex-string' type. + + The most significant byte in the hex-string is the farthest + to the left in the byte sequence. Leading zero bytes in the + configured value may be omitted for brevity."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering + RFC 7308: Extended Administrative Groups in MPLS Traffic + Engineering (MPLS-TE)"; + } + + typedef admin-groups { + type union { + type admin-group; + type extended-admin-group; + } + description + "Derived types for TE administrative groups."; + } + + typedef extended-admin-group { + type yang:hex-string; + description + "Extended administrative group / resource class / color + representation in 'hex-string' type. + + The most significant byte in the hex-string is the farthest + to the left in the byte sequence. Leading zero bytes in the + configured value may be omitted for brevity."; + reference + "RFC 7308: Extended Administrative Groups in MPLS Traffic + Engineering (MPLS-TE)"; + } + + typedef path-attribute-flags { + type union { + type identityref { + base session-attributes-flags; + } + type identityref { + base lsp-attributes-flags; + } + } + description + "Path attributes flags type."; + } + + typedef performance-metrics-normality { + type enumeration { + enum unknown { + value 0; + description + "Unknown."; + } + enum normal { + value 1; + description + "Normal. Indicates that the anomalous bit is not set."; + } + enum abnormal { + value 2; + description + "Abnormal. Indicates that the anomalous bit is set."; + } + } + description + "Indicates whether a performance metric is normal (anomalous + bit not set), abnormal (anomalous bit set), or unknown."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions + RFC 7823: Performance-Based Path Selection for Explicitly + Routed Label Switched Paths (LSPs) Using TE Metric + Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions"; + } + + typedef srlg { + type uint32; + description + "SRLG type."; + reference + "RFC 4203: OSPF Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS) + RFC 5307: IS-IS Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS)"; + } + + typedef te-common-status { + type enumeration { + enum up { + description + "Enabled."; + } + enum down { + description + "Disabled."; + } + enum testing { + description + "In some test mode."; + } + enum preparing-maintenance { + description + "The resource is disabled in the control plane to prepare + for a graceful shutdown for maintenance purposes."; + reference + "RFC 5817: Graceful Shutdown in MPLS and Generalized MPLS + Traffic Engineering Networks"; + } + enum maintenance { + description + "The resource is disabled in the data plane for maintenance + purposes."; + } + enum unknown { + description + "Status is unknown."; + } + } + description + "Defines a type representing the common states of a TE + resource."; + } + + typedef te-bandwidth { + type string { + pattern '0[xX](0((\.0?)?[pP](\+)?0?|(\.0?))|' + + '1(\.([\da-fA-F]{0,5}[02468aAcCeE]?)?)?' + + '[pP](\+)?(12[0-7]|' + + '1[01]\d|0?\d?\d)?)|0[xX][\da-fA-F]{1,8}|\d+' + + '(,(0[xX](0((\.0?)?[pP](\+)?0?|(\.0?))|' + + '1(\.([\da-fA-F]{0,5}[02468aAcCeE]?)?)?' + + '[pP](\+)?(12[0-7]|' + + '1[01]\d|0?\d?\d)?)|0[xX][\da-fA-F]{1,8}|\d+))*'; + } + description + "This is the generic bandwidth type. It is a string containing + a list of numbers separated by commas, where each of these + numbers can be non-negative decimal, hex integer, or + hex float: + + (dec | hex | float)[*(','(dec | hex | float))] + + For the packet-switching type, the string encoding follows + the type 'bandwidth-ieee-float32' as defined in RFC 8294 + (e.g., 0x1p10), where the units are in bytes per second. + + For the Optical Transport Network (OTN) switching type, + a list of integers can be used, such as '0,2,3,1', indicating + two ODU0s and one ODU3. ('ODU' stands for 'Optical Data + Unit'.) For Dense Wavelength Division Multiplexing (DWDM), + a list of pairs of slot numbers and widths can be used, + such as '0,2,3,3', indicating a frequency slot 0 with + slot width 2 and a frequency slot 3 with slot width 3. + Canonically, the string is represented as all lowercase and in + hex, where the prefix '0x' precedes the hex number."; + reference + "RFC 8294: Common YANG Data Types for the Routing Area + ITU-T G.709: Interfaces for the optical transport network - + Edition 6.0 (06/2020)"; + } + + typedef te-ds-class { + type uint8 { + range "0..7"; + } + description + "The Differentiated Services Class-Type of traffic."; + reference + "RFC 4124: Protocol Extensions for Support of Diffserv-aware + MPLS Traffic Engineering, Section 4.3.1"; + } + + typedef te-global-id { + type uint32; + description + "An identifier to uniquely identify an operator, which can be + either a provider or a client. + + The definition of this type is taken from RFCs 6370 and 5003. + + This attribute type is used solely to provide a globally + unique context for TE topologies."; + reference + "RFC 5003: Attachment Individual Identifier (AII) Types for + Aggregation + RFC 6370: MPLS Transport Profile (MPLS-TP) Identifiers"; + } + + typedef te-hop-type { + type enumeration { + enum loose { + description + "A loose hop in an explicit path."; + } + enum strict { + description + "A strict hop in an explicit path."; + } + } + description + "Enumerated type for specifying loose or strict paths."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 4.3.3"; + } + + typedef te-link-access-type { + type enumeration { + enum point-to-point { + description + "The link is point-to-point."; + } + enum multi-access { + description + "The link is multi-access, including broadcast and NBMA."; + } + } + description + "Defines a type representing the access type of a TE link."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2"; + } + + typedef te-label-direction { + type enumeration { + enum forward { + description + "Label allocated for the forward LSP direction."; + } + enum reverse { + description + "Label allocated for the reverse LSP direction."; + } + } + description + "Enumerated type for specifying the forward or reverse + label."; + } + + typedef te-link-direction { + type enumeration { + enum incoming { + description + "The explicit route represents an incoming link on + a node."; + } + enum outgoing { + description + "The explicit route represents an outgoing link on + a node."; + } + } + description + "Enumerated type for specifying the direction of a link on + a node."; + } + + typedef te-metric { + type uint32; + description + "TE metric."; + reference + "RFC 3785: Use of Interior Gateway Protocol (IGP) Metric as a + second MPLS Traffic Engineering (TE) Metric"; + } + + typedef te-node-id { + type union { + type yang:dotted-quad; + type inet:ipv6-address-no-zone; + } + description + "A type representing the identifier for a node in a TE + topology. + + The identifier is represented either as 4 octets in + dotted-quad notation, or as 16 octets in full, mixed, + shortened, or shortened-mixed IPv6 address notation. + + This attribute MAY be mapped to the Router Address TLV + described in Section 2.4.1 of RFC 3630, the TE Router ID + described in Section 3 of RFC 6827, the Traffic Engineering + Router ID TLV described in Section 4.3 of RFC 5305, the TE + Router ID TLV described in Section 3.2.1 of RFC 6119, or the + IPv6 TE Router ID TLV described in Section 4.1 of RFC 6119. + + The reachability of such a TE node MAY be achieved by a + mechanism such as that described in Section 6.2 of RFC 6827."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2, Section 2.4.1 + RFC 5305: IS-IS Extensions for Traffic Engineering, + Section 4.3 + RFC 6119: IPv6 Traffic Engineering in IS-IS, Section 3.2.1 + RFC 6827: Automatically Switched Optical Network (ASON) + Routing for OSPFv2 Protocols, Section 3"; + } + + typedef te-oper-status { + type te-common-status; + description + "Defines a type representing the operational status of + a TE resource."; + } + + typedef te-admin-status { + type te-common-status; + description + "Defines a type representing the administrative status of + a TE resource."; + } + + typedef te-path-disjointness { + type bits { + bit node { + position 0; + description + "Node disjoint."; + } + bit link { + position 1; + description + "Link disjoint."; + } + bit srlg { + position 2; + description + "SRLG (Shared Risk Link Group) disjoint."; + } + } + description + "Type of the resource disjointness for a TE tunnel path."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + typedef te-recovery-status { + type enumeration { + enum normal { + description + "Both the recovery span and the working span are fully + allocated and active, data traffic is being + transported over (or selected from) the working + span, and no trigger events are reported."; + } + enum recovery-started { + description + "The recovery action has been started but not completed."; + } + enum recovery-succeeded { + description + "The recovery action has succeeded. The working span has + reported a failure/degrade condition, and the user traffic + is being transported (or selected) on the recovery span."; + } + enum recovery-failed { + description + "The recovery action has failed."; + } + enum reversion-started { + description + "The reversion has started."; + } + enum reversion-succeeded { + description + "The reversion action has succeeded."; + } + enum reversion-failed { + description + "The reversion has failed."; + } + enum recovery-unavailable { + description + "The recovery is unavailable, as a result of either an + operator's lockout command or a failure condition + detected on the recovery span."; + } + enum recovery-admin { + description + "The operator has issued a command to switch the user + traffic to the recovery span."; + } + enum wait-to-restore { + description + "The recovery domain is recovering from a failure/degrade + condition on the working span that is being controlled by + the Wait-to-Restore (WTR) timer."; + } + } + description + "Defines the status of a recovery action."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + typedef te-template-name { + type string { + pattern '/?([a-zA-Z0-9\-_.]+)(/[a-zA-Z0-9\-_.]+)*'; + } + description + "A type for the name of a TE node template or TE link + template."; + } + + typedef te-topology-event-type { + type enumeration { + enum add { + value 0; + description + "A TE node or TE link has been added."; + } + enum remove { + value 1; + description + "A TE node or TE link has been removed."; + } + enum update { + value 2; + description + "A TE node or TE link has been updated."; + } + } + description + "TE event type for notifications."; + } + + typedef te-topology-id { + type union { + type string { + length "0"; + // empty string + } + type string { + pattern '([a-zA-Z0-9\-_.]+:)*' + + '/?([a-zA-Z0-9\-_.]+)(/[a-zA-Z0-9\-_.]+)*'; + } + } + description + "An identifier for a topology. + + It is optional to have one or more prefixes at the beginning, + separated by colons. The prefixes can be 'network-types' as + defined in the 'ietf-network' module in RFC 8345, to help the + user better understand the topology before further inquiry + is made."; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + + typedef te-tp-id { + type union { + type uint32; + // Unnumbered + type inet:ip-address; + // IPv4 or IPv6 address + } + description + "An identifier for a TE link endpoint on a node. + + This attribute is mapped to a local or remote link identifier + as defined in RFCs 3630 and 5305."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + } + + typedef path-type { + type enumeration { + enum primary-path { + description + "Indicates that the TE path is a primary path."; + } + enum secondary-path { + description + "Indicates that the TE path is a secondary path."; + } + enum primary-reverse-path { + description + "Indicates that the TE path is a primary reverse path."; + } + enum secondary-reverse-path { + description + "Indicates that the TE path is a secondary reverse path."; + } + } + description + "The type of TE path, indicating whether a path is a primary, + or a reverse primary, or a secondary, or a reverse secondary + path."; + } + + /* TE features */ + + feature p2mp-te { + description + "Indicates support for Point-to-Multipoint TE (P2MP-TE)."; + reference + "RFC 4875: Extensions to Resource Reservation Protocol - + Traffic Engineering (RSVP-TE) for + Point-to-Multipoint TE Label Switched Paths (LSPs)"; + } + + feature frr-te { + description + "Indicates support for TE Fast Reroute (FRR)."; + reference + "RFC 4090: Fast Reroute Extensions to RSVP-TE for LSP Tunnels"; + } + + feature extended-admin-groups { + description + "Indicates support for TE link extended administrative + groups."; + reference + "RFC 7308: Extended Administrative Groups in MPLS Traffic + Engineering (MPLS-TE)"; + } + + feature named-path-affinities { + description + "Indicates support for named path affinities."; + } + + feature named-extended-admin-groups { + description + "Indicates support for named extended administrative groups."; + } + + feature named-srlg-groups { + description + "Indicates support for named SRLG groups."; + } + + feature named-path-constraints { + description + "Indicates support for named path constraints."; + } + + feature path-optimization-metric { + description + "Indicates support for path optimization metrics."; + } + + feature path-optimization-objective-function { + description + "Indicates support for path optimization objective functions."; + } + + /* + * Identities + */ + + identity lsp-provisioning-error-reason { + description + "Base identity for LSP provisioning errors."; + } + + identity session-attributes-flags { + description + "Base identity for the RSVP-TE session attributes flags."; + } + + identity local-protection-desired { + base session-attributes-flags; + description + "Local protection is desired."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 4.7.1"; + } + + identity se-style-desired { + base session-attributes-flags; + description + "Shared explicit style, to allow the LSP to be established + and share resources with the old LSP."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels"; + } + + identity local-recording-desired { + base session-attributes-flags; + description + "Label recording is desired."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 4.7.1"; + } + + identity bandwidth-protection-desired { + base session-attributes-flags; + description + "Requests FRR bandwidth protection on LSRs, if present."; + reference + "RFC 4090: Fast Reroute Extensions to RSVP-TE for LSP + Tunnels"; + } + + identity node-protection-desired { + base session-attributes-flags; + description + "Requests FRR node protection on LSRs, if present."; + reference + "RFC 4090: Fast Reroute Extensions to RSVP-TE for LSP + Tunnels"; + } + + identity path-reevaluation-request { + base session-attributes-flags; + description + "This flag indicates that a path re-evaluation (of the + current path in use) is requested. Note that this does + not trigger any LSP reroutes but instead just signals a + request to evaluate whether a preferable path exists."; + reference + "RFC 4736: Reoptimization of Multiprotocol Label Switching + (MPLS) Traffic Engineering (TE) Loosely Routed + Label Switched Path (LSP)"; + } + + identity soft-preemption-desired { + base session-attributes-flags; + description + "Soft preemption of LSP resources is desired."; + reference + "RFC 5712: MPLS Traffic Engineering Soft Preemption"; + } + + identity lsp-attributes-flags { + description + "Base identity for LSP attributes flags."; + } + + identity end-to-end-rerouting-desired { + base lsp-attributes-flags; + description + "Indicates end-to-end rerouting behavior for an LSP + undergoing establishment. This MAY also be used to + specify the behavior of end-to-end LSP recovery for + established LSPs."; + reference + "RFC 4920: Crankback Signaling Extensions for MPLS and GMPLS + RSVP-TE + RFC 5420: Encoding of Attributes for MPLS LSP Establishment + Using Resource Reservation Protocol Traffic + Engineering (RSVP-TE) + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity boundary-rerouting-desired { + base lsp-attributes-flags; + description + "Indicates boundary rerouting behavior for an LSP undergoing + establishment. This MAY also be used to specify + segment-based LSP recovery through nested crankback for + established LSPs. The boundary Area Border Router (ABR) / + Autonomous System Border Router (ASBR) can decide to forward + the PathErr message upstream to either an upstream boundary + ABR/ASBR or the ingress LSR. Alternatively, it can try to + select another egress boundary LSR."; + reference + "RFC 4920: Crankback Signaling Extensions for MPLS and GMPLS + RSVP-TE + RFC 5420: Encoding of Attributes for MPLS LSP Establishment + Using Resource Reservation Protocol Traffic + Engineering (RSVP-TE) + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity segment-based-rerouting-desired { + base lsp-attributes-flags; + description + "Indicates segment-based rerouting behavior for an LSP + undergoing establishment. This MAY also be used to specify + segment-based LSP recovery for established LSPs."; + reference + "RFC 4920: Crankback Signaling Extensions for MPLS and GMPLS + RSVP-TE + RFC 5420: Encoding of Attributes for MPLS LSP Establishment + Using Resource Reservation Protocol + Traffic Engineering (RSVP-TE) + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity lsp-integrity-required { + base lsp-attributes-flags; + description + "Indicates that LSP integrity is required."; + reference + "RFC 4875: Extensions to Resource Reservation Protocol - + Traffic Engineering (RSVP-TE) for + Point-to-Multipoint TE Label Switched Paths (LSPs) + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity contiguous-lsp-desired { + base lsp-attributes-flags; + description + "Indicates that a contiguous LSP is desired."; + reference + "RFC 5151: Inter-Domain MPLS and GMPLS Traffic Engineering -- + Resource Reservation Protocol-Traffic Engineering + (RSVP-TE) Extensions + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity lsp-stitching-desired { + base lsp-attributes-flags; + description + "Indicates that LSP stitching is desired."; + reference + "RFC 5150: Label Switched Path Stitching with Generalized + Multiprotocol Label Switching Traffic Engineering + (GMPLS TE) + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity pre-planned-lsp-flag { + base lsp-attributes-flags; + description + "Indicates that the LSP MUST be provisioned in the + control plane only."; + reference + "RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions for + Multi-Layer and Multi-Region Networks (MLN/MRN) + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity non-php-behavior-flag { + base lsp-attributes-flags; + description + "Indicates that non-PHP (non-Penultimate Hop Popping) + behavior for the LSP is desired."; + reference + "RFC 6511: Non-Penultimate Hop Popping Behavior and + Out-of-Band Mapping for RSVP-TE Label Switched + Paths + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity oob-mapping-flag { + base lsp-attributes-flags; + description + "Indicates that signaling of the egress binding information + is out of band (e.g., via the Border Gateway Protocol + (BGP))."; + reference + "RFC 6511: Non-Penultimate Hop Popping Behavior and + Out-of-Band Mapping for RSVP-TE Label Switched + Paths + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity entropy-label-capability { + base lsp-attributes-flags; + description + "Indicates entropy label capability."; + reference + "RFC 6790: The Use of Entropy Labels in MPLS Forwarding + RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO)"; + } + + identity oam-mep-entity-desired { + base lsp-attributes-flags; + description + "OAM Maintenance Entity Group End Point (MEP) entities + desired."; + reference + "RFC 7260: GMPLS RSVP-TE Extensions for Operations, + Administration, and Maintenance (OAM) + Configuration"; + } + + identity oam-mip-entity-desired { + base lsp-attributes-flags; + description + "OAM Maintenance Entity Group Intermediate Points (MIP) + entities desired."; + reference + "RFC 7260: GMPLS RSVP-TE Extensions for Operations, + Administration, and Maintenance (OAM) + Configuration"; + } + + identity srlg-collection-desired { + base lsp-attributes-flags; + description + "SRLG collection desired."; + reference + "RFC 7570: Label Switched Path (LSP) Attribute in the + Explicit Route Object (ERO) + RFC 8001: RSVP-TE Extensions for Collecting Shared Risk + Link Group (SRLG) Information"; + } + + identity loopback-desired { + base lsp-attributes-flags; + description + "This flag indicates that a particular node on the LSP is + required to enter loopback mode. This can also be + used to specify the loopback state of the node."; + reference + "RFC 7571: GMPLS RSVP-TE Extensions for Lock Instruct and + Loopback"; + } + + identity p2mp-te-tree-eval-request { + base lsp-attributes-flags; + description + "P2MP-TE tree re-evaluation request."; + reference + "RFC 8149: RSVP Extensions for Reoptimization of Loosely + Routed Point-to-Multipoint Traffic Engineering + Label Switched Paths (LSPs)"; + } + + identity rtm-set-desired { + base lsp-attributes-flags; + description + "Residence Time Measurement (RTM) attribute flag requested."; + reference + "RFC 8169: Residence Time Measurement in MPLS Networks"; + } + + identity link-protection-type { + description + "Base identity for the link protection type."; + } + + identity link-protection-unprotected { + base link-protection-type; + description + "Unprotected link type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity link-protection-extra-traffic { + base link-protection-type; + description + "Extra-Traffic protected link type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity link-protection-shared { + base link-protection-type; + description + "Shared protected link type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity link-protection-1-for-1 { + base link-protection-type; + description + "One-for-one (1:1) protected link type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity link-protection-1-plus-1 { + base link-protection-type; + description + "One-plus-one (1+1) protected link type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity link-protection-enhanced { + base link-protection-type; + description + "A compound link protection type derived from the underlay + TE tunnel protection configuration supporting the TE link."; + } + + identity association-type { + description + "Base identity for the tunnel association."; + } + + identity association-type-recovery { + base association-type; + description + "Association type for recovery, used to associate LSPs of the + same tunnel for recovery."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery + RFC 6780: RSVP ASSOCIATION Object Extensions"; + } + + identity association-type-resource-sharing { + base association-type; + description + "Association type for resource sharing, used to enable + resource sharing during make-before-break."; + reference + "RFC 4873: GMPLS Segment Recovery + RFC 6780: RSVP ASSOCIATION Object Extensions"; + } + + identity association-type-double-sided-bidir { + base association-type; + description + "Association type for double-sided bidirectional LSPs, + used to associate two LSPs of two tunnels that are + independently configured on either endpoint."; + reference + "RFC 7551: RSVP-TE Extensions for Associated Bidirectional + Label Switched Paths (LSPs)"; + } + + identity association-type-single-sided-bidir { + base association-type; + description + "Association type for single-sided bidirectional LSPs, + used to associate two LSPs of two tunnels, where one + tunnel is configured on one side/endpoint and the other + tunnel is dynamically created on the other endpoint."; + reference + "RFC 6780: RSVP ASSOCIATION Object Extensions + RFC 7551: RSVP-TE Extensions for Associated Bidirectional + Label Switched Paths (LSPs)"; + } + + identity association-type-diversity { + base association-type; + description + "Association Type diversity used to associate LSPs whose + paths are to be diverse from each other."; + reference + "RFC 8800: Path Computation Element Communication Protocol + (PCEP) Extension for Label Switched Path (LSP) + Diversity Constraint Signaling"; + } + + identity objective-function-type { + description + "Base identity for path objective function types."; + } + + identity of-minimize-cost-path { + base objective-function-type; + description + "Objective function for minimizing path cost."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity of-minimize-load-path { + base objective-function-type; + description + "Objective function for minimizing the load on one or more + paths."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity of-maximize-residual-bandwidth { + base objective-function-type; + description + "Objective function for maximizing residual bandwidth."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity of-minimize-agg-bandwidth-consumption { + base objective-function-type; + status obsolete; + description + "Objective function for minimizing aggregate bandwidth + consumption. + + This identity has been obsoleted: the + 'svec-of-minimize-agg-bandwidth-consumption' identity SHOULD + be used instead."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity of-minimize-load-most-loaded-link { + base objective-function-type; + status obsolete; + description + "Objective function for minimizing the load on the link that + is carrying the highest load. + + This identity has been obsoleted: the + 'svec-of-minimize-load-most-loaded-link' identity SHOULD + be used instead."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity of-minimize-cost-path-set { + base objective-function-type; + status obsolete; + description + "Objective function for minimizing the cost on a path set. + + This identity has been obsoleted: the + 'svec-of-minimize-cost-path-set' identity SHOULD + be used instead."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity path-computation-method { + description + "Base identity for supported path computation mechanisms."; + } + + identity path-locally-computed { + base path-computation-method; + description + "Indicates a constrained-path LSP in which the + path is computed by the local LER."; + reference + "RFC 9522: Overview and Principles of Internet Traffic + Engineering, Section 4.4"; + } + + identity path-externally-queried { + base path-computation-method; + description + "Constrained-path LSP in which the path is obtained by + querying an external source, such as a PCE server. + In the case that an LSP is defined to be externally queried, + it may also have associated explicit definitions (provided + to the external source to aid computation). The path that + is returned by the external source may require further local + computation on the device."; + reference + "RFC 9522: Overview and Principles of Internet Traffic + Engineering + RFC 4657: Path Computation Element (PCE) Communication + Protocol Generic Requirements"; + } + + identity path-explicitly-defined { + base path-computation-method; + description + "Constrained-path LSP in which the path is + explicitly specified as a collection of strict and/or loose + hops."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 9522: Overview and Principles of Internet Traffic + Engineering"; + } + + identity lsp-metric-type { + description + "Base identity for the LSP metric specification types."; + } + + identity lsp-metric-relative { + base lsp-metric-type; + description + "The metric specified for the LSPs to which this identity + refers is specified as a value relative to the IGP metric + cost to the LSP's tail end."; + reference + "RFC 4657: Path Computation Element (PCE) Communication + Protocol Generic Requirements"; + } + + identity lsp-metric-absolute { + base lsp-metric-type; + description + "The metric specified for the LSPs to which this identity + refers is specified as an absolute value."; + reference + "RFC 4657: Path Computation Element (PCE) Communication + Protocol Generic Requirements"; + } + + identity lsp-metric-inherited { + base lsp-metric-type; + description + "The metric for the LSPs to which this identity refers is + not specified explicitly; rather, it is directly inherited + from the IGP cost."; + reference + "RFC 4657: Path Computation Element (PCE) Communication + Protocol Generic Requirements"; + } + + identity te-tunnel-type { + description + "Base identity from which specific tunnel types are derived."; + } + + identity te-tunnel-p2p { + base te-tunnel-type; + description + "TE Point-to-Point (P2P) tunnel type."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels"; + } + + identity te-tunnel-p2mp { + base te-tunnel-type; + description + "TE P2MP tunnel type."; + reference + "RFC 4875: Extensions to Resource Reservation Protocol - + Traffic Engineering (RSVP-TE) for + Point-to-Multipoint TE Label Switched Paths + (LSPs)"; + } + + identity tunnel-action-type { + description + "Base identity from which specific tunnel action types + are derived."; + } + + identity tunnel-action-resetup { + base tunnel-action-type; + description + "TE tunnel action that tears down the tunnel's current LSP + (if any) and attempts to re-establish a new LSP."; + } + + identity tunnel-action-reoptimize { + base tunnel-action-type; + description + "TE tunnel action that reoptimizes the placement of the + tunnel LSP(s)."; + } + + identity tunnel-action-switchpath { + base tunnel-action-type; + description + "TE tunnel action that switches the tunnel's LSP to use the + specified path."; + } + + identity te-action-result { + description + "Base identity from which specific TE action results + are derived."; + } + + identity te-action-success { + base te-action-result; + description + "TE action was successful."; + } + + identity te-action-fail { + base te-action-result; + description + "TE action failed."; + } + + identity tunnel-action-inprogress { + base te-action-result; + description + "TE action is in progress."; + } + + identity tunnel-admin-state-type { + description + "Base identity for TE tunnel administrative states."; + } + + identity tunnel-admin-state-up { + base tunnel-admin-state-type; + description + "Tunnel's administrative state is up."; + } + + identity tunnel-admin-state-down { + base tunnel-admin-state-type; + description + "Tunnel's administrative state is down."; + } + + identity tunnel-admin-state-auto { + base tunnel-admin-state-type; + description + "Tunnel administrative auto state. The administrative status + in state datastore transitions to 'tunnel-admin-up' when the + tunnel used by the client layer, and to 'tunnel-admin-down' + when it is not used by the client layer."; + } + + identity tunnel-state-type { + description + "Base identity for TE tunnel states."; + } + + identity tunnel-state-up { + base tunnel-state-type; + description + "Tunnel's state is up."; + } + + identity tunnel-state-down { + base tunnel-state-type; + description + "Tunnel's state is down."; + } + + identity lsp-state-type { + description + "Base identity for TE LSP states."; + } + + identity lsp-path-computing { + base lsp-state-type; + description + "State path computation is in progress."; + } + + identity lsp-path-computation-ok { + base lsp-state-type; + description + "State path computation was successful."; + } + + identity lsp-path-computation-failed { + base lsp-state-type; + description + "State path computation failed."; + } + + identity lsp-state-setting-up { + base lsp-state-type; + description + "State is being set up."; + } + + identity lsp-state-setup-ok { + base lsp-state-type; + description + "State setup was successful."; + } + + identity lsp-state-setup-failed { + base lsp-state-type; + description + "State setup failed."; + } + + identity lsp-state-up { + base lsp-state-type; + description + "State is up."; + } + + identity lsp-state-tearing-down { + base lsp-state-type; + description + "State is being torn down."; + } + + identity lsp-state-down { + base lsp-state-type; + description + "State is down."; + } + + identity path-invalidation-action-type { + description + "Base identity for TE path invalidation action types."; + } + + identity path-invalidation-action-drop { + base path-invalidation-action-type; + description + "Upon invalidation of the TE tunnel path, the tunnel remains + valid, but any packet mapped over the tunnel is dropped."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 2.5"; + } + + identity path-invalidation-action-teardown { + base path-invalidation-action-type; + description + "TE path invalidation action teardown."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 2.5"; + } + + identity lsp-restoration-type { + description + "Base identity from which LSP restoration types are derived."; + } + + identity lsp-restoration-restore-none { + base lsp-restoration-type; + description + "No LSP affected by a failure is restored."; + } + + identity lsp-restoration-restore-any { + base lsp-restoration-type; + description + "Any LSP affected by a failure is restored."; + } + + identity lsp-restoration-restore-all { + base lsp-restoration-type; + description + "Affected LSPs are restored after all LSPs of the tunnel are + broken."; + } + + identity restoration-scheme-type { + description + "Base identity for LSP restoration schemes."; + } + + identity restoration-scheme-rerouting { + base restoration-scheme-type; + description + "Restoration LSP is computed after the failure detection. + + This restoration scheme is also known as + 'Full LSP Re-routing.'"; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity restoration-scheme-preconfigured { + base restoration-scheme-type; + description + "Restoration LSP is preconfigured prior to the failure."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity restoration-scheme-precomputed { + base restoration-scheme-type; + description + "Restoration LSP is precomputed prior to the failure."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity restoration-scheme-presignaled { + base restoration-scheme-type; + description + "Restoration LSP is presignaled prior to the failure."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-type { + description + "Base identity from which LSP protection types are derived."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-unprotected { + base lsp-protection-type; + description + "'Unprotected' LSP protection type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-reroute-extra { + base lsp-protection-type; + status obsolete; + description + "'(Full) Rerouting' LSP protection type. + + This identity has been obsoleted: the + 'restoration-scheme-rerouting' identity SHOULD be used + instead."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-reroute { + base lsp-protection-type; + status obsolete; + description + "'Rerouting without Extra-Traffic' LSP protection type. + + This identity has been obsoleted: the + 'restoration-scheme-rerouting' identity SHOULD be used + instead."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-1-for-n { + base lsp-protection-type; + description + "'1:N Protection with Extra-Traffic' LSP protection type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-1-for-1 { + base lsp-protection-type; + description + "LSP protection '1:1 Protection Type'."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-unidir-1-plus-1 { + base lsp-protection-type; + description + "'1+1 Unidirectional Protection' LSP protection type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-bidir-1-plus-1 { + base lsp-protection-type; + description + "'1+1 Bidirectional Protection' LSP protection type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-extra-traffic { + base lsp-protection-type; + description + "Extra-Traffic LSP protection type."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + + identity lsp-protection-state { + description + "Base identity of protection states for reporting purposes."; + } + + identity normal { + base lsp-protection-state; + description + "Normal state."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity signal-fail-of-protection { + base lsp-protection-state; + description + "The protection transport entity has a signal fail condition + that is of higher priority than the forced switchover + command."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity lockout-of-protection { + base lsp-protection-state; + description + "A Loss of Protection (LoP) command is active."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity forced-switch { + base lsp-protection-state; + description + "A forced switchover command is active."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity signal-fail { + base lsp-protection-state; + description + "There is a signal fail condition on either the working path + or the protection path."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity signal-degrade { + base lsp-protection-state; + description + "There is a signal degrade condition on either the working + path or the protection path."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity manual-switch { + base lsp-protection-state; + description + "A manual switchover command is active."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity wait-to-restore { + base lsp-protection-state; + description + "A Wait-to-Restore (WTR) timer is running."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity do-not-revert { + base lsp-protection-state; + description + "A Do Not Revert (DNR) condition is active because of + non-revertive behavior."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity failure-of-protocol { + base lsp-protection-state; + description + "LSP protection is not working because of a protocol failure + condition."; + reference + "RFC 7271: MPLS Transport Profile (MPLS-TP) Linear Protection + to Match the Operational Expectations of + Synchronous Digital Hierarchy, Optical Transport + Network, and Ethernet Transport Network Operators + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity protection-external-commands { + description + "Base identity from which protection-related external commands + used for troubleshooting purposes are derived."; + } + + identity action-freeze { + base protection-external-commands; + description + "A temporary configuration action initiated by an operator + command that prevents any switchover action from being taken + and, as such, freezes the current state."; + reference + "RFC 7271: MPLS Transport Profile (MPLS-TP) Linear Protection + to Match the Operational Expectations of + Synchronous Digital Hierarchy, Optical Transport + Network, and Ethernet Transport Network Operators + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity clear-freeze { + base protection-external-commands; + description + "An action that clears the active freeze state."; + reference + "RFC 7271: MPLS Transport Profile (MPLS-TP) Linear Protection + to Match the Operational Expectations of + Synchronous Digital Hierarchy, Optical Transport + Network, and Ethernet Transport Network Operators + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity action-lockout-of-normal { + base protection-external-commands; + description + "A temporary configuration action initiated by an operator + command to ensure that the normal traffic is not allowed + to use the protection transport entity."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity clear-lockout-of-normal { + base protection-external-commands; + description + "An action that clears the active lockout of the + normal state."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity action-lockout-of-protection { + base protection-external-commands; + description + "A temporary configuration action initiated by an operator + command to ensure that the protection transport entity is + temporarily not available to transport a traffic signal + (either normal or Extra-Traffic)."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity action-forced-switch { + base protection-external-commands; + description + "A switchover action initiated by an operator command to + switch the Extra-Traffic signal, the normal traffic signal, + or the null signal to the protection transport entity, + unless a switchover command of equal or higher priority is + in effect."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity action-manual-switch { + base protection-external-commands; + description + "A switchover action initiated by an operator command to + switch the Extra-Traffic signal, the normal traffic signal, + or the null signal to the protection transport entity, + unless a fault condition exists on other transport entities + or a switchover command of equal or higher priority is in + effect."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity action-exercise { + base protection-external-commands; + description + "An action that starts testing whether or not Automatic + Protection Switching (APS) communication is operating + correctly. It is of lower priority than any + other state or command."; + reference + "RFC 7271: MPLS Transport Profile (MPLS-TP) Linear Protection + to Match the Operational Expectations of + Synchronous Digital Hierarchy, Optical Transport + Network, and Ethernet Transport Network Operators + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity clear { + base protection-external-commands; + description + "An action that clears the active near-end lockout of a + protection, forced switchover, manual switchover, + Wait-to-Restore (WTR) state, or exercise command."; + reference + "RFC 6378: MPLS Transport Profile (MPLS-TP) Linear Protection + RFC 4427: Recovery (Protection and Restoration) Terminology + for Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + + identity switching-capabilities { + description + "Base identity for interface switching capabilities."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity switching-psc1 { + base switching-capabilities; + description + "Packet-Switch Capable-1 (PSC-1)."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity switching-evpl { + base switching-capabilities; + description + "Ethernet Virtual Private Line (EVPL)."; + reference + "RFC 6004: Generalized MPLS (GMPLS) Support for Metro + Ethernet Forum and G.8011 Ethernet Service + Switching"; + } + + identity switching-l2sc { + base switching-capabilities; + description + "Layer-2 Switch Capable (L2SC)."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity switching-tdm { + base switching-capabilities; + description + "Time-Division-Multiplex Capable (TDM)."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity switching-otn { + base switching-capabilities; + description + "OTN-TDM capable."; + reference + "RFC 7138: Traffic Engineering Extensions to OSPF for GMPLS + Control of Evolving G.709 Optical Transport + Networks"; + } + + identity switching-dcsc { + base switching-capabilities; + description + "Data Channel Switching Capable (DCSC)."; + reference + "RFC 6002: Generalized MPLS (GMPLS) Data Channel + Switching Capable (DCSC) and Channel Set Label + Extensions"; + } + + identity switching-lsc { + base switching-capabilities; + description + "Lambda-Switch Capable (LSC)."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity switching-fsc { + base switching-capabilities; + description + "Fiber-Switch Capable (FSC)."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-types { + description + "Base identity for encoding types."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-packet { + base lsp-encoding-types; + description + "Packet LSP encoding."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-ethernet { + base lsp-encoding-types; + description + "Ethernet LSP encoding."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-pdh { + base lsp-encoding-types; + description + "ANSI/ETSI PDH LSP encoding."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-sdh { + base lsp-encoding-types; + description + "SDH ITU-T G.707 / SONET ANSI T1.105 LSP encoding."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-digital-wrapper { + base lsp-encoding-types; + description + "Digital Wrapper LSP encoding."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-lambda { + base lsp-encoding-types; + description + "Lambda (photonic) LSP encoding."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-fiber { + base lsp-encoding-types; + description + "Fiber LSP encoding."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-fiber-channel { + base lsp-encoding-types; + description + "FiberChannel LSP encoding."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description"; + } + + identity lsp-encoding-oduk { + base lsp-encoding-types; + description + "G.709 ODUk (Digital Path) LSP encoding."; + reference + "RFC 4328: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Extensions for G.709 Optical Transport + Networks Control"; + } + + identity lsp-encoding-optical-channel { + base lsp-encoding-types; + description + "G.709 Optical Channel LSP encoding."; + reference + "RFC 4328: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Extensions for G.709 Optical Transport + Networks Control"; + } + + identity lsp-encoding-line { + base lsp-encoding-types; + description + "Line (e.g., 8B/10B) LSP encoding."; + reference + "RFC 6004: Generalized MPLS (GMPLS) Support for Metro + Ethernet Forum and G.8011 Ethernet Service + Switching"; + } + + identity path-signaling-type { + description + "Base identity from which specific LSP path setup types + are derived."; + } + + identity path-setup-static { + base path-signaling-type; + description + "Static LSP provisioning path setup."; + } + + identity path-setup-rsvp { + base path-signaling-type; + description + "RSVP-TE signaling path setup."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels"; + } + + identity path-setup-sr { + base path-signaling-type; + description + "Segment-routing path setup."; + } + + identity path-scope-type { + description + "Base identity from which specific path scope types are + derived."; + } + + identity path-scope-segment { + base path-scope-type; + description + "Path scope segment."; + reference + "RFC 4873: GMPLS Segment Recovery"; + } + + identity path-scope-end-to-end { + base path-scope-type; + description + "Path scope end to end."; + reference + "RFC 4873: GMPLS Segment Recovery"; + } + + identity route-usage-type { + description + "Base identity for route usage."; + } + + identity route-include-object { + base route-usage-type; + description + "'Include route' object."; + } + + identity route-exclude-object { + base route-usage-type; + description + "'Exclude route' object."; + reference + "RFC 4874: Exclude Routes - Extension to Resource ReserVation + Protocol-Traffic Engineering (RSVP-TE)"; + } + + identity route-exclude-srlg { + base route-usage-type; + description + "Excludes SRLGs."; + reference + "RFC 4874: Exclude Routes - Extension to Resource ReserVation + Protocol-Traffic Engineering (RSVP-TE)"; + } + + identity path-metric-optimization-type { + description + "Base identity used to define the path metric optimization + types."; + } + + identity link-path-metric-type { + description + "Base identity used to define the link and the path metric + types. + + The unit of the path metric value is interpreted in the + context of the path metric type and the derived identities + SHOULD describe the unit of the path metric types they + define."; + } + + identity link-metric-type { + base link-path-metric-type; + description + "Base identity for the link metric types."; + } + + identity link-metric-te { + base link-metric-type; + description + "Traffic Engineering (TE) Link Metric."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2, Section 2.5.5 + RFC 5305: IS-IS Extensions for Traffic Engineering, + Section 3.7"; + } + + identity link-metric-igp { + base link-metric-type; + description + "Interior Gateway Protocol (IGP) Link Metric."; + reference + "RFC 3785: Use of Interior Gateway Protocol (IGP) Metric + as a second MPLS Traffic Engineering (TE) + Metric"; + } + + identity link-metric-delay-average { + base link-metric-type; + description + "Unidirectional Link Delay, measured in units of + microseconds."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions, Section 4.1 + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions, Section 4.1"; + } + + identity link-metric-delay-minimum { + base link-metric-type; + description + "Minimum unidirectional Link Delay, measured in units of + microseconds."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions, Section 4.2 + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions, Section 4.2"; + } + + identity link-metric-delay-maximum { + base link-metric-type; + description + "Maximum unidirectional Link Delay, measured in units of + microseconds."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions, Section 4.2 + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions, Section 4.2"; + } + + identity link-metric-residual-bandwidth { + base link-metric-type; + description + "Unidirectional Residual Bandwidth, measured in units of + bytes per second. + + It is defined to be Maximum Bandwidth minus the bandwidth + currently allocated to LSPs."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric + Extensions, Section 4.5 + RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions, Section 4.5"; + } + + identity path-metric-type { + base link-path-metric-type; + base path-metric-optimization-type; + description + "Base identity for the path metric types."; + } + + identity path-metric-te { + base path-metric-type; + description + "Traffic Engineering (TE) Path Metric."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP), Section 7.8"; + } + + identity path-metric-igp { + base path-metric-type; + description + "Interior Gateway Protocol (IGP) Path Metric."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP), section 7.8"; + } + + identity path-metric-hop { + base path-metric-type; + description + "Hop Count Path Metric."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP), Section 7.8"; + } + + identity path-metric-delay-average { + base path-metric-type; + description + "The Path Delay Metric, measured in units of + microseconds."; + reference + "RFC8233: Extensions to the Path Computation Element + Communication Protocol (PCEP) to Compute + Service-Aware Label Switched Paths (LSPs), + Section 3.1.1"; + } + + identity path-metric-delay-minimum { + base path-metric-type; + description + "The Path Min Delay Metric, measured in units of + microseconds."; + reference + "I-D.ietf-pce-sid-algo: Carrying SR-Algorithm information + in PCE-based Networks, + draft-ietf-pce-sid-algo-14, + Sections 3.5.1 and 3.5.2"; + } + + identity path-metric-residual-bandwidth { + base path-metric-type; + description + "The Path Residual Bandwidth, defined as the minimum Link + Residual Bandwidth all the links along the path. + + The Path Residual Bandwidth can be seen as the path + metric associated with the Maximum residual Bandwidth Path + (MBP) objective function."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity path-metric-optimize-includes { + base path-metric-optimization-type; + description + "A metric that optimizes the number of included resources + specified in a set."; + } + + identity path-metric-optimize-excludes { + base path-metric-optimization-type; + description + "A metric that optimizes to a maximum the number of excluded + resources specified in a set."; + } + + identity path-tiebreaker-type { + description + "Base identity for the path tiebreaker type."; + } + + identity path-tiebreaker-minfill { + base path-tiebreaker-type; + description + "Min-Fill LSP path placement: selects the path with the most + available bandwidth (load balance LSPs over more links)."; + } + + identity path-tiebreaker-maxfill { + base path-tiebreaker-type; + description + "Max-Fill LSP path placement: selects the path with the least + available bandwidth (packing more LSPs over few links)."; + } + + identity path-tiebreaker-random { + base path-tiebreaker-type; + description + "Random LSP path placement."; + } + + identity resource-affinities-type { + description + "Base identity for resource class affinities."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 2702: Requirements for Traffic Engineering Over MPLS"; + } + + identity resource-aff-include-all { + base resource-affinities-type; + description + "The set of attribute filters associated with a + tunnel, all of which must be present for a link + to be acceptable."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 2702: Requirements for Traffic Engineering Over MPLS"; + } + + identity resource-aff-include-any { + base resource-affinities-type; + description + "The set of attribute filters associated with a + tunnel, any of which must be present for a link + to be acceptable."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 2702: Requirements for Traffic Engineering Over MPLS"; + } + + identity resource-aff-exclude-any { + base resource-affinities-type; + description + "The set of attribute filters associated with a + tunnel, any of which renders a link unacceptable."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 2702: Requirements for Traffic Engineering Over MPLS"; + } + + identity te-optimization-criterion { + description + "Base identity for the TE optimization criteria."; + reference + "RFC 9522: Overview and Principles of Internet Traffic + Engineering"; + } + + identity not-optimized { + base te-optimization-criterion; + description + "Optimization is not applied."; + } + + identity cost { + base te-optimization-criterion; + description + "Optimized on cost."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity delay { + base te-optimization-criterion; + description + "Optimized on delay."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity path-computation-srlg-type { + description + "Base identity for SRLG path computation."; + } + + identity srlg-ignore { + base path-computation-srlg-type; + description + "Ignores SRLGs in the path computation."; + } + + identity srlg-strict { + base path-computation-srlg-type; + description + "Includes a strict SRLG check in the path computation."; + } + + identity srlg-preferred { + base path-computation-srlg-type; + description + "Includes a preferred SRLG check in the path computation."; + } + + identity srlg-weighted { + base path-computation-srlg-type; + description + "Includes a weighted SRLG check in the path computation."; + } + + identity path-computation-error-reason { + description + "Base identity for path computation error reasons."; + } + + identity path-computation-error-path-not-found { + base path-computation-error-reason; + description + "Path computation has failed because of an unspecified + reason."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP), Section 7.5"; + } + + identity path-computation-error-no-topology { + base path-computation-error-reason; + description + "Path computation has failed because there is no topology + with the provided topology-identifier."; + } + + identity path-computation-error-no-dependent-server { + base path-computation-error-reason; + description + "Path computation has failed because one or more dependent + path computation servers are unavailable. + + The dependent path computation server could be + a Backward-Recursive Path Computation (BRPC) downstream + PCE or a child PCE."; + reference + "RFC 5441: A Backward-Recursive PCE-Based Computation (BRPC) + Procedure to Compute Shortest Constrained + Inter-Domain Traffic Engineering Label Switched + Paths + RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture"; + } + + identity path-computation-error-pce-unavailable { + base path-computation-error-reason; + description + "Path computation has failed because PCE is not available. + + It corresponds to bit 31 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP) + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-no-inclusion-hop { + base path-computation-error-reason; + description + "Path computation has failed because there is no + node or link provided by one or more inclusion hops."; + } + + identity path-computation-error-destination-unknown-in-domain { + base path-computation-error-reason; + description + "Path computation has failed because the destination node is + unknown in indicated destination domain. + + It corresponds to bit 19 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-no-resource { + base path-computation-error-reason; + description + "Path computation has failed because there is no + available resource in one or more domains. + + It corresponds to bit 20 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-child-pce-unresponsive { + base path-computation-error-no-dependent-server; + description + "Path computation has failed because child PCE is not + responsive. + + It corresponds to bit 21 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-destination-domain-unknown { + base path-computation-error-reason; + description + "Path computation has failed because the destination domain + was unknown. + + It corresponds to bit 22 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-p2mp { + base path-computation-error-reason; + description + "Path computation has failed because of P2MP reachability + problem. + + It corresponds to bit 24 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 8306: Extensions to the Path Computation Element + Communication Protocol (PCEP) for + Point-to-Multipoint Traffic Engineering Label + Switched Paths + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-no-gco-migration { + base path-computation-error-reason; + description + "Path computation has failed because of no Global Concurrent + Optimization (GCO) migration path found. + + It corresponds to bit 26 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 5557: Path Computation Element Communication Protocol + (PCEP) Requirements and Protocol Extensions in + Support of Global Concurrent Optimization + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-no-gco-solution { + base path-computation-error-reason; + description + "Path computation has failed because of no GCO solution + found. + + It corresponds to bit 25 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 5557: Path Computation Element Communication Protocol + (PCEP) Requirements and Protocol Extensions in + Support of Global Concurrent Optimization + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-pks-expansion { + base path-computation-error-reason; + description + "Path computation has failed because of Path-Key Subobject + (PKS) expansion failure. + + It corresponds to bit 27 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 5520: Preserving Topology Confidentiality in + Inter-Domain Path Computation Using a + Path-Key-Based Mechanism + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-brpc-chain-unavailable { + base path-computation-error-no-dependent-server; + description + "Path computation has failed because PCE BRPC chain + unavailable. + + It corresponds to bit 28 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 5441: A Backward-Recursive PCE-Based Computation (BRPC) + Procedure to Compute Shortest Constrained + Inter-Domain Traffic Engineering Label Switched + Paths + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-source-unknown { + base path-computation-error-reason; + description + "Path computation has failed because source node is + unknown. + + It corresponds to bit 29 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP); + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-destination-unknown { + base path-computation-error-reason; + description + "Path computation has failed because destination node is + unknown. + + It corresponds to bit 30 of the Flags field of the + NO-PATH-VECTOR TLV."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP); + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity path-computation-error-no-server { + base path-computation-error-reason; + description + "Path computation has failed because path computation + server is unavailable."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP); + + https://www.iana.org/assignments/pcep + /pcep.xhtml#no-path-vector-tlv"; + } + + identity protocol-origin-type { + description + "Base identity for protocol origin type."; + } + + identity protocol-origin-api { + base protocol-origin-type; + description + "Protocol origin is via Application Programming Interface + (API)."; + } + + identity protocol-origin-pcep { + base protocol-origin-type; + description + "Protocol origin is Path Computation Engine Protocol + (PCEP)."; + reference + "RFC 5440: Path Computation Element (PCE) Communication + Protocol (PCEP)"; + } + + identity protocol-origin-bgp { + base protocol-origin-type; + description + "Protocol origin is Border Gateway Protocol (BGP)."; + reference + "RFC 9012: The BGP Tunnel Encapsulation Attribute"; + } + + identity svec-objective-function-type { + description + "Base identity for SVEC objective function type."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol (PCEP)"; + } + + identity svec-of-minimize-agg-bandwidth-consumption { + base svec-objective-function-type; + description + "Objective function for minimizing aggregate bandwidth + consumption (MBC)."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity svec-of-minimize-load-most-loaded-link { + base svec-objective-function-type; + description + "Objective function for minimizing the load on the link that + is carrying the highest load (MLL)."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity svec-of-minimize-cost-path-set { + base svec-objective-function-type; + description + "Objective function for minimizing the cost on a path set + (MCC)."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity svec-of-minimize-common-transit-domain { + base svec-objective-function-type; + description + "Objective function for minimizing the number of common + transit domains (MCTD)."; + reference + "RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture."; + } + + identity svec-of-minimize-shared-link { + base svec-objective-function-type; + description + "Objective function for minimizing the number of shared + links (MSL)."; + reference + "RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture."; + } + + identity svec-of-minimize-shared-srlg { + base svec-objective-function-type; + description + "Objective function for minimizing the number of shared + Shared Risk Link Groups (SRLG) (MSS)."; + reference + "RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture."; + } + + identity svec-of-minimize-shared-nodes { + base svec-objective-function-type; + description + "Objective function for minimizing the number of shared + nodes (MSN)."; + reference + "RFC 8685: Path Computation Element Communication Protocol + (PCEP) Extensions for the Hierarchical Path + Computation Element (H-PCE) Architecture."; + } + + identity svec-metric-type { + description + "Base identity for SVEC metric type."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol (PCEP)"; + } + + identity svec-metric-cumulative-te { + base svec-metric-type; + description + "Cumulative TE cost."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity svec-metric-cumulative-igp { + base svec-metric-type; + description + "Cumulative IGP cost."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity svec-metric-cumulative-hop { + base svec-metric-type; + description + "Cumulative Hop path metric."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity svec-metric-aggregate-bandwidth-consumption { + base svec-metric-type; + description + "Aggregate bandwidth consumption."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + identity svec-metric-load-of-the-most-loaded-link { + base svec-metric-type; + description + "Load of the most loaded link."; + reference + "RFC 5541: Encoding of Objective Functions in the Path + Computation Element Communication Protocol + (PCEP)"; + } + + /** + * TE bandwidth groupings + **/ + + grouping te-bandwidth { + description + "This grouping defines the generic TE bandwidth. + For some known data-plane technologies, specific modeling + structures are specified. The string-encoded 'te-bandwidth' + type is used for unspecified technologies. + The modeling structure can be augmented later for other + technologies."; + container te-bandwidth { + description + "Container that specifies TE bandwidth. The choices + can be augmented for specific data-plane technologies."; + choice technology { + default "generic"; + description + "Data-plane technology type."; + case generic { + leaf generic { + type te-bandwidth; + description + "Bandwidth specified in a generic format."; + } + } + } + } + } + + /** + * TE label groupings + **/ + + grouping te-label { + description + "This grouping defines the generic TE label. + The modeling structure can be augmented for each technology. + For unspecified technologies, 'rt-types:generalized-label' + is used."; + container te-label { + description + "Container that specifies the TE label. The choices can + be augmented for specific data-plane technologies."; + choice technology { + default "generic"; + description + "Data-plane technology type."; + case generic { + leaf generic { + type rt-types:generalized-label; + description + "TE label specified in a generic format."; + } + } + } + leaf direction { + type te-label-direction; + default "forward"; + description + "Label direction."; + } + } + } + + grouping te-topology-identifier { + description + "Augmentation for a TE topology."; + container te-topology-identifier { + description + "TE topology identifier container."; + leaf provider-id { + type te-global-id; + default "0"; + description + "An identifier to uniquely identify a provider. + If omitted, it assumes that the topology provider ID + value = 0 (the default)."; + } + leaf client-id { + type te-global-id; + default "0"; + description + "An identifier to uniquely identify a client. + If omitted, it assumes that the topology client ID + value = 0 (the default)."; + } + leaf topology-id { + type te-topology-id; + default ""; + description + "When the datastore contains several topologies, + 'topology-id' distinguishes between them. If omitted, + the default (empty) string for this leaf is assumed."; + } + } + } + + /** + * TE performance metrics groupings + **/ + + grouping performance-metrics-one-way-delay-loss { + description + "Performance Metrics (PM) information in real time that can + be applicable to links or connections. PM defined in this + grouping are applicable to generic TE PM as well as packet TE + PM."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions + RFC 7823: Performance-Based Path Selection for Explicitly + Routed Label Switched Paths (LSPs) Using TE Metric + Extensions"; + leaf one-way-delay { + type uint32 { + range "0..16777215"; + } + description + "One-way delay or latency in microseconds."; + } + leaf one-way-delay-normality { + type te-types:performance-metrics-normality; + description + "One-way delay normality."; + } + } + + grouping performance-metrics-two-way-delay-loss { + description + "PM information in real time that can be applicable to links or + connections. PM defined in this grouping are applicable to + generic TE PM as well as packet TE PM."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions + RFC 7823: Performance-Based Path Selection for Explicitly + Routed Label Switched Paths (LSPs) Using TE Metric + Extensions"; + leaf two-way-delay { + type uint32 { + range "0..16777215"; + } + description + "Two-way delay or latency in microseconds."; + } + leaf two-way-delay-normality { + type te-types:performance-metrics-normality; + description + "Two-way delay normality."; + } + } + + grouping performance-metrics-one-way-bandwidth { + description + "PM information in real time that can be applicable to links. + PM defined in this grouping are applicable to generic TE PM + as well as packet TE PM."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions + RFC 7823: Performance-Based Path Selection for Explicitly + Routed Label Switched Paths (LSPs) Using TE Metric + Extensions"; + leaf one-way-residual-bandwidth { + type rt-types:bandwidth-ieee-float32; + units "bytes per second"; + default "0x0p0"; + description + "Residual bandwidth that subtracts tunnel reservations from + Maximum Bandwidth (or link capacity) (RFC 3630) and + provides an aggregated remainder across QoS classes."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2"; + } + leaf one-way-residual-bandwidth-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "Residual bandwidth normality."; + } + leaf one-way-available-bandwidth { + type rt-types:bandwidth-ieee-float32; + units "bytes per second"; + default "0x0p0"; + description + "Available bandwidth that is defined to be residual + bandwidth minus the measured bandwidth used for the + actual forwarding of non-RSVP-TE LSP packets. For a + bundled link, available bandwidth is defined to be the + sum of the component link available bandwidths."; + } + leaf one-way-available-bandwidth-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "Available bandwidth normality."; + } + leaf one-way-utilized-bandwidth { + type rt-types:bandwidth-ieee-float32; + units "bytes per second"; + default "0x0p0"; + description + "Bandwidth utilization that represents the actual + utilization of the link (i.e., as measured in the router). + For a bundled link, bandwidth utilization is defined to + be the sum of the component link bandwidth utilizations."; + } + leaf one-way-utilized-bandwidth-normality { + type te-types:performance-metrics-normality; + default "normal"; + description + "Bandwidth utilization normality."; + } + } + + grouping one-way-performance-metrics { + description + "One-way PM throttle grouping."; + leaf one-way-delay { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "One-way delay or latency in microseconds."; + } + leaf one-way-residual-bandwidth { + type rt-types:bandwidth-ieee-float32; + units "bytes per second"; + default "0x0p0"; + description + "Residual bandwidth that subtracts tunnel reservations from + Maximum Bandwidth (or link capacity) (RFC 3630) and + provides an aggregated remainder across QoS classes."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2"; + } + leaf one-way-available-bandwidth { + type rt-types:bandwidth-ieee-float32; + units "bytes per second"; + default "0x0p0"; + description + "Available bandwidth that is defined to be residual + bandwidth minus the measured bandwidth used for the + actual forwarding of non-RSVP-TE LSP packets. For a + bundled link, available bandwidth is defined to be the + sum of the component link available bandwidths."; + } + leaf one-way-utilized-bandwidth { + type rt-types:bandwidth-ieee-float32; + units "bytes per second"; + default "0x0p0"; + description + "Bandwidth utilization that represents the actual + utilization of the link (i.e., as measured in the router). + For a bundled link, bandwidth utilization is defined to + be the sum of the component link bandwidth utilizations."; + } + } + + grouping two-way-performance-metrics { + description + "Two-way PM throttle grouping."; + leaf two-way-delay { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "Two-way delay or latency in microseconds."; + } + } + + grouping performance-metrics-thresholds { + description + "Grouping for configurable thresholds for measured + attributes."; + uses one-way-performance-metrics; + uses two-way-performance-metrics; + } + + grouping performance-metrics-attributes { + description + "Contains PM attributes."; + container performance-metrics-one-way { + description + "One-way link performance information in real time."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions + RFC 7823: Performance-Based Path Selection for Explicitly + Routed Label Switched Paths (LSPs) Using TE Metric + Extensions"; + uses performance-metrics-one-way-delay-loss; + uses performance-metrics-one-way-bandwidth; + } + container performance-metrics-two-way { + description + "Two-way link performance information in real time."; + reference + "RFC 6374: Packet Loss and Delay Measurement for MPLS + Networks"; + uses performance-metrics-two-way-delay-loss; + } + } + + grouping performance-metrics-throttle-container { + description + "Controls PM throttling."; + container throttle { + must 'suppression-interval >= measure-interval' { + error-message "'suppression-interval' cannot be less than " + + "'measure-interval'."; + description + "Constraint on 'suppression-interval' and + 'measure-interval'."; + } + description + "Link performance information in real time."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions + RFC 8570: IS-IS Traffic Engineering (TE) Metric Extensions + RFC 7823: Performance-Based Path Selection for Explicitly + Routed Label Switched Paths (LSPs) Using TE Metric + Extensions"; + leaf one-way-delay-offset { + type uint32 { + range "0..16777215"; + } + default "0"; + description + "Offset value to be added to the measured delay value."; + } + leaf measure-interval { + type uint32; + default "30"; + description + "Interval, in seconds, to measure the extended metric + values."; + } + leaf advertisement-interval { + type uint32; + default "0"; + description + "Interval, in seconds, to advertise the extended metric + values."; + } + leaf suppression-interval { + type uint32 { + range "1..max"; + } + default "120"; + description + "Interval, in seconds, to suppress advertisement of the + extended metric values."; + reference + "RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions, Section 6"; + } + container threshold-out { + uses performance-metrics-thresholds; + description + "If the measured parameter falls outside an upper bound + for all but the minimum-delay metric (or a lower bound + for the minimum-delay metric only) and the advertised + value is not already outside that bound, an 'anomalous' + announcement (anomalous bit set) will be triggered."; + } + container threshold-in { + uses performance-metrics-thresholds; + description + "If the measured parameter falls inside an upper bound + for all but the minimum-delay metric (or a lower bound + for the minimum-delay metric only) and the advertised + value is not already inside that bound, a 'normal' + announcement (anomalous bit cleared) will be triggered."; + } + container threshold-accelerated-advertisement { + description + "When the difference between the last advertised value and + the current measured value exceeds this threshold, an + 'anomalous' announcement (anomalous bit set) will be + triggered."; + uses performance-metrics-thresholds; + } + } + } + + /** + * TE tunnel generic groupings + **/ + + grouping explicit-route-hop { + description + "The explicit route entry grouping."; + choice type { + description + "The explicit route entry type."; + case numbered-node-hop { + container numbered-node-hop { + must "node-id-uri or node-id" { + description + "At least one node identifier MUST be present."; + } + leaf node-id-uri { + type nw:node-id; + description + "The identifier of a node in the topology."; + } + leaf node-id { + type te-node-id; + description + "The identifier of a node in the TE topology."; + } + leaf hop-type { + type te-hop-type; + default "strict"; + description + "Strict or loose hop."; + } + description + "Numbered node route hop."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 4.3, EXPLICIT_ROUTE in RSVP-TE + RFC 3477: Signalling Unnumbered Links in Resource + ReSerVation Protocol - Traffic Engineering + (RSVP-TE)"; + } + } + case numbered-link-hop { + container numbered-link-hop { + leaf link-tp-id { + type te-tp-id; + mandatory true; + description + "TE Link Termination Point (LTP) identifier."; + } + leaf hop-type { + type te-hop-type; + default "strict"; + description + "Strict or loose hop."; + } + leaf direction { + type te-link-direction; + default "outgoing"; + description + "Link route object direction."; + } + description + "Numbered link explicit route hop."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 4.3, EXPLICIT_ROUTE in RSVP-TE + RFC 3477: Signalling Unnumbered Links in Resource + ReSerVation Protocol - Traffic Engineering + (RSVP-TE)"; + } + } + case unnumbered-link-hop { + container unnumbered-link-hop { + must "(link-tp-id-uri or link-tp-id) and " + + "(node-id-uri or node-id)" { + description + "At least one node identifier and at least one Link + Termination Point (LTP) identifier MUST be present."; + } + leaf link-tp-id-uri { + type nt:tp-id; + description + "Link Termination Point (LTP) identifier."; + } + leaf link-tp-id { + type te-tp-id; + description + "TE LTP identifier. The combination of the TE link ID + and the TE node ID is used to identify an unnumbered + TE link."; + } + leaf node-id-uri { + type nw:node-id; + description + "The identifier of a node in the topology."; + } + leaf node-id { + type te-node-id; + description + "The identifier of a node in the TE topology."; + } + leaf hop-type { + type te-hop-type; + default "strict"; + description + "Strict or loose hop."; + } + leaf direction { + type te-link-direction; + default "outgoing"; + description + "Link route object direction."; + } + description + "Unnumbered link explicit route hop."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 4.3, EXPLICIT_ROUTE in RSVP-TE + RFC 3477: Signalling Unnumbered Links in Resource + ReSerVation Protocol - Traffic Engineering + (RSVP-TE)"; + } + } + case as-number { + container as-number-hop { + leaf as-number { + type inet:as-number; + mandatory true; + description + "The Autonomous System (AS) number."; + } + leaf hop-type { + type te-hop-type; + default "strict"; + description + "Strict or loose hop."; + } + description + "AS explicit route hop."; + } + } + case label { + container label-hop { + description + "Label hop type."; + uses te-label; + } + description + "The label explicit route hop type."; + } + } + } + + grouping record-route-state { + description + "The Record Route grouping."; + leaf index { + type uint32; + description + "Record Route hop index. The index is used to + identify an entry in the list. The order of entries + is defined by the user without relying on key values."; + } + choice type { + description + "The Record Route entry type."; + case numbered-node-hop { + container numbered-node-hop { + must "node-id-uri or node-id" { + description + "At least one node identifier MUST be present."; + } + description + "Numbered node route hop container."; + leaf node-id-uri { + type nw:node-id; + description + "The identifier of a node in the topology."; + } + leaf node-id { + type te-node-id; + description + "The identifier of a node in the TE topology."; + } + leaf-list flags { + type path-attribute-flags; + description + "Path attributes flags."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 4090: Fast Reroute Extensions to RSVP-TE for LSP + Tunnels + RFC 4561: Definition of a Record Route Object (RRO) + Node-Id Sub-Object"; + } + } + description + "Numbered node route hop."; + } + case numbered-link-hop { + container numbered-link-hop { + description + "Numbered link route hop container."; + leaf link-tp-id { + type te-tp-id; + mandatory true; + description + "Numbered TE LTP identifier."; + } + leaf-list flags { + type path-attribute-flags; + description + "Path attributes flags."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 4090: Fast Reroute Extensions to RSVP-TE for LSP + Tunnels + RFC 4561: Definition of a Record Route Object (RRO) + Node-Id Sub-Object"; + } + } + description + "Numbered link route hop."; + } + case unnumbered-link-hop { + container unnumbered-link-hop { + must "(link-tp-id-uri or link-tp-id) and " + + "(node-id-uri or node-id)" { + description + "At least one node identifier and at least one Link + Termination Point (LTP) identifier MUST be present."; + } + leaf link-tp-id-uri { + type nt:tp-id; + description + "Link Termination Point (LTP) identifier."; + } + leaf link-tp-id { + type te-tp-id; + description + "TE LTP identifier. The combination of the TE link ID + and the TE node ID is used to identify an unnumbered + TE link."; + } + leaf node-id-uri { + type nw:node-id; + description + "The identifier of a node in the topology."; + } + leaf node-id { + type te-node-id; + description + "The identifier of a node in the TE topology."; + } + leaf-list flags { + type path-attribute-flags; + description + "Path attributes flags."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 4090: Fast Reroute Extensions to RSVP-TE for LSP + Tunnels + RFC 4561: Definition of a Record Route Object (RRO) + Node-Id Sub-Object"; + } + description + "Unnumbered link Record Route hop."; + reference + "RFC 3477: Signalling Unnumbered Links in Resource + ReSerVation Protocol - Traffic Engineering + (RSVP-TE)"; + } + description + "Unnumbered link route hop."; + } + case label { + container label-hop { + description + "Label route hop type."; + uses te-label; + leaf-list flags { + type path-attribute-flags; + description + "Path attributes flags."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels + RFC 4090: Fast Reroute Extensions to RSVP-TE for LSP + Tunnels + RFC 4561: Definition of a Record Route Object (RRO) + Node-Id Sub-Object"; + } + } + description + "The label Record Route entry types."; + } + } + } + + grouping label-restriction-info { + description + "Label set item information."; + leaf restriction { + type enumeration { + enum inclusive { + description + "The label or label range is inclusive."; + } + enum exclusive { + description + "The label or label range is exclusive."; + } + } + default "inclusive"; + description + "Indicates whether the list item is inclusive or exclusive."; + } + leaf index { + type uint32; + description + "The index of the label restriction list entry."; + } + container label-start { + must "(not(../label-end/te-label/direction) and" + + " not(te-label/direction))" + + " or " + + "(../label-end/te-label/direction = te-label/direction)" + + " or " + + "(not(te-label/direction) and" + + " (../label-end/te-label/direction = 'forward'))" + + " or " + + "(not(../label-end/te-label/direction) and" + + " (te-label/direction = 'forward'))" { + error-message "'label-start' and 'label-end' must have the " + + "same direction."; + } + description + "This is the starting label if a label range is specified. + This is the label value if a single label is specified, + in which case the 'label-end' attribute is not set."; + uses te-label; + } + container label-end { + must "(not(../label-start/te-label/direction) and" + + " not(te-label/direction))" + + " or " + + "(../label-start/te-label/direction = te-label/direction)" + + " or " + + "(not(te-label/direction) and" + + " (../label-start/te-label/direction = 'forward'))" + + " or " + + "(not(../label-start/te-label/direction) and" + + " (te-label/direction = 'forward'))" { + error-message "'label-start' and 'label-end' must have the " + + "same direction."; + } + description + "This is the ending label if a label range is specified. + This attribute is not set if a single label is specified."; + uses te-label; + } + container label-step { + description + "The step increment between labels in the label range. + The label start/end values will have to be consistent + with the sign of label step. For example, + 'label-start' < 'label-end' enforces 'label-step' > 0 + 'label-start' > 'label-end' enforces 'label-step' < 0."; + choice technology { + default "generic"; + description + "Data-plane technology type."; + case generic { + leaf generic { + type int32; + default "1"; + description + "Label range step."; + } + } + } + } + leaf range-bitmap { + type yang:hex-string; + description + "When there are gaps between 'label-start' and 'label-end', + this attribute is used to specify the positions + of the used labels. This is represented in big endian as + 'hex-string'. + + In case the restriction is 'inclusive', the bit-position is + set if the corresponding mapped label is available. + In this case, if the range-bitmap is not present, all the + labels in the range are available. + + In case the restriction is 'exclusive', the bit-position is + set if the corresponding mapped label is not available. + In this case, if the range-bitmap is not present, all the + labels in the range are not available. + + The most significant byte in the hex-string is the farthest + to the left in the byte sequence. Leading zero bytes in the + configured value may be omitted for brevity. + Each bit position in the 'range-bitmap' 'hex-string' maps + to a label in the range derived from 'label-start'. + + For example, assuming that 'label-start' = 16000 and + 'range-bitmap' = 0x01000001, then: + + - bit position (0) is set, and the corresponding mapped + label from the range is 16000 + (0 * 'label-step') or + 16000 for default 'label-step' = 1. + - bit position (24) is set, and the corresponding mapped + label from the range is 16000 + (24 * 'label-step') or + 16024 for default 'label-step' = 1."; + } + } + + grouping label-set-info { + description + "Grouping for the list of label restrictions specifying what + labels may or may not be used."; + container label-restrictions { + description + "The label restrictions container."; + list label-restriction { + key "index"; + description + "The absence of the label restrictions container implies + that all labels are acceptable; otherwise, only restricted + labels are available."; + reference + "RFC 7579: General Network Element Constraint Encoding + for GMPLS-Controlled Networks"; + uses label-restriction-info; + } + } + } + + grouping optimization-metric-entry { + description + "Optimization metrics configuration grouping."; + leaf metric-type { + type identityref { + base path-metric-optimization-type; + } + description + "Identifies the 'metric-type' that the path computation + process uses for optimization."; + } + leaf weight { + type uint8; + default "1"; + description + "TE path metric normalization weight."; + } + container explicit-route-exclude-objects { + when "../metric-type = " + + "'te-types:path-metric-optimize-excludes'"; + description + "Container for the 'exclude route' object list."; + uses path-route-exclude-objects; + } + container explicit-route-include-objects { + when "../metric-type = " + + "'te-types:path-metric-optimize-includes'"; + description + "Container for the 'include route' object list."; + uses path-route-include-objects; + } + } + + grouping common-constraints { + description + "Common constraints grouping that can be set on + a constraint set or directly on the tunnel."; + uses te-bandwidth { + description + "A requested bandwidth to use for path computation."; + } + leaf link-protection { + type identityref { + base link-protection-type; + } + default "te-types:link-protection-unprotected"; + description + "Link protection type required for the links included + in the computed path."; + reference + "RFC 4202: Routing Extensions in Support of + Generalized Multi-Protocol Label Switching + (GMPLS)"; + } + leaf setup-priority { + type uint8 { + range "0..7"; + } + default "7"; + description + "TE LSP requested setup priority."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels"; + } + leaf hold-priority { + type uint8 { + range "0..7"; + } + default "7"; + description + "TE LSP requested hold priority."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels"; + } + leaf signaling-type { + type identityref { + base path-signaling-type; + } + default "te-types:path-setup-rsvp"; + description + "TE tunnel path signaling type."; + } + } + + grouping tunnel-constraints { + description + "Tunnel constraints grouping that can be set on + a constraint set or directly on the tunnel."; + leaf network-id { + type nw:network-id; + description + "The network topology identifier."; + } + uses te-topology-identifier; + uses common-constraints; + } + + grouping path-constraints-route-objects { + description + "List of route entries to be included or excluded when + performing the path computation."; + container explicit-route-objects { + description + "Container for the explicit route object lists."; + list route-object-exclude-always { + key "index"; + ordered-by user; + description + "List of route objects to always exclude from the path + computation."; + leaf index { + type uint32; + description + "Explicit Route Object index. The index is used to + identify an entry in the list. The order of entries + is defined by the user without relying on key values."; + } + uses explicit-route-hop; + } + list route-object-include-exclude { + key "index"; + ordered-by user; + description + "List of route objects to include or exclude in the path + computation."; + leaf explicit-route-usage { + type identityref { + base route-usage-type; + } + default "te-types:route-include-object"; + description + "Indicates whether to include or exclude the + route object. The default is to include it."; + } + leaf index { + type uint32; + description + "Route object include-exclude index. The index is used + to identify an entry in the list. The order of entries + is defined by the user without relying on key values."; + } + uses explicit-route-hop { + augment "type" { + case srlg { + container srlg { + description + "SRLG container."; + leaf srlg { + type uint32; + description + "SRLG value."; + } + } + description + "An SRLG value to be included or excluded."; + } + description + "Augmentation for a generic explicit route for SRLG + exclusion."; + } + } + } + } + } + + grouping path-route-include-objects { + description + "List of route objects to be included when performing + the path computation."; + list route-object-include-object { + key "index"; + ordered-by user; + description + "List of Explicit Route Objects to be included in the + path computation."; + leaf index { + type uint32; + description + "Route object entry index. The index is used to + identify an entry in the list. The order of entries + is defined by the user without relying on key values."; + } + uses explicit-route-hop; + } + } + + grouping path-route-exclude-objects { + description + "List of route objects to be excluded when performing + the path computation."; + list route-object-exclude-object { + key "index"; + ordered-by user; + description + "List of Explicit Route Objects to be excluded in the + path computation."; + leaf index { + type uint32; + description + "Route object entry index. The index is used to + identify an entry in the list. The order of entries + is defined by the user without relying on key values."; + } + uses explicit-route-hop { + augment "type" { + case srlg { + container srlg { + description + "SRLG container."; + leaf srlg { + type uint32; + description + "SRLG value."; + } + } + description + "An SRLG value to be included or excluded."; + } + description + "Augmentation for a generic explicit route for SRLG + exclusion."; + } + } + } + } + + grouping generic-path-metric-bounds { + description + "TE path metric bounds grouping."; + container path-metric-bounds { + description + "Top-level container for the list of path metric bounds."; + list path-metric-bound { + key "metric-type"; + description + "List of path metric bounds, which can apply to link and + path metrics. + + TE paths which have at least one path metric which + exceeds the specified bounds MUST NOT be selected. + + TE paths that traverse TE links which have at least one + link metric which exceeds the specified bounds MUST NOT + be selected."; + leaf metric-type { + type identityref { + base link-path-metric-type; + } + description + "Identifies an entry in the list of 'metric-type' items + bound for the TE path."; + } + leaf upper-bound { + type uint64; + default "0"; + description + "Upper bound on the specified 'metric-type'. + + A zero indicates an unbounded upper limit for the + specificied 'metric-type'. + + The unit of is interpreted in the context of the + 'metric-type' identity."; + } + } + } + } + + grouping generic-path-optimization { + description + "TE generic path optimization grouping."; + container optimizations { + description + "The objective function container that includes + attributes to impose when computing a TE path."; + choice algorithm { + description + "Optimizations algorithm."; + case metric { + if-feature "path-optimization-metric"; + /* Optimize by metric */ + list optimization-metric { + key "metric-type"; + description + "TE path metric type."; + uses optimization-metric-entry; + } + /* Tiebreakers */ + container tiebreakers { + status deprecated; + description + "Container for the list of tiebreakers. + + This container has been deprecated by the tiebreaker + leaf."; + list tiebreaker { + key "tiebreaker-type"; + status deprecated; + description + "The list of tiebreaker criteria to apply on an + equally favored set of paths, in order to pick + the best."; + leaf tiebreaker-type { + type identityref { + base path-metric-type; + } + status deprecated; + description + "Identifies an entry in the list of tiebreakers."; + } + } + } + } + case objective-function { + if-feature "path-optimization-objective-function"; + /* Objective functions */ + container objective-function { + description + "The objective function container that includes + attributes to impose when computing a TE path."; + leaf objective-function-type { + type identityref { + base objective-function-type; + } + default "te-types:of-minimize-cost-path"; + description + "Objective function entry."; + } + } + } + } + } + leaf tiebreaker { + type identityref { + base path-tiebreaker-type; + } + default "te-types:path-tiebreaker-random"; + description + "The tiebreaker criteria to apply on an equally favored set + of paths, in order to pick the best."; + } + } + + grouping generic-path-affinities { + description + "Path affinities grouping."; + container path-affinities-values { + description + "Path affinities represented as values."; + list path-affinities-value { + key "usage"; + description + "List of named affinity constraints."; + leaf usage { + type identityref { + base resource-affinities-type; + } + description + "Identifies an entry in the list of value affinity + constraints."; + } + leaf value { + type admin-groups; + default ""; + description + "The affinity value. The default is empty."; + } + } + } + container path-affinity-names { + description + "Path affinities represented as names."; + list path-affinity-name { + key "usage"; + description + "List of named affinity constraints."; + leaf usage { + type identityref { + base resource-affinities-type; + } + description + "Identifies an entry in the list of named affinity + constraints."; + } + list affinity-name { + key "name"; + leaf name { + type string; + description + "Identifies a named affinity entry."; + } + description + "List of named affinities."; + } + } + } + } + + grouping generic-path-srlgs { + description + "Path SRLG grouping."; + container path-srlgs-lists { + description + "Path SRLG properties container."; + list path-srlgs-list { + key "usage"; + description + "List of SRLG values to be included or excluded."; + leaf usage { + type identityref { + base route-usage-type; + } + description + "Identifies an entry in a list of SRLGs to either + include or exclude."; + } + leaf-list values { + type srlg; + description + "List of SRLG values."; + } + } + } + container path-srlgs-names { + description + "Container for the list of named SRLGs."; + list path-srlgs-name { + key "usage"; + description + "List of named SRLGs to be included or excluded."; + leaf usage { + type identityref { + base route-usage-type; + } + description + "Identifies an entry in a list of named SRLGs to either + include or exclude."; + } + leaf-list names { + type string; + description + "List of named SRLGs."; + } + } + } + } + + grouping generic-path-disjointness { + description + "Path disjointness grouping."; + leaf disjointness { + type te-path-disjointness; + description + "The type of resource disjointness. + When configured for a primary path, the disjointness level + applies to all secondary LSPs. When configured for a + secondary path, the disjointness level overrides the level + configured for the primary path."; + } + } + + grouping common-path-constraints-attributes { + description + "Common path constraints configuration grouping."; + uses common-constraints; + uses generic-path-metric-bounds; + uses generic-path-affinities; + uses generic-path-srlgs; + } + + grouping generic-path-constraints { + description + "Global named path constraints configuration grouping."; + container path-constraints { + description + "TE named path constraints container."; + uses common-path-constraints-attributes; + uses generic-path-disjointness; + } + } + + grouping generic-path-properties { + description + "TE generic path properties grouping."; + container path-properties { + config false; + description + "The TE path properties."; + list path-metric { + key "metric-type"; + description + "TE path metric type."; + leaf metric-type { + type identityref { + base path-metric-type; + } + description + "TE path metric type."; + } + leaf accumulative-value { + type uint64; + description + "TE path metric accumulative value."; + } + } + uses generic-path-affinities; + uses generic-path-srlgs; + container path-route-objects { + description + "Container for the list of route objects either returned by + the computation engine or actually used by an LSP."; + list path-route-object { + key "index"; + ordered-by user; + description + "List of route objects either returned by the computation + engine or actually used by an LSP."; + leaf index { + type uint32; + description + "Route object entry index. The index is used to + identify an entry in the list. The order of entries + is defined by the user without relying on key + values."; + } + uses explicit-route-hop; + } + } + } + } + + grouping encoding-and-switching-type { + description + "Common grouping to define the LSP encoding and + switching types"; + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "LSP encoding type."; + reference + "RFC 3945: Generalized Multi-Protocol Label Switching (GMPLS) + Architecture"; + } + leaf switching-type { + type identityref { + base te-types:switching-capabilities; + } + description + "LSP switching type."; + reference + "RFC 3945: Generalized Multi-Protocol Label Switching (GMPLS) + Architecture"; + } + } + + grouping te-generic-node-id { + description + "A reusable grouping for a TE generic node identifier."; + leaf id { + type union { + type te-node-id; + type inet:ip-address; + type nw:node-id; + } + description + "The identifier of the node. + + It can be represented as IP address or dotted quad address + or as an URI. + + The type data node disambiguates the union type."; + } + leaf type { + type enumeration { + enum ip { + description + "IP address representation of the node identifier."; + } + enum te-id { + description + "TE identifier of the node"; + } + enum node-id { + description + "URI representation of the node identifier."; + } + } + description + "Type of node identifier representation."; + } + } +} diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-vpn-common@2021-09-10.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-vpn-common@2021-09-10.yang new file mode 100644 index 0000000000000000000000000000000000000000..075437d3a43dd7d81f98f1103fd2e34e2571005b --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/yang/ietf-vpn-common@2021-09-10.yang @@ -0,0 +1,2205 @@ +module ietf-vpn-common { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-vpn-common"; + prefix vpn-common; + + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + import ietf-routing-types { + prefix rt-types; + reference + "RFC 8294: Common YANG Data Types for the Routing Area"; + } + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types, Section 3"; + } + import ietf-packet-fields { + prefix packet-fields; + reference + "RFC 8519: YANG Data Model for Network Access + Control Lists (ACLs)"; + } + + organization + "IETF OPSAWG (Operations and Management Area Working Group)"; + contact + "WG Web: <https://datatracker.ietf.org/wg/opsawg/> + WG List: <mailto:opsawg@ietf.org> + + Editor: Mohamed Boucadair + <mailto:mohamed.boucadair@orange.com> + Author: Samier Barguil + <mailto:samier.barguilgiraldo.ext@telefonica.com> + Author: Oscar Gonzalez de Dios + <mailto:oscar.gonzalezdedios@telefonica.com> + Author: Qin Wu + <mailto:bill.wu@huawei.com>"; + description + "This YANG module defines a common module that is meant + to be reused by various VPN-related modules (e.g., + Layer 3 VPN Service Model (L3SM), Layer 2 VPN Service + Model (L2SM), Layer 3 VPN Network Model (L3NM), Layer 2 + VPN Network Model (L2NM)). + + Copyright (c) 2021 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + revision 2021-09-10 { + description + "Initial revision."; + reference + "RFC XXXX: A Layer 2/3 VPN Common YANG Model"; + } + + /******** Collection of VPN-related Features ********/ + /* + * Features related to encapsulation schemes + */ + + feature dot1q { + description + "Indicates the support for the Dot1q encapsulation."; + reference + "IEEE Std 802.1Q: Bridges and Bridged Networks"; + } + + feature qinq { + description + "Indicates the support for the QinQ encapsulation."; + reference + "IEEE Std 802.1ad: Provider Bridges"; + } + + feature vxlan { + description + "Indicates the support for the Virtual eXtensible + Local Area Network (VXLAN) encapsulation."; + reference + "RFC 7348: Virtual eXtensible Local Area Network (VXLAN): + A Framework for Overlaying Virtualized Layer 2 + Networks over Layer 3 Networks"; + } + + feature qinany { + description + "Indicates the support for the QinAny encapsulation. + The outer VLAN tag is set to a specific value but + the inner VLAN tag is set to any."; + } + + feature lag-interface { + description + "Indicates the support for Link Aggregation Group (LAG) + between VPN network accesses."; + reference + "IEEE Std. 802.1AX: Link Aggregation"; + } + + /* + * Features related to multicast + */ + + feature multicast { + description + "Indicates multicast capabilities support in a VPN."; + reference + "RFC 6513: Multicast in MPLS/BGP IP VPNs"; + } + + feature igmp { + description + "Indicates support for Internet Group Management Protocol + (IGMP)."; + reference + "RFC 1112: Host Extensions for IP Multicasting + RFC 2236: Internet Group Management Protocol, Version 2 + RFC 3376: Internet Group Management Protocol, Version 3"; + } + + feature mld { + description + "Indicates support for Multicast Listener Discovery (MLD)."; + reference + "RFC 2710: Multicast Listener Discovery (MLD) for IPv6 + RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) + for IPv6"; + } + + feature pim { + description + "Indicates support for Protocol Independent Multicast (PIM)."; + reference + "RFC 7761: Protocol Independent Multicast - Sparse Mode + (PIM-SM): Protocol Specification (Revised)"; + } + + /* + * Features related to address family types + */ + + feature ipv4 { + description + "Indicates IPv4 support in a VPN. That is, IPv4 traffic + can be carried in the VPN, IPv4 addresses/prefixes can + be assigned to a VPN network access, IPv4 routes can be + installed for the CE/PE link, etc."; + reference + "RFC 791: Internet Protocol"; + } + + feature ipv6 { + description + "Indicates IPv6 support in a VPN. That is, IPv6 traffic + can be carried in the VPN, IPv6 addresses/prefixes can + be assigned to a VPN network access, IPv6 routes can be + installed for the CE/PE link, etc."; + reference + "RFC 8200: Internet Protocol, Version 6 (IPv6)"; + } + + /* + * Features related to routing protocols + */ + + feature rtg-ospf { + description + "Indicates support for the OSPF as the Provider Edge (PE)/ + Customer Edge (CE) routing protocol."; + reference + "RFC 4577: OSPF as the Provider/Customer Edge Protocol + for BGP/MPLS IP Virtual Private Networks (VPNs) + RFC 6565: OSPFv3 as a Provider Edge to Customer Edge + (PE-CE) Routing Protocol"; + } + + feature rtg-ospf-sham-link { + description + "Indicates support for OSPF sham links."; + reference + "RFC 4577: OSPF as the Provider/Customer Edge Protocol + for BGP/MPLS IP Virtual Private Networks (VPNs), + Section 4.2.7 + RFC 6565: OSPFv3 as a Provider Edge to Customer Edge + (PE-CE) Routing Protocol, Section 5"; + } + + feature rtg-bgp { + description + "Indicates support for BGP as the PE/CE routing protocol."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4)"; + } + feature rtg-rip { + description + "Indicates support for RIP as the PE/CE routing protocol."; + reference + "RFC 2453: RIP Version 2 + RFC 2080: RIPng for IPv6"; + } + + feature rtg-isis { + description + "Indicates support for IS-IS as the PE/CE routing protocol."; + reference + "ISO10589: Intermediate System to Intermediate System intra- + domain routeing information exchange protocol for + use in conjunction with the protocol for providing + the connectionless-mode network service + (ISO 8473)"; + } + + feature rtg-vrrp { + description + "Indicates support for the Virtual Router Redundancy + Protocol (VRRP) in CE/PE link."; + reference + "RFC 5798: Virtual Router Redundancy Protocol (VRRP) Version 3 + for IPv4 and IPv6"; + } + + feature bfd { + description + "Indicates support for Bidirectional Forwarding Detection (BFD) + between the CE and the PE."; + reference + "RFC 5880: Bidirectional Forwarding Detection (BFD)"; + } + + /* + * Features related to VPN service constraints + */ + + feature bearer-reference { + description + "A bearer refers to properties of the CE-PE attachment that + are below Layer 3. + This feature indicates support for the bearer reference access + constraint. That is, the reuse of a network connection that was + already ordered to the service provider apart from the IP VPN + site."; + } + + feature placement-diversity { + description + "Indicates support for placement diversity constraints in the + customer premises. An example of these constraints may be to + avoid connecting a site network access to the same Provider + Edge as a target site network access."; + } + + /* + * Features related to bandwidth and Quality of Service (QoS) + */ + + feature qos { + description + "Indicates support for Classes of Service (CoSes) in the VPN."; + } + + feature inbound-bw { + description + "Indicates support for the inbound bandwidth in a VPN. That is, + support for specifying the download bandwidth from the service + provider network to the VPN site. Note that the L3SM uses + 'input' to identify the same feature. That terminology should + be deprecated in favor of the one defined in this module."; + } + + feature outbound-bw { + description + "Indicates support for the outbound bandwidth in a VPN. That is, + support for specifying the upload bandwidth from the VPN site + to the service provider network. Note that the L3SM uses + 'output' to identify the same feature. That terminology should + be deprecated in favor of the one defined in this module."; + } + + /* + * Features related to security and resilience + */ + + feature encryption { + description + "Indicates support for encryption in the VPN."; + } + + feature fast-reroute { + description + "Indicates support for Fast Reroute (FRR) capabilities for + a VPN site."; + } + + /* + * Features related to advanced VPN options + */ + + feature external-connectivity { + description + "Indicates support for the VPN to provide external + connectivity (e.g., Internet, private or public cloud)."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks + (VPNs), Section 11"; + } + + feature extranet-vpn { + description + "Indicates support for extranet VPNs. That is, the capability of + a VPN to access a list of other VPNs."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks + (VPNs), Section 1.1"; + } + + feature carriers-carrier { + description + "Indicates support for Carrier-of-Carrier VPNs."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks + (VPNs), Section 9"; + } + + /* + * Address family related identities + */ + + identity address-family { + description + "Defines a type for the address family."; + } + + identity ipv4 { + base address-family; + description + "Identity for IPv4 address family."; + } + identity ipv6 { + base address-family; + description + "Identity for IPv6 address family."; + } + + identity dual-stack { + base address-family; + description + "Identity for IPv4 and IPv6 address family."; + } + + /* + * Identities related to VPN topology + */ + + identity vpn-topology { + description + "Base identity of the VPN topology."; + } + + identity any-to-any { + base vpn-topology; + description + "Identity for any-to-any VPN topology. All VPN sites + can communicate with each other without any restrictions."; + } + + identity hub-spoke { + base vpn-topology; + description + "Identity for Hub-and-Spoke VPN topology. All Spokes can + communicate only with Hubs but not with each other. Hubs + can communicate with each other."; + } + + identity hub-spoke-disjoint { + base vpn-topology; + description + "Identity for Hub-and-Spoke VPN topology where Hubs cannot + communicate with each other."; + } + + identity custom { + base vpn-topology; + description + "Identity for custom VPN topologies where the role of the nodes + is not strictly Hub or Spoke. The VPN topology is controlled by + the import/export policies. The custom topology reflects more + complex VPN nodes such as VPN node that acts as Hub for certain + nodes and Spoke to others."; + } + + /* + * Identities related to network access types + */ + + identity site-network-access-type { + description + "Base identity for site network access type."; + } + + identity point-to-point { + base site-network-access-type; + description + "Point-to-point access type."; + } + + identity multipoint { + base site-network-access-type; + description + "Multipoint access type."; + } + + identity irb { + base site-network-access-type; + description + "Integrated Routing Bridge (IRB). + Identity for pseudowire connections."; + } + + identity loopback { + base site-network-access-type; + description + "Loopback access type."; + } + + /* + * Identities related to operational and administrative status + */ + + identity operational-status { + description + "Base identity for the operational status."; + } + + identity op-up { + base operational-status; + description + "Operational status is Up/Enabled."; + } + + identity op-down { + base operational-status; + description + "Operational status is Down/Disabled."; + } + + identity op-unknown { + base operational-status; + description + "Operational status is Unknown."; + } + + identity administrative-status { + description + "Base identity for administrative status."; + } + + identity admin-up { + base administrative-status; + description + "Administrative status is Up/Enabled."; + } + + identity admin-down { + base administrative-status; + description + "Administrative status is Down/Disabled."; + } + + identity admin-testing { + base administrative-status; + description + "Administrative status is up for testing purposes."; + } + + identity admin-pre-deployment { + base administrative-status; + description + "Administrative status is pre-deployment phase. That is, + prior to the actual deployment of a service."; + } + + /* + * Identities related to site or node role + */ + + identity role { + description + "Base identity of a site or a node role."; + } + + identity any-to-any-role { + base role; + description + "Any-to-any role."; + } + + identity spoke-role { + base role; + description + "A node or a site is acting as a Spoke."; + } + + identity hub-role { + base role; + description + "A node or a site is acting as a Hub."; + } + + identity custom-role { + base role; + description + "VPN node with custom or complex role in the VPN. For some + sources/destinations it can behave as a Hub, but for others it + can act as a Spoke depending on the configured policy."; + } + + /* + * Identities related to VPN service constraints + */ + + identity placement-diversity { + description + "Base identity for access placement constraints."; + } + + identity bearer-diverse { + base placement-diversity; + description + "Bearer diversity. + The bearers should not use common elements."; + } + + identity pe-diverse { + base placement-diversity; + description + "PE diversity."; + } + + identity pop-diverse { + base placement-diversity; + description + "Point Of Presence (POP) diversity."; + } + + identity linecard-diverse { + base placement-diversity; + description + "Linecard diversity."; + } + + identity same-pe { + base placement-diversity; + description + "Having sites connected on the same PE."; + } + + identity same-bearer { + base placement-diversity; + description + "Having sites connected using the same bearer."; + } + + /* + * Identities related to service types + */ + + identity service-type { + description + "Base identity for service type."; + } + + identity l3vpn { + base service-type; + description + "L3VPN service."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs)"; + } + + identity vpls { + base service-type; + description + "VPLS service."; + reference + "RFC 4761: Virtual Private LAN Service (VPLS) Using BGP for + Auto-Discovery and Signaling + RFC 4762: Virtual Private LAN Service (VPLS) Using Label + Distribution Protocol (LDP) Signaling"; + } + + identity vpws { + base service-type; + description + "Virtual Private Wire Service (VPWS) service."; + reference + "RFC 4664: Framework for Layer 2 Virtual Private Networks + (L2VPNs), Section 3.1.1"; + } + + identity vpws-evpn { + base service-type; + description + "EVPN used to support VPWS service."; + reference + "RFC 8214: Virtual Private Wire Service Support in Ethernet VPN"; + } + + identity pbb-evpn { + base service-type; + description + "Provider Backbone Bridging (PBB) EVPNs service."; + reference + "RFC 7623: Provider Backbone Bridging Combined with Ethernet VPN + (PBB-EVPN)"; + } + + identity mpls-evpn { + base service-type; + description + "MPLS-based EVPN service."; + reference + "RFC 7432: BGP MPLS-Based Ethernet VPN"; + } + + identity vxlan-evpn { + base service-type; + description + "VXLAN-based EVPN service."; + reference + "RFC 8365: A Network Virtualization Overlay Solution Using + Ethernet VPN (EVPN)"; + } + + /* + * Identities related to VPN signaling type + */ + + identity vpn-signaling-type { + description + "Base identity for VPN signaling types"; + } + + identity bgp-signaling { + base vpn-signaling-type; + description + "Layer 2 VPNs using BGP signaling."; + reference + "RFC 6624: Layer 2 Virtual Private Networks Using BGP for + Auto-Discovery and Signaling + RFC 7432: BGP MPLS-Based Ethernet VPN"; + } + + identity ldp-signaling { + base vpn-signaling-type; + description + "Targeted Label Distribution Protocol (LDP) signaling."; + reference + "RFC 5036: LDP Specification"; + } + + identity l2tp-signaling { + base vpn-signaling-type; + description + "Layer Two Tunneling Protocol (L2TP) signaling."; + reference + "RFC 3931: Layer Two Tunneling Protocol - Version 3 (L2TPv3)"; + } + + /* + * Identities related to routing protocols + */ + + identity routing-protocol-type { + description + "Base identity for routing protocol type."; + } + + identity static-routing { + base routing-protocol-type; + description + "Static routing protocol."; + } + + identity bgp-routing { + if-feature "rtg-bgp"; + base routing-protocol-type; + description + "BGP routing protocol."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4)"; + } + + identity ospf-routing { + if-feature "rtg-ospf"; + base routing-protocol-type; + description + "OSPF routing protocol."; + reference + "RFC 4577: OSPF as the Provider/Customer Edge Protocol + for BGP/MPLS IP Virtual Private Networks(VPNs) + RFC 6565: OSPFv3 as a Provider Edge to Customer Edge + (PE-CE) Routing Protocol"; + } + + identity rip-routing { + if-feature "rtg-rip"; + base routing-protocol-type; + description + "RIP routing protocol."; + reference + "RFC 2453: RIP Version 2 + RFC 2080: RIPng for IPv6"; + } + + identity isis-routing { + if-feature "rtg-isis"; + base routing-protocol-type; + description + "IS-IS routing protocol."; + reference + "ISO10589: Intermediate System to Intermediate System intra- + domain routeing information exchange protocol for + use in conjunction with the protocol for providing + the connectionless-mode network service + (ISO 8473)"; + } + + identity vrrp-routing { + if-feature "rtg-vrrp"; + base routing-protocol-type; + description + "VRRP protocol. + + This is to be used when LANs are directly connected to PEs."; + reference + "RFC 5798: Virtual Router Redundancy Protocol (VRRP) Version 3 + for IPv4 and IPv6"; + } + + identity direct-routing { + base routing-protocol-type; + description + "Direct routing. + + This is to be used when LANs are directly connected to PEs + and must be advertised in the VPN."; + } + + identity any-routing { + base routing-protocol-type; + description + "Any routing protocol. + + This can be, e.g., used to set policies that apply to any + routing protocol in place."; + } + + identity isis-level { + if-feature "rtg-isis"; + description + "Base identity for the IS-IS level."; + reference + "ISO10589: Intermediate System to Intermediate System intra- + domain routeing information exchange protocol for + use in conjunction with the protocol for providing + the connectionless-mode network service + (ISO 8473)"; + } + + identity level-1 { + base isis-level; + description + "IS-IS level 1."; + } + + identity level-2 { + base isis-level; + description + "IS-IS level 2."; + } + + identity level-1-2 { + base isis-level; + description + "IS-IS levels 1 and 2."; + } + + identity bfd-session-type { + if-feature "bfd"; + description + "Base identity for the BFD session type."; + } + + identity classic-bfd { + base bfd-session-type; + description + "Classic BFD."; + reference + "RFC 5880: Bidirectional Forwarding Detection (BFD)"; + } + + identity s-bfd { + base bfd-session-type; + description + "Seamless BFD."; + reference + "RFC 7880: Seamless Bidirectional Forwarding Detection (S-BFD)"; + } + + /* + * Identities related to Routes Import and Export + */ + + identity ie-type { + description + "Base identity for 'import/export' routing profiles. + These profiles can be reused between VPN nodes."; + } + + identity import { + base ie-type; + description + "'Import' routing profile."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks + (VPNs), Section 4.3.1"; + } + + identity export { + base ie-type; + description + "'Export' routing profile."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks + (VPNs), Section 4.3.1"; + } + + identity import-export { + base ie-type; + description + "'Import/export' routing profile."; + } + + /* + * Identities related to bandwidth and QoS + */ + + identity bw-direction { + description + "Base identity for the bandwidth direction."; + } + + identity inbound-bw { + if-feature "inbound-bw"; + base bw-direction; + description + "Inbound bandwidth."; + } + + identity outbound-bw { + if-feature "outbound-bw"; + base bw-direction; + description + "Outbound bandwidth."; + } + identity bw-type { + description + "Base identity for the bandwidth type."; + } + + identity bw-per-cos { + if-feature "qos"; + base bw-type; + description + "The bandwidth is per-CoS."; + } + + identity bw-per-port { + base bw-type; + description + "The bandwidth is per-site network access."; + } + + identity bw-per-site { + base bw-type; + description + "The bandwidth is per-site. It is applicable to all the site + network accesses within a site."; + } + + identity bw-per-service { + base bw-type; + description + "The bandwidth is per-VPN service."; + } + + identity qos-profile-direction { + if-feature "qos"; + description + "Base identity for the QoS profile direction."; + } + + identity site-to-wan { + base qos-profile-direction; + description + "Customer site to provider's network direction. + This is typically the CE-to-PE direction."; + } + + identity wan-to-site { + base qos-profile-direction; + description + "Provider's network to customer site direction. + This is typically the PE-to-CE direction."; + } + + identity both { + base qos-profile-direction; + description + "Both WAN-to-Site and Site-to-WAN directions."; + } + + /* + * Identities related to underlay transport instances + */ + + identity transport-instance-type { + description + "Base identity for underlay transport instance type."; + } + + identity virtual-network { + base transport-instance-type; + description + "Virtual network."; + reference + "RFC 8453: Framework for Abstraction and Control of TE + Networks (ACTN)"; + } + + identity enhanced-vpn { + base transport-instance-type; + description + "Enhanced VPN (VPN+). VPN+ is an approach that is + based on existing VPN and Traffic Engineering (TE) + technologies but adds characteristics that specific + services require over and above classical VPNs."; + reference + "I-D.ietf-teas-enhanced-vpn: + A Framework for Enhanced Virtual Private Network + (VPN+) Services"; + } + + identity ietf-network-slice { + base transport-instance-type; + description + "IETF network slice. An IETF network slice + is a logical network topology connecting a number of + endpoints using a set of shared or dedicated network + resources that are used to satisfy specific service + objectives."; + reference + "I-D.ietf-teas-ietf-network-slices: + Framework for IETF Network Slices"; + } + + /* + * Identities related to protocol types. These types are typically + * used to identify the underlay transport. + */ + + identity protocol-type { + description + "Base identity for Protocol Type."; + } + + identity ip-in-ip { + base protocol-type; + description + "Transport is based on IP-in-IP."; + reference + "RFC 2003: IP Encapsulation within IP + RFC 2473: Generic Packet Tunneling in IPv6 Specification"; + } + + identity ip-in-ipv4 { + base ip-in-ip; + description + "Transport is based on IP over IPv4."; + reference + "RFC 2003: IP Encapsulation within IP"; + } + + identity ip-in-ipv6 { + base ip-in-ip; + description + "Transport is based on IP over IPv6."; + reference + "RFC 2473: Generic Packet Tunneling in IPv6 Specification"; + } + + identity gre { + base protocol-type; + description + "Transport is based on Generic Routing Encapsulation (GRE)."; + reference + "RFC 1701: Generic Routing Encapsulation (GRE) + RFC 1702: Generic Routing Encapsulation over IPv4 networks + RFC 7676: IPv6 Support for Generic Routing Encapsulation (GRE)"; + } + + identity gre-v4 { + base gre; + description + "Transport is based on GRE over IPv4."; + reference + "RFC 1702: Generic Routing Encapsulation over IPv4 networks"; + } + + identity gre-v6 { + base gre; + description + "Transport is based on GRE over IPv6."; + reference + "RFC 7676: IPv6 Support for Generic Routing Encapsulation (GRE)"; + } + + identity vxlan-trans { + base protocol-type; + description + "Transport is based on VXLAN."; + reference + "RFC 7348: Virtual eXtensible Local Area Network (VXLAN): + A Framework for Overlaying Virtualized Layer 2 + Networks over Layer 3 Networks"; + } + + identity geneve { + base protocol-type; + description + "Transport is based on Generic Network Virtualization + Encapsulation (GENEVE)."; + reference + "RFC 8926: Geneve: Generic Network Virtualization Encapsulation"; + } + + identity ldp { + base protocol-type; + description + "Transport is based on LDP."; + reference + "RFC 5036: LDP Specification"; + } + + identity mpls-in-udp { + base protocol-type; + description + "Transport is MPLS in UDP."; + reference + "RFC 7510: Encapsulating MPLS in UDP"; + } + + identity sr { + base protocol-type; + description + "Transport is based on Segment Routing (SR)."; + reference + "RFC 8660: Segment Routing with the MPLS Data Plane + RFC 8663: MPLS Segment Routing over IP + RFC 8754: IPv6 Segment Routing Header (SRH)"; + } + + identity sr-mpls { + base sr; + description + "Transport is based on SR with MPLS."; + reference + "RFC 8660: Segment Routing with the MPLS Data Plane"; + } + + identity srv6 { + base sr; + description + "Transport is based on SR over IPv6."; + reference + "RFC 8754: IPv6 Segment Routing Header (SRH)"; + } + + identity sr-mpls-over-ip { + base sr; + description + "Transport is based on SR over MPLS over IP."; + reference + "RFC 8663: MPLS Segment Routing over IP"; + } + + identity rsvp-te { + base protocol-type; + description + "Transport setup relies upon RSVP-TE."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels"; + } + + identity bgp-lu { + base protocol-type; + description + "Transport setup relies upon BGP-LU."; + reference + "RFC 8277: Using BGP to Bind MPLS Labels to Address Prefixes"; + } + + identity unknown { + base protocol-type; + description + "Not known protocol type."; + } + + /* + * Identities related to encapsulations + */ + + identity encapsulation-type { + description + "Base identity for the encapsulation type."; + } + + identity priority-tagged { + base encapsulation-type; + description + "Priority-tagged interface."; + } + + identity dot1q { + if-feature "dot1q"; + base encapsulation-type; + description + "Dot1q encapsulation."; + } + + identity qinq { + if-feature "qinq"; + base encapsulation-type; + description + "QinQ encapsulation."; + } + + identity qinany { + if-feature "qinany"; + base encapsulation-type; + description + "QinAny encapsulation."; + } + identity vxlan { + if-feature "vxlan"; + base encapsulation-type; + description + "VxLAN encapsulation."; + } + + identity ethernet-type { + base encapsulation-type; + description + "Ethernet encapsulation type."; + } + + identity vlan-type { + base encapsulation-type; + description + "VLAN encapsulation type."; + } + + identity untagged-int { + base encapsulation-type; + description + "Untagged interface type."; + } + + identity tagged-int { + base encapsulation-type; + description + "Tagged interface type."; + } + + identity lag-int { + if-feature "lag-interface"; + base encapsulation-type; + description + "LAG interface type."; + } + + /* + * Identities related to VLAN Tag + */ + + identity tag-type { + description + "Base identity for the tag types."; + } + + identity c-vlan { + base tag-type; + description + "Indicates Customer VLAN (C-VLAN) tag, normally using + the 0x8100 Ethertype."; + } + + identity s-vlan { + base tag-type; + description + "Indicates Service VLAN (S-VLAN) tag."; + } + + identity s-c-vlan { + base tag-type; + description + "Uses both an S-VLAN tag and a C-VLAN tag."; + } + + /* + * Identities related to VXLAN + */ + + identity vxlan-peer-mode { + if-feature "vxlan"; + description + "Base identity for the VXLAN peer mode."; + } + + identity static-mode { + base vxlan-peer-mode; + description + "VXLAN access in the static mode."; + } + + identity bgp-mode { + base vxlan-peer-mode; + description + "VXLAN access by BGP EVPN learning."; + } + + /* + * Identities related to multicast + */ + + identity multicast-gp-address-mapping { + if-feature "multicast"; + description + "Base identity for multicast group mapping type."; + } + + identity static-mapping { + base multicast-gp-address-mapping; + description + "Static mapping, i.e., attach the interface to the + multicast group as a static member."; + } + + identity dynamic-mapping { + base multicast-gp-address-mapping; + description + "Dynamic mapping, i.e., an interface is added to the + multicast group as a result of snooping."; + } + + identity multicast-tree-type { + if-feature "multicast"; + description + "Base identity for multicast tree type."; + } + + identity ssm-tree-type { + base multicast-tree-type; + description + "Source-Specific Multicast (SSM) tree type."; + } + + identity asm-tree-type { + base multicast-tree-type; + description + "Any-Source Multicast (ASM) tree type."; + } + + identity bidir-tree-type { + base multicast-tree-type; + description + "Bidirectional tree type."; + } + + identity multicast-rp-discovery-type { + if-feature "multicast"; + description + "Base identity for Rendezvous Point (RP) discovery type."; + } + + identity auto-rp { + base multicast-rp-discovery-type; + description + "Auto-RP discovery type."; + } + + identity static-rp { + base multicast-rp-discovery-type; + description + "Static type."; + } + + identity bsr-rp { + base multicast-rp-discovery-type; + description + "Bootstrap Router (BSR) discovery type."; + } + + identity group-management-protocol { + if-feature "multicast"; + description + "Base identity for multicast group management protocol."; + } + + identity igmp-proto { + base group-management-protocol; + description + "IGMP."; + reference + "RFC 1112: Host Extensions for IP Multicasting + RFC 2236: Internet Group Management Protocol, Version 2 + RFC 3376: Internet Group Management Protocol, Version 3"; + } + + identity mld-proto { + base group-management-protocol; + description + "MLD."; + reference + "RFC 2710: Multicast Listener Discovery (MLD) for IPv6 + RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) + for IPv6"; + } + + identity pim-proto { + if-feature "pim"; + base routing-protocol-type; + description + "PIM."; + reference + "RFC 7761: Protocol Independent Multicast - Sparse Mode + (PIM-SM): Protocol Specification (Revised)"; + } + + identity igmp-version { + if-feature "igmp"; + description + "Base identity for IGMP version."; + } + + identity igmpv1 { + base igmp-version; + description + "IGMPv1."; + reference + "RFC 1112: Host Extensions for IP Multicasting"; + } + + identity igmpv2 { + base igmp-version; + description + "IGMPv2."; + reference + "RFC 2236: Internet Group Management Protocol, Version 2"; + } + + identity igmpv3 { + base igmp-version; + description + "IGMPv3."; + reference + "RFC 3376: Internet Group Management Protocol, Version 3"; + } + + identity mld-version { + if-feature "mld"; + description + "Base identity for MLD version."; + } + + identity mldv1 { + base mld-version; + description + "MLDv1."; + reference + "RFC 2710: Multicast Listener Discovery (MLD) for IPv6"; + } + + identity mldv2 { + base mld-version; + description + "MLDv2."; + reference + "RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) + for IPv6"; + } + + /* + * Identities related to traffic types + */ + + identity tf-type { + description + "Base identity for the traffic type."; + } + + identity multicast-traffic { + base tf-type; + description + "Multicast traffic."; + } + + identity broadcast-traffic { + base tf-type; + description + "Broadcast traffic."; + } + + identity unknown-unicast-traffic { + base tf-type; + description + "Unknown unicast traffic."; + } + + /* + * Identities related to customer applications + */ + + identity customer-application { + description + "Base identity for customer applications."; + } + + identity web { + base customer-application; + description + "Web applications (e.g., HTTP, HTTPS)."; + } + + identity mail { + base customer-application; + description + "Mail application."; + } + + identity file-transfer { + base customer-application; + description + "File transfer application (e.g., FTP, SFTP)."; + } + + identity database { + base customer-application; + description + "Database application."; + } + + identity social { + base customer-application; + description + "Social-network application."; + } + + identity games { + base customer-application; + description + "Gaming application."; + } + + identity p2p { + base customer-application; + description + "Peer-to-peer application."; + } + + identity network-management { + base customer-application; + description + "Management application (e.g., Telnet, syslog, + SNMP)."; + } + + identity voice { + base customer-application; + description + "Voice application."; + } + + identity video { + base customer-application; + description + "Video conference application."; + } + + identity embb { + base customer-application; + description + "Enhanced Mobile Broadband (eMBB) application. + Note that an eMBB application demands network performance with a + wide variety of characteristics, such as data rate, latency, + loss rate, reliability, and many other parameters."; + } + + identity urllc { + base customer-application; + description + "Ultra-Reliable and Low Latency Communications + (URLLC) application. Note that an URLLC application demands + network performance with a wide variety of characteristics, such + as latency, reliability, and many other parameters."; + } + + identity mmtc { + base customer-application; + description + "Massive Machine Type Communications (mMTC) application. + Note that an mMTC application demands network performance with + a wide variety of characteristics, such as data rate, latency, + loss rate, reliability, and many other parameters."; + } + + /* + * Identities related to service bundling + */ + + identity bundling-type { + description + "The base identity for the bundling type. It supports a subset or + all CE-VLANs associated with an L2VPN service."; + } + + identity multi-svc-bundling { + base bundling-type; + description + "Multi-service bundling, i.e., multiple C-VLAN IDs + can be associated with an L2VPN service at a site."; + } + + identity one2one-bundling { + base bundling-type; + description + "One-to-one service bundling, i.e., each L2VPN can + be associated with only one C-VLAN ID at a site."; + } + + identity all2one-bundling { + base bundling-type; + description + "All-to-one bundling, i.e., all C-VLAN IDs are mapped + to one L2VPN service."; + } + + /* + * Identities related to Ethernet Services + */ + + identity control-mode { + description + "Base Identity for the type of control mode on Layer 2 + Control Protocol (L2CP)."; + } + + identity peer { + base control-mode; + description + "'peer' mode, i.e., participate in the protocol towards the CE. + Peering is common for Link Aggregation Control Protocol (LACP) + and the Ethernet Local Management Interface (E-LMI) and, + occasionally, for Link Layer Discovery Protocol (LLDP). + For VPLSs and VPWSs, the subscriber can also request that the + peer service provider enables spanning tree."; + } + + identity tunnel { + base control-mode; + description + "'tunnel' mode, i.e., pass to the egress or destination site. For + Ethernet Private Lines (EPLs), the expectation is that L2CP + frames are tunnelled."; + } + identity discard { + base control-mode; + description + "'Discard' mode, i.e., discard the frame."; + } + + identity neg-mode { + description + "Base identity for the negotiation mode."; + } + + identity full-duplex { + base neg-mode; + description + "Full-duplex negotiation mode."; + } + + identity auto-neg { + base neg-mode; + description + "Auto-negotiation mode."; + } + + /******** Collection of VPN-related Types ********/ + + typedef vpn-id { + type string; + description + "Defines an identifier that is used with a VPN module. + This can be, for example, a service identifier, a node + identifier, etc."; + } + + /******* VPN-related reusable groupings *******/ + + grouping vpn-description { + description + "Provides common VPN information."; + leaf vpn-id { + type vpn-common:vpn-id; + description + "A VPN identifier that uniquely identifies a VPN. + This identifier has a local meaning, e.g., within + a service provider network."; + } + leaf vpn-name { + type string; + description + "Used to associate a name with the service + in order to facilitate the identification of + the service."; + } + leaf vpn-description { + type string; + description + "Textual description of a VPN."; + } + leaf customer-name { + type string; + description + "Name of the customer that actually uses the VPN."; + } + } + + grouping vpn-profile-cfg { + description + "Grouping for VPN Profile configuration."; + container valid-provider-identifiers { + description + "Container for valid provider profile identifiers."; + list external-connectivity-identifier { + if-feature "external-connectivity"; + key "id"; + description + "List for profile identifiers that uniquely identify profiles + governing how external connectivity is provided to a VPN. + A profile indicates the type of external connectivity + (Internet, cloud, etc.), the sites/nodes that are associated + with a connectivity profile, etc. A profile can also indicate + filtering rules and/or address translation rules. Such + features may involve PE, P, or dedicated nodes as a function + of the deployment."; + leaf id { + type string; + description + "Identification of an external connectivity profile. The + profile only has significance within the service provider's + administrative domain."; + } + } + list encryption-profile-identifier { + key "id"; + description + "List for encryption profile identifiers."; + leaf id { + type string; + description + "Identification of the encryption profile to be used. The + profile only has significance within the service provider's + administrative domain."; + } + } + list qos-profile-identifier { + key "id"; + description + "List for QoS Profile Identifiers."; + leaf id { + type string; + description + "Identification of the QoS profile to be used. The + profile only has significance within the service provider's + administrative domain."; + } + } + list bfd-profile-identifier { + key "id"; + description + "List for BFD profile identifiers."; + leaf id { + type string; + description + "Identification of the BFD profile to be used. The + profile only has significance within the service provider's + administrative domain."; + } + } + list forwarding-profile-identifier { + key "id"; + description + "List for forwarding profile identifiers."; + leaf id { + type string; + description + "Identification of the forwarding profile to be used. + The profile only has significance within the service + provider's administrative domain."; + } + } + list routing-profile-identifier { + key "id"; + description + "List for Routing Profile Identifiers."; + leaf id { + type string; + description + "Identification of the routing profile to be used by the + routing protocols within sites, vpn-network-accesses, or + vpn-nodes for refering VRF's import/export policies. + + The profile only has significance within the service + provider's administrative domain."; + } + } + nacm:default-deny-write; + } + } + + grouping oper-status-timestamp { + description + "This grouping defines some operational parameters for the + service."; + leaf status { + type identityref { + base operational-status; + } + config false; + description + "Operations status."; + } + leaf last-change { + type yang:date-and-time; + config false; + description + "Indicates the actual date and time of the service status + change."; + } + } + + grouping service-status { + description + "Service status grouping."; + container status { + description + "Service status."; + container admin-status { + description + "Administrative service status."; + leaf status { + type identityref { + base administrative-status; + } + description + "Administrative service status."; + } + leaf last-change { + type yang:date-and-time; + description + "Indicates the actual date and time of the service status + change."; + } + } + container oper-status { + description + "Operational service status."; + uses oper-status-timestamp; + } + } + } + + grouping underlay-transport { + description + "This grouping defines the type of underlay transport for the + VPN service or how that underlay is set. It can include an + identifier to an abstract transport instance to which the VPN + is grafted or indicate a technical implementation that is + expressed as an ordered list of protocols."; + choice type { + description + "A choice based on the type of underlay transport + constraints."; + case abstract { + description + "Indicates that the transport constraint is an abstract + concept."; + leaf transport-instance-id { + type string; + description + "An optional identifier of the abstract transport instance."; + } + leaf instance-type { + type identityref { + base transport-instance-type; + } + description + "Indicates a transport instance type. For example, it can + be a VPN+, an IETF network slice, a virtual network, etc."; + } + } + case protocol { + description + "Indicates a list of protocols."; + leaf-list protocol { + type identityref { + base protocol-type; + } + ordered-by user; + description + "A client ordered list of transport protocols."; + } + } + } + } + + grouping vpn-route-targets { + description + "A grouping that specifies Route Target (RT) import-export rules + used in a BGP-enabled VPN."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs) + RFC 4664: Framework for Layer 2 Virtual Private Networks + (L2VPNs)"; + list vpn-target { + key "id"; + description + "Route targets. AND/OR operations may be defined + based on the RTs assigment."; + leaf id { + type uint8; + description + "Identifies each VPN Target."; + } + list route-targets { + key "route-target"; + description + "List of RTs."; + leaf route-target { + type rt-types:route-target; + description + "Conveys an RT value."; + } + } + leaf route-target-type { + type rt-types:route-target-type; + mandatory true; + description + "Import/export type of the RT."; + } + } + container vpn-policies { + description + "VPN service policies. It contains references to the + import and export policies to be associated with the + VPN service."; + leaf import-policy { + type string; + description + "Identifies the 'import' policy."; + } + leaf export-policy { + type string; + description + "Identifies the 'export' policy."; + } + } + } + + grouping route-distinguisher { + description + "Grouping for route distinguisher (RD)."; + choice rd-choice { + description + "Route distinguisher choice between several options + on providing the route distinguisher value."; + case directly-assigned { + description + "Explicitly assign an RD value."; + leaf rd { + type rt-types:route-distinguisher; + description + "Indicates an RD value that is explicitly + assigned."; + } + } + case directly-assigned-suffix { + description + "The value of the Assigned Number subfield of the RD. + The Administrator subfield of the RD will be + based on other configuration information such as + router-id or ASN."; + leaf rd-suffix { + type uint16; + description + "Indicates the value of the Assigned Number + subfield that is explicitly assigned."; + } + } + case auto-assigned { + description + "The RD is auto-assigned."; + container rd-auto { + description + "The RD is auto-assigned."; + choice auto-mode { + description + "Indicates the auto-assignment mode. RD can be + automatically assigned with or without + indicating a pool from which the RD should be + taken. + + For both cases, the server will auto-assign an RD + value 'auto-assigned-rd' and use that value + operationally."; + case from-pool { + leaf rd-pool-name { + type string; + description + "The auto-assignment will be made from the pool + identified by the rd-pool-name."; + } + } + case full-auto { + leaf auto { + type empty; + description + "Indicates an RD is fully auto-assigned."; + } + } + } + leaf auto-assigned-rd { + type rt-types:route-distinguisher; + config false; + description + "The value of the auto-assigned RD."; + } + } + } + case auto-assigned-suffix { + description + "The value of the Assigned Number subfield will + be auto-assigned. The Administrator subfield + will be based on other configuration information such as + router-id or ASN."; + container rd-auto-suffix { + description + "The Assigned Number subfield is auto-assigned."; + choice auto-mode { + description + "Indicates the auto-assignment mode of the Assigned Number + subfield. This number can be automatically assigned + with or without indicating a pool from which the value + should be taken. + + For both cases, the server will auto-assign + 'auto-assigned-rd-suffix' and use that value to build + the RD that will be used operationally."; + case from-pool { + leaf rd-pool-name { + type string; + description + "The assignment will be made from the pool identified + by the rd-pool-name."; + } + } + case full-auto { + leaf auto { + type empty; + description + "Indicates that the Assigned Number is fully auto + assigned."; + } + } + } + leaf auto-assigned-rd-suffix { + type uint16; + config false; + description + "Includes the value of the Assigned Number subfield that + is auto-assigned ."; + } + } + } + case no-rd { + description + "Use the empty type to indicate RD has no value and is not to + be auto-assigned."; + leaf no-rd { + type empty; + description + "No RD is assigned."; + } + } + } + } + + grouping vpn-components-group { + description + "Grouping definition to assign group-ids to associate VPN nodes, + sites, or network accesses."; + container groups { + description + "Lists the groups to which a VPN node, a site, or a network + access belongs to."; + list group { + key "group-id"; + description + "List of group-ids."; + leaf group-id { + type string; + description + "Is the group-id to which a VPN node, a site, or a network + access belongs to."; + } + } + } + } + + grouping placement-constraints { + description + "Constraints for placing a network access."; + list constraint { + key "constraint-type"; + description + "List of constraints."; + leaf constraint-type { + type identityref { + base placement-diversity; + } + description + "Diversity constraint type."; + } + container target { + description + "The constraint will apply against this list of groups."; + choice target-flavor { + description + "Choice for the group definition."; + case id { + list group { + key "group-id"; + description + "List of groups."; + leaf group-id { + type string; + description + "The constraint will apply against this particular + group-id."; + } + } + } + case all-accesses { + leaf all-other-accesses { + type empty; + description + "The constraint will apply against all other network + accesses of a site."; + } + } + case all-groups { + leaf all-other-groups { + type empty; + description + "The constraint will apply against all other groups that + the customer is managing."; + } + } + } + } + } + } + + grouping ports { + description + "Choice of specifying a source or destination port numbers."; + choice source-port { + description + "Choice of specifying the source port or referring to a group + of source port numbers."; + container source-port-range-or-operator { + description + "Source port definition."; + uses packet-fields:port-range-or-operator; + } + } + choice destination-port { + description + "Choice of specifying a destination port or referring to a group + of destination port numbers."; + container destination-port-range-or-operator { + description + "Destination port definition."; + uses packet-fields:port-range-or-operator; + } + } + } + + grouping qos-classification-policy { + description + "Configuration of the traffic classification policy."; + list rule { + key "id"; + ordered-by user; + description + "List of marking rules."; + leaf id { + type string; + description + "An identifier of the QoS classification policy rule."; + } + choice match-type { + default "match-flow"; + description + "Choice for classification."; + case match-flow { + choice l3 { + description + "Either IPv4 or IPv6."; + container ipv4 { + description + "Rule set that matches IPv4 header."; + uses packet-fields:acl-ip-header-fields; + uses packet-fields:acl-ipv4-header-fields; + } + container ipv6 { + description + "Rule set that matches IPv6 header."; + uses packet-fields:acl-ip-header-fields; + uses packet-fields:acl-ipv6-header-fields; + } + } + choice l4 { + description + "Includes Layer 4 specific information. + This version focuses on TCP and UDP."; + container tcp { + description + "Rule set that matches TCP header."; + uses packet-fields:acl-tcp-header-fields; + uses ports; + } + container udp { + description + "Rule set that matches UDP header."; + uses packet-fields:acl-udp-header-fields; + uses ports; + } + } + } + case match-application { + leaf match-application { + type identityref { + base customer-application; + } + description + "Defines the application to match."; + } + } + } + leaf target-class-id { + if-feature "qos"; + type string; + description + "Identification of the class of service. This identifier is + internal to the administration."; + } + } + } +} diff --git a/src/nbi/tests/data/agg-net-topology.json b/src/nbi/tests/data/agg-net-topology.json new file mode 100644 index 0000000000000000000000000000000000000000..bb27d6f258fb317662a30ab746bbe0d0ffff331f --- /dev/null +++ b/src/nbi/tests/data/agg-net-topology.json @@ -0,0 +1,858 @@ +{ + "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": "ip-net-controller" + } + }, + "name": "ip-net-controller", + "device_type": "ip-sdn-controller", + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "10.0.58.29" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "80" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "endpoints": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + } + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": false + } + } + } + ] + }, + "device_endpoints": [] + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "name": "172.16.182.25", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "128.32.33.254", + "address_prefix": "24", + "site_location": "access" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "name": "172.16.185.31", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "name": "172.16.185.33", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "name": "172.16.185.32", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "172.10.33.254", + "address_prefix": "24", + "site_location": "cloud" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "device_type": "emu-datacenter", + "device_drivers": [ + 0 + ], + "device_endpoints": [], + "device_operational_status": 1, + "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", + "uuid": "500" + }, + { + "sample_types": [], + "type": "optical", + "uuid": "200" + }, + { + "sample_types": [], + "type": "optical", + "uuid": "201" + } + ] + } + } + } + ] + } + } + ], + "links": [ + { + "link_id": { + "link_uuid": { + "uuid": "ip-net-controller/mgmt==172.16.182.25/mgmt" + } + }, + "name": "ip-net-controller/mgmt==172.16.182.25/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-net-controller/mgmt==172.16.185.31/mgmt" + } + }, + "name": "ip-net-controller/mgmt==172.16.185.31/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-net-controller/mgmt==172.16.185.33/mgmt" + } + }, + "name": "ip-net-controller/mgmt==172.16.185.33/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-net-controller/mgmt==172.16.185.32/mgmt" + } + }, + "name": "ip-net-controller/mgmt==172.16.185.32/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-500" + } + }, + "name": "172.16.182.25-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-500" + } + }, + "name": "172.16.185.33-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-501" + } + }, + "name": "172.16.182.25-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-501" + } + }, + "name": "172.16.185.31-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-500" + } + }, + "name": "172.16.185.31-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-500" + } + }, + "name": "172.16.185.32-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-501" + } + }, + "name": "172.16.185.33-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-501" + } + }, + "name": "172.16.185.32-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-200" + } + }, + "name": "172.16.185.32-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.204.220-500" + } + }, + "name": "172.16.204.220-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + } + ] +} diff --git a/src/nbi/tests/data/camara-e2e-topology.json b/src/nbi/tests/data/camara-e2e-topology.json new file mode 100644 index 0000000000000000000000000000000000000000..02a21e6918c5d8fb49016d7babe75a51bf751979 --- /dev/null +++ b/src/nbi/tests/data/camara-e2e-topology.json @@ -0,0 +1,1725 @@ +{ + "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": "ip-transport-controller" + } + }, + "name": "ip-transport-controller", + "device_type": "ietf-slice", + "device_operational_status": 1, + "device_drivers": [ + 14 + ], + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "10.0.58.9" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "80" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "endpoints": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + } + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": false + } + } + } + ] + }, + "device_endpoints": [] + }, + { + "device_id": { + "device_uuid": { + "uuid": "agg-net-controller" + } + }, + "name": "agg-net-controller", + "device_type": "ietf-slice", + "device_operational_status": 1, + "device_drivers": [ + 14 + ], + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "10.0.58.9" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "80" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "endpoints": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + } + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": false + } + } + } + ] + }, + "device_endpoints": [] + }, + { + "device_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "name": "nce-controller", + "device_type": "nce", + "device_operational_status": 1, + "device_drivers": [ + 15 + ], + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "1.1.1.1" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "80" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "endpoints": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + } + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": false + } + } + } + ] + }, + "device_endpoints": [] + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "name": "172.16.182.25", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 0, + 14 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "128.32.33.254", + "address_prefix": "24", + "site_location": "access", + "mtu": "1500" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "name": "172.16.185.31", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 0, + 14 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "name": "172.16.185.33", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 0, + 14 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "name": "172.16.185.32", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 0, + 14 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "ce-ip": "172.10.33.2", + "address_ip": "172.10.33.254", + "address_prefix": "24", + "site_location": "cloud", + "mtu": "1500" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "name": "172.16.58.10", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 15 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "0.0.0.0", + "address_prefix": "24" + }, + { + "uuid": "201", + "name": "201", + "type": "optical", + "address_ip": "0.0.0.0", + "address_prefix": "24" + }, + { + "uuid": "500", + "name": "500", + "type": "optical", + "address_ip": "128.32.33.2", + "address_prefix": "24" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "name": "172.16.61.10", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 15 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "0.0.0.0", + "address_prefix": "24" + }, + { + "uuid": "500", + "name": "500", + "type": "optical", + "address_ip": "128.32.33.2", + "address_prefix": "24" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "name": "172.16.61.11", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 15 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "0.0.0.0", + "address_prefix": "24" + }, + { + "uuid": "500", + "name": "500", + "type": "optical", + "address_ip": "128.32.33.2", + "address_prefix": "24" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.221" + } + }, + "device_type": "emu-datacenter", + "device_drivers": [ + 0 + ], + "device_endpoints": [], + "device_operational_status": 1, + "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", + "uuid": "eth0" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.222" + } + }, + "device_type": "emu-datacenter", + "device_drivers": [ + 0 + ], + "device_endpoints": [], + "device_operational_status": 1, + "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", + "uuid": "eth0" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "device_type": "emu-datacenter", + "device_drivers": [ + 0 + ], + "device_endpoints": [], + "device_operational_status": 1, + "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", + "uuid": "500" + }, + { + "sample_types": [], + "type": "optical", + "uuid": "200" + }, + { + "sample_types": [], + "type": "optical", + "uuid": "201" + } + ] + } + } + } + ] + } + } + ], + "links": [ + { + "link_id": { + "link_uuid": { + "uuid": "agg-net-controller/mgmt==ip-transport-controller/mgmt" + } + }, + "name": "agg-net-controller/mgmt==ip-transport-controller/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "agg-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "nce-controller/mgmt==172.16.61.11/mgmt" + } + }, + "name": "nce-controller/mgmt==172.16.61.11/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "nce-controller/mgmt==172.16.61.10/mgmt" + } + }, + "name": "nce-controller/mgmt==172.16.61.10/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "nce-controller/mgmt==172.16.58.10/mgmt" + } + }, + "name": "nce-controller/mgmt==172.16.58.10/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-transport-controller/mgmt==172.16.185.33/mgmt" + } + }, + "name": "ip-transport-controller/mgmt==172.16.185.33/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-transport-controller/mgmt==172.16.185.31/mgmt" + } + }, + "name": "ip-transport-controller/mgmt==172.16.185.31/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-transport-controller/mgmt==172.16.185.32/mgmt" + } + }, + "name": "ip-transport-controller/mgmt==172.16.185.32/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-transport-controller/mgmt==172.16.182.25/mgmt" + } + }, + "name": "ip-transport-controller/mgmt==172.16.182.25/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-500" + } + }, + "name": "172.16.182.25-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-500" + } + }, + "name": "172.16.185.33-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-501" + } + }, + "name": "172.16.182.25-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-501" + } + }, + "name": "172.16.185.31-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-500" + } + }, + "name": "172.16.185.31-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-500" + } + }, + "name": "172.16.185.32-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-501" + } + }, + "name": "172.16.185.33-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-501" + } + }, + "name": "172.16.185.32-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-200" + } + }, + "name": "172.16.185.32-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.204.220-500" + } + }, + "name": "172.16.204.220-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-200" + } + }, + "name": "172.16.182.25-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.58.10-500" + } + }, + "name": "172.16.58.10-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.58.10-200" + } + }, + "name": "172.16.58.10-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.61.10-500" + } + }, + "name": "172.16.61.10-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.58.10-201" + } + }, + "name": "172.16.58.10-201", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "201" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.61.11-500" + } + }, + "name": "172.16.61.11-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "201" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.61.10-200" + } + }, + "name": "172.16.61.10-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.221" + } + }, + "endpoint_uuid": { + "uuid": "eth0" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.104.221-eth0" + } + }, + "name": "172.16.104.221-eth0", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.221" + } + }, + "endpoint_uuid": { + "uuid": "eth0" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.61.11-200" + } + }, + "name": "172.16.61.11-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.222" + } + }, + "endpoint_uuid": { + "uuid": "eth0" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.104.222-eth0" + } + }, + "name": "172.16.104.222-eth0", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.222" + } + }, + "endpoint_uuid": { + "uuid": "eth0" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + } + ] +} diff --git a/src/nbi/tests/data/ip-net-descriptor.json b/src/nbi/tests/data/ip-net-descriptor.json new file mode 100644 index 0000000000000000000000000000000000000000..dafeeb5bc22dd457fa7b924a30feac660b38f491 --- /dev/null +++ b/src/nbi/tests/data/ip-net-descriptor.json @@ -0,0 +1,535 @@ +{ + "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": "172.16.182.25" + } + }, + "name": "172.16.182.25", + "device_type": "emu-packet-router", + "device_operational_status": 1, + "device_drivers": [ + 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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "name": "172.16.185.31", + "device_type": "emu-packet-router", + "device_operational_status": 1, + "device_drivers": [ + 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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "name": "172.16.185.33", + "device_type": "emu-packet-router", + "device_operational_status": 1, + "device_drivers": [ + 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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "name": "172.16.185.32", + "device_type": "emu-packet-router", + "device_operational_status": 1, + "device_drivers": [ + 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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + } + ], + "links": [ + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-500" + } + }, + "name": "172.16.182.25-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-500" + } + }, + "name": "172.16.185.33-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-501" + } + }, + "name": "172.16.182.25-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-501" + } + }, + "name": "172.16.185.31-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-500" + } + }, + "name": "172.16.185.31-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-500" + } + }, + "name": "172.16.185.32-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-501" + } + }, + "name": "172.16.185.33-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-501" + } + }, + "name": "172.16.185.32-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/nbi/tests/data/slice/post_connection_group_to_network_slice1.json b/src/nbi/tests/data/slice/post_connection_group_to_network_slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..d39a837bd8c3719463e8ecfd3fbfc2d25111afef --- /dev/null +++ b/src/nbi/tests/data/slice/post_connection_group_to_network_slice1.json @@ -0,0 +1,62 @@ +{ + "connection-group": [ + { + "id": "line2", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "3", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "10" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "5000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-sender-sdp": "3", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "20" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "1000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/nbi/tests/data/slice/post_connection_group_to_network_slice2.json b/src/nbi/tests/data/slice/post_connection_group_to_network_slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..d39a837bd8c3719463e8ecfd3fbfc2d25111afef --- /dev/null +++ b/src/nbi/tests/data/slice/post_connection_group_to_network_slice2.json @@ -0,0 +1,62 @@ +{ + "connection-group": [ + { + "id": "line2", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "3", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "10" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "5000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-sender-sdp": "3", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "20" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "1000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/nbi/tests/data/slice/post_match_criteria_to_sdp1_in_slice1.json b/src/nbi/tests/data/slice/post_match_criteria_to_sdp1_in_slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..16a36d45b86230b27eafa45a612b95c248a7b3ac --- /dev/null +++ b/src/nbi/tests/data/slice/post_match_criteria_to_sdp1_in_slice1.json @@ -0,0 +1,40 @@ +{ + "match-criterion": [ + { + "index": 2, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "101" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line2" + } + ] +} \ No newline at end of file diff --git a/src/nbi/tests/data/slice/post_match_criteria_to_sdp1_in_slice2.json b/src/nbi/tests/data/slice/post_match_criteria_to_sdp1_in_slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..8ceefdc2f2471af225143e5a1def2d7ba71e2ab1 --- /dev/null +++ b/src/nbi/tests/data/slice/post_match_criteria_to_sdp1_in_slice2.json @@ -0,0 +1,40 @@ +{ + "match-criterion": [ + { + "index": 2, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "201" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line2" + } + ] +} \ No newline at end of file diff --git a/src/nbi/tests/data/slice/post_network_slice1.json b/src/nbi/tests/data/slice/post_network_slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..e6e0ee90a25ff12f73c8f8896f9c2c74ab6b4019 --- /dev/null +++ b/src/nbi/tests/data/slice/post_network_slice1.json @@ -0,0 +1,188 @@ +{ + "slice-service": [ + { + "id": "slice1", + "description": "network slice 1, connect to VM1", + "sdps": { + "sdp": [ + { + "id": "1", + "node-id": "172.16.204.220", + "sdp-ip-address": [ + "172.16.204.220" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "101" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC POP to VM1", + "description": "AC VM1 connected to POP", + "ac-node-id": "172.16.204.220", + "ac-tp-id": "200" + } + ] + } + }, + { + "id": "2", + "node-id": "172.16.61.10", + "sdp-ip-address": [ + "172.16.61.10" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "21" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC ONT", + "description": "AC connected to PC1", + "ac-node-id": "172.16.61.10", + "ac-tp-id": "200" + } + ] + } + } + ] + }, + "connection-groups": { + "connection-group": [ + { + "id": "line1", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "10" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "5000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-sender-sdp": "2", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "20" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "1000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/nbi/tests/data/slice/post_network_slice2.json b/src/nbi/tests/data/slice/post_network_slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..97e6ade27449be0a3816085aa31b707ffbb6f813 --- /dev/null +++ b/src/nbi/tests/data/slice/post_network_slice2.json @@ -0,0 +1,189 @@ +{ + "slice-service": [ + { + "id": "slice2", + "description": "network slice 2, connect to VM2", + "sdps": { + "sdp": [ + { + "id": "1", + "node-id": "172.16.204.220", + "sdp-ip-address": [ + "172.16.204.220" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "201" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC POP to VM2", + "description": "AC VM2 connected to POP", + "ac-node-id": "172.16.204.220", + "ac-tp-id": "201" + } + ] + } + }, + { + "id": "2", + "node-id": "172.16.61.10", + "sdp-ip-address": [ + "172.16.61.10" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "31" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC ONT", + "description": "AC connected to PC", + "ac-node-id": "172.16.61.10", + "ac-tp-id": "200", + "ac-ipv4-address": "172.16.61.10" + } + ] + } + } + ] + }, + "connection-groups": { + "connection-group": [ + { + "id": "line1", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "10" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "5000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-sender-sdp": "2", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "20" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "1000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ] + } + ] + } + } + ] +} diff --git a/src/nbi/tests/data/slice/post_sdp_to_network_slice1.json b/src/nbi/tests/data/slice/post_sdp_to_network_slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..bd3895fc4ae5a9a0b2059be3f6b31a05451abd22 --- /dev/null +++ b/src/nbi/tests/data/slice/post_sdp_to_network_slice1.json @@ -0,0 +1,61 @@ +{ + "sdp": [ + { + "id": "3", + "node-id": "172.16.61.11", + "sdp-ip-address": [ + "172.16.61.11" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "21" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line2" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC ONT", + "description": "AC connected to PC2", + "ac-node-id": "172.16.61.11", + "ac-tp-id": "200" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/nbi/tests/data/slice/post_sdp_to_network_slice2.json b/src/nbi/tests/data/slice/post_sdp_to_network_slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..0b147125bd7eb3efc84c87bebab919639782f760 --- /dev/null +++ b/src/nbi/tests/data/slice/post_sdp_to_network_slice2.json @@ -0,0 +1,62 @@ +{ + "sdp": [ + { + "id": "3", + "node-id": "172.16.61.11", + "sdp-ip-address": [ + "172.16.61.11" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "31" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line2" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC ONT", + "description": "AC connected to PC2", + "ac-node-id": "172.16.61.11", + "ac-tp-id": "200", + "ac-ipv4-address": "172.16.61.11" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/nbi/tests/test_slice_2.py b/src/nbi/tests/test_slice_2.py new file mode 100644 index 0000000000000000000000000000000000000000..5722e3d922a79bd169fc80a5374c4daab4f5d7d9 --- /dev/null +++ b/src/nbi/tests/test_slice_2.py @@ -0,0 +1,205 @@ +# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 Optional + +from common.proto.context_pb2 import ConfigRule, ServiceConfig, SliceList +from context.client.ContextClient import ContextClient +from nbi.service.rest_server.nbi_plugins.ietf_network_slice.ietf_slice_handler import ( + IETFSliceHandler, +) + + +def get_custom_config_rule( + service_config: ServiceConfig, resource_key: str +) -> Optional[ConfigRule]: + for cr in service_config.config_rules: + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key == resource_key + ): + return cr + + +RUNNING_RESOURCE_KEY = "running_ietf_slice" +CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" + +context_client = ContextClient() + +with open("nbi/tests/data/slice/post_network_slice1.json", mode="r") as f: + post_slice_request = json.load(f) + +with open("nbi/tests/data/slice/post_sdp_to_network_slice1.json", mode="r") as f: + post_sdp_request = json.load(f) + +with open( + "nbi/tests/data/slice/post_connection_group_to_network_slice1.json", mode="r" +) as f: + post_connection_group_request = json.load(f) + +with open( + "nbi/tests/data/slice/post_match_criteria_to_sdp1_in_slice1.json", mode="r" +) as f: + post_match_criteria_request = json.load(f) + +slice_1 = None + + +def select_slice(*args) -> SliceList: + slice_list = SliceList() + slice_list.slices.extend([slice_1]) + return slice_list + + +def test_create_slice(): + global slice_1 + + slice_1 = IETFSliceHandler.create_slice_service(post_slice_request) + candidate_ietf_data = json.loads( + get_custom_config_rule( + slice_1.slice_config, CANDIDATE_RESOURCE_KEY + ).custom.resource_value + ) + assert candidate_ietf_data["network-slice-services"] == post_slice_request + assert slice_1.slice_endpoint_ids[0].device_id.device_uuid.uuid == "172.16.204.220" + assert slice_1.slice_endpoint_ids[1].device_id.device_uuid.uuid == "172.16.61.10" + assert slice_1.slice_id.slice_uuid.uuid == "slice1" + + +def test_create_sdp(monkeypatch): + global slice_1 + + monkeypatch.setattr(context_client, "SelectSlice", select_slice) + + slice_1 = IETFSliceHandler.create_sdp(post_sdp_request, "slice1", context_client) + candidate_ietf_data = json.loads( + get_custom_config_rule( + slice_1.slice_config, CANDIDATE_RESOURCE_KEY + ).custom.resource_value + ) + slice_services = candidate_ietf_data["network-slice-services"]["slice-service"] + slice_service = slice_services[0] + slice_sdps = slice_service["sdps"]["sdp"] + assert len(slice_sdps) == 3 + + +def test_create_connection_group(monkeypatch): + global slice_1 + + monkeypatch.setattr(context_client, "SelectSlice", select_slice) + + slice_1 = IETFSliceHandler.create_connection_group( + post_connection_group_request, "slice1", context_client + ) + candidate_ietf_data = json.loads( + get_custom_config_rule( + slice_1.slice_config, CANDIDATE_RESOURCE_KEY + ).custom.resource_value + ) + slice_services = candidate_ietf_data["network-slice-services"]["slice-service"] + slice_service = slice_services[0] + slice_connection_groups = slice_service["connection-groups"]["connection-group"] + + assert slice_connection_groups[0]["id"] == "line1" + assert slice_connection_groups[1]["id"] == "line2" + assert len(slice_connection_groups) == 2 + + +def test_create_match_criteria(monkeypatch): + global slice_1 + + monkeypatch.setattr(context_client, "SelectSlice", select_slice) + + slice_1 = IETFSliceHandler.create_match_criteria( + post_match_criteria_request, "slice1", "1", context_client + ) + candidate_ietf_data = json.loads( + get_custom_config_rule( + slice_1.slice_config, CANDIDATE_RESOURCE_KEY + ).custom.resource_value + ) + slice_services = candidate_ietf_data["network-slice-services"]["slice-service"] + slice_service = slice_services[0] + slice_sdps = slice_service["sdps"]["sdp"] + sdp1_match_criteria = slice_sdps[0]["service-match-criteria"]["match-criterion"] + + slice_1 = IETFSliceHandler.copy_candidate_ietf_slice_data_to_running("slice1", context_client) + assert len(sdp1_match_criteria) == 2 + assert sdp1_match_criteria[0]["target-connection-group-id"] == "line1" + assert sdp1_match_criteria[1]["target-connection-group-id"] == "line2" + assert slice_1.slice_endpoint_ids[0].device_id.device_uuid.uuid == "172.16.204.220" + assert slice_1.slice_endpoint_ids[1].device_id.device_uuid.uuid == "172.16.61.11" + + +def test_delete_sdp(monkeypatch): + global slice_1 + + monkeypatch.setattr(context_client, "SelectSlice", select_slice) + + slice_1 = IETFSliceHandler.delete_sdp("slice1", "3", context_client) + candidate_ietf_data = json.loads( + get_custom_config_rule( + slice_1.slice_config, CANDIDATE_RESOURCE_KEY + ).custom.resource_value + ) + slice_services = candidate_ietf_data["network-slice-services"]["slice-service"] + slice_service = slice_services[0] + slice_sdps = slice_service["sdps"]["sdp"] + assert len(slice_sdps) == 2 + assert "3" not in (sdp["id"] for sdp in slice_sdps) + + +def test_delete_connection_group(monkeypatch): + global slice_1 + + monkeypatch.setattr(context_client, "SelectSlice", select_slice) + running_ietf_data = json.loads( + get_custom_config_rule( + slice_1.slice_config, RUNNING_RESOURCE_KEY + ).custom.resource_value + ) + slice_1 = IETFSliceHandler.delete_connection_group( + "slice1", "line2", context_client + ) + + candidate_ietf_data = json.loads( + get_custom_config_rule( + slice_1.slice_config, CANDIDATE_RESOURCE_KEY + ).custom.resource_value + ) + slice_services = candidate_ietf_data["network-slice-services"]["slice-service"] + slice_service = slice_services[0] + slice_connection_groups = slice_service["connection-groups"]["connection-group"] + assert len(slice_connection_groups) == 1 + assert slice_connection_groups[0]["id"] == "line1" + + +def test_delete_match_criteria(monkeypatch): + global slice_1 + + monkeypatch.setattr(context_client, "SelectSlice", select_slice) + + slice_1 = IETFSliceHandler.delete_match_criteria("slice1", "1", 2, context_client) + candidate_ietf_data = json.loads( + get_custom_config_rule( + slice_1.slice_config, CANDIDATE_RESOURCE_KEY + ).custom.resource_value + ) + slice_services = candidate_ietf_data["network-slice-services"]["slice-service"] + slice_service = slice_services[0] + slice_sdps = slice_service["sdps"]["sdp"] + sdp1_match_criteria = slice_sdps[0]["service-match-criteria"]["match-criterion"] + assert len(sdp1_match_criteria) == 1 + assert sdp1_match_criteria[0]["target-connection-group-id"] == "line1" diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py index f0775371d0f3fea01411ce4049226377feebc27e..9eac4d353ef48f6e75b478d137e6e9bb9c3e1c03 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py @@ -22,6 +22,8 @@ LOGGER = logging.getLogger(__name__) SETTINGS_RULE_NAME = '/settings' STATIC_ROUTING_RULE_NAME = '/static_routing' +RUNNING_RESOURCE_KEY = "running_ietf_slice" +CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" RE_UUID = re.compile(r'([0-9a-fA-F]{8})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{12})') @@ -93,6 +95,8 @@ def compose_l3nm_config_rules(main_service_config_rules : List, subservice_confi CONFIG_RULES = [ (SETTINGS_RULE_NAME, L3NM_SETTINGS_FIELD_DEFAULTS), (STATIC_ROUTING_RULE_NAME, {}), + (RUNNING_RESOURCE_KEY, {}), + (CANDIDATE_RESOURCE_KEY, {}), ] for rule_name, defaults in CONFIG_RULES: compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py index b08830332f7fc6f526a19516b120e94a1a98b232..db0c552487363e4bb283832c7e40a4e7623e994c 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py @@ -28,6 +28,9 @@ DEVICE_TYPE_TO_DEEPNESS = { DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value : 80, DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER.value : 80, DeviceTypeEnum.IP_SDN_CONTROLLER.value : 80, + DeviceTypeEnum.IETF_SLICE.value : 80, + DeviceTypeEnum.NCE.value : 80, + DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : 70, DeviceTypeEnum.PACKET_ROUTER.value : 70, diff --git a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py index 8230092c2decc0b2c988f63a2677f879f7ec944f..ae567d9d65e4d971930fecb0971672f5bdb1ab73 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py @@ -22,6 +22,7 @@ NETWORK_DEVICE_TYPES = { PACKET_DEVICE_TYPES = { DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + DeviceTypeEnum.IETF_SLICE, DeviceTypeEnum.NCE, DeviceTypeEnum.IP_SDN_CONTROLLER, DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER, DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER, DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH, diff --git a/src/service/requirements.in b/src/service/requirements.in index 3f8d2a35453691420a9469dfffd0a0d2648c6397..d0dd6dcfe13610fc315a50437fb5f3e094b4ee5e 100644 --- a/src/service/requirements.in +++ b/src/service/requirements.in @@ -13,6 +13,7 @@ # limitations under the License. +deepdiff==6.7.* anytree==2.8.0 geopy==2.3.0 netaddr==0.9.0 diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py index 78f084605bcd759825975cb7f11abc659506755b..e47fd635f0c02667a052ebb9cff0569496c5fbec 100644 --- a/src/service/service/service_handler_api/FilterFields.py +++ b/src/service/service/service_handler_api/FilterFields.py @@ -42,8 +42,12 @@ DEVICE_DRIVER_VALUES = { DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_OPTICAL_TFS, DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN, + DeviceDriverEnum.DEVICEDRIVER_NCE, + DeviceDriverEnum.DEVICEDRIVER_IETF_SLICE, + DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN, DeviceDriverEnum.DEVICEDRIVER_OC, DeviceDriverEnum.DEVICEDRIVER_QKD, + DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN, } # Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index f93cf011fe02139ae350b91eab52eb71ded0574d..85545d238f2b93bd77b1beb1fce2d46b01b06800 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -16,11 +16,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 .l3nm_ietfl3vpn.L3NM_IETFL3VPN_ServiceHandler import L3NM_IETFL3VPN_ServiceHandler from .l2nm_openconfig.L2NMOpenConfigServiceHandler import L2NMOpenConfigServiceHandler from .l3nm_emulated.L3NMEmulatedServiceHandler import L3NMEmulatedServiceHandler from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceHandler from .l3nm_gnmi_openconfig.L3NMGnmiOpenConfigServiceHandler import L3NMGnmiOpenConfigServiceHandler from .l3nm_ietf_actn.L3NMIetfActnServiceHandler import L3NMIetfActnServiceHandler +from .l3nm_nce.L3NMNCEServiceHandler import L3NMNCEServiceHandler +from .l3slice_ietfslice.L3SliceIETFSliceServiceHandler import L3NMSliceIETFSliceServiceHandler from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler from .p4.p4_service_handler import P4ServiceHandler from .tapi_tapi.TapiServiceHandler import TapiServiceHandler @@ -66,6 +69,24 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN, } ]), + (L3NM_IETFL3VPN_ServiceHandler, [ + { + FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L3NM, + FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN, + } + ]), + (L3NMNCEServiceHandler, [ + { + FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L3NM, + FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_NCE, + } + ]), + (L3NMSliceIETFSliceServiceHandler, [ + { + FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L3NM, + FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_SLICE, + } + ]), (TapiServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, diff --git a/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py b/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py index 1d6764619e81864e9b048319a087f1a10f17d601..f1b02eab564c6a2eb38f25e4567a2a04ca0156ed 100644 --- a/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py +++ b/src/service/service/service_handlers/l3nm_emulated/ConfigRules.py @@ -43,7 +43,7 @@ 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 - if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) + if_subif_name = '{:s}.{:s}'.format(endpoint_name, str(vlan_id)) json_config_rules = [ json_config_rule_set( @@ -57,7 +57,7 @@ def setup_config_rules( 'name': endpoint_name, 'description': network_interface_desc, 'mtu': mtu, }), json_config_rule_set( - '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_name, sub_interface_index), { + '/interface[{:s}]/subinterface[{:s}]'.format(endpoint_name, str(sub_interface_index)), { 'name': endpoint_name, 'index': sub_interface_index, 'description': network_subinterface_desc, 'vlan_id': vlan_id, 'address_ip': address_ip, 'address_prefix': address_prefix, @@ -163,7 +163,7 @@ def teardown_config_rules( #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 - if_subif_name = '{:s}.{:d}'.format(endpoint_name, vlan_id) + if_subif_name = '{:s}.{:s}'.format(endpoint_name, str(vlan_id)) service_short_uuid = service_uuid.split('-')[-1] network_instance_name = '{:s}-NetInst'.format(service_short_uuid) #network_interface_desc = '{:s}-NetIf'.format(service_uuid) @@ -175,7 +175,7 @@ def teardown_config_rules( 'name': network_instance_name, 'id': if_subif_name, }), json_config_rule_delete( - '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_name, sub_interface_index), { + '/interface[{:s}]/subinterface[{:s}]'.format(endpoint_name, str(sub_interface_index)), { 'name': endpoint_name, 'index': sub_interface_index, }), json_config_rule_delete( diff --git a/src/service/service/service_handlers/l3nm_ietfl3vpn/ConfigRules.py b/src/service/service/service_handlers/l3nm_ietfl3vpn/ConfigRules.py new file mode 100644 index 0000000000000000000000000000000000000000..c5638fc104c253be20ef1bbeb6c69a4392095ad2 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfl3vpn/ConfigRules.py @@ -0,0 +1,316 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 typing import Dict, List, Tuple, TypedDict + +from common.proto.context_pb2 import Link +from common.tools.object_factory.ConfigRule import ( + json_config_rule_delete, + json_config_rule_set, +) +from context.client.ContextClient import ContextClient + + +class LANPrefixesDict(TypedDict): + lan: str + lan_tag: str + + +SITE_NETWORK_ACCESS_TYPE = "ietf-l3vpn-svc:multipoint" + + +def create_site_dict( + site_id: str, + site_location: str, + device_uuid: str, + endpoint_uuid: str, + service_uuid: str, + role: str, + management_type: str, + ce_address: str, + pe_address: str, + ce_pe_network_prefix: int, + mtu: int, + input_bw: int, + output_bw: int, + qos_profile_id: str, + qos_profile_direction: str, + qos_profile_latency: int, + qos_profile_bw_guarantee: int, + lan_prefixes: List[LANPrefixesDict], +) -> Dict: + """ + Helper function that creates a dictionary representing a single 'site' + entry (including management, locations, devices, routing-protocols, and + site-network-accesses). + """ + site_lan_prefixes = [ + { + "lan": lp["lan"], + "lan-tag": lp["lan_tag"], + "next-hop": ce_address, + } + for lp in lan_prefixes + ] + + return { + "site-id": site_id, + "management": {"type": management_type}, + "locations": {"location": [{"location-id": site_location}]}, + "devices": { + "device": [ + { + "device-id": device_uuid, + "location": site_location, + } + ] + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": "ietf-l3vpn-svc:static", + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": site_lan_prefixes + } + }, + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": endpoint_uuid, + "site-network-access-type": SITE_NETWORK_ACCESS_TYPE, + "device-reference": device_uuid, + "vpn-attachment": { + "vpn-id": service_uuid, + "site-role": role, + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": pe_address, + "customer-address": ce_address, + "prefix-length": ce_pe_network_prefix, + }, + } + }, + "service": { + "svc-mtu": mtu, + "svc-input-bandwidth": input_bw, + "svc-output-bandwidth": output_bw, + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "class-id": qos_profile_id, + "direction": qos_profile_direction, + "latency": { + "latency-boundary": qos_profile_latency + }, + "bandwidth": { + "guaranteed-bw-percent": qos_profile_bw_guarantee + }, + } + ] + } + } + }, + }, + } + ] + }, + } + + +def setup_config_rules( + service_uuid: str, json_settings: Dict, operation_type: str +) -> List[Dict]: + # --- Extract common or required fields for the source site --- + src_device_uuid: str = json_settings["src_device_name"] + src_endpoint_uuid: str = json_settings["src_endpoint_name"] + src_site_location: str = json_settings["src_site_location"] + src_ipv4_lan_prefixes: list[LANPrefixesDict] = json_settings.get( + "src_ipv4_lan_prefixes" + ) + src_site_id: str = json_settings.get("src_site_id", f"site_{src_site_location}") + src_management_type: str = json_settings.get( + "src_management_type", "ietf-l3vpn-svc:provider-managed" + ) + if src_management_type != "ietf-l3vpn-svc:provider-managed": + raise Exception("management type %s not supported", src_management_type) + + src_role: str = "ietf-l3vpn-svc:hub-role" + src_ce_address: str = json_settings["src_ce_address"] + src_pe_address: str = json_settings["src_pe_address"] + src_ce_pe_network_prefix: int = json_settings["src_ce_pe_network_prefix"] + src_mtu: int = json_settings["src_mtu"] + src_input_bw: int = json_settings["src_input_bw"] + src_output_bw: int = json_settings["src_output_bw"] + src_qos_profile_id = "qos-realtime" + src_qos_profile_direction = "ietf-l3vpn-svc:both" + src_qos_profile_latency: int = json_settings["src_qos_profile_latency"] + src_qos_profile_bw_guarantee: int = json_settings.get( + "src_qos_profile_bw_guarantee", 100 + ) + + # --- Extract common or required fields for the destination site --- + dst_device_uuid = json_settings["dst_device_name"] + dst_endpoint_uuid = json_settings["dst_endpoint_name"] + dst_site_location: str = json_settings["dst_site_location"] + dst_ipv4_lan_prefixes: list[LANPrefixesDict] = json_settings[ + "dst_ipv4_lan_prefixes" + ] + dst_site_id: str = json_settings.get("dst_site_id", f"site_{dst_site_location}") + dst_management_type: str = json_settings.get( + "dst_management_type", "ietf-l3vpn-svc:provider-managed" + ) + if dst_management_type != "ietf-l3vpn-svc:provider-managed": + raise Exception("management type %s not supported", dst_management_type) + + dst_role: str = "ietf-l3vpn-svc:spoke-role" + dst_ce_address: str = json_settings["dst_ce_address"] + dst_pe_address: str = json_settings["dst_pe_address"] + dst_ce_pe_network_prefix: int = json_settings["dst_ce_pe_network_prefix"] + dst_mtu: int = json_settings["dst_mtu"] + dst_input_bw: int = json_settings["dst_input_bw"] + dst_output_bw: int = json_settings["dst_output_bw"] + dst_qos_profile_id = "qos-realtime" + dst_qos_profile_direction = "ietf-l3vpn-svc:both" + dst_qos_profile_latency: int = json_settings["dst_qos_profile_latency"] + dst_qos_profile_bw_guarantee: int = json_settings.get( + "dst_qos_profile_bw_guarantee", 100 + ) + + # --- Build site dictionaries using the helper function --- + src_site_dict = create_site_dict( + site_id=src_site_id, + site_location=src_site_location, + device_uuid=src_device_uuid, + endpoint_uuid=src_endpoint_uuid, + service_uuid=service_uuid, + role=src_role, + management_type=src_management_type, + ce_address=src_ce_address, + pe_address=src_pe_address, + ce_pe_network_prefix=src_ce_pe_network_prefix, + mtu=src_mtu, + input_bw=src_input_bw, + output_bw=src_output_bw, + qos_profile_id=src_qos_profile_id, + qos_profile_direction=src_qos_profile_direction, + qos_profile_latency=src_qos_profile_latency, + qos_profile_bw_guarantee=src_qos_profile_bw_guarantee, + lan_prefixes=src_ipv4_lan_prefixes, + ) + + dst_site_dict = create_site_dict( + site_id=dst_site_id, + site_location=dst_site_location, + device_uuid=dst_device_uuid, + endpoint_uuid=dst_endpoint_uuid, + service_uuid=service_uuid, + role=dst_role, + management_type=dst_management_type, + ce_address=dst_ce_address, + pe_address=dst_pe_address, + ce_pe_network_prefix=dst_ce_pe_network_prefix, + mtu=dst_mtu, + input_bw=dst_input_bw, + output_bw=dst_output_bw, + qos_profile_id=dst_qos_profile_id, + qos_profile_direction=dst_qos_profile_direction, + qos_profile_latency=dst_qos_profile_latency, + qos_profile_bw_guarantee=dst_qos_profile_bw_guarantee, + lan_prefixes=dst_ipv4_lan_prefixes, + ) + + # --- Combine both sites into one structure --- + sites = { + "site": [ + src_site_dict, + dst_site_dict, + ] + } + + l3_vpn_data_model = { + "ietf-l3vpn-svc:l3vpn-svc": { + "vpn-services": {"vpn-service": [{"vpn-id": service_uuid}]}, + "sites": sites, + } + } + + json_config_rules = [ + json_config_rule_set( + "/service[{:s}]/IETFL3VPN".format(service_uuid), + l3_vpn_data_model, + ), + json_config_rule_set( + "/service[{:s}]/IETFL3VPN/operation".format(service_uuid), + {"type": operation_type}, + ), + ] + + return json_config_rules + + +def teardown_config_rules(service_uuid: str) -> List[Dict]: + json_config_rules = [ + json_config_rule_delete( + "/service[{:s}]/IETFL3VPN".format(service_uuid), + {"id": service_uuid}, + ), + json_config_rule_delete( + "/service[{:s}]/IETFL3VPN/operation".format(service_uuid), + {}, + ), + ] + return json_config_rules + + +def get_link_ep_device_names( + link: Link, context_client: ContextClient +) -> Tuple[str, str, str, str]: + ep_ids = link.link_endpoint_ids + ep_device_id_1 = ep_ids[0].device_id + ep_uuid_1 = ep_ids[0].endpoint_uuid.uuid + device_obj_1 = context_client.GetDevice(ep_device_id_1) + for d_ep in device_obj_1.device_endpoints: + if d_ep.endpoint_id.endpoint_uuid.uuid == ep_uuid_1: + ep_name_1 = d_ep.name + break + else: + raise Exception("endpoint not found") + device_obj_name_1 = device_obj_1.name + ep_device_id_2 = ep_ids[1].device_id + ep_uuid_2 = ep_ids[1].endpoint_uuid.uuid + device_obj_2 = context_client.GetDevice(ep_device_id_2) + for d_ep in device_obj_2.device_endpoints: + if d_ep.endpoint_id.endpoint_uuid.uuid == ep_uuid_2: + ep_name_2 = d_ep.name + break + else: + raise Exception("endpoint not found") + device_obj_name_2 = device_obj_2.name + return ( + device_obj_name_1, + ep_name_1, + device_obj_1, + device_obj_name_2, + ep_name_2, + device_obj_2, + ) diff --git a/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py b/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..aa650c8096b9443b85114166ad1d0bc5b6f2fc55 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py @@ -0,0 +1,546 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import logging +from typing import Any, List, Optional, Tuple, TypedDict, Union + +from deepdiff import DeepDiff + +from common.DeviceTypes import DeviceTypeEnum +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.proto.context_pb2 import ( + ConfigRule, + Device, + DeviceId, + Service, + ServiceConfig, +) +from common.tools.object_factory.Device import json_device_id +from common.type_checkers.Checkers import chk_type +from service.service.service_handler_api._ServiceHandler import _ServiceHandler +from service.service.service_handler_api.SettingsHandler import SettingsHandler +from service.service.service_handler_api.Tools import ( + get_device_endpoint_uuids, + get_endpoint_matching, +) +from service.service.task_scheduler.TaskExecutor import TaskExecutor + +from .ConfigRules import setup_config_rules, teardown_config_rules + +RUNNING_RESOURCE_KEY = "running_ietf_slice" +CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" +MTU = 1500 + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool("Service", "Handler", labels={"handler": "l3nm_ietf_l3vpn"}) + + +class LANPrefixesDict(TypedDict): + lan: str + lan_tag: str + + +class Ipv4Info(TypedDict): + src_lan: str + dst_lan: str + src_port: str + dst_port: str + vlan: str + + +class QoSInfo(TypedDict): + src_qos_profile_latency: int + src_input_bw: int + src_output_bw: int + dst_qos_profile_latency: int + dst_input_bw: int + dst_output_bw: int + + +def get_custom_config_rule( + service_config: ServiceConfig, resource_key: str +) -> Optional[ConfigRule]: + """ + Return the custom ConfigRule from the ServiceConfig matching the given resource_key, + or None if not found. + """ + for cr in service_config.config_rules: + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key == resource_key + ): + return cr + return None + + +def load_json_rule_data(service_config: ServiceConfig) -> Tuple[dict, dict]: + """ + Loads the running/candidate JSON data from the service_config for IETF slice data. + Raises an exception if either is missing. + """ + running_cr = get_custom_config_rule(service_config, RUNNING_RESOURCE_KEY) + candidate_cr = get_custom_config_rule(service_config, CANDIDATE_RESOURCE_KEY) + + if not running_cr or not candidate_cr: + raise ValueError("Missing running/candidate IETF slice config rules.") + + running_data = json.loads(running_cr.custom.resource_value) + candidate_data = json.loads(candidate_cr.custom.resource_value) + return running_data, candidate_data + + +def extract_match_criterion_ipv4_info(match_criterion: dict) -> Ipv4Info: + """ + Extracts IPv4 match criteria data (src/dst IP, ports, VLAN) from a match_criterion dict. + """ + src_lan = dst_lan = src_port = dst_port = vlan = "" + for type_value in match_criterion["match-type"]: + value = type_value["value"][0] + if type_value["type"] == "ietf-network-slice-service:source-ip-prefix": + src_lan = value + elif type_value["type"] == "ietf-network-slice-service:destination-ip-prefix": + dst_lan = value + elif type_value["type"] == "ietf-network-slice-service:source-tcp-port": + src_port = value + elif type_value["type"] == "ietf-network-slice-service:destination-tcp-port": + dst_port = value + elif type_value["type"] == "ietf-network-slice-service:vlan": + vlan = value + + return Ipv4Info( + src_lan=src_lan, + dst_lan=dst_lan, + src_port=src_port, + dst_port=dst_port, + vlan=vlan, + ) + + +def extract_qos_info_from_connection_group( + src_sdp_id: str, dst_sdp_id: str, connectivity_constructs: list +) -> QoSInfo: + """ + Given a pair of SDP ids and a list of connectivity constructs, extract QoS info + such as latency and bandwidth (for both directions). + """ + + def _extract_qos_fields(cc: dict) -> Tuple[int, int]: + max_delay = 0 + bandwidth = 0 + metric_bounds = cc["service-slo-sle-policy"]["slo-policy"]["metric-bound"] + for metric_bound in metric_bounds: + if ( + metric_bound["metric-type"] + == "ietf-network-slice-service:one-way-delay-maximum" + and metric_bound["metric-unit"] == "milliseconds" + ): + max_delay = int(metric_bound["bound"]) + elif ( + metric_bound["metric-type"] + == "ietf-network-slice-service:one-way-bandwidth" + and metric_bound["metric-unit"] == "Mbps" + ): + # Convert from Mbps to bps + bandwidth = int(metric_bound["bound"]) * 1000000 + return max_delay, bandwidth + + src_cc = next( + cc + for cc in connectivity_constructs + if cc["p2p-sender-sdp"] == src_sdp_id and cc["p2p-receiver-sdp"] == dst_sdp_id + ) + dst_cc = next( + cc + for cc in connectivity_constructs + if cc["p2p-sender-sdp"] == dst_sdp_id and cc["p2p-receiver-sdp"] == src_sdp_id + ) + src_max_delay, src_bandwidth = _extract_qos_fields(src_cc) + dst_max_delay, dst_bandwidth = _extract_qos_fields(dst_cc) + + return QoSInfo( + src_qos_profile_latency=src_max_delay, + src_input_bw=src_bandwidth, + src_output_bw=dst_bandwidth, + dst_qos_profile_latency=dst_max_delay, + dst_input_bw=dst_bandwidth, + dst_output_bw=src_bandwidth, + ) + + +def get_endpoint_settings(device_obj: Device, endpoint_name: str) -> dict: + """ + Helper to retrieve endpoint settings from a device's config rules given an endpoint name. + Raises an exception if not found. + """ + for rule in device_obj.device_config.config_rules: + if ( + rule.WhichOneof("config_rule") == "custom" + and rule.custom.resource_key == f"/endpoints/endpoint[{endpoint_name}]" + ): + return json.loads(rule.custom.resource_value) + raise ValueError(f"Endpoint settings not found for endpoint {endpoint_name}") + + +class L3NM_IETFL3VPN_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) + + def __find_IP_transport_edge_endpoints( + self, endpoints + ) -> Tuple[str, str, str, str, Device]: + """ + Searches for two endpoints whose device controllers are IP_SDN_CONTROLLER. + Returns (src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid, controller_device). + Raises an exception if not found or if the two IP devices differ. + """ + + # Find the first IP transport edge endpoint from the head of endpoints + for ep in endpoints: + device_uuid, endpoint_uuid = get_device_endpoint_uuids(ep) + device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(device_uuid)) + ) + device_controller = self.__task_executor.get_device_controller(device_obj) + if device_controller.device_type == DeviceTypeEnum.IP_SDN_CONTROLLER.value: + src_device_uuid, src_endpoint_uuid = device_uuid, endpoint_uuid + src_device_controller = device_controller + break + else: + raise Exception("No IP transport edge endpoints found") + + # Find the second IP transport edge endpoint from the tail of endpoints + for ep in reversed(endpoints): + device_uuid, endpoint_uuid = get_device_endpoint_uuids(ep) + device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(device_uuid)) + ) + device_controller = self.__task_executor.get_device_controller(device_obj) + if device_controller.device_type == DeviceTypeEnum.IP_SDN_CONTROLLER.value: + dst_device_uuid, dst_endpoint_uuid = device_uuid, endpoint_uuid + dst_device_controller = device_controller + break + else: + raise Exception("No IP transport edge endpoints found") + + if src_device_controller != dst_device_controller: + raise Exception("Different Src-Dst devices not supported by now") + + return ( + src_device_uuid, + src_endpoint_uuid, + dst_device_uuid, + dst_endpoint_uuid, + src_device_controller, + ) + + def __build_resource_value_dict( + self, + service_id: str, + src_device_obj: Device, + dst_device_obj: Device, + src_endpoint_name: str, + dst_endpoint_name: str, + qos_info: QoSInfo, + src_endpoint_settings: dict, + dst_endpoint_settings: dict, + src_match_criterion_ipv4_info: Ipv4Info, + dst_match_criterion_ipv4_info: Ipv4Info, + ) -> dict: + """ + Builds the final resource-value dict to be used when calling setup_config_rules(). + """ + # Prepare data for source + src_device_name = src_device_obj.name + src_ce_ip = src_endpoint_settings["address_ip"] + src_ce_prefix = src_endpoint_settings["address_prefix"] + src_lan_prefixes = [ + LANPrefixesDict( + lan=src_match_criterion_ipv4_info["dst_lan"], + lan_tag=src_match_criterion_ipv4_info["vlan"], + ) + ] + + # Prepare data for destination + dst_device_name = dst_device_obj.name + dst_ce_ip = dst_endpoint_settings["address_ip"] + dst_ce_prefix = dst_endpoint_settings["address_prefix"] + dst_lan_prefixes = [ + LANPrefixesDict( + lan=dst_match_criterion_ipv4_info["dst_lan"], + lan_tag=dst_match_criterion_ipv4_info["vlan"], + ) + ] + + return { + "uuid": service_id, + "src_device_name": src_device_name, + "src_endpoint_name": src_endpoint_name, + "src_site_location": src_endpoint_settings["site_location"], + "src_ipv4_lan_prefixes": src_lan_prefixes, + "src_ce_address": src_ce_ip, + "src_pe_address": src_ce_ip, + "src_ce_pe_network_prefix": src_ce_prefix, + "src_mtu": MTU, + "src_qos_profile_latency": qos_info["src_qos_profile_latency"], + "src_input_bw": qos_info["src_input_bw"], + "src_output_bw": qos_info["src_output_bw"], + "dst_device_name": dst_device_name, + "dst_endpoint_name": dst_endpoint_name, + "dst_site_location": dst_endpoint_settings["site_location"], + "dst_ipv4_lan_prefixes": dst_lan_prefixes, + "dst_ce_address": dst_ce_ip, + "dst_pe_address": dst_ce_ip, + "dst_ce_pe_network_prefix": dst_ce_prefix, + "dst_mtu": MTU, + "dst_qos_profile_latency": qos_info["dst_qos_profile_latency"], + "dst_input_bw": qos_info["dst_input_bw"], + "dst_output_bw": qos_info["dst_output_bw"], + } + + @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 [] + + results = [] + service_config = self.__service.service_config + + try: + # Identify IP transport edge endpoints + ( + src_device_uuid, + src_endpoint_uuid, + dst_device_uuid, + dst_endpoint_uuid, + controller, + ) = self.__find_IP_transport_edge_endpoints(endpoints) + + # Retrieve device objects + src_device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(src_device_uuid)) + ) + src_endpoint_obj = get_endpoint_matching(src_device_obj, src_endpoint_uuid) + + dst_device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(dst_device_uuid)) + ) + dst_endpoint_obj = get_endpoint_matching(dst_device_obj, dst_endpoint_uuid) + + # Obtain endpoint settings + src_endpoint_settings = get_endpoint_settings( + src_device_obj, src_endpoint_obj.name + ) + dst_endpoint_settings = get_endpoint_settings( + dst_device_obj, dst_endpoint_obj.name + ) + + # Load running & candidate data, compute diff + running_data, candidate_data = load_json_rule_data(service_config) + running_candidate_diff = DeepDiff(running_data, candidate_data) + + # Determine service_id and operation_type + slice_service = candidate_data["network-slice-services"]["slice-service"][0] + service_id = slice_service["id"] + if not running_candidate_diff: + operation_type = "create" + elif "values_changed" in running_candidate_diff: + operation_type = "update" + + # Parse relevant connectivity data + sdps = slice_service["sdps"]["sdp"] + connection_group = slice_service["connection-groups"]["connection-group"][0] + connecitivity_constructs = connection_group["connectivity-construct"] + + # The code below assumes a single connectivity construct or + # that the relevant one is the first in the list: + connecitivity_construct = connecitivity_constructs[0] + src_sdp_idx = connecitivity_construct["p2p-sender-sdp"] + dst_sdp_idx = connecitivity_construct["p2p-receiver-sdp"] + + # QoS + qos_info = extract_qos_info_from_connection_group( + src_sdp_idx, dst_sdp_idx, connecitivity_constructs + ) + + # Retrieve match-criterion info + src_sdp = next(sdp for sdp in sdps if sdp["id"] == src_sdp_idx) + dst_sdp = next(sdp for sdp in sdps if sdp["id"] == dst_sdp_idx) + + src_match_criterion = src_sdp["service-match-criteria"]["match-criterion"][ + 0 + ] + dst_match_criterion = dst_sdp["service-match-criteria"]["match-criterion"][ + 0 + ] + src_match_criterion_ipv4_info = extract_match_criterion_ipv4_info( + src_match_criterion + ) + dst_match_criterion_ipv4_info = extract_match_criterion_ipv4_info( + dst_match_criterion + ) + + # Build resource dict & config rules + resource_value_dict = self.__build_resource_value_dict( + service_id=service_id, + src_device_obj=src_device_obj, + dst_device_obj=dst_device_obj, + src_endpoint_name=src_endpoint_obj.name, + dst_endpoint_name=dst_endpoint_obj.name, + qos_info=qos_info, + src_endpoint_settings=src_endpoint_settings, + dst_endpoint_settings=dst_endpoint_settings, + src_match_criterion_ipv4_info=src_match_criterion_ipv4_info, + dst_match_criterion_ipv4_info=dst_match_criterion_ipv4_info, + ) + json_config_rules = setup_config_rules( + service_id, resource_value_dict, operation_type + ) + + # Configure device + del controller.device_config.config_rules[:] + for jcr in json_config_rules: + controller.device_config.config_rules.append(ConfigRule(**jcr)) + self.__task_executor.configure_device(controller) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception( + "Unable to SetEndpoint for Service({:s})".format(str(service_id)) + ) + 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_config = self.__service.service_config + ietf_slice_candidate_cr = get_custom_config_rule( + service_config, CANDIDATE_RESOURCE_KEY + ) + candidate_resource_value_dict = json.loads( + ietf_slice_candidate_cr.custom.resource_value + ) + service_id = candidate_resource_value_dict["network-slice-services"][ + "slice-service" + ][0]["id"] + results = [] + try: + src_device_uuid, _ = get_device_endpoint_uuids(endpoints[0]) + src_device = self.__task_executor.get_device( + DeviceId(**json_device_id(src_device_uuid)) + ) + src_controller = self.__task_executor.get_device_controller(src_device) + + dst_device_uuid, _ = get_device_endpoint_uuids(endpoints[1]) + dst_device = self.__task_executor.get_device( + DeviceId(**json_device_id(dst_device_uuid)) + ) + dst_controller = self.__task_executor.get_device_controller(dst_device) + if ( + src_controller.device_id.device_uuid.uuid + != dst_controller.device_id.device_uuid.uuid + ): + raise Exception("Different Src-Dst devices not supported by now") + controller = src_controller + json_config_rules = teardown_config_rules(service_id) + del controller.device_config.config_rules[:] + for jcr in json_config_rules: + controller.device_config.config_rules.append(ConfigRule(**jcr)) + 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_id)) + ) + 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/l3nm_ietfl3vpn/__init__.py b/src/service/service/service_handlers/l3nm_ietfl3vpn/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..906dd19f3c948b03263251f60addb49e2fb522dc --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfl3vpn/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/service/service/service_handlers/l3nm_nce/ConfigRules.py b/src/service/service/service_handlers/l3nm_nce/ConfigRules.py new file mode 100644 index 0000000000000000000000000000000000000000..0544d897606afe950725349bfeb68c365189aa21 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_nce/ConfigRules.py @@ -0,0 +1,120 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 typing import Dict, List + +from common.tools.object_factory.ConfigRule import ( + json_config_rule_delete, + json_config_rule_set, +) + + +def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: + operation_type: str = json_settings["operation_type"] + app_flow_id: str = json_settings["app_flow_id"] + app_flow_user_id: str = json_settings["app_flow_user_id"] + max_latency: int = json_settings["max_latency"] + max_jitter: int = json_settings["max_jitter"] + max_loss: float = json_settings["max_loss"] + upstream_assure_bw: str = json_settings["upstream_assure_bw"] + upstream_max_bw: str = json_settings["upstream_max_bw"] + downstream_assure_bw: str = json_settings["downstream_assure_bw"] + downstream_max_bw: str = json_settings["downstream_max_bw"] + src_ip: str = json_settings["src_ip"] + src_port: str = json_settings["src_port"] + dst_ip: str = json_settings["dst_ip"] + dst_port: str = json_settings["dst_port"] + + app_flow_app_name: str = f"App_Flow_{app_flow_id}" + app_flow_service_profile: str = f"service_{app_flow_id}" + app_id: str = f"app_{app_flow_id}" + app_feature_id: str = f"feature_{app_flow_id}" + app_flow_name: str = f"App_Flow_{app_flow_id}" + app_flow_max_online_users: int = json_settings.get("app_flow_max_online_users", 1) + app_flow_stas: str = json_settings.get("stas", "00:3D:E1:18:82:9E") + qos_profile_name: str = json_settings.get("app_flow_qos_profile", "AR_VR_Gaming") + app_flow_duration: int = json_settings.get("app_flow_duration", 9999) + protocol: str = json_settings.get("protocol", "tcp") + + app_flow = { + "name": app_flow_name, + "user-id": app_flow_user_id, + "app-name": app_flow_app_name, + "max-online-users": app_flow_max_online_users, + "stas": app_flow_stas, + "qos-profile": qos_profile_name, + "service-profile": app_flow_service_profile, + "duration": app_flow_duration, + } + qos_profile = { + "name": qos_profile_name, + "max-latency": max_latency, + "max-jitter": max_jitter, + "max-loss": max_loss, + "upstream": { + "assure-bandwidth": upstream_assure_bw, + "max-bandwidth": upstream_max_bw, + }, + "downstream": { + "assure-bandwidth": downstream_assure_bw, + "max-bandwidth": downstream_max_bw, + }, + } + application = { + "name": app_flow_app_name, + "app-id": app_id, + "app-features": { + "app-feature": [ + { + "id": app_feature_id, + "dest-ip": dst_ip, + "dest-port": dst_port, + "src-ip": src_ip, + "src-port": src_port, + "protocol": protocol, + } + ] + }, + } + app_flow_datamodel = { + "huawei-nce-app-flow:app-flows": { + "app-flow": [app_flow], + "qos-profiles": {"qos-profile": [qos_profile]}, + "applications": {"application": [application]}, + } + } + json_config_rules = [ + json_config_rule_set( + "/service[{:s}]/AppFlow".format(service_uuid), app_flow_datamodel + ), + json_config_rule_set( + "/service[{:s}]/AppFlow/operation".format(service_uuid), + {"type": operation_type}, + ), + ] + return json_config_rules + + +def teardown_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: + json_config_rules = [ + json_config_rule_delete( + "/service[{:s}]/AppFlow".format(service_uuid), + {}, + ), + json_config_rule_delete( + "/service[{:s}]/AppFlow/operation".format(service_uuid), + {}, + ), + ] + return json_config_rules diff --git a/src/service/service/service_handlers/l3nm_nce/L3NMNCEServiceHandler.py b/src/service/service/service_handlers/l3nm_nce/L3NMNCEServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..1317bd0615e4789d7ba76e8c0c6b0923d8f2dec7 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_nce/L3NMNCEServiceHandler.py @@ -0,0 +1,564 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import logging +import re +from typing import Any, List, Optional, Tuple, Union, TypedDict, Dict +from uuid import uuid4 + +from deepdiff import DeepDiff + +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.proto.context_pb2 import ConfigRule, DeviceId, Empty, Service, ServiceConfig +from common.tools.object_factory.Device import json_device_id +from common.type_checkers.Checkers import chk_type +from context.client.ContextClient import ContextClient +from service.service.service_handler_api._ServiceHandler import _ServiceHandler +from service.service.service_handler_api.SettingsHandler import SettingsHandler +from service.service.service_handler_api.Tools import ( + get_device_endpoint_uuids, +) +from service.service.task_scheduler.TaskExecutor import TaskExecutor + +from .ConfigRules import setup_config_rules, teardown_config_rules + +RUNNING_RESOURCE_KEY = "running_ietf_slice" +CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool("Service", "Handler", labels={"handler": "l3nm_nce"}) + +SDP_DIFF_RE = re.compile( + r"^root\[\'network-slice-services\'\]\[\'slice-service\'\]\[0\]\[\'sdps\'\]\[\'sdp\'\]\[(\d)\]$" +) +CONNECTION_GROUP_DIFF_RE = re.compile( + r"^root\[\'network-slice-services\'\]\[\'slice-service\'\]\[0\]\[\'connection-groups\'\]\[\'connection-group\'\]\[(\d)\]$" +) +MATCH_CRITERION_DIFF_RE = re.compile( + r"^root\[\'network-slice-services\'\]\[\'slice-service\'\]\[0\]\[\'sdps\'\]\[\'sdp\'\]\[(\d)\]\[\'service-match-criteria\'\]\[\'match-criterion\'\]\[(\d)\]$" +) + + +class Ipv4Info(TypedDict): + src_ip: str + dst_ip: str + src_port: str + dst_port: str + + +def get_removed_items( + candidate_ietf_slice_dict: dict, running_ietf_slice_dict: dict +) -> dict: + """ + For the 'iterable_item_removed' scenario, returns dict with removed sdp / connection_group / match_criterion info. + Raises an exception if there's inconsistent data or multiple items removed (which is not supported). + """ + removed_items = { + "sdp": {"sdp_idx": None, "value": {}}, + "connection_group": {"connection_group_idx": None, "value": {}}, + "match_criterion": { + "sdp_idx": None, + "match_criterion_idx": None, + "value": {}, + }, + } + + running_slice_services = running_ietf_slice_dict["network-slice-services"][ + "slice-service" + ][0] + candidate_slice_services = candidate_ietf_slice_dict["network-slice-services"][ + "slice-service" + ][0] + + running_slice_sdps = [sdp["id"] for sdp in running_slice_services["sdps"]["sdp"]] + candidiate_slice_sdps = [ + sdp["id"] for sdp in candidate_slice_services["sdps"]["sdp"] + ] + removed_sdps = set(running_slice_sdps) - set(candidiate_slice_sdps) + + if len(removed_sdps) > 1: + raise Exception("Multiple SDPs removed - not supported.") + removed_sdp_id = removed_sdps.pop() + + removed_items["sdp"]["sdp_idx"] = running_slice_sdps.index(removed_sdp_id) + removed_items["sdp"]["value"] = next( + sdp + for sdp in running_slice_services["sdps"]["sdp"] + if sdp["id"] == removed_sdp_id + ) + + match_criteria = removed_items["sdp"]["value"]["service-match-criteria"][ + "match-criterion" + ] + if len(match_criteria) > 1: + raise Exception("Multiple match criteria found - not supported") + match_criterion = match_criteria[0] + connection_grp_id = match_criterion["target-connection-group-id"] + connection_groups = running_slice_services["connection-groups"]["connection-group"] + connection_group = next( + (idx, cg) + for idx, cg in enumerate(connection_groups) + if cg["id"] == connection_grp_id + ) + removed_items["connection_group"]["connection_group_idx"] = connection_group[0] + removed_items["connection_group"]["value"] = connection_group[1] + + for sdp in running_slice_services["sdps"]["sdp"]: + if sdp["id"] == removed_sdp_id: + continue + for mc in sdp["service-match-criteria"]["match-criterion"]: + if mc["target-connection-group-id"] == connection_grp_id: + removed_items["match_criterion"]["sdp_idx"] = running_slice_sdps.index( + sdp["id"] + ) + removed_items["match_criterion"]["match_criterion_idx"] = sdp[ + "service-match-criteria" + ]["match-criterion"].index(mc) + removed_items["match_criterion"]["value"] = mc + break + + if ( + removed_items["match_criterion"]["sdp_idx"] is None + or removed_items["sdp"]["sdp_idx"] is None + or removed_items["connection_group"]["connection_group_idx"] is None + ): + raise Exception("sdp, connection group or match criterion not found") + + return removed_items + + +def get_custom_config_rule( + service_config: ServiceConfig, resource_key: str +) -> Optional[ConfigRule]: + """ + Returns the ConfigRule from service_config matching the provided resource_key + if found, otherwise returns None. + """ + for cr in service_config.config_rules: + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key == resource_key + ): + return cr + return None + + +def get_running_candidate_ietf_slice_data_diff(service_config: ServiceConfig) -> Dict: + """ + Loads the JSON from the running/candidate resource ConfigRules and returns + their DeepDiff comparison. + """ + running_cr = get_custom_config_rule(service_config, RUNNING_RESOURCE_KEY) + candidate_cr = get_custom_config_rule(service_config, CANDIDATE_RESOURCE_KEY) + + running_value_dict = json.loads(running_cr.custom.resource_value) + candidate_value_dict = json.loads(candidate_cr.custom.resource_value) + + return DeepDiff(running_value_dict, candidate_value_dict) + + +def extract_qos_info( + connection_groups: List, connection_grp_id: str, src_sdp_idx: str, dst_sdp_idx: str +) -> Dict: + """ + Extract QoS information from connection groups based on the connection group ID. + """ + qos_info = { + "upstream": {"max_delay": "0", "bw": "0", "packet_loss": "0"}, + "downstream": {"max_delay": "0", "bw": "0", "packet_loss": "0"}, + } + connection_group = next( + (cg for cg in connection_groups if cg["id"] == connection_grp_id), None + ) + + if not connection_group: + return qos_info + + for cc in connection_group["connectivity-construct"]: + if ( + cc["p2p-sender-sdp"] == src_sdp_idx + and cc["p2p-receiver-sdp"] == dst_sdp_idx + ): + direction = "upstream" + elif ( + cc["p2p-sender-sdp"] == dst_sdp_idx + and cc["p2p-receiver-sdp"] == src_sdp_idx + ): + direction = "downstream" + else: + raise Exception("invalid sender and receiver sdp ids") + for metric_bound in cc["service-slo-sle-policy"]["slo-policy"]["metric-bound"]: + if ( + metric_bound["metric-type"] + == "ietf-network-slice-service:one-way-delay-maximum" + and metric_bound["metric-unit"] == "milliseconds" + ): + qos_info[direction]["max_delay"] = metric_bound["bound"] + elif ( + metric_bound["metric-type"] + == "ietf-network-slice-service:one-way-bandwidth" + and metric_bound["metric-unit"] == "Mbps" + ): + qos_info[direction]["bw"] = metric_bound["bound"] + elif ( + metric_bound["metric-type"] + == "ietf-network-slice-service:two-way-packet-loss" + and metric_bound["metric-unit"] == "percentage" + ): + qos_info[direction]["packet_loss"] = metric_bound["percentile-value"] + + return qos_info + + +def extract_match_criterion_ipv4_info(match_criterion: Dict) -> Ipv4Info: + """ + Extracts IPv4 info from the match criterion dictionary. + """ + src_ip = dst_ip = src_port = dst_port = "" + + for type_value in match_criterion["match-type"]: + m_type = type_value["type"] + val = type_value["value"][0] + if m_type == "ietf-network-slice-service:source-ip-prefix": + src_ip = val.split("/")[0] + elif m_type == "ietf-network-slice-service:destination-ip-prefix": + dst_ip = val.split("/")[0] + elif m_type == "ietf-network-slice-service:source-tcp-port": + src_port = val + elif m_type == "ietf-network-slice-service:destination-tcp-port": + dst_port = val + + return Ipv4Info( + src_ip=src_ip, + dst_ip=dst_ip, + src_port=src_port, + dst_port=dst_port, + ) + + +class L3NMNCEServiceHandler(_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) == 0: + return [] + + results = [] + try: + context_client = ContextClient() + service_config = self.__service.service_config + settings = self.__settings_handler.get("/settings") + + src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0]) + src_device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(src_device_uuid)) + ) + controller = self.__task_executor.get_device_controller(src_device_obj) + + list_devices = context_client.ListDevices(Empty()) + devices = list_devices.devices + device_name_map = {d.name: d for d in devices} + + running_candidate_diff = get_running_candidate_ietf_slice_data_diff( + service_config + ) + candidate_ietf_slice_cr = get_custom_config_rule( + service_config, CANDIDATE_RESOURCE_KEY + ) + candidate_resource_value_dict = json.loads( + candidate_ietf_slice_cr.custom.resource_value + ) + running_ietf_slice_cr = get_custom_config_rule( + service_config, RUNNING_RESOURCE_KEY + ) + running_resource_value_dict = json.loads( + running_ietf_slice_cr.custom.resource_value + ) + + service_name = running_resource_value_dict["network-slice-services"][ + "slice-service" + ][0]["id"] + + if not running_candidate_diff: # Slice Creation + operation_type = "create" + + slice_service = candidate_resource_value_dict["network-slice-services"][ + "slice-service" + ][0] + sdps = slice_service["sdps"]["sdp"] + connection_groups = slice_service["connection-groups"][ + "connection-group" + ] + sdp_ids = [sdp["id"] for sdp in sdps] + for sdp in sdps: + node_id = sdp["node-id"] + device_obj = device_name_map[node_id] + device_controller = self.__task_executor.get_device_controller( + device_obj + ) + if ( + device_controller is None + or controller.name != device_controller.name + ): + continue + src_sdp_idx = sdp_ids.pop(sdp_ids.index(sdp["id"])) + dst_sdp_idx = sdp_ids[0] + match_criteria = sdp["service-match-criteria"]["match-criterion"] + match_criterion = match_criteria[0] + connection_grp_id = match_criterion["target-connection-group-id"] + break + else: + raise Exception("connection group id not found") + elif "iterable_item_added" in running_candidate_diff: # new SDP added + operation_type = "create" + + slice_service = candidate_resource_value_dict["network-slice-services"][ + "slice-service" + ][0] + sdps = slice_service["sdps"]["sdp"] + connection_groups = slice_service["connection-groups"][ + "connection-group" + ] + added_items = { + "sdp": {"sdp_idx": None, "value": {}}, + "connection_group": {"connection_group_idx": None, "value": {}}, + "match_criterion": { + "sdp_idx": None, + "match_criterion_idx": None, + "value": {}, + }, + } + for added_key, added_value in running_candidate_diff[ + "iterable_item_added" + ].items(): + sdp_match = SDP_DIFF_RE.match(added_key) + connection_group_match = CONNECTION_GROUP_DIFF_RE.match(added_key) + match_criterion_match = MATCH_CRITERION_DIFF_RE.match(added_key) + if sdp_match: + added_items["sdp"] = { + "sdp_idx": int(sdp_match.groups()[0]), + "value": added_value, + } + elif connection_group_match: + added_items["connection_group"] = { + "connection_group_idx": int( + connection_group_match.groups()[0] + ), + "value": added_value, + } + elif match_criterion_match: + added_items["match_criterion"] = { + "sdp_idx": int(match_criterion_match.groups()[0]), + "match_criterion_idx": int( + match_criterion_match.groups()[1] + ), + "value": added_value, + } + new_sdp = sdps[added_items["sdp"]["sdp_idx"]] + src_sdp_idx = new_sdp["id"] + dst_sdp_idx = sdps[added_items["match_criterion"]["sdp_idx"]]["id"] + connection_grp_id = connection_groups[ + added_items["connection_group"]["connection_group_idx"] + ]["id"] + + if ( + connection_grp_id + != added_items["match_criterion"]["value"][ + "target-connection-group-id" + ] + ): + raise Exception( + "connection group missmatch in destination sdp and added connection group" + ) + match_criteria = new_sdp["service-match-criteria"]["match-criterion"] + match_criterion = match_criteria[0] + elif "iterable_item_removed" in running_candidate_diff: # new SDP added + operation_type = "delete" + + slice_service = running_resource_value_dict["network-slice-services"][ + "slice-service" + ][0] + sdps = slice_service["sdps"]["sdp"] + connection_groups = slice_service["connection-groups"][ + "connection-group" + ] + removed_items = get_removed_items( + candidate_resource_value_dict, running_resource_value_dict + ) + removed_sdp = sdps[removed_items["sdp"]["sdp_idx"]] + src_sdp_idx = removed_sdp["id"] + dst_sdp_idx = sdps[removed_items["match_criterion"]["sdp_idx"]]["id"] + connection_grp_id = connection_groups[ + removed_items["connection_group"]["connection_group_idx"] + ]["id"] + + if ( + connection_grp_id + != removed_items["match_criterion"]["value"][ + "target-connection-group-id" + ] + ): + raise Exception( + "connection group missmatch in destination sdp and added connection group" + ) + match_criteria = removed_sdp["service-match-criteria"][ + "match-criterion" + ] + match_criterion = match_criteria[0] + else: + raise Exception( + "transition from candidate to running info not supported" + ) + + ip_info = extract_match_criterion_ipv4_info(match_criterion) + + qos_info = extract_qos_info( + connection_groups, connection_grp_id, src_sdp_idx, dst_sdp_idx + ) + + resource_value_dict = { + "uuid": service_name, + "operation_type": operation_type, + "app_flow_id": f"{src_sdp_idx}_{dst_sdp_idx}_{service_name}", + "app_flow_user_id": str(uuid4()), + "max_latency": int(qos_info["upstream"]["max_delay"]), + "max_jitter": 10, + "max_loss": float(qos_info["upstream"]["packet_loss"]), + "upstream_assure_bw": int(qos_info["upstream"]["bw"]) * 1e6, + "upstream_max_bw": 2 * int(qos_info["upstream"]["bw"]) * 1e6, + "downstream_assure_bw": int(qos_info["downstream"]["bw"]) * 1e6, + "downstream_max_bw": 2 * int(qos_info["downstream"]["bw"]) * 1e6, + "src_ip": ip_info["src_ip"], + "src_port": ip_info["src_port"], + "dst_ip": ip_info["dst_ip"], + "dst_port": ip_info["dst_port"], + } + json_config_rules = setup_config_rules(service_name, resource_value_dict) + + del controller.device_config.config_rules[:] + for jcr in json_config_rules: + controller.device_config.config_rules.append(ConfigRule(**jcr)) + + self.__task_executor.configure_device(controller) + LOGGER.debug('Configured device "{:s}"'.format(controller.name)) + + except Exception as e: # pylint: disable=broad-except + 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) == 0: + return [] + service_uuid = self.__service.service_id.service_uuid.uuid + results = [] + try: + src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0]) + src_device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(src_device_uuid)) + ) + controller = self.__task_executor.get_device_controller(src_device_obj) + json_config_rules = teardown_config_rules(service_uuid, {}) + if len(json_config_rules) > 0: + del controller.device_config.config_rules[:] + for json_config_rule in json_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 + 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/l3nm_nce/__init__.py b/src/service/service/service_handlers/l3nm_nce/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_nce/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/service/service/service_handlers/l3slice_ietfslice/ConfigRules.py b/src/service/service/service_handlers/l3slice_ietfslice/ConfigRules.py new file mode 100644 index 0000000000000000000000000000000000000000..173d4ba10dbf1f6a8aead912a2a1435632f94569 --- /dev/null +++ b/src/service/service/service_handlers/l3slice_ietfslice/ConfigRules.py @@ -0,0 +1,301 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 typing import Dict, List, Tuple + +from common.proto.context_pb2 import Link +from common.tools.object_factory.ConfigRule import ( + json_config_rule_delete, + json_config_rule_set, +) +from context.client.ContextClient import ContextClient + + +def build_match_criterion( + vlan: str, + src_ip: str, + src_port: str, + dst_ip: str, + dst_port: str, + target_conn_group_id: str = "line1", + index: int = 1, +) -> Dict: + """ + Build the match-criterion structure used in the 'service-match-criteria'. + """ + return { + "index": index, + "match-type": [ + {"type": "ietf-network-slice-service:vlan", "value": [vlan]}, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [src_ip], + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [src_port], + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [dst_ip], + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [dst_port], + }, + ], + "target-connection-group-id": target_conn_group_id, + } + + +def build_sdp( + sdp_id: str, + node_id: str, + mgmt_ip: str, + ac_node_id: str, + ac_ep_id: str, + match_criterion: Dict, + attachment_id: str = "0", + attachment_description: str = "dsc", +) -> Dict: + """ + Build the sdp structure used in the 'slice_service' dictionary. + """ + return { + "id": sdp_id, + "node-id": node_id, + "sdp-ip-address": [mgmt_ip], + "service-match-criteria": {"match-criterion": [match_criterion]}, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": attachment_id, + "description": attachment_description, + "ac-node-id": ac_node_id, + "ac-tp-id": ac_ep_id, + } + ] + }, + } + + +def build_slo_policy_bound( + one_way_delay: int, one_way_bandwidth: int, one_way_packet_loss: float +) -> List[Dict]: + """ + Build the 'metric-bound' portion of the 'slo-policy' dictionary. + """ + return [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": one_way_delay, + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": one_way_bandwidth, + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": one_way_packet_loss, + }, + ] + + +def _get_device_endpoint_name(device_obj, endpoint_uuid: str) -> str: + """ + Given a device object and an endpoint UUID, return the device endpoint name. + Raises an exception if not found. + """ + for d_ep in device_obj.device_endpoints: + if d_ep.endpoint_id.endpoint_uuid.uuid == endpoint_uuid: + return d_ep.name + raise Exception("Endpoint not found") + + +def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: + operation_type: str = json_settings["operation_type"] + + # Source parameters + src_node_id: str = json_settings["src_node_id"] + src_mgmt_ip_address: str = json_settings["src_mgmt_ip_address"] + src_ac_node_id: str = json_settings["src_ac_node_id"] + src_ac_ep_id: str = json_settings["src_ac_ep_id"] + src_vlan: str = json_settings["src_vlan"] + src_source_ip_prefix: str = json_settings["src_source_ip_prefix"] + src_source_tcp_port: str = json_settings["src_source_tcp_port"] + src_destination_ip_prefix: str = json_settings["src_destination_ip_prefix"] + src_destination_tcp_port: str = json_settings["src_destination_tcp_port"] + source_one_way_delay: int = int(json_settings["source_one_way_delay"]) + source_one_way_bandwidth: int = int(json_settings["source_one_way_bandwidth"]) + source_one_way_packet_loss: float = float( + json_settings["source_one_way_packet_loss"] + ) + + # Destination parameters + dst_node_id: str = json_settings["dst_node_id"] + dst_mgmt_ip_address: str = json_settings["dst_mgmt_ip_address"] + dst_ac_node_id: str = json_settings["dst_ac_node_id"] + dst_ac_ep_id: str = json_settings["dst_ac_ep_id"] + dst_vlan: str = json_settings["dst_vlan"] + dst_source_ip_prefix: str = json_settings["dst_source_ip_prefix"] + dst_source_tcp_port: str = json_settings["dst_source_tcp_port"] + dst_destination_ip_prefix: str = json_settings["dst_destination_ip_prefix"] + dst_destination_tcp_port: str = json_settings["dst_destination_tcp_port"] + destination_one_way_delay: int = int(json_settings["destination_one_way_delay"]) + destination_one_way_bandwidth: int = int( + json_settings["destination_one_way_bandwidth"] + ) + destination_one_way_packet_loss: float = float( + json_settings["destination_one_way_packet_loss"] + ) + + # Slice ID + slice_id: str = json_settings["slice_id"] + + # build source & destination match criteria + src_match_criterion = build_match_criterion( + vlan=src_vlan, + src_ip=src_source_ip_prefix, + src_port=src_source_tcp_port, + dst_ip=src_destination_ip_prefix, + dst_port=src_destination_tcp_port, + ) + dst_match_criterion = build_match_criterion( + vlan=dst_vlan, + src_ip=dst_source_ip_prefix, + src_port=dst_source_tcp_port, + dst_ip=dst_destination_ip_prefix, + dst_port=dst_destination_tcp_port, + ) + + # Build SDPs + sdp_src = build_sdp( + sdp_id="1", + node_id=src_node_id, + mgmt_ip=src_mgmt_ip_address, + ac_node_id=src_ac_node_id, + ac_ep_id=src_ac_ep_id, + match_criterion=src_match_criterion, + ) + sdp_dst = build_sdp( + sdp_id="2", + node_id=dst_node_id, + mgmt_ip=dst_mgmt_ip_address, + ac_node_id=dst_ac_node_id, + ac_ep_id=dst_ac_ep_id, + match_criterion=dst_match_criterion, + ) + + sdps = [sdp_src, sdp_dst] + + # Build connection-groups + connection_groups = [ + { + "id": "line1", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": build_slo_policy_bound( + one_way_delay=source_one_way_delay, + one_way_bandwidth=source_one_way_bandwidth, + one_way_packet_loss=source_one_way_packet_loss, + ) + } + }, + }, + { + "id": 2, + "p2p-sender-sdp": "2", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": build_slo_policy_bound( + one_way_delay=destination_one_way_delay, + one_way_bandwidth=destination_one_way_bandwidth, + one_way_packet_loss=destination_one_way_packet_loss, + ) + } + }, + }, + ], + } + ] + + slice_service = { + "id": slice_id, + "description": "dsc", + "sdps": {"sdp": sdps}, + "connection-groups": {"connection-group": connection_groups}, + } + slice_data_model = {"network-slice-services": {"slice-service": [slice_service]}} + + json_config_rules = [ + json_config_rule_set( + "/service[{:s}]/IETFSlice".format(service_uuid), + slice_data_model, + ), + json_config_rule_set( + "/service[{:s}]/IETFSlice/operation".format(service_uuid), + {"type": operation_type}, + ), + ] + return json_config_rules + + +def teardown_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: + json_config_rules = [ + json_config_rule_delete( + "/service[{:s}]/IETFSlice".format(service_uuid), + {}, + ), + json_config_rule_delete( + "/service[{:s}]/IETFSlice/operation".format(service_uuid), + {}, + ), + ] + return json_config_rules + + +def get_link_ep_device_names( + link: Link, context_client: ContextClient +) -> Tuple[str, str, str, str]: + ep_ids = link.link_endpoint_ids + ep_device_id_1 = ep_ids[0].device_id + ep_uuid_1 = ep_ids[0].endpoint_uuid.uuid + device_obj_1 = context_client.GetDevice(ep_device_id_1) + ep_name_1 = _get_device_endpoint_name(device_obj_1, ep_uuid_1) + device_obj_name_1 = device_obj_1.name + + ep_device_id_2 = ep_ids[1].device_id + ep_uuid_2 = ep_ids[1].endpoint_uuid.uuid + device_obj_2 = context_client.GetDevice(ep_device_id_2) + ep_name_2 = _get_device_endpoint_name(device_obj_2, ep_uuid_2) + device_obj_name_2 = device_obj_2.name + + return ( + device_obj_name_1, + ep_name_1, + device_obj_1, + device_obj_name_2, + ep_name_2, + device_obj_2, + ) diff --git a/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py b/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..0df8b56e3495dcf70dcfd78b8e3ea83bef93dc46 --- /dev/null +++ b/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py @@ -0,0 +1,959 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import logging +import re +from typing import Any, Dict, List, Optional, Tuple, TypedDict, Union + +from deepdiff import DeepDiff +from dataclasses import dataclass + +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.proto.context_pb2 import ConfigRule, DeviceId, Empty, Service, ServiceConfig +from common.tools.object_factory.Device import json_device_id +from common.type_checkers.Checkers import chk_type +from context.client.ContextClient import ContextClient +from service.service.service_handler_api._ServiceHandler import _ServiceHandler +from service.service.service_handler_api.SettingsHandler import SettingsHandler +from service.service.service_handler_api.Tools import ( + get_device_endpoint_uuids, +) +from service.service.task_scheduler.TaskExecutor import TaskExecutor + +from .ConfigRules import ( + get_link_ep_device_names, + setup_config_rules, + teardown_config_rules, +) + +RUNNING_RESOURCE_KEY = "running_ietf_slice" +CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" + +SDP_DIFF_RE = re.compile( + r"^root\[\'network-slice-services\'\]\[\'slice-service\'\]\[0\]\[\'sdps\'\]\[\'sdp\'\]\[(\d)\]$" +) +CONNECTION_GROUP_DIFF_RE = re.compile( + r"^root\[\'network-slice-services\'\]\[\'slice-service\'\]\[0\]\[\'connection-groups\'\]\[\'connection-group\'\]\[(\d)\]$" +) +MATCH_CRITERION_DIFF_RE = re.compile( + r"^root\[\'network-slice-services\'\]\[\'slice-service\'\]\[0\]\[\'sdps\'\]\[\'sdp\'\]\[(\d)\]\[\'service-match-criteria\'\]\[\'match-criterion\'\]\[(\d)\]$" +) + +RE_GET_ENDPOINT_FROM_INTERFACE = re.compile(r"^\/interface\[([^\]]+)\].*") + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool( + "Service", "Handler", labels={"handler": "l3slice_ietfslice"} +) + + +RAISE_IF_DIFFERS = True + + +class Ipv4Info(TypedDict): + src_lan: str + dst_lan: str + src_port: str + dst_port: str + vlan: str + + +class DeviceEpInfo(TypedDict): + ipv4_info: Ipv4Info + node_name: str + endpoint_name: str + one_way_delay: int + one_way_bandwidth: int + one_way_packet_loss: float + + +@dataclass +class ConnectivityConstructInfo: + bandwidth: int = 0 + delay: int = 0 + packet_loss: float = 0.0 + + +def get_custom_config_rule( + service_config: ServiceConfig, resource_key: str +) -> Optional[ConfigRule]: + """ + Returns the ConfigRule from service_config matching the provided resource_key + if found, otherwise returns None. + """ + for cr in service_config.config_rules: + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key == resource_key + ): + return cr + return None + + +def get_running_candidate_ietf_slice_data_diff(service_config: ServiceConfig) -> Dict: + """ + Loads the JSON from the running/candidate resource ConfigRules and returns + their DeepDiff comparison. + """ + running_cr = get_custom_config_rule(service_config, RUNNING_RESOURCE_KEY) + candidate_cr = get_custom_config_rule(service_config, CANDIDATE_RESOURCE_KEY) + running_value_dict = json.loads(running_cr.custom.resource_value) + candidate_value_dict = json.loads(candidate_cr.custom.resource_value) + return DeepDiff(running_value_dict, candidate_value_dict) + + +def extract_match_criterion_ipv4_info(match_criterion: Dict) -> Ipv4Info: + """ + Extracts IPv4 info from the match criterion dictionary. + """ + src_lan = dst_lan = src_port = dst_port = vlan = "" + for type_value in match_criterion["match-type"]: + m_type = type_value["type"] + val = type_value["value"][0] + if m_type == "ietf-network-slice-service:source-ip-prefix": + src_lan = val + elif m_type == "ietf-network-slice-service:destination-ip-prefix": + dst_lan = val + elif m_type == "ietf-network-slice-service:source-tcp-port": + src_port = val + elif m_type == "ietf-network-slice-service:destination-tcp-port": + dst_port = val + elif m_type == "ietf-network-slice-service:vlan": + vlan = val + return Ipv4Info( + src_lan=src_lan, + dst_lan=dst_lan, + src_port=src_port, + dst_port=dst_port, + vlan=vlan, + ) + + +def get_removed_items( + candidate_ietf_slice_dict: dict, running_ietf_slice_dict: dict +) -> dict: + """ + For the 'iterable_item_removed' scenario, returns dict with removed sdp / connection_group / match_criterion info. + Raises an exception if there's inconsistent data or multiple items removed (which is not supported). + """ + removed_items = { + "sdp": {"sdp_idx": None, "value": {}}, + "connection_group": {"connection_group_idx": None, "value": {}}, + "match_criterion": { + "sdp_idx": None, + "match_criterion_idx": None, + "value": {}, + }, + } + + running_slice_services = running_ietf_slice_dict["network-slice-services"][ + "slice-service" + ][0] + candidate_slice_services = candidate_ietf_slice_dict["network-slice-services"][ + "slice-service" + ][0] + + running_slice_sdps = [sdp["id"] for sdp in running_slice_services["sdps"]["sdp"]] + candidiate_slice_sdps = [ + sdp["id"] for sdp in candidate_slice_services["sdps"]["sdp"] + ] + removed_sdps = set(running_slice_sdps) - set(candidiate_slice_sdps) + + if len(removed_sdps) > 1: + raise Exception("Multiple SDPs removed - not supported.") + removed_sdp_id = removed_sdps.pop() + + removed_items["sdp"]["sdp_idx"] = running_slice_sdps.index(removed_sdp_id) + removed_items["sdp"]["value"] = next( + sdp + for sdp in running_slice_services["sdps"]["sdp"] + if sdp["id"] == removed_sdp_id + ) + + match_criteria = removed_items["sdp"]["value"]["service-match-criteria"][ + "match-criterion" + ] + if len(match_criteria) > 1: + raise Exception("Multiple match criteria found - not supported") + match_criterion = match_criteria[0] + connection_grp_id = match_criterion["target-connection-group-id"] + connection_groups = running_slice_services["connection-groups"]["connection-group"] + connection_group = next( + (idx, cg) + for idx, cg in enumerate(connection_groups) + if cg["id"] == connection_grp_id + ) + removed_items["connection_group"]["connection_group_idx"] = connection_group[0] + removed_items["connection_group"]["value"] = connection_group[1] + + for sdp in running_slice_services["sdps"]["sdp"]: + if sdp["id"] == removed_sdp_id: + continue + for mc in sdp["service-match-criteria"]["match-criterion"]: + if mc["target-connection-group-id"] == connection_grp_id: + removed_items["match_criterion"]["sdp_idx"] = running_slice_sdps.index( + sdp["id"] + ) + removed_items["match_criterion"]["match_criterion_idx"] = sdp[ + "service-match-criteria" + ]["match-criterion"].index(mc) + removed_items["match_criterion"]["value"] = mc + break + + if ( + removed_items["match_criterion"]["sdp_idx"] is None + or removed_items["sdp"]["sdp_idx"] is None + or removed_items["connection_group"]["connection_group_idx"] is None + ): + raise Exception("sdp, connection group or match criterion not found") + return removed_items + + +def gather_connectivity_construct_info( + candidate_connection_groups: List[Dict], +) -> Dict[Tuple[str, str], ConnectivityConstructInfo]: + """ + Creates a dict mapping (sender_sdp, receiver_sdp) -> ConnectivityConstructInfo + from the given list of candidate connection groups. + """ + cc_info: Dict[Tuple[str, str], ConnectivityConstructInfo] = {} + for cg in candidate_connection_groups: + for cc in cg["connectivity-construct"]: + cc_sender = cc["p2p-sender-sdp"] + cc_receiver = cc["p2p-receiver-sdp"] + cc_key = (cc_sender, cc_receiver) + cc_info[cc_key] = ConnectivityConstructInfo() + for metric_bound in cc["service-slo-sle-policy"]["slo-policy"][ + "metric-bound" + ]: + if ( + metric_bound["metric-type"] + == "ietf-network-slice-service:one-way-delay-maximum" + and metric_bound["metric-unit"] == "milliseconds" + ): + cc_info[cc_key].delay = int(metric_bound["bound"]) + elif ( + metric_bound["metric-type"] + == "ietf-network-slice-service:two-way-packet-loss" + and metric_bound["metric-unit"] == "percentage" + ): + cc_info[cc_key].packet_loss = float( + metric_bound["percentile-value"] + ) + elif ( + metric_bound["metric-type"] + == "ietf-network-slice-service:one-way-bandwidth" + and metric_bound["metric-unit"] == "Mbps" + ): + cc_info[cc_key].bandwidth = int(metric_bound["bound"]) + return cc_info + + +def extract_source_destination_device_endpoint_info( + device_ep_pairs: list, connection_group: Dict, candidate_connection_groups: List +) -> Tuple[DeviceEpInfo, DeviceEpInfo]: + """ + Given device_ep_pairs, the relevant connection_group data, and all candidate + connection groups, figure out the final DeviceEpInfo for source and destination. + This includes computing the combined bandwidth, min delay, etc. + """ + connectivity_construct = connection_group["connectivity-construct"][0] + sender_sdp = connectivity_construct["p2p-sender-sdp"] + receiver_sdp = connectivity_construct["p2p-receiver-sdp"] + + # If the first pair is not the sender, we invert them + if sender_sdp == device_ep_pairs[0][4]: + ... + elif sender_sdp == device_ep_pairs[1][4]: + device_ep_pairs = device_ep_pairs[::-1] + else: + raise Exception("Sender SDP not found in device_ep_pairs") + + # Gather info from candidate connection groups + cc_info = gather_connectivity_construct_info(candidate_connection_groups) + + source_delay = int(1e6) + source_bandwidth = 0 + source_packet_loss = 1.0 + destination_delay = int(1e6) + destination_bandwidth = 0 + destination_packet_loss = 1.0 + + if cc_info: + common_sdps = set.intersection(*[set(key) for key in cc_info.keys()]) + if len(cc_info) > 2 and len(common_sdps) != 1: + raise Exception( + "There should be one common sdp in all connectivity constructs, otherwise, it is not supported" + ) + if len(common_sdps) == 1: + common_sdp = common_sdps.pop() + elif len(common_sdps) == 2: + # Fallback if intersection is 2 => pick sender_sdp + common_sdp = sender_sdp + else: + raise Exception("Invalid number of common sdps") + + for (sender, receiver), metrics in cc_info.items(): + cc_bandwidth = metrics.bandwidth + cc_max_delay = metrics.delay + cc_packet_loss = metrics.packet_loss + if sender == common_sdp: + source_bandwidth += cc_bandwidth + if cc_max_delay < source_delay: + source_delay = cc_max_delay + if cc_packet_loss < source_packet_loss: + source_packet_loss = cc_packet_loss + else: + destination_bandwidth += cc_bandwidth + if cc_max_delay < destination_delay: + destination_delay = cc_max_delay + if cc_packet_loss < destination_packet_loss: + destination_packet_loss = cc_packet_loss + + source_device_ep_info = DeviceEpInfo( + ipv4_info=device_ep_pairs[0][5], + node_name=device_ep_pairs[0][2], + endpoint_name=device_ep_pairs[0][3], + one_way_delay=source_delay, + one_way_bandwidth=source_bandwidth, + one_way_packet_loss=source_packet_loss, + ) + destination_device_ep_info = DeviceEpInfo( + ipv4_info=device_ep_pairs[1][5], + node_name=device_ep_pairs[1][2], + endpoint_name=device_ep_pairs[1][3], + one_way_delay=destination_delay, + one_way_bandwidth=destination_bandwidth, + one_way_packet_loss=destination_packet_loss, + ) + + return source_device_ep_info, destination_device_ep_info + + +def _parse_item_added(diff: Dict) -> dict: + """ + Helper to parse 'iterable_item_added' from the running_candidate_diff + and return the relevant items for sdp, connection_group, match_criterion, etc. + """ + added_items = { + "sdp": {"sdp_idx": None, "value": {}}, + "connection_group": {"connection_group_idx": None, "value": {}}, + "match_criterion": { + "sdp_idx": None, + "match_criterion_idx": None, + "value": {}, + }, + } + for added_key, added_value in diff["iterable_item_added"].items(): + sdp_match = SDP_DIFF_RE.match(added_key) + connection_group_match = CONNECTION_GROUP_DIFF_RE.match(added_key) + match_criterion_match = MATCH_CRITERION_DIFF_RE.match(added_key) + if sdp_match: + added_items["sdp"] = { + "sdp_idx": int(sdp_match.groups()[0]), + "value": added_value, + } + elif connection_group_match: + added_items["connection_group"] = { + "connection_group_idx": int(connection_group_match.groups()[0]), + "value": added_value, + } + elif match_criterion_match: + added_items["match_criterion"] = { + "sdp_idx": int(match_criterion_match.groups()[0]), + "match_criterion_idx": int(match_criterion_match.groups()[1]), + "value": added_value, + } + return added_items + + +class L3NMSliceIETFSliceServiceHandler(_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) == 0: + return [] + + results = [] + try: + service_config = self.__service.service_config + + # 1. Identify source and destination devices + src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0]) + src_device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(src_device_uuid)) + ) + src_device_name = src_device_obj.name + src_controller = self.__task_executor.get_device_controller(src_device_obj) + + dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids( + endpoints[-1] + ) + dst_device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(dst_device_uuid)) + ) + dst_device_name = dst_device_obj.name + dst_controller = self.__task_executor.get_device_controller(dst_device_obj) + + 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 # same device controller + + # 2. Determine how the candidate & running resources differ + running_candidate_diff = get_running_candidate_ietf_slice_data_diff( + service_config + ) + candidate_ietf_slice_cr = get_custom_config_rule( + service_config, CANDIDATE_RESOURCE_KEY + ) + candidate_resource_value_dict = json.loads( + candidate_ietf_slice_cr.custom.resource_value + ) + running_ietf_slice_cr = get_custom_config_rule( + service_config, RUNNING_RESOURCE_KEY + ) + running_resource_value_dict = json.loads( + running_ietf_slice_cr.custom.resource_value + ) + slice_name = running_resource_value_dict["network-slice-services"][ + "slice-service" + ][0]["id"] + + # 3. Retrieve the context links for matching endpoints + context_client = ContextClient() + links = context_client.ListLinks(Empty()).links + + device_ep_pairs = [] + sdp_ids = [] + target_connection_group_id = None + operation_type = "update" # default fallback + + # 4. Handle creation vs additions vs removals + if not running_candidate_diff: # Slice Creation + # 4a. New Slice Creation + operation_type = "create" + + candidate_slice_service = candidate_resource_value_dict[ + "network-slice-services" + ]["slice-service"][0] + full_connection_groups = candidate_slice_service["connection-groups"][ + "connection-group" + ] + sdps = candidate_slice_service["sdps"]["sdp"] + sdp_ids = [sdp["node-id"] for sdp in sdps] + + # figure out which device is connected to which link + edge_device_names = [src_device_name, dst_device_name] + for sdp in sdps: + node_id = sdp["node-id"] + for link in links: + info = get_link_ep_device_names(link, context_client) + dev1, ep1, _, dev2, ep2, _ = info + if dev1 == node_id and dev2 in edge_device_names: + edge_device_names.remove(dev2) + match_criteria = sdp["service-match-criteria"][ + "match-criterion" + ] + if len(match_criteria) != 1: + raise Exception( + "Only one match criteria allowed for initial slice creation" + ) + match_criterion = match_criteria[0] + ipv4_info = extract_match_criterion_ipv4_info( + match_criterion + ) + device_ep_pairs.append( + ( + node_id, + ep1, + dev2, + ep2, + sdp["id"], + ipv4_info, + ) + ) + target_connection_group_id = match_criterion[ + "target-connection-group-id" + ] + sdp_ids.remove(node_id) + break + + # find the second link + if not edge_device_names: + raise Exception("Edge device names exhausted unexpectedly.") + + # second link logic + for link in links: + info = get_link_ep_device_names(link, context_client) + dev1, ep1, device_obj_1, dev2, ep2, device_obj_2 = info + if ( + dev1 == edge_device_names[0] + and device_obj_2.controller_id != device_obj_1.controller_id + ): + for sdp in sdps: + if sdp["node-id"] != sdp_ids[0]: + continue + match_criteria = sdp["service-match-criteria"][ + "match-criterion" + ] + if len(match_criteria) != 1: + raise Exception( + "Only one match criteria allowed for initial slice creation" + ) + match_criterion = match_criteria[0] + ipv4_info = extract_match_criterion_ipv4_info( + match_criterion + ) + device_ep_pairs.append( + ( + dev2, + ep2, + dev1, + ep1, + sdp["id"], + ipv4_info, + ) + ) + break + else: + raise Exception("No matching sdp found for second link.") + break + else: + raise Exception("sdp between the domains not found") + + elif "iterable_item_added" in running_candidate_diff: # new SDP added + # 4b. A new SDP was added + operation_type = "update" + + candidate_slice_service = candidate_resource_value_dict[ + "network-slice-services" + ]["slice-service"][0] + sdps = candidate_slice_service["sdps"]["sdp"] + full_connection_groups = candidate_slice_service["connection-groups"][ + "connection-group" + ] + added_items = _parse_item_added(running_candidate_diff) + + new_sdp = sdps[added_items["sdp"]["sdp_idx"]] + src_sdp_name = new_sdp["node-id"] + dst_sdp_idx = sdps[added_items["match_criterion"]["sdp_idx"]]["id"] + dst_sdp_name = sdps[added_items["match_criterion"]["sdp_idx"]][ + "node-id" + ] + for link in links: + info = get_link_ep_device_names(link, context_client) + dev1, ep1, device_obj_1, dev2, ep2, device_obj_2 = info + if ( + dev1 == src_sdp_name + and device_obj_2.controller_id != device_obj_1.controller_id + ): + for sdp in sdps: + if sdp["node-id"] != src_sdp_name: + continue + match_criteria = sdp["service-match-criteria"][ + "match-criterion" + ] + if len(match_criteria) != 1: + raise Exception( + "Only one match criteria allowed for initial slice creation" + ) + match_criterion = match_criteria[0] + ipv4_info = extract_match_criterion_ipv4_info( + match_criterion + ) + device_ep_pairs.append( + ( + dev2, + ep2, + dev1, + ep1, + sdp["id"], + ipv4_info, + ) + ) + target_connection_group_id = match_criterion[ + "target-connection-group-id" + ] + break + else: + raise Exception("sdp between the domains not found") + for link in links: + info = get_link_ep_device_names(link, context_client) + dev1, ep1, device_obj_1, dev2, ep2, device_obj_2 = info + if ( + dev1 == dst_sdp_name + and device_obj_2.controller_id != device_obj_1.controller_id + ): + for sdp in sdps: + if sdp["node-id"] != dst_sdp_name: + continue + match_criteria = sdp["service-match-criteria"][ + "match-criterion" + ] + vlan_id = set() + for match in match_criteria: + for type_value in match["match-type"]: + if ( + type_value["type"] + == "ietf-network-slice-service:vlan" + ): + vlan_id.add(type_value["value"][0]) + if len(vlan_id) != 1: + raise Exception( + "one vlan id found in SDP match criteria" + ) + match_criterion = match_criteria[ + added_items["match_criterion"]["match_criterion_idx"] + ] + ipv4_info = extract_match_criterion_ipv4_info( + match_criterion + ) + device_ep_pairs.append( + ( + dev2, + ep2, + dev1, + ep1, + sdp["id"], + ipv4_info, + ) + ) + break + else: + raise Exception("sdp between the domains not found") + elif "iterable_item_removed" in running_candidate_diff: # an SDP removed + # 4c. An existing SDP was removed + operation_type = "update" + + slice_services = running_resource_value_dict["network-slice-services"][ + "slice-service" + ] + candidate_slice_services = candidate_resource_value_dict[ + "network-slice-services" + ]["slice-service"] + candidate_slice_service = candidate_slice_services[0] + slice_service = slice_services[0] + full_connection_groups = slice_service["connection-groups"][ + "connection-group" + ] + sdps = slice_service["sdps"]["sdp"] + removed_items = get_removed_items( + candidate_resource_value_dict, running_resource_value_dict + ) + new_sdp = sdps[removed_items["sdp"]["sdp_idx"]] + src_sdp_name = new_sdp["node-id"] + dst_sdp_idx = sdps[removed_items["match_criterion"]["sdp_idx"]]["id"] + dst_sdp_name = sdps[removed_items["match_criterion"]["sdp_idx"]][ + "node-id" + ] + for link in links: + ( + device_obj_name_1, + ep_name_1, + device_obj_1, + device_obj_name_2, + ep_name_2, + device_obj_2, + ) = get_link_ep_device_names(link, context_client) + if ( + device_obj_name_1 == src_sdp_name + and device_obj_2.controller_id != device_obj_1.controller_id + ): + for sdp in sdps: + if sdp["node-id"] != src_sdp_name: + continue + match_criteria = sdp["service-match-criteria"][ + "match-criterion" + ] + if len(match_criteria) != 1: + raise Exception( + "Only one match criteria allowed for new SDP addition" + ) + match_criterion = match_criteria[0] + ipv4_info = extract_match_criterion_ipv4_info( + match_criterion + ) + device_ep_pairs.append( + ( + device_obj_name_2, + ep_name_2, + device_obj_name_1, + ep_name_1, + sdp["id"], + ipv4_info, + ) + ) + target_connection_group_id = match_criterion[ + "target-connection-group-id" + ] + break + else: + raise Exception("sdp between the domains not found") + for link in links: + ( + device_obj_name_1, + ep_name_1, + device_obj_1, + device_obj_name_2, + ep_name_2, + device_obj_2, + ) = get_link_ep_device_names(link, context_client) + if ( + device_obj_name_1 == dst_sdp_name + and device_obj_2.controller_id != device_obj_1.controller_id + ): + for sdp in sdps: + if sdp["node-id"] != dst_sdp_name: + continue + match_criteria = sdp["service-match-criteria"][ + "match-criterion" + ] + vlan_id = set() + for match in match_criteria: + for type_value in match["match-type"]: + if ( + type_value["type"] + == "ietf-network-slice-service:vlan" + ): + vlan_id.add(type_value["value"][0]) + if len(vlan_id) != 1: + raise Exception( + "one vlan id found in SDP match criteria" + ) + match_criterion = match_criteria[ + removed_items["match_criterion"]["match_criterion_idx"] + ] + ipv4_info = extract_match_criterion_ipv4_info( + match_criterion + ) + device_ep_pairs.append( + ( + device_obj_name_2, + ep_name_2, + device_obj_name_1, + ep_name_1, + sdp["id"], + ipv4_info, + ) + ) + break + else: + raise Exception("sdp between the domains not found") + else: + raise Exception( + "transition from candidate to running info not supported" + ) + + candidate_connection_groups = candidate_slice_service["connection-groups"][ + "connection-group" + ] + + if ( + len( + candidate_resource_value_dict["network-slice-services"][ + "slice-service" + ][0]["connection-groups"]["connection-group"] + ) + == 0 + ): + # 5. If connection_groups is now empty => operation = delete + operation_type = "delete" + + # 6. Retrieve actual target connection_group from the full connection groups + if not target_connection_group_id: + raise Exception("No target_connection_group_id found.") + target_connection_group = next( + cg + for cg in full_connection_groups + if cg["id"] == target_connection_group_id + ) + + # 7. Build source/destination device info + source_device_ep_info, destination_device_ep_info = ( + extract_source_destination_device_endpoint_info( + device_ep_pairs, + target_connection_group, + candidate_connection_groups, + ) + ) + resource_value_dict = { + "uuid": slice_name, + "operation_type": operation_type, + "src_node_id": source_device_ep_info["node_name"], + "src_mgmt_ip_address": source_device_ep_info["node_name"], + "src_ac_node_id": source_device_ep_info["node_name"], + "src_ac_ep_id": source_device_ep_info["endpoint_name"], + "src_vlan": source_device_ep_info["ipv4_info"]["vlan"], + "src_source_ip_prefix": source_device_ep_info["ipv4_info"]["src_lan"], + "src_source_tcp_port": source_device_ep_info["ipv4_info"]["src_port"], + "src_destination_ip_prefix": source_device_ep_info["ipv4_info"][ + "dst_lan" + ], + "src_destination_tcp_port": source_device_ep_info["ipv4_info"][ + "dst_port" + ], + "source_one_way_delay": source_device_ep_info["one_way_delay"], + "source_one_way_bandwidth": source_device_ep_info["one_way_bandwidth"], + "source_one_way_packet_loss": source_device_ep_info[ + "one_way_packet_loss" + ], + "dst_node_id": destination_device_ep_info["node_name"], + "dst_mgmt_ip_address": destination_device_ep_info["node_name"], + "dst_ac_node_id": destination_device_ep_info["node_name"], + "dst_ac_ep_id": destination_device_ep_info["endpoint_name"], + "dst_vlan": destination_device_ep_info["ipv4_info"]["vlan"], + "dst_source_ip_prefix": destination_device_ep_info["ipv4_info"][ + "src_lan" + ], + "dst_source_tcp_port": destination_device_ep_info["ipv4_info"][ + "src_port" + ], + "dst_destination_ip_prefix": destination_device_ep_info["ipv4_info"][ + "dst_lan" + ], + "dst_destination_tcp_port": destination_device_ep_info["ipv4_info"][ + "dst_port" + ], + "destination_one_way_delay": destination_device_ep_info[ + "one_way_delay" + ], + "destination_one_way_bandwidth": destination_device_ep_info[ + "one_way_bandwidth" + ], + "destination_one_way_packet_loss": destination_device_ep_info[ + "one_way_packet_loss" + ], + "slice_id": slice_name, + } + + # 9. Create config rules and configure device + json_config_rules = setup_config_rules(slice_name, resource_value_dict) + del controller.device_config.config_rules[:] + for jcr in json_config_rules: + controller.device_config.config_rules.append(ConfigRule(**jcr)) + + self.__task_executor.configure_device(controller) + except Exception as e: # pylint: disable=broad-except + raise e + 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) == 0: + return [] + service_uuid = self.__service.service_id.service_uuid.uuid + results = [] + try: + src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0]) + src_device_obj = self.__task_executor.get_device( + DeviceId(**json_device_id(src_device_uuid)) + ) + controller = self.__task_executor.get_device_controller(src_device_obj) + json_config_rules = teardown_config_rules(service_uuid, {}) + if len(json_config_rules) > 0: + del controller.device_config.config_rules[:] + for json_config_rule in json_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 + 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/l3slice_ietfslice/__init__.py b/src/service/service/service_handlers/l3slice_ietfslice/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/service/service/service_handlers/l3slice_ietfslice/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index 6fb1eca3497fef311605e2cd202ca74bd9cb730f..51fc42a5a5ea06d5abaa49946a154d0c38f6de8b 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -287,8 +287,12 @@ class TaskExecutor: devices.setdefault(device_type, dict())[device_uuid] = device else: if not exclude_managed_by_controller: - device_type = DeviceTypeEnum._value2member_map_[device.device_type] - devices.setdefault(device_type, dict())[device_uuid] = device + controller_device_type_enum = DeviceTypeEnum._value2member_map_[controller.device_type] + if controller_device_type_enum == DeviceTypeEnum.IETF_SLICE: + devices.setdefault(controller_device_type_enum, dict())[device_uuid] = device + else: + device_type = DeviceTypeEnum._value2member_map_[device.device_type] + devices.setdefault(device_type, dict())[device_uuid] = device device_type = DeviceTypeEnum._value2member_map_[controller.device_type] devices.setdefault(device_type, dict())[controller.device_id.device_uuid.uuid] = controller return devices @@ -321,7 +325,7 @@ class TaskExecutor: self, connection : Connection, service : Service, **service_handler_settings ) -> Dict[DeviceTypeEnum, Tuple['_ServiceHandler', Dict[str, Device]]]: connection_device_types : Dict[DeviceTypeEnum, Dict[str, Device]] = self.get_devices_from_connection( - connection, exclude_managed_by_controller=True + connection, exclude_managed_by_controller=False ) service_handlers : Dict[DeviceTypeEnum, Tuple['_ServiceHandler', Dict[str, Device]]] = dict() for device_type, connection_devices in connection_device_types.items(): diff --git a/src/slice/service/SliceServiceServicerImpl.py b/src/slice/service/SliceServiceServicerImpl.py index 007d012ed432a6d4ab589f6dfe28648a9a6d2a85..68bd0aef153d2b784605abfe03b891b6990d23d5 100644 --- a/src/slice/service/SliceServiceServicerImpl.py +++ b/src/slice/service/SliceServiceServicerImpl.py @@ -19,7 +19,7 @@ from common.proto.context_pb2 import ( from common.proto.slice_pb2_grpc import SliceServiceServicer from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.tools.context_queries.InterDomain import is_inter_domain #, is_multi_domain -from common.tools.context_queries.Slice import get_slice_by_id +from common.tools.context_queries.Slice import get_slice_by_defualt_id, get_slice_by_id from common.tools.grpc.ConfigRules import copy_config_rules from common.tools.grpc.Constraints import copy_constraints from common.tools.grpc.EndPointIds import copy_endpoint_ids @@ -45,6 +45,8 @@ class SliceServiceServicerImpl(SliceServiceServicer): # being modified. context_client = ContextClient() slice_ro : Optional[Slice] = get_slice_by_id(context_client, request.slice_id, rw_copy=False) + if not slice_ro: + slice_ro : Optional[Slice] = get_slice_by_defualt_id(context_client, request.slice_id, rw_copy=False) slice_rw = Slice() slice_rw.CopyFrom(request if slice_ro is None else slice_ro) @@ -52,9 +54,9 @@ class SliceServiceServicerImpl(SliceServiceServicer): slice_rw.slice_owner.CopyFrom(request.slice_owner) # pylint: disable=no-member slice_rw.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED # pylint: disable=no-member - copy_endpoint_ids(request.slice_endpoint_ids, slice_rw.slice_endpoint_ids ) # pylint: disable=no-member - copy_constraints (request.slice_constraints, slice_rw.slice_constraints ) # pylint: disable=no-member - copy_config_rules(request.slice_config.config_rules, slice_rw.slice_config.config_rules) # pylint: disable=no-member + copy_endpoint_ids(request.slice_endpoint_ids, slice_rw.slice_endpoint_ids ) # pylint: disable=no-member + copy_constraints (request.slice_constraints, slice_rw.slice_constraints ) # pylint: disable=no-member + copy_config_rules(request.slice_config.config_rules, slice_rw.slice_config.config_rules, False) # pylint: disable=no-member slice_id_with_uuids = context_client.SetSlice(slice_rw) @@ -112,7 +114,7 @@ class SliceServiceServicerImpl(SliceServiceServicer): # pylint: disable=no-member copy_endpoint_ids(request.slice_endpoint_ids, service_request.service_endpoint_ids) copy_constraints(request.slice_constraints, service_request.service_constraints) - copy_config_rules(request.slice_config.config_rules, service_request.service_config.config_rules) + copy_config_rules(request.slice_config.config_rules, service_request.service_config.config_rules, False) service_request.service_type = ServiceTypeEnum.SERVICETYPE_UNKNOWN for config_rule in request.slice_config.config_rules: diff --git a/src/tests/.gitlab-ci.yml b/src/tests/.gitlab-ci.yml index fdc86805ba4824f64844a9b4bc78c3e27b606945..b49d40c1142522ab0fb3fa1936395f2aadc80baf 100644 --- a/src/tests/.gitlab-ci.yml +++ b/src/tests/.gitlab-ci.yml @@ -21,4 +21,6 @@ include: #- local: '/src/tests/ofc23/.gitlab-ci.yml' - local: '/src/tests/ofc24/.gitlab-ci.yml' - local: '/src/tests/eucnc24/.gitlab-ci.yml' + - local: '/src/tests/ofc25-camara-agg-net-controller/.gitlab-ci.yml' + - local: '/src/tests/ofc25-camara-e2e-controller/.gitlab-ci.yml' #- local: '/src/tests/ecoc24/.gitlab-ci.yml' diff --git a/src/tests/eucnc24/.gitlab-ci.yml b/src/tests/eucnc24/.gitlab-ci.yml index 02c4cced3981f4c12042e636652dca95c384a169..1ae05f27469220711344728429c135858e3a345e 100644 --- a/src/tests/eucnc24/.gitlab-ci.yml +++ b/src/tests/eucnc24/.gitlab-ci.yml @@ -46,7 +46,7 @@ end2end_test eucnc24: before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - docker rm -f ${TEST_NAME} || true - - sudo containerlab destroy --all --cleanup || true + - containerlab destroy --all --cleanup || true script: # Download Docker image to run the test @@ -63,7 +63,7 @@ end2end_test eucnc24: - cp -R src/tests/${TEST_NAME}/clab/* /tmp/clab/${TEST_NAME} - tree -la /tmp/clab/${TEST_NAME} - cd /tmp/clab/${TEST_NAME} - - sudo containerlab deploy --reconfigure --topo eucnc24.clab.yml + - containerlab deploy --reconfigure --topo eucnc24.clab.yml - cd $RUNNER_PATH # Wait for initialization of Device NOSes @@ -71,9 +71,9 @@ end2end_test eucnc24: - docker ps -a # Dump configuration of the routers (before any configuration) - - sudo containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" # Configure TeraFlowSDN deployment # Uncomment if DEBUG log level is needed for the components @@ -132,27 +132,27 @@ end2end_test eucnc24: $CI_REGISTRY_IMAGE/${TEST_NAME}:latest /var/teraflow/run-service-tfs-create.sh # Dump configuration of the routers (after configure TFS service) - - sudo containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" # Run end-to-end test: test connectivity with ping - - export TEST1_10=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.1.10' --format json) + - export TEST1_10=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.1.10' --format json) - echo $TEST1_10 - echo $TEST1_10 | grep -E '3 packets transmitted, 3 received, 0\% packet loss' - - export TEST1_1=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.1.1' --format json) + - export TEST1_1=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.1.1' --format json) - echo $TEST1_1 - echo $TEST1_1 | grep -E '3 packets transmitted, 3 received, 0\% packet loss' - - export TEST2_1=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.2.1' --format json) + - export TEST2_1=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.2.1' --format json) - echo $TEST2_1 - echo $TEST2_1 | grep -E '3 packets transmitted, 3 received, 0\% packet loss' - - export TEST2_10=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.2.10' --format json) + - export TEST2_10=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.2.10' --format json) - echo $TEST2_10 - echo $TEST2_10 | grep -E '3 packets transmitted, 3 received, 0\% packet loss' - - export TEST3_1=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.3.1' --format json) + - export TEST3_1=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.3.1' --format json) - echo $TEST3_1 - echo $TEST3_1 | grep -E '3 packets transmitted, 0 received, 100\% packet loss' - - export TEST3_10=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.3.10' --format json) + - export TEST3_10=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.3.10' --format json) - echo $TEST3_10 - echo $TEST3_10 | grep -E '3 packets transmitted, 0 received, 100\% packet loss' @@ -164,9 +164,9 @@ end2end_test eucnc24: $CI_REGISTRY_IMAGE/${TEST_NAME}:latest /var/teraflow/run-service-tfs-remove.sh # Dump configuration of the routers (after deconfigure TFS service) - - sudo containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" # Run end-to-end test: configure service IETF - > @@ -176,27 +176,27 @@ end2end_test eucnc24: $CI_REGISTRY_IMAGE/${TEST_NAME}:latest /var/teraflow/run-service-ietf-create.sh # Dump configuration of the routers (after configure IETF service) - - sudo containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" # Run end-to-end test: test connectivity with ping - - export TEST1_10=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.1.10' --format json) + - export TEST1_10=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.1.10' --format json) - echo $TEST1_10 - echo $TEST1_10 | grep -E '3 packets transmitted, 3 received, 0\% packet loss' - - export TEST1_1=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.1.1' --format json) + - export TEST1_1=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.1.1' --format json) - echo $TEST1_1 - echo $TEST1_1 | grep -E '3 packets transmitted, 3 received, 0\% packet loss' - - export TEST2_1=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.2.1' --format json) + - export TEST2_1=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.2.1' --format json) - echo $TEST2_1 - echo $TEST2_1 | grep -E '3 packets transmitted, 3 received, 0\% packet loss' - - export TEST2_10=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.2.10' --format json) + - export TEST2_10=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.2.10' --format json) - echo $TEST2_10 - echo $TEST2_10 | grep -E '3 packets transmitted, 3 received, 0\% packet loss' - - export TEST3_1=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.3.1' --format json) + - export TEST3_1=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.3.1' --format json) - echo $TEST3_1 - echo $TEST3_1 | grep -E '3 packets transmitted, 0 received, 100\% packet loss' - - export TEST3_10=$(sudo containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.3.10' --format json) + - export TEST3_10=$(containerlab exec --name eucnc24 --label clab-node-name=dc1 --cmd 'ping -n -c3 172.16.3.10' --format json) - echo $TEST3_10 - echo $TEST3_10 | grep -E '3 packets transmitted, 0 received, 100\% packet loss' @@ -208,9 +208,9 @@ end2end_test eucnc24: $CI_REGISTRY_IMAGE/${TEST_NAME}:latest /var/teraflow/run-service-ietf-remove.sh # Dump configuration of the routers (after deconfigure IETF service) - - sudo containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" # Run end-to-end test: cleanup scenario - > @@ -221,9 +221,9 @@ end2end_test eucnc24: after_script: # Dump configuration of the routers (on after_script) - - sudo containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" - - sudo containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r1 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r2 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" + - containerlab exec --name eucnc24 --label clab-node-name=r3 --cmd "Cli --command \"enable"$'\n'$"show running-config\"" # Dump TeraFlowSDN component logs - source src/tests/${TEST_NAME}/deploy_specs.sh @@ -239,7 +239,7 @@ end2end_test eucnc24: - RUNNER_PATH=`pwd` #- cd $PWD/src/tests/${TEST_NAME} - cd /tmp/clab/${TEST_NAME} - - sudo containerlab destroy --topo eucnc24.clab.yml --cleanup || true + - containerlab destroy --topo eucnc24.clab.yml --cleanup || true - sudo rm -rf clab-eucnc24/ .eucnc24.clab.yml.bak || true - cd $RUNNER_PATH - kubectl delete namespaces tfs || true diff --git a/src/tests/ofc25-camara-agg-net-controller/.gitignore b/src/tests/ofc25-camara-agg-net-controller/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..24a4b233365e23a9462f4b64e8b60fef6a62bee4 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/.gitignore @@ -0,0 +1,5 @@ +clab-*/ +images/ +*.clab.yml.bak +*.tar +*.tar.gz diff --git a/src/tests/ofc25-camara-agg-net-controller/.gitlab-ci.yml b/src/tests/ofc25-camara-agg-net-controller/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..f69c37b38a5a4414c2d75f816ed12035cbb0d53c --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/.gitlab-ci.yml @@ -0,0 +1,90 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# Deploy TeraFlowSDN and Execute end-2-end test +end2end_test ofc25_camara_agg_net: + variables: + TEST_NAME: 'ofc25-camara-agg-net-controller' + IP_NAME: 'ip' + IP_PORT: '9092' + stage: end2end_test + # Disable to force running it after all other tasks + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + - HOST_IP=$(kubectl get nodes -o json | jq -r '.items[].status.addresses[] | select(.type=="InternalIP") | .address') + - sed -i "s/IP_NET_IP/${HOST_IP}/g" src/tests/${TEST_NAME}/data/agg-net-descriptor.json + - sed -i "s/IP_NET_PORT/${IP_PORT}/g" src/tests/${TEST_NAME}/data/agg-net-descriptor.json + - docker buildx build -t "${TEST_NAME}:latest" -f ./src/tests/${TEST_NAME}/Dockerfile . + - docker buildx build -t "${IP_NAME}:latest" -f ./src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/Dockerfile ./src/tests/tools/mock_ietf_l3vpn_sdn_ctrl + - docker rm -f ${TEST_NAME} || true + - docker rm -f ${IP_NAME} || true + - docker run -d --name ${IP_NAME} -p ${IP_PORT}:8443 ${IP_NAME}:latest + + script: + # Check MicroK8s is ready + - microk8s status --wait-ready + - kubectl get pods --all-namespaces + + - source src/tests/${TEST_NAME}/deploy_specs.sh + + # Deploy TeraFlowSDN + - ./deploy/crdb.sh + - ./deploy/nats.sh + - ./deploy/qdb.sh + - ./deploy/kafka.sh + - ./deploy/tfs.sh + - ./deploy/show.sh + + - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server + + # Run end-to-end test: onboard scenario + - > + docker run -t --rm --name ${TEST_NAME} --network=host + --volume "$PWD/tfs_runtime_env_vars.sh:/var/teraflow/tfs_runtime_env_vars.sh" + --volume "$PWD/src/tests/${TEST_NAME}:/opt/results" + ${TEST_NAME}:latest /var/teraflow/run-onboarding.sh + + # Run end-to-end test: configure service TFS + - > + docker run -t --rm --name ${TEST_NAME} --network=host + --volume "$PWD/tfs_runtime_env_vars.sh:/var/teraflow/tfs_runtime_env_vars.sh" + --volume "$PWD/src/tests/${TEST_NAME}:/opt/results" + ${TEST_NAME}:latest /var/teraflow/run-agg-net-ietf-slice-operations.sh + + after_script: + - kubectl --namespace tfs logs deployment/contextservice -c server + - kubectl --namespace tfs logs deployment/deviceservice -c server + - kubectl --namespace tfs logs deployment/pathcompservice -c frontend + - kubectl --namespace tfs logs deployment/serviceservice -c server + - kubectl --namespace tfs logs deployment/sliceservice -c server + - kubectl --namespace tfs logs deployment/nbiservice -c server + - docker logs ${IP_NAME} + + # Destroy Scenario + - kubectl delete namespaces tfs || true + + - docker rm -f ${TEST_NAME} || true + - docker rm -f ${IP_NAME} || true + + # Clean old docker images + - docker images --filter="dangling=true" --quiet | xargs -r docker rmi + + #coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' + artifacts: + when: always + reports: + junit: ./src/tests/${TEST_NAME}/report_*.xml diff --git a/src/tests/ofc25-camara-agg-net-controller/Dockerfile b/src/tests/ofc25-camara-agg-net-controller/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..36ab9d366bd186f4ac0ade9f9dcea21f0a2a46e8 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/Dockerfile @@ -0,0 +1,84 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 + +# Install dependencies +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install wget g++ git && \ + rm -rf /var/lib/apt/lists/* + +# 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 + +# Get common Python packages +# Note: this step enables sharing the previous Docker build steps among all the Python components +WORKDIR /var/teraflow +COPY common_requirements.in common_requirements.in +RUN pip-compile --quiet --output-file=common_requirements.txt common_requirements.in +RUN python3 -m pip install -r common_requirements.txt + +# Add common files into working directory +WORKDIR /var/teraflow/common +COPY src/common/. ./ +RUN rm -rf proto + +# Create proto sub-folder, copy .proto files, and generate Python code +RUN mkdir -p /var/teraflow/common/proto +WORKDIR /var/teraflow/common/proto +RUN touch __init__.py +COPY proto/*.proto ./ +RUN python3 -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. *.proto +RUN rm *.proto +RUN find . -type f -exec sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' {} \; + +# Create component sub-folders, get specific Python packages +RUN mkdir -p /var/teraflow/tests/ofc25-camara-agg-net-controller +WORKDIR /var/teraflow/tests/ofc25-camara-agg-net-controller +COPY src/tests/ofc25-camara-agg-net-controller/requirements.in requirements.in +RUN pip-compile --quiet --output-file=requirements.txt requirements.in +RUN python3 -m pip install -r requirements.txt + +# Add component files into working directory +WORKDIR /var/teraflow +COPY src/__init__.py ./__init__.py +COPY src/common/*.py ./common/ +COPY src/common/tests/. ./common/tests/ +COPY src/common/tools/. ./common/tools/ +COPY src/context/__init__.py context/__init__.py +COPY src/context/client/. context/client/ +COPY src/device/__init__.py device/__init__.py +COPY src/device/client/. device/client/ +COPY src/monitoring/__init__.py monitoring/__init__.py +COPY src/monitoring/client/. monitoring/client/ +COPY src/service/__init__.py service/__init__.py +COPY src/service/client/. service/client/ +COPY src/slice/__init__.py slice/__init__.py +COPY src/slice/client/. slice/client/ +COPY src/tests/*.py ./tests/ +COPY src/tests/ofc25-camara-agg-net-controller/__init__.py ./tests/ofc25-camara-agg-net-controller/__init__.py +COPY src/tests/ofc25-camara-agg-net-controller/data/. ./tests/ofc25-camara-agg-net-controller/data/ +COPY src/tests/ofc25-camara-agg-net-controller/tests/. ./tests/ofc25-camara-agg-net-controller/tests/ +COPY src/tests/ofc25-camara-agg-net-controller/scripts/. ./ + +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install tree && \ + rm -rf /var/lib/apt/lists/* + +RUN tree -la /var/teraflow diff --git a/src/tests/ofc25-camara-agg-net-controller/__init__.py b/src/tests/ofc25-camara-agg-net-controller/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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/ofc25-camara-agg-net-controller/data/agg-net-descriptor.json b/src/tests/ofc25-camara-agg-net-controller/data/agg-net-descriptor.json new file mode 100644 index 0000000000000000000000000000000000000000..5e0e612ddb4206974bb7b8b9d37f62365ade0dfa --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/agg-net-descriptor.json @@ -0,0 +1,858 @@ +{ + "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": "ip-net-controller" + } + }, + "name": "ip-net-controller", + "device_type": "ip-sdn-controller", + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "IP_NET_IP" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "IP_NET_PORT" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "endpoints": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + } + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": false + } + } + } + ] + }, + "device_endpoints": [] + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "name": "172.16.182.25", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "128.32.33.254", + "address_prefix": "24", + "site_location": "access" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "name": "172.16.185.31", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "name": "172.16.185.33", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "name": "172.16.185.32", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 13 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "172.10.33.254", + "address_prefix": "24", + "site_location": "cloud" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "device_type": "emu-datacenter", + "device_drivers": [ + 0 + ], + "device_endpoints": [], + "device_operational_status": 1, + "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", + "uuid": "500" + }, + { + "sample_types": [], + "type": "optical", + "uuid": "200" + }, + { + "sample_types": [], + "type": "optical", + "uuid": "201" + } + ] + } + } + } + ] + } + } + ], + "links": [ + { + "link_id": { + "link_uuid": { + "uuid": "ip-net-controller/mgmt==172.16.182.25/mgmt" + } + }, + "name": "ip-net-controller/mgmt==172.16.182.25/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-net-controller/mgmt==172.16.185.31/mgmt" + } + }, + "name": "ip-net-controller/mgmt==172.16.185.31/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-net-controller/mgmt==172.16.185.33/mgmt" + } + }, + "name": "ip-net-controller/mgmt==172.16.185.33/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-net-controller/mgmt==172.16.185.32/mgmt" + } + }, + "name": "ip-net-controller/mgmt==172.16.185.32/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-500" + } + }, + "name": "172.16.182.25-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-500" + } + }, + "name": "172.16.185.33-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-501" + } + }, + "name": "172.16.182.25-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-501" + } + }, + "name": "172.16.185.31-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-500" + } + }, + "name": "172.16.185.31-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-500" + } + }, + "name": "172.16.185.32-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-501" + } + }, + "name": "172.16.185.33-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-501" + } + }, + "name": "172.16.185.32-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-200" + } + }, + "name": "172.16.185.32-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.204.220-500" + } + }, + "name": "172.16.204.220-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + } + ] +} diff --git a/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice1_post_ietf_network_slice.json b/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice1_post_ietf_network_slice.json new file mode 100644 index 0000000000000000000000000000000000000000..ac1f09dd838d60ab42c64e134203f398179874e7 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice1_post_ietf_network_slice.json @@ -0,0 +1,190 @@ +{ + "network-slice-services": { + "slice-service": [ + { + "connection-groups": { + "connection-group": [ + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 5000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 1000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + } + ] + }, + "description": "dsc", + "id": "slice1", + "sdps": { + "sdp": [ + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.185.32", + "ac-tp-id": "200", + "description": "dsc", + "id": "0" + } + ] + }, + "id": "1", + "node-id": "172.16.185.32", + "sdp-ip-address": [ + "172.16.185.32" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "101" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + }, + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.182.25", + "ac-tp-id": "200", + "description": "dsc", + "id": "0" + } + ] + }, + "id": "2", + "node-id": "172.16.182.25", + "sdp-ip-address": [ + "172.16.182.25" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "21" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + } + ] + } + } + ] + } +} diff --git a/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice1_put_ietf_network_slice.json b/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice1_put_ietf_network_slice.json new file mode 100644 index 0000000000000000000000000000000000000000..690a84d915620667121cd6893e430576c592322a --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice1_put_ietf_network_slice.json @@ -0,0 +1,58 @@ +{ + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 5000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 1000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + } diff --git a/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice2_post_ietf_network_slice.json b/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice2_post_ietf_network_slice.json new file mode 100644 index 0000000000000000000000000000000000000000..079239a8bef20c67aaac4a7707a1041650c9bdf9 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice2_post_ietf_network_slice.json @@ -0,0 +1,190 @@ +{ + "network-slice-services": { + "slice-service": [ + { + "connection-groups": { + "connection-group": [ + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 5000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 1000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + } + ] + }, + "description": "dsc", + "id": "slice2", + "sdps": { + "sdp": [ + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.185.32", + "ac-tp-id": "200", + "description": "dsc", + "id": "0" + } + ] + }, + "id": "1", + "node-id": "172.16.185.32", + "sdp-ip-address": [ + "172.16.185.32" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "201" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + }, + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.182.25", + "ac-tp-id": "200", + "description": "dsc", + "id": "0" + } + ] + }, + "id": "2", + "node-id": "172.16.182.25", + "sdp-ip-address": [ + "172.16.182.25" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "31" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + } + ] + } + } + ] + } +} diff --git a/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice2_put_ietf_network_slice.json b/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice2_put_ietf_network_slice.json new file mode 100644 index 0000000000000000000000000000000000000000..948276a5a79048cf2980c60c57f697b491d19f44 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/pc1_slice2_put_ietf_network_slice.json @@ -0,0 +1,58 @@ +{ + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 5000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 1000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" +} diff --git a/src/tests/ofc25-camara-agg-net-controller/data/pc2_slice1_put_ietf_network_slice.json b/src/tests/ofc25-camara-agg-net-controller/data/pc2_slice1_put_ietf_network_slice.json new file mode 100644 index 0000000000000000000000000000000000000000..66e386c483ca1989519ec9ae5c4eef468c41f4bf --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/pc2_slice1_put_ietf_network_slice.json @@ -0,0 +1,58 @@ +{ + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 10000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 2000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" +} diff --git a/src/tests/ofc25-camara-agg-net-controller/data/pc2_slice2_put_ietf_network_slice.json b/src/tests/ofc25-camara-agg-net-controller/data/pc2_slice2_put_ietf_network_slice.json new file mode 100644 index 0000000000000000000000000000000000000000..66e386c483ca1989519ec9ae5c4eef468c41f4bf --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/pc2_slice2_put_ietf_network_slice.json @@ -0,0 +1,58 @@ +{ + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 10000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 2000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" +} diff --git a/src/tests/ofc25-camara-agg-net-controller/data/target-l3vpn-slice1-stages.json b/src/tests/ofc25-camara-agg-net-controller/data/target-l3vpn-slice1-stages.json new file mode 100644 index 0000000000000000000000000000000000000000..5e75b0ff71eee30c3e73e58e92a5830981a2ef96 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/target-l3vpn-slice1-stages.json @@ -0,0 +1,557 @@ +[ + { + "ietf-l3vpn-svc:l3vpn-svc": { + "sites": { + "site": [ + { + "devices": { + "device": [ + { + "device-id": "172.16.185.32", + "location": "cloud" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "cloud" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.16.104.221/24", + "lan-tag": "101", + "next-hop": "172.10.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_cloud", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.185.32", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "172.10.33.254", + "prefix-length": "24", + "provider-address": "172.10.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + } + } + ] + } + } + }, + "svc-input-bandwidth": 5000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 1000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:hub-role", + "vpn-id": "slice1" + } + } + ] + } + }, + { + "devices": { + "device": [ + { + "device-id": "172.16.182.25", + "location": "access" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "access" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.101.22/24", + "lan-tag": "21", + "next-hop": "128.32.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_access", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.182.25", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "128.32.33.254", + "prefix-length": "24", + "provider-address": "128.32.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 20 + } + } + ] + } + } + }, + "svc-input-bandwidth": 1000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 5000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:spoke-role", + "vpn-id": "slice1" + } + } + ] + } + } + ] + }, + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "slice1" + } + ] + } + } + }, + { + "ietf-l3vpn-svc:l3vpn-svc": { + "sites": { + "site": [ + { + "devices": { + "device": [ + { + "device-id": "172.16.185.32", + "location": "cloud" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "cloud" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.16.104.221/24", + "lan-tag": "101", + "next-hop": "172.10.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_cloud", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.185.32", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "172.10.33.254", + "prefix-length": "24", + "provider-address": "172.10.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + } + } + ] + } + } + }, + "svc-input-bandwidth": 10000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 2000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:hub-role", + "vpn-id": "slice1" + } + } + ] + } + }, + { + "devices": { + "device": [ + { + "device-id": "172.16.182.25", + "location": "access" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "access" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.101.22/24", + "lan-tag": "21", + "next-hop": "128.32.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_access", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.182.25", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "128.32.33.254", + "prefix-length": "24", + "provider-address": "128.32.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 20 + } + } + ] + } + } + }, + "svc-input-bandwidth": 2000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 10000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:spoke-role", + "vpn-id": "slice1" + } + } + ] + } + } + ] + }, + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "slice1" + } + ] + } + } + }, + { + "ietf-l3vpn-svc:l3vpn-svc": { + "sites": { + "site": [ + { + "devices": { + "device": [ + { + "device-id": "172.16.185.32", + "location": "cloud" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "cloud" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.16.104.221/24", + "lan-tag": "101", + "next-hop": "172.10.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_cloud", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.185.32", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "172.10.33.254", + "prefix-length": "24", + "provider-address": "172.10.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + } + } + ] + } + } + }, + "svc-input-bandwidth": 5000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 1000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:hub-role", + "vpn-id": "slice1" + } + } + ] + } + }, + { + "devices": { + "device": [ + { + "device-id": "172.16.182.25", + "location": "access" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "access" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.101.22/24", + "lan-tag": "21", + "next-hop": "128.32.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_access", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.182.25", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "128.32.33.254", + "prefix-length": "24", + "provider-address": "128.32.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 20 + } + } + ] + } + } + }, + "svc-input-bandwidth": 1000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 5000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:spoke-role", + "vpn-id": "slice1" + } + } + ] + } + } + ] + }, + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "slice1" + } + ] + } + } + } +] diff --git a/src/tests/ofc25-camara-agg-net-controller/data/target-l3vpn-slice2-stages.json b/src/tests/ofc25-camara-agg-net-controller/data/target-l3vpn-slice2-stages.json new file mode 100644 index 0000000000000000000000000000000000000000..216287af816b4b8656c9bd386eb6f622c0afe987 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/data/target-l3vpn-slice2-stages.json @@ -0,0 +1,557 @@ +[ + { + "ietf-l3vpn-svc:l3vpn-svc": { + "sites": { + "site": [ + { + "devices": { + "device": [ + { + "device-id": "172.16.185.32", + "location": "cloud" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "cloud" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.16.104.221/24", + "lan-tag": "201", + "next-hop": "172.10.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_cloud", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.185.32", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "172.10.33.254", + "prefix-length": "24", + "provider-address": "172.10.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + } + } + ] + } + } + }, + "svc-input-bandwidth": 5000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 1000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:hub-role", + "vpn-id": "slice2" + } + } + ] + } + }, + { + "devices": { + "device": [ + { + "device-id": "172.16.182.25", + "location": "access" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "access" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.201.22/24", + "lan-tag": "31", + "next-hop": "128.32.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_access", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.182.25", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "128.32.33.254", + "prefix-length": "24", + "provider-address": "128.32.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 20 + } + } + ] + } + } + }, + "svc-input-bandwidth": 1000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 5000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:spoke-role", + "vpn-id": "slice2" + } + } + ] + } + } + ] + }, + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "slice2" + } + ] + } + } + }, + { + "ietf-l3vpn-svc:l3vpn-svc": { + "sites": { + "site": [ + { + "devices": { + "device": [ + { + "device-id": "172.16.185.32", + "location": "cloud" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "cloud" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.16.104.221/24", + "lan-tag": "201", + "next-hop": "172.10.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_cloud", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.185.32", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "172.10.33.254", + "prefix-length": "24", + "provider-address": "172.10.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + } + } + ] + } + } + }, + "svc-input-bandwidth": 10000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 2000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:hub-role", + "vpn-id": "slice2" + } + } + ] + } + }, + { + "devices": { + "device": [ + { + "device-id": "172.16.182.25", + "location": "access" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "access" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.201.22/24", + "lan-tag": "31", + "next-hop": "128.32.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_access", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.182.25", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "128.32.33.254", + "prefix-length": "24", + "provider-address": "128.32.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 20 + } + } + ] + } + } + }, + "svc-input-bandwidth": 2000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 10000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:spoke-role", + "vpn-id": "slice2" + } + } + ] + } + } + ] + }, + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "slice2" + } + ] + } + } + }, + { + "ietf-l3vpn-svc:l3vpn-svc": { + "sites": { + "site": [ + { + "devices": { + "device": [ + { + "device-id": "172.16.185.32", + "location": "cloud" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "cloud" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.16.104.221/24", + "lan-tag": "201", + "next-hop": "172.10.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_cloud", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.185.32", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "172.10.33.254", + "prefix-length": "24", + "provider-address": "172.10.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 10 + } + } + ] + } + } + }, + "svc-input-bandwidth": 5000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 1000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:hub-role", + "vpn-id": "slice2" + } + } + ] + } + }, + { + "devices": { + "device": [ + { + "device-id": "172.16.182.25", + "location": "access" + } + ] + }, + "locations": { + "location": [ + { + "location-id": "access" + } + ] + }, + "management": { + "type": "ietf-l3vpn-svc:provider-managed" + }, + "routing-protocols": { + "routing-protocol": [ + { + "static": { + "cascaded-lan-prefixes": { + "ipv4-lan-prefixes": [ + { + "lan": "172.1.201.22/24", + "lan-tag": "31", + "next-hop": "128.32.33.254" + } + ] + } + }, + "type": "ietf-l3vpn-svc:static" + } + ] + }, + "site-id": "site_access", + "site-network-accesses": { + "site-network-access": [ + { + "device-reference": "172.16.182.25", + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "customer-address": "128.32.33.254", + "prefix-length": "24", + "provider-address": "128.32.33.254" + } + } + }, + "service": { + "qos": { + "qos-profile": { + "classes": { + "class": [ + { + "bandwidth": { + "guaranteed-bw-percent": 100 + }, + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": { + "latency-boundary": 20 + } + } + ] + } + } + }, + "svc-input-bandwidth": 1000000000, + "svc-mtu": 1500, + "svc-output-bandwidth": 5000000000 + }, + "site-network-access-id": "200", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "vpn-attachment": { + "site-role": "ietf-l3vpn-svc:spoke-role", + "vpn-id": "slice2" + } + } + ] + } + } + ] + }, + "vpn-services": { + "vpn-service": [ + { + "vpn-id": "slice2" + } + ] + } + } + } +] diff --git a/src/tests/ofc25-camara-agg-net-controller/deploy_specs.sh b/src/tests/ofc25-camara-agg-net-controller/deploy_specs.sh new file mode 100755 index 0000000000000000000000000000000000000000..9ae83e7b126aa2913cd3c30887292b4626dd5855 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/deploy_specs.sh @@ -0,0 +1,208 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. +#export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_generator" +export TFS_COMPONENTS="context device pathcomp service slice nbi" + +# Uncomment to activate Monitoring (old) +#export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" + +# Uncomment to activate Monitoring Framework (new) +#export TFS_COMPONENTS="${TFS_COMPONENTS} kpi_manager kpi_value_writer kpi_value_api telemetry analytics automation" + +# Uncomment to activate QoS Profiles +#export TFS_COMPONENTS="${TFS_COMPONENTS} qos_profile" + +# Uncomment to activate BGP-LS Speaker +#export TFS_COMPONENTS="${TFS_COMPONENTS} bgpls_speaker" + +# Uncomment to activate Optical Controller +# To manage optical connections, "service" requires "opticalcontroller" to be deployed +# before "service", thus we "hack" the TFS_COMPONENTS environment variable prepending the +# "opticalcontroller" only if "service" is already in TFS_COMPONENTS, and re-export it. +#if [[ "$TFS_COMPONENTS" == *"service"* ]]; then +# BEFORE="${TFS_COMPONENTS% service*}" +# AFTER="${TFS_COMPONENTS#* service}" +# export TFS_COMPONENTS="${BEFORE} opticalcontroller service ${AFTER}" +#fi + +# Uncomment to activate ZTP +#export TFS_COMPONENTS="${TFS_COMPONENTS} ztp" + +# Uncomment to activate Policy Manager +#export TFS_COMPONENTS="${TFS_COMPONENTS} policy" + +# Uncomment to activate Optical CyberSecurity +#export TFS_COMPONENTS="${TFS_COMPONENTS} dbscanserving opticalattackmitigator opticalattackdetector opticalattackmanager" + +# Uncomment to activate L3 CyberSecurity +#export TFS_COMPONENTS="${TFS_COMPONENTS} l3_attackmitigator l3_centralizedattackdetector" + +# Uncomment to activate TE +#export TFS_COMPONENTS="${TFS_COMPONENTS} te" + +# Uncomment to activate Forecaster +#export TFS_COMPONENTS="${TFS_COMPONENTS} forecaster" + +# Uncomment to activate E2E Orchestrator +#export TFS_COMPONENTS="${TFS_COMPONENTS} e2e_orchestrator" + +# Uncomment to activate DLT and Interdomain +#export TFS_COMPONENTS="${TFS_COMPONENTS} interdomain dlt" +#if [[ "$TFS_COMPONENTS" == *"dlt"* ]]; then +# export KEY_DIRECTORY_PATH="src/dlt/gateway/keys/priv_sk" +# export CERT_DIRECTORY_PATH="src/dlt/gateway/keys/cert.pem" +# export TLS_CERT_PATH="src/dlt/gateway/keys/ca.crt" +#fi + +# Uncomment to activate QKD App +# To manage QKD Apps, "service" requires "qkd_app" to be deployed +# before "service", thus we "hack" the TFS_COMPONENTS environment variable prepending the +# "qkd_app" only if "service" is already in TFS_COMPONENTS, and re-export it. +#if [[ "$TFS_COMPONENTS" == *"service"* ]]; then +# BEFORE="${TFS_COMPONENTS% service*}" +# AFTER="${TFS_COMPONENTS#* service}" +# export TFS_COMPONENTS="${BEFORE} qkd_app service ${AFTER}" +#fi + + +# 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" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" + +# Uncomment to monitor performance of components +#export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/servicemonitors.yaml" + +# Uncomment when deploying Optical CyberSecurity +#export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/cachingservice.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 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" + +# 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" + +# Set NATS installation mode to 'single'. This option is convenient for development and testing. +# See ./deploy/all.sh or ./deploy/nats.sh for additional details +export NATS_DEPLOY_MODE="single" + +# Disable flag for re-deploying NATS from scratch. +export NATS_REDEPLOY="" + + +# ----- QuestDB ---------------------------------------------------------------- + +# 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" + +# 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="" + + +# ----- K8s Observability ------------------------------------------------------ + +# Set the external port Prometheus Mgmt HTTP GUI interface will be exposed to. +export PROM_EXT_PORT_HTTP="9090" + +# Set the external port Grafana HTTP Dashboards will be exposed to. +export GRAF_EXT_PORT_HTTP="3000" + + +# ----- Apache Kafka ----------------------------------------------------------- + +# Set the namespace where Apache Kafka will be deployed. +export KFK_NAMESPACE="kafka" + +# Set the port Apache Kafka server will be exposed to. +export KFK_SERVER_PORT="9092" + +# Set the flag to YES for redeploying of Apache Kafka +export KFK_REDEPLOY="" diff --git a/src/tests/ofc25-camara-agg-net-controller/redeploy-tfs.sh b/src/tests/ofc25-camara-agg-net-controller/redeploy-tfs.sh new file mode 100755 index 0000000000000000000000000000000000000000..7f4e0c0f4048348b4b220508d60088e69a3219fb --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/redeploy-tfs.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +source ~/tfs-ctrl/src/tests/ofc25-camara-agg-net-controller/deploy_specs.sh +./deploy/all.sh diff --git a/src/tests/ofc25-camara-agg-net-controller/requirements.in b/src/tests/ofc25-camara-agg-net-controller/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..1bdaec9997da4b83fa89c1bf0d00d4c3a73558a4 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/requirements.in @@ -0,0 +1,30 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +deepdiff==6.7.* +requests==2.27.* + +coverage==6.3 +grpcio==1.47.* +grpcio-health-checking==1.47.* +grpcio-reflection==1.47.* +grpcio-tools==1.47.* +grpclib==0.4.4 +prettytable==3.5.0 +prometheus-client==0.13.0 +protobuf==3.20.* +pytest==6.2.5 +pytest-benchmark==3.4.1 +python-dateutil==2.8.2 +pytest-depends==1.0.1 diff --git a/src/tests/ofc25-camara-agg-net-controller/scripts/run-agg-net-ietf-slice-operations.sh b/src/tests/ofc25-camara-agg-net-controller/scripts/run-agg-net-ietf-slice-operations.sh new file mode 100755 index 0000000000000000000000000000000000000000..001380ea2fcb88f8546bfd2345fab9431c5e1d03 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/scripts/run-agg-net-ietf-slice-operations.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +source /var/teraflow/tfs_runtime_env_vars.sh +export PYTHONPATH=/var/teraflow +pytest --verbose --log-level=INFO \ + --junitxml=/opt/results/report_e2e_ietf_l3vpn_operations.xml \ + /var/teraflow/tests/ofc25-camara-agg-net-controller/tests/test_agg_net_ietf_slice_operations.py diff --git a/src/tests/ofc25-camara-agg-net-controller/scripts/run-onboarding.sh b/src/tests/ofc25-camara-agg-net-controller/scripts/run-onboarding.sh new file mode 100755 index 0000000000000000000000000000000000000000..f62294a934ac4aed1140143d2ffbfe02568a6405 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/scripts/run-onboarding.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +source /var/teraflow/tfs_runtime_env_vars.sh +export PYTHONPATH=/var/teraflow +pytest --verbose --log-level=INFO \ + --junitxml=/opt/results/report_onboarding.xml \ + /var/teraflow/tests/ofc25-camara-agg-net-controller/tests/test_onboarding.py diff --git a/src/tests/ofc25-camara-agg-net-controller/tests/Fixtures.py b/src/tests/ofc25-camara-agg-net-controller/tests/Fixtures.py new file mode 100644 index 0000000000000000000000000000000000000000..15978851faae668339fa4eed6db8ab7e1be2eb5e --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/tests/Fixtures.py @@ -0,0 +1,43 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (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 pytest +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from monitoring.client.MonitoringClient import MonitoringClient +from service.client.ServiceClient import ServiceClient + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient() + yield _client + _client.close() + +@pytest.fixture(scope='session') +def device_client(): + _client = DeviceClient() + yield _client + _client.close() + +@pytest.fixture(scope='session') +def monitoring_client(): + _client = MonitoringClient() + yield _client + _client.close() + +@pytest.fixture(scope='session') +def service_client(): + _client = ServiceClient() + yield _client + _client.close() diff --git a/src/tests/ofc25-camara-agg-net-controller/tests/Tools.py b/src/tests/ofc25-camara-agg-net-controller/tests/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..cd2add49edd23f4c169b5fdc3c5123b2b31daa8d --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/tests/Tools.py @@ -0,0 +1,109 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (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 enum, logging, requests +from typing import Any, Dict, List, Optional, Set, Union +from common.Constants import ServiceNameEnum +from common.Settings import get_service_host, get_service_port_http + +NBI_ADDRESS = get_service_host(ServiceNameEnum.NBI) +NBI_PORT = get_service_port_http(ServiceNameEnum.NBI) +NBI_USERNAME = 'admin' +NBI_PASSWORD = 'admin' +NBI_BASE_URL = '' + +class RestRequestMethod(enum.Enum): + GET = 'get' + POST = 'post' + PUT = 'put' + PATCH = 'patch' + DELETE = 'delete' + +EXPECTED_STATUS_CODES : Set[int] = { + requests.codes['OK' ], + requests.codes['CREATED' ], + requests.codes['ACCEPTED' ], + requests.codes['NO_CONTENT'], +} + +def do_rest_request( + method : RestRequestMethod, url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + request_url = 'http://{:s}:{:s}@{:s}:{:d}{:s}{:s}'.format( + NBI_USERNAME, NBI_PASSWORD, NBI_ADDRESS, NBI_PORT, str(NBI_BASE_URL), url + ) + + if logger is not None: + msg = 'Request: {:s} {:s}'.format(str(method.value).upper(), str(request_url)) + if body is not None: msg += ' body={:s}'.format(str(body)) + logger.warning(msg) + reply = requests.request(method.value, request_url, headers={'Content-Type': 'application/json'}, timeout=timeout, json=body, allow_redirects=allow_redirects) + if logger is not None: + logger.warning('Reply: {:s}'.format(str(reply.text))) + assert reply.status_code in expected_status_codes, 'Reply failed with status code {:d}'.format(reply.status_code) + + if reply.content and len(reply.content) > 0: return reply.json() + return None + +def do_rest_get_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.GET, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_post_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.POST, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_put_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.PUT, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_patch_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.PATCH, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_delete_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.DELETE, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) diff --git a/src/tests/ofc25-camara-agg-net-controller/tests/__init__.py b/src/tests/ofc25-camara-agg-net-controller/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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/ofc25-camara-agg-net-controller/tests/test_agg_net_ietf_slice_operations.py b/src/tests/ofc25-camara-agg-net-controller/tests/test_agg_net_ietf_slice_operations.py new file mode 100644 index 0000000000000000000000000000000000000000..d15a8bbb6d3e9007da24985c6d452e80f9b7db5a --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/tests/test_agg_net_ietf_slice_operations.py @@ -0,0 +1,206 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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, os +import requests +from deepdiff import DeepDiff + + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +HEADERS = {"Content-Type": "application/json"} + +TARGET_L3VPN_SLICE1_STAGES = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "target-l3vpn-slice1-stages.json", +) + +TARGET_L3VPN_SLICE2_STAGES = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "target-l3vpn-slice2-stages.json", +) + +OP1_IETF_SLICE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "pc1_slice1_post_ietf_network_slice.json", +) + +OP2_IETF_SLICE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "pc2_slice1_put_ietf_network_slice.json", +) + +OP3_IETF_SLICE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "pc1_slice2_post_ietf_network_slice.json", +) + +OP4_IETF_SLICE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "pc2_slice2_put_ietf_network_slice.json", +) + +OP6_IETF_SLICE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "pc1_slice1_put_ietf_network_slice.json", +) + +OP8_IETF_SLICE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "pc1_slice2_put_ietf_network_slice.json", +) + +NBI_ADDRESS = "localhost" +NBI_PORT = "80" +NBI_USERNAME = "admin" +NBI_PASSWORD = "admin" + +IP_ADDRESS = "localhost" +IP_PORT = 9092 + +BASE_IETF_SLICE_URL = f"http://{NBI_ADDRESS}:{NBI_PORT}/restconf/data/ietf-network-slice-service:network-slice-services" +IP_L3VPN_URL = f"http://{IP_ADDRESS}:{IP_PORT}/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" + +# pylint: disable=redefined-outer-name, unused-argument +def test_ietf_slice_creation_removal(): + # Issue service creation request + with open(OP1_IETF_SLICE, "r", encoding="UTF-8") as f: + op1_ietf_slice = json.load(f) + with open(OP2_IETF_SLICE, "r", encoding="UTF-8") as f: + op2_ietf_slice = json.load(f) + with open(OP3_IETF_SLICE, "r", encoding="UTF-8") as f: + op3_ietf_slice = json.load(f) + with open(OP4_IETF_SLICE, "r", encoding="UTF-8") as f: + op4_ietf_slice = json.load(f) + with open(OP6_IETF_SLICE, "r", encoding="UTF-8") as f: + op6_ietf_slice = json.load(f) + with open(OP8_IETF_SLICE, "r", encoding="UTF-8") as f: + op8_ietf_slice = json.load(f) + with open(TARGET_L3VPN_SLICE1_STAGES, "r", encoding="UTF-8") as f: + target_l3vpn_slice1_stages = json.load(f) + with open(TARGET_L3VPN_SLICE2_STAGES, "r", encoding="UTF-8") as f: + target_l3vpn_slice2_stages = json.load(f) + + # op 1 + URL = BASE_IETF_SLICE_URL + requests.post(URL, headers=HEADERS, json=op1_ietf_slice) + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + slice_name = "slice1" + diff = DeepDiff(target_l3vpn_slice1_stages[0], l3vpns[slice_name]) + assert not diff + + # op 2 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1/connection-groups/connection-group=line1" + requests.put(URL, headers=HEADERS, json=op2_ietf_slice) + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + slice_name = "slice1" + diff = DeepDiff(target_l3vpn_slice1_stages[1], l3vpns[slice_name]) + assert not diff + + # op 3 + URL = BASE_IETF_SLICE_URL + requests.post(URL, headers=HEADERS, json=op3_ietf_slice) + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + slice_name = "slice2" + diff = DeepDiff(target_l3vpn_slice2_stages[0], l3vpns[slice_name]) + assert not diff + + # op 4 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2/connection-groups/connection-group=line1" + requests.put(URL, headers=HEADERS, json=op4_ietf_slice) + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + slice_name = "slice2" + diff = DeepDiff(target_l3vpn_slice2_stages[1], l3vpns[slice_name]) + assert not diff + + # op 5 + + + # op 6 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1/connection-groups/connection-group=line1" + requests.put(URL, headers=HEADERS, json=op6_ietf_slice) + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + slice_name = "slice1" + diff = DeepDiff(target_l3vpn_slice1_stages[2], l3vpns[slice_name]) + assert not diff + + # op 7 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1" + requests.delete(URL) + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + slice_name = "slice1" + assert slice_name not in l3vpns + + # op 8 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2/connection-groups/connection-group=line1" + requests.put(URL, headers=HEADERS, json=op8_ietf_slice) + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + slice_name = "slice2" + diff = DeepDiff(target_l3vpn_slice2_stages[2], l3vpns[slice_name]) + assert not diff + + # op 9 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2" + requests.delete(URL) + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + slice_name = "slice2" + assert slice_name not in l3vpns + + # op 10 + + URL = IP_L3VPN_URL + l3vpns = requests.get(URL).json() + + assert not l3vpns diff --git a/src/tests/ofc25-camara-agg-net-controller/tests/test_onboarding.py b/src/tests/ofc25-camara-agg-net-controller/tests/test_onboarding.py new file mode 100644 index 0000000000000000000000000000000000000000..7173ddacc6e6a06aad9cba099a2cb26bab56f7a9 --- /dev/null +++ b/src/tests/ofc25-camara-agg-net-controller/tests/test_onboarding.py @@ -0,0 +1,67 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (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, os, time +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import ContextId, DeviceOperationalStatusEnum, Empty +from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results, validate_empty_scenario +from common.tools.object_factory.Context import json_context_id +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from .Fixtures import context_client, device_client # pylint: disable=unused-import + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'data', 'agg-net-descriptor.json') +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) + +def test_scenario_onboarding( + context_client : ContextClient, # pylint: disable=redefined-outer-name + device_client : DeviceClient, # pylint: disable=redefined-outer-name +) -> None: + validate_empty_scenario(context_client) + + descriptor_loader = DescriptorLoader( + descriptors_file=DESCRIPTOR_FILE, context_client=context_client, device_client=device_client) + results = descriptor_loader.process() + check_descriptor_load_results(results, descriptor_loader) + # descriptor_loader.validate() + + # Verify the scenario has no services/slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.service_ids) == 0 + assert len(response.slice_ids) == 0 + +def test_scenario_devices_enabled( + context_client : ContextClient, # pylint: disable=redefined-outer-name +) -> None: + """ + This test validates that the devices are enabled. + """ + DEVICE_OP_STATUS_ENABLED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED + + num_devices = -1 + num_devices_enabled, num_retry = 0, 0 + while (num_devices != num_devices_enabled) and (num_retry < 10): + time.sleep(1.0) + response = context_client.ListDevices(Empty()) + num_devices = len(response.devices) + num_devices_enabled = 0 + for device in response.devices: + if device.device_operational_status != DEVICE_OP_STATUS_ENABLED: continue + num_devices_enabled += 1 + LOGGER.info('Num Devices enabled: {:d}/{:d}'.format(num_devices_enabled, num_devices)) + num_retry += 1 + assert num_devices_enabled == num_devices diff --git a/src/tests/ofc25-camara-e2e-controller/.gitignore b/src/tests/ofc25-camara-e2e-controller/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..24a4b233365e23a9462f4b64e8b60fef6a62bee4 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/.gitignore @@ -0,0 +1,5 @@ +clab-*/ +images/ +*.clab.yml.bak +*.tar +*.tar.gz diff --git a/src/tests/ofc25-camara-e2e-controller/.gitlab-ci.yml b/src/tests/ofc25-camara-e2e-controller/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..c78468180ee8fdf855c0dd096031d49bd53ceec3 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/.gitlab-ci.yml @@ -0,0 +1,101 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# Deploy TeraFlowSDN and Execute end-2-end test +end2end_test ofc25_camara_e2e: + variables: + TEST_NAME: 'ofc25-camara-e2e-controller' + NCE_NAME: 'nce' + AGG_NET_NAME: 'agg_net' + NCE_PORT: '9090' + AGG_NET_PORT: '9091' + stage: end2end_test + # Disable to force running it after all other tasks + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + - HOST_IP=$(kubectl get nodes -o json | jq -r '.items[].status.addresses[] | select(.type=="InternalIP") | .address') + - sed -i "s/AGG_NET_IP/${HOST_IP}/g" src/tests/${TEST_NAME}/data/camara-e2e-topology.json + - sed -i "s/NCE_IP/${HOST_IP}/g" src/tests/${TEST_NAME}/data/camara-e2e-topology.json + - sed -i "s/AGG_NET_PORT/${AGG_NET_PORT}/g" src/tests/${TEST_NAME}/data/camara-e2e-topology.json + - sed -i "s/NCE_PORT/${NCE_PORT}/g" src/tests/${TEST_NAME}/data/camara-e2e-topology.json + - docker buildx build -t "${TEST_NAME}:latest" -f ./src/tests/${TEST_NAME}/Dockerfile . + - docker buildx build -t "${NCE_NAME}:latest" -f ./src/tests/tools/mock_nce_ctrl/Dockerfile ./src/tests/tools/mock_nce_ctrl + - docker buildx build -t "${AGG_NET_NAME}:latest" -f ./src/tests/tools/mock_ietf_network_slice_sdn_ctrl/Dockerfile ./src/tests/tools/mock_ietf_network_slice_sdn_ctrl + - docker rm -f ${TEST_NAME} || true + - docker rm -f ${AGG_NET_NAME} || true + - docker rm -f ${NCE_NAME} || true + - docker run -d --name ${NCE_NAME} -p ${NCE_PORT}:8443 ${NCE_NAME}:latest + - docker run -d --name ${AGG_NET_NAME} -p ${AGG_NET_PORT}:8443 ${AGG_NET_NAME}:latest + + script: + # Check MicroK8s is ready + - microk8s status --wait-ready + - kubectl get pods --all-namespaces + + - source src/tests/${TEST_NAME}/deploy_specs.sh + + # Deploy TeraFlowSDN + - ./deploy/crdb.sh + - ./deploy/nats.sh + - ./deploy/qdb.sh + - ./deploy/kafka.sh + - ./deploy/tfs.sh + - ./deploy/show.sh + + - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server + + # Run end-to-end test: onboard scenario + - > + docker run -t --rm --name ${TEST_NAME} --network=host + --volume "$PWD/tfs_runtime_env_vars.sh:/var/teraflow/tfs_runtime_env_vars.sh" + --volume "$PWD/src/tests/${TEST_NAME}:/opt/results" + ${TEST_NAME}:latest /var/teraflow/run-onboarding.sh + + # Run end-to-end test: configure service TFS + - > + docker run -t --rm --name ${TEST_NAME} --network=host + --volume "$PWD/tfs_runtime_env_vars.sh:/var/teraflow/tfs_runtime_env_vars.sh" + --volume "$PWD/src/tests/${TEST_NAME}:/opt/results" + ${TEST_NAME}:latest /var/teraflow/run-e2e-ietf-slice-operations.sh + + after_script: + - kubectl --namespace tfs logs deployment/contextservice -c server + - kubectl --namespace tfs logs deployment/deviceservice -c server + - kubectl --namespace tfs logs deployment/pathcompservice -c frontend + - kubectl --namespace tfs logs deployment/serviceservice -c server + - kubectl --namespace tfs logs deployment/sliceservice -c server + - kubectl --namespace tfs logs deployment/nbiservice -c server + + - docker logs ${NCE_NAME} + + - docker logs ${AGG_NET_NAME} + + # Destroy Scenario + - kubectl delete namespaces tfs || true + + - docker rm -f ${TEST_NAME} || true + - docker rm -f ${AGG_NET_NAME} || true + - docker rm -f ${NCE_NAME} || true + + # Clean old docker images + - docker images --filter="dangling=true" --quiet | xargs -r docker rmi + + #coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' + artifacts: + when: always + reports: + junit: ./src/tests/${TEST_NAME}/report_*.xml diff --git a/src/tests/ofc25-camara-e2e-controller/Dockerfile b/src/tests/ofc25-camara-e2e-controller/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..79b709c13dc352bb853bbe626183e72d37a074f0 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/Dockerfile @@ -0,0 +1,84 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (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 + +# Install dependencies +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install wget g++ git && \ + rm -rf /var/lib/apt/lists/* + +# 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 + +# Get common Python packages +# Note: this step enables sharing the previous Docker build steps among all the Python components +WORKDIR /var/teraflow +COPY common_requirements.in common_requirements.in +RUN pip-compile --quiet --output-file=common_requirements.txt common_requirements.in +RUN python3 -m pip install -r common_requirements.txt + +# Add common files into working directory +WORKDIR /var/teraflow/common +COPY src/common/. ./ +RUN rm -rf proto + +# Create proto sub-folder, copy .proto files, and generate Python code +RUN mkdir -p /var/teraflow/common/proto +WORKDIR /var/teraflow/common/proto +RUN touch __init__.py +COPY proto/*.proto ./ +RUN python3 -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. *.proto +RUN rm *.proto +RUN find . -type f -exec sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' {} \; + +# Create component sub-folders, get specific Python packages +RUN mkdir -p /var/teraflow/tests/ofc25-camara-e2e-controller +WORKDIR /var/teraflow/tests/ofc25-camara-e2e-controller +COPY src/tests/ofc25-camara-e2e-controller/requirements.in requirements.in +RUN pip-compile --quiet --output-file=requirements.txt requirements.in +RUN python3 -m pip install -r requirements.txt + +# Add component files into working directory +WORKDIR /var/teraflow +COPY src/__init__.py ./__init__.py +COPY src/common/*.py ./common/ +COPY src/common/tests/. ./common/tests/ +COPY src/common/tools/. ./common/tools/ +COPY src/context/__init__.py context/__init__.py +COPY src/context/client/. context/client/ +COPY src/device/__init__.py device/__init__.py +COPY src/device/client/. device/client/ +COPY src/monitoring/__init__.py monitoring/__init__.py +COPY src/monitoring/client/. monitoring/client/ +COPY src/service/__init__.py service/__init__.py +COPY src/service/client/. service/client/ +COPY src/slice/__init__.py slice/__init__.py +COPY src/slice/client/. slice/client/ +COPY src/tests/*.py ./tests/ +COPY src/tests/ofc25-camara-e2e-controller/__init__.py ./tests/ofc25-camara-e2e-controller/__init__.py +COPY src/tests/ofc25-camara-e2e-controller/data/. ./tests/ofc25-camara-e2e-controller/data/ +COPY src/tests/ofc25-camara-e2e-controller/tests/. ./tests/ofc25-camara-e2e-controller/tests/ +COPY src/tests/ofc25-camara-e2e-controller/scripts/. ./ + +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install tree && \ + rm -rf /var/lib/apt/lists/* + +RUN tree -la /var/teraflow diff --git a/src/tests/ofc25-camara-e2e-controller/__init__.py b/src/tests/ofc25-camara-e2e-controller/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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/ofc25-camara-e2e-controller/data/camara-e2e-topology.json b/src/tests/ofc25-camara-e2e-controller/data/camara-e2e-topology.json new file mode 100644 index 0000000000000000000000000000000000000000..b2a8617e2c3e211a3e0ef1facb05b40788aa82cf --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/camara-e2e-topology.json @@ -0,0 +1,1725 @@ +{ + "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": "ip-transport-controller" + } + }, + "name": "ip-transport-controller", + "device_type": "ietf-slice", + "device_operational_status": 1, + "device_drivers": [ + 14 + ], + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "AGG_NET_IP" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "AGG_NET_PORT" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "endpoints": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + } + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": false + } + } + } + ] + }, + "device_endpoints": [] + }, + { + "device_id": { + "device_uuid": { + "uuid": "agg-net-controller" + } + }, + "name": "agg-net-controller", + "device_type": "ietf-slice", + "device_operational_status": 1, + "device_drivers": [ + 14 + ], + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "AGG_NET_IP" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "AGG_NET_PORT" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "endpoints": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + } + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": false + } + } + } + ] + }, + "device_endpoints": [] + }, + { + "device_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "name": "nce-controller", + "device_type": "nce", + "device_operational_status": 1, + "device_drivers": [ + 15 + ], + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "NCE_IP" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "NCE_PORT" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "endpoints": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + } + ], + "scheme": "http", + "username": "admin", + "password": "admin", + "base_url": "/restconf/v2/data", + "timeout": 120, + "verify": false + } + } + } + ] + }, + "device_endpoints": [] + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "name": "172.16.182.25", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 0, + 14 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "128.32.33.254", + "address_prefix": "24", + "site_location": "access", + "mtu": "1500" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "name": "172.16.185.31", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 0, + 14 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "name": "172.16.185.33", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 0, + 14 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "name": "172.16.185.32", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 0, + 14 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "ce-ip": "172.10.33.2", + "address_ip": "172.10.33.254", + "address_prefix": "24", + "site_location": "cloud", + "mtu": "1500" + }, + { + "uuid": "500", + "name": "500", + "type": "optical" + }, + { + "uuid": "501", + "name": "501", + "type": "optical" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "name": "172.16.58.10", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 15 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "0.0.0.0", + "address_prefix": "24" + }, + { + "uuid": "201", + "name": "201", + "type": "optical", + "address_ip": "0.0.0.0", + "address_prefix": "24" + }, + { + "uuid": "500", + "name": "500", + "type": "optical", + "address_ip": "128.32.33.2", + "address_prefix": "24" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "name": "172.16.61.10", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 15 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "0.0.0.0", + "address_prefix": "24" + }, + { + "uuid": "500", + "name": "500", + "type": "optical", + "address_ip": "128.32.33.2", + "address_prefix": "24" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "name": "172.16.61.11", + "device_type": "emu-packet-router", + "controller_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "device_operational_status": 1, + "device_drivers": [ + 15 + ], + "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": [ + { + "uuid": "mgmt", + "name": "mgmt", + "type": "mgmt" + }, + { + "uuid": "200", + "name": "200", + "type": "optical", + "address_ip": "0.0.0.0", + "address_prefix": "24" + }, + { + "uuid": "500", + "name": "500", + "type": "optical", + "address_ip": "128.32.33.2", + "address_prefix": "24" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.221" + } + }, + "device_type": "emu-datacenter", + "device_drivers": [ + 0 + ], + "device_endpoints": [], + "device_operational_status": 1, + "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", + "uuid": "eth0" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.222" + } + }, + "device_type": "emu-datacenter", + "device_drivers": [ + 0 + ], + "device_endpoints": [], + "device_operational_status": 1, + "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", + "uuid": "eth0" + } + ] + } + } + } + ] + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "device_type": "emu-datacenter", + "device_drivers": [ + 0 + ], + "device_endpoints": [], + "device_operational_status": 1, + "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", + "uuid": "500" + }, + { + "sample_types": [], + "type": "optical", + "uuid": "200" + }, + { + "sample_types": [], + "type": "optical", + "uuid": "201" + } + ] + } + } + } + ] + } + } + ], + "links": [ + { + "link_id": { + "link_uuid": { + "uuid": "agg-net-controller/mgmt==ip-transport-controller/mgmt" + } + }, + "name": "agg-net-controller/mgmt==ip-transport-controller/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "agg-net-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "nce-controller/mgmt==172.16.61.11/mgmt" + } + }, + "name": "nce-controller/mgmt==172.16.61.11/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "nce-controller/mgmt==172.16.61.10/mgmt" + } + }, + "name": "nce-controller/mgmt==172.16.61.10/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "nce-controller/mgmt==172.16.58.10/mgmt" + } + }, + "name": "nce-controller/mgmt==172.16.58.10/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "nce-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-transport-controller/mgmt==172.16.185.33/mgmt" + } + }, + "name": "ip-transport-controller/mgmt==172.16.185.33/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-transport-controller/mgmt==172.16.185.31/mgmt" + } + }, + "name": "ip-transport-controller/mgmt==172.16.185.31/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-transport-controller/mgmt==172.16.185.32/mgmt" + } + }, + "name": "ip-transport-controller/mgmt==172.16.185.32/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "ip-transport-controller/mgmt==172.16.182.25/mgmt" + } + }, + "name": "ip-transport-controller/mgmt==172.16.182.25/mgmt", + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "ip-transport-controller" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-500" + } + }, + "name": "172.16.182.25-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-500" + } + }, + "name": "172.16.185.33-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-501" + } + }, + "name": "172.16.182.25-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-501" + } + }, + "name": "172.16.185.31-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.31-500" + } + }, + "name": "172.16.185.31-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-500" + } + }, + "name": "172.16.185.32-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.31" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.33-501" + } + }, + "name": "172.16.185.33-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-501" + } + }, + "name": "172.16.185.32-501", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.33" + } + }, + "endpoint_uuid": { + "uuid": "501" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.185.32-200" + } + }, + "name": "172.16.185.32-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.204.220-500" + } + }, + "name": "172.16.204.220-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.204.220" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.185.32" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.182.25-200" + } + }, + "name": "172.16.182.25-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.58.10-500" + } + }, + "name": "172.16.58.10-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.182.25" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.58.10-200" + } + }, + "name": "172.16.58.10-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.61.10-500" + } + }, + "name": "172.16.61.10-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.58.10-201" + } + }, + "name": "172.16.58.10-201", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "201" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.61.11-500" + } + }, + "name": "172.16.61.11-500", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "500" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.58.10" + } + }, + "endpoint_uuid": { + "uuid": "201" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.61.10-200" + } + }, + "name": "172.16.61.10-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.221" + } + }, + "endpoint_uuid": { + "uuid": "eth0" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.104.221-eth0" + } + }, + "name": "172.16.104.221-eth0", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.221" + } + }, + "endpoint_uuid": { + "uuid": "eth0" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.10" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.61.11-200" + } + }, + "name": "172.16.61.11-200", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.222" + } + }, + "endpoint_uuid": { + "uuid": "eth0" + } + } + ] + }, + { + "link_id": { + "link_uuid": { + "uuid": "172.16.104.222-eth0" + } + }, + "name": "172.16.104.222-eth0", + "attributes": { + "total_capacity_gbps": 10, + "used_capacity_gbps": 0 + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "172.16.104.222" + } + }, + "endpoint_uuid": { + "uuid": "eth0" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "172.16.61.11" + } + }, + "endpoint_uuid": { + "uuid": "200" + } + } + ] + } + ] +} diff --git a/src/tests/ofc25-camara-e2e-controller/data/slice/post_connection_group_to_network_slice1.json b/src/tests/ofc25-camara-e2e-controller/data/slice/post_connection_group_to_network_slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..d39a837bd8c3719463e8ecfd3fbfc2d25111afef --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/slice/post_connection_group_to_network_slice1.json @@ -0,0 +1,62 @@ +{ + "connection-group": [ + { + "id": "line2", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "3", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "10" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "5000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-sender-sdp": "3", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "20" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "1000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc25-camara-e2e-controller/data/slice/post_connection_group_to_network_slice2.json b/src/tests/ofc25-camara-e2e-controller/data/slice/post_connection_group_to_network_slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..d39a837bd8c3719463e8ecfd3fbfc2d25111afef --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/slice/post_connection_group_to_network_slice2.json @@ -0,0 +1,62 @@ +{ + "connection-group": [ + { + "id": "line2", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "3", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "10" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "5000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-sender-sdp": "3", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "20" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "1000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc25-camara-e2e-controller/data/slice/post_match_criteria_to_sdp1_in_slice1.json b/src/tests/ofc25-camara-e2e-controller/data/slice/post_match_criteria_to_sdp1_in_slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..16a36d45b86230b27eafa45a612b95c248a7b3ac --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/slice/post_match_criteria_to_sdp1_in_slice1.json @@ -0,0 +1,40 @@ +{ + "match-criterion": [ + { + "index": 2, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "101" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line2" + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc25-camara-e2e-controller/data/slice/post_match_criteria_to_sdp1_in_slice2.json b/src/tests/ofc25-camara-e2e-controller/data/slice/post_match_criteria_to_sdp1_in_slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..8ceefdc2f2471af225143e5a1def2d7ba71e2ab1 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/slice/post_match_criteria_to_sdp1_in_slice2.json @@ -0,0 +1,40 @@ +{ + "match-criterion": [ + { + "index": 2, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "201" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line2" + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc25-camara-e2e-controller/data/slice/post_network_slice1.json b/src/tests/ofc25-camara-e2e-controller/data/slice/post_network_slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..e6e0ee90a25ff12f73c8f8896f9c2c74ab6b4019 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/slice/post_network_slice1.json @@ -0,0 +1,188 @@ +{ + "slice-service": [ + { + "id": "slice1", + "description": "network slice 1, connect to VM1", + "sdps": { + "sdp": [ + { + "id": "1", + "node-id": "172.16.204.220", + "sdp-ip-address": [ + "172.16.204.220" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "101" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC POP to VM1", + "description": "AC VM1 connected to POP", + "ac-node-id": "172.16.204.220", + "ac-tp-id": "200" + } + ] + } + }, + { + "id": "2", + "node-id": "172.16.61.10", + "sdp-ip-address": [ + "172.16.61.10" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "21" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC ONT", + "description": "AC connected to PC1", + "ac-node-id": "172.16.61.10", + "ac-tp-id": "200" + } + ] + } + } + ] + }, + "connection-groups": { + "connection-group": [ + { + "id": "line1", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "10" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "5000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-sender-sdp": "2", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "20" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "1000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc25-camara-e2e-controller/data/slice/post_network_slice2.json b/src/tests/ofc25-camara-e2e-controller/data/slice/post_network_slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..97e6ade27449be0a3816085aa31b707ffbb6f813 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/slice/post_network_slice2.json @@ -0,0 +1,189 @@ +{ + "slice-service": [ + { + "id": "slice2", + "description": "network slice 2, connect to VM2", + "sdps": { + "sdp": [ + { + "id": "1", + "node-id": "172.16.204.220", + "sdp-ip-address": [ + "172.16.204.220" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "201" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC POP to VM2", + "description": "AC VM2 connected to POP", + "ac-node-id": "172.16.204.220", + "ac-tp-id": "201" + } + ] + } + }, + { + "id": "2", + "node-id": "172.16.61.10", + "sdp-ip-address": [ + "172.16.61.10" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "31" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC ONT", + "description": "AC connected to PC", + "ac-node-id": "172.16.61.10", + "ac-tp-id": "200", + "ac-ipv4-address": "172.16.61.10" + } + ] + } + } + ] + }, + "connection-groups": { + "connection-group": [ + { + "id": "line1", + "connectivity-type": "point-to-point", + "connectivity-construct": [ + { + "id": 1, + "p2p-sender-sdp": "1", + "p2p-receiver-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "10" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "5000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-sender-sdp": "2", + "p2p-receiver-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds", + "bound": "20" + }, + { + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps", + "bound": "1000" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ] + } + ] + } + } + ] +} diff --git a/src/tests/ofc25-camara-e2e-controller/data/slice/post_sdp_to_network_slice1.json b/src/tests/ofc25-camara-e2e-controller/data/slice/post_sdp_to_network_slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..bd3895fc4ae5a9a0b2059be3f6b31a05451abd22 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/slice/post_sdp_to_network_slice1.json @@ -0,0 +1,61 @@ +{ + "sdp": [ + { + "id": "3", + "node-id": "172.16.61.11", + "sdp-ip-address": [ + "172.16.61.11" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "21" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line2" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC ONT", + "description": "AC connected to PC2", + "ac-node-id": "172.16.61.11", + "ac-tp-id": "200" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc25-camara-e2e-controller/data/slice/post_sdp_to_network_slice2.json b/src/tests/ofc25-camara-e2e-controller/data/slice/post_sdp_to_network_slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..0b147125bd7eb3efc84c87bebab919639782f760 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/slice/post_sdp_to_network_slice2.json @@ -0,0 +1,62 @@ +{ + "sdp": [ + { + "id": "3", + "node-id": "172.16.61.11", + "sdp-ip-address": [ + "172.16.61.11" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "31" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line2" + } + ] + }, + "attachment-circuits": { + "attachment-circuit": [ + { + "id": "AC ONT", + "description": "AC connected to PC2", + "ac-node-id": "172.16.61.11", + "ac-tp-id": "200", + "ac-ipv4-address": "172.16.61.11" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc25-camara-e2e-controller/data/target-full-ietf-slice.json b/src/tests/ofc25-camara-e2e-controller/data/target-full-ietf-slice.json new file mode 100644 index 0000000000000000000000000000000000000000..c99876ad9e00e2f94ce44d17b2376f61282e60d7 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/target-full-ietf-slice.json @@ -0,0 +1,678 @@ +{ + "network-slice-services": { + "slice-service": [ + { + "connection-groups": { + "connection-group": [ + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": "10", + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": "5000", + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": "20", + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": "1000", + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + }, + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "3", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": "10", + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": "5000", + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "3", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": "20", + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": "1000", + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line2" + } + ] + }, + "description": "network slice 2, connect to VM2", + "id": "slice2", + "sdps": { + "sdp": [ + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.204.220", + "ac-tp-id": "201", + "description": "AC VM2 connected to POP", + "id": "AC POP to VM2" + } + ] + }, + "id": "1", + "node-id": "172.16.204.220", + "sdp-ip-address": [ + "172.16.204.220" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "201" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + }, + { + "index": 2, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "201" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line2" + } + ] + } + }, + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-ipv4-address": "172.16.61.10", + "ac-node-id": "172.16.61.10", + "ac-tp-id": "200", + "description": "AC connected to PC", + "id": "AC ONT" + } + ] + }, + "id": "2", + "node-id": "172.16.61.10", + "sdp-ip-address": [ + "172.16.61.10" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "31" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + }, + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-ipv4-address": "172.16.61.11", + "ac-node-id": "172.16.61.11", + "ac-tp-id": "200", + "description": "AC connected to PC2", + "id": "AC ONT" + } + ] + }, + "id": "3", + "node-id": "172.16.61.11", + "sdp-ip-address": [ + "172.16.61.11" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "31" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line2" + } + ] + } + } + ] + } + }, + { + "connection-groups": { + "connection-group": [ + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": "10", + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": "5000", + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": "20", + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": "1000", + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + }, + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "3", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": "10", + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": "5000", + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "3", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": "20", + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": "1000", + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": "0.001" + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line2" + } + ] + }, + "description": "network slice 1, connect to VM1", + "id": "slice1", + "sdps": { + "sdp": [ + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.204.220", + "ac-tp-id": "200", + "description": "AC VM1 connected to POP", + "id": "AC POP to VM1" + } + ] + }, + "id": "1", + "node-id": "172.16.204.220", + "sdp-ip-address": [ + "172.16.204.220" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "101" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + }, + { + "index": 2, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "101" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line2" + } + ] + } + }, + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.61.10", + "ac-tp-id": "200", + "description": "AC connected to PC1", + "id": "AC ONT" + } + ] + }, + "id": "2", + "node-id": "172.16.61.10", + "sdp-ip-address": [ + "172.16.61.10" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "21" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + }, + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.61.11", + "ac-tp-id": "200", + "description": "AC connected to PC2", + "id": "AC ONT" + } + ] + }, + "id": "3", + "node-id": "172.16.61.11", + "sdp-ip-address": [ + "172.16.61.11" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "21" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.222/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line2" + } + ] + } + } + ] + } + } + ] + } +} diff --git a/src/tests/ofc25-camara-e2e-controller/data/target-ietf-slice-posted-slices.json b/src/tests/ofc25-camara-e2e-controller/data/target-ietf-slice-posted-slices.json new file mode 100644 index 0000000000000000000000000000000000000000..004d3cafff0decf4cbe550f555e99b2229702b07 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/target-ietf-slice-posted-slices.json @@ -0,0 +1,382 @@ +[ + { + "network-slice-services": { + "slice-service": [ + { + "connection-groups": { + "connection-group": [ + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 5000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 1000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + } + ] + }, + "description": "dsc", + "id": "slice1", + "sdps": { + "sdp": [ + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.185.32", + "ac-tp-id": "200", + "description": "dsc", + "id": "0" + } + ] + }, + "id": "1", + "node-id": "172.16.185.32", + "sdp-ip-address": [ + "172.16.185.32" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "101" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + }, + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.182.25", + "ac-tp-id": "200", + "description": "dsc", + "id": "0" + } + ] + }, + "id": "2", + "node-id": "172.16.182.25", + "sdp-ip-address": [ + "172.16.182.25" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "21" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.101.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + } + ] + } + } + ] + } + }, + { + "network-slice-services": { + "slice-service": [ + { + "connection-groups": { + "connection-group": [ + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 5000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 1000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + } + ] + }, + "description": "dsc", + "id": "slice2", + "sdps": { + "sdp": [ + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.185.32", + "ac-tp-id": "200", + "description": "dsc", + "id": "0" + } + ] + }, + "id": "1", + "node-id": "172.16.185.32", + "sdp-ip-address": [ + "172.16.185.32" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "201" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10200" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10500" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + }, + { + "attachment-circuits": { + "attachment-circuit": [ + { + "ac-node-id": "172.16.182.25", + "ac-tp-id": "200", + "description": "dsc", + "id": "0" + } + ] + }, + "id": "2", + "node-id": "172.16.182.25", + "sdp-ip-address": [ + "172.16.182.25" + ], + "service-match-criteria": { + "match-criterion": [ + { + "index": 1, + "match-type": [ + { + "type": "ietf-network-slice-service:vlan", + "value": [ + "31" + ] + }, + { + "type": "ietf-network-slice-service:source-ip-prefix", + "value": [ + "172.16.104.221/24" + ] + }, + { + "type": "ietf-network-slice-service:source-tcp-port", + "value": [ + "10500" + ] + }, + { + "type": "ietf-network-slice-service:destination-ip-prefix", + "value": [ + "172.1.201.22/24" + ] + }, + { + "type": "ietf-network-slice-service:destination-tcp-port", + "value": [ + "10200" + ] + } + ], + "target-connection-group-id": "line1" + } + ] + } + } + ] + } + } + ] + } + } +] diff --git a/src/tests/ofc25-camara-e2e-controller/data/target-ietf-slice-put-connection-groups.json b/src/tests/ofc25-camara-e2e-controller/data/target-ietf-slice-put-connection-groups.json new file mode 100644 index 0000000000000000000000000000000000000000..7526ebd8b92a2eab7f30d94a035ba63f0a503c2d --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/target-ietf-slice-put-connection-groups.json @@ -0,0 +1,234 @@ +[ + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 10000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 2000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + }, + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 10000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 2000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + }, + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 5000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 1000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + }, + { + "connectivity-construct": [ + { + "id": 1, + "p2p-receiver-sdp": "2", + "p2p-sender-sdp": "1", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 10, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 5000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + }, + { + "id": 2, + "p2p-receiver-sdp": "1", + "p2p-sender-sdp": "2", + "service-slo-sle-policy": { + "slo-policy": { + "metric-bound": [ + { + "bound": 20, + "metric-type": "ietf-network-slice-service:one-way-delay-maximum", + "metric-unit": "milliseconds" + }, + { + "bound": 1000, + "metric-type": "ietf-network-slice-service:one-way-bandwidth", + "metric-unit": "Mbps" + }, + { + "metric-type": "ietf-network-slice-service:two-way-packet-loss", + "metric-unit": "percentage", + "percentile-value": 0.001 + } + ] + } + } + } + ], + "connectivity-type": "point-to-point", + "id": "line1" + } +] diff --git a/src/tests/ofc25-camara-e2e-controller/data/target-nce-app-flows.json b/src/tests/ofc25-camara-e2e-controller/data/target-nce-app-flows.json new file mode 100644 index 0000000000000000000000000000000000000000..66f7ed2543f7f5472e50ccda9755dd965bc73452 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/target-nce-app-flows.json @@ -0,0 +1,58 @@ +{ + "App_Flow_2_1_slice1": { + "app-flow": [ + { + "app-name": "App_Flow_2_1_slice1", + "duration": 9999, + "max-online-users": 1, + "name": "App_Flow_2_1_slice1", + "qos-profile": "AR_VR_Gaming", + "service-profile": "service_2_1_slice1", + "stas": "00:3D:E1:18:82:9E", + "user-id": "a8b5b840-1548-46c9-892e-5c18f9ec8d99" + } + ] + }, + "App_Flow_3_1_slice1": { + "app-flow": [ + { + "app-name": "App_Flow_3_1_slice1", + "duration": 9999, + "max-online-users": 1, + "name": "App_Flow_3_1_slice1", + "qos-profile": "AR_VR_Gaming", + "service-profile": "service_3_1_slice1", + "stas": "00:3D:E1:18:82:9E", + "user-id": "28a1c47b-0179-4ab8-85da-632e6f946491" + } + ] + }, + "App_Flow_2_1_slice2": { + "app-flow": [ + { + "app-name": "App_Flow_2_1_slice2", + "duration": 9999, + "max-online-users": 1, + "name": "App_Flow_2_1_slice2", + "qos-profile": "AR_VR_Gaming", + "service-profile": "service_2_1_slice2", + "stas": "00:3D:E1:18:82:9E", + "user-id": "9df7b98a-d5c0-43d4-bd0e-8d81ee4485f0" + } + ] + }, + "App_Flow_3_1_slice2": { + "app-flow": [ + { + "app-name": "App_Flow_3_1_slice2", + "duration": 9999, + "max-online-users": 1, + "name": "App_Flow_3_1_slice2", + "qos-profile": "AR_VR_Gaming", + "service-profile": "service_3_1_slice2", + "stas": "00:3D:E1:18:82:9E", + "user-id": "79b2becb-8500-42cc-b6be-c27c2ea60b22" + } + ] + } +} diff --git a/src/tests/ofc25-camara-e2e-controller/data/target-nce-apps.json b/src/tests/ofc25-camara-e2e-controller/data/target-nce-apps.json new file mode 100644 index 0000000000000000000000000000000000000000..11a25897910cbbfd7fb666ddd86babc1972e7052 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/data/target-nce-apps.json @@ -0,0 +1,82 @@ +{ + "App_Flow_2_1_slice1": { + "application": [ + { + "app-features": { + "app-feature": [ + { + "dest-ip": "172.1.101.22", + "dest-port": "10200", + "id": "feature_2_1_slice1", + "protocol": "tcp", + "src-ip": "172.16.104.221", + "src-port": "10500" + } + ] + }, + "app-id": "app_2_1_slice1", + "name": "App_Flow_2_1_slice1" + } + ] + }, + "App_Flow_3_1_slice1": { + "application": [ + { + "app-features": { + "app-feature": [ + { + "dest-ip": "172.1.101.22", + "dest-port": "10200", + "id": "feature_3_1_slice1", + "protocol": "tcp", + "src-ip": "172.16.104.222", + "src-port": "10500" + } + ] + }, + "app-id": "app_3_1_slice1", + "name": "App_Flow_3_1_slice1" + } + ] + }, + "App_Flow_2_1_slice2": { + "application": [ + { + "app-features": { + "app-feature": [ + { + "dest-ip": "172.1.201.22", + "dest-port": "10200", + "id": "feature_2_1_slice2", + "protocol": "tcp", + "src-ip": "172.16.104.221", + "src-port": "10500" + } + ] + }, + "app-id": "app_2_1_slice2", + "name": "App_Flow_2_1_slice2" + } + ] + }, + "App_Flow_3_1_slice2": { + "application": [ + { + "app-features": { + "app-feature": [ + { + "dest-ip": "172.1.201.22", + "dest-port": "10200", + "id": "feature_3_1_slice2", + "protocol": "tcp", + "src-ip": "172.16.104.222", + "src-port": "10500" + } + ] + }, + "app-id": "app_3_1_slice2", + "name": "App_Flow_3_1_slice2" + } + ] + } +} diff --git a/src/tests/ofc25-camara-e2e-controller/deploy_specs.sh b/src/tests/ofc25-camara-e2e-controller/deploy_specs.sh new file mode 100755 index 0000000000000000000000000000000000000000..9ae83e7b126aa2913cd3c30887292b4626dd5855 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/deploy_specs.sh @@ -0,0 +1,208 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. +#export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_generator" +export TFS_COMPONENTS="context device pathcomp service slice nbi" + +# Uncomment to activate Monitoring (old) +#export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" + +# Uncomment to activate Monitoring Framework (new) +#export TFS_COMPONENTS="${TFS_COMPONENTS} kpi_manager kpi_value_writer kpi_value_api telemetry analytics automation" + +# Uncomment to activate QoS Profiles +#export TFS_COMPONENTS="${TFS_COMPONENTS} qos_profile" + +# Uncomment to activate BGP-LS Speaker +#export TFS_COMPONENTS="${TFS_COMPONENTS} bgpls_speaker" + +# Uncomment to activate Optical Controller +# To manage optical connections, "service" requires "opticalcontroller" to be deployed +# before "service", thus we "hack" the TFS_COMPONENTS environment variable prepending the +# "opticalcontroller" only if "service" is already in TFS_COMPONENTS, and re-export it. +#if [[ "$TFS_COMPONENTS" == *"service"* ]]; then +# BEFORE="${TFS_COMPONENTS% service*}" +# AFTER="${TFS_COMPONENTS#* service}" +# export TFS_COMPONENTS="${BEFORE} opticalcontroller service ${AFTER}" +#fi + +# Uncomment to activate ZTP +#export TFS_COMPONENTS="${TFS_COMPONENTS} ztp" + +# Uncomment to activate Policy Manager +#export TFS_COMPONENTS="${TFS_COMPONENTS} policy" + +# Uncomment to activate Optical CyberSecurity +#export TFS_COMPONENTS="${TFS_COMPONENTS} dbscanserving opticalattackmitigator opticalattackdetector opticalattackmanager" + +# Uncomment to activate L3 CyberSecurity +#export TFS_COMPONENTS="${TFS_COMPONENTS} l3_attackmitigator l3_centralizedattackdetector" + +# Uncomment to activate TE +#export TFS_COMPONENTS="${TFS_COMPONENTS} te" + +# Uncomment to activate Forecaster +#export TFS_COMPONENTS="${TFS_COMPONENTS} forecaster" + +# Uncomment to activate E2E Orchestrator +#export TFS_COMPONENTS="${TFS_COMPONENTS} e2e_orchestrator" + +# Uncomment to activate DLT and Interdomain +#export TFS_COMPONENTS="${TFS_COMPONENTS} interdomain dlt" +#if [[ "$TFS_COMPONENTS" == *"dlt"* ]]; then +# export KEY_DIRECTORY_PATH="src/dlt/gateway/keys/priv_sk" +# export CERT_DIRECTORY_PATH="src/dlt/gateway/keys/cert.pem" +# export TLS_CERT_PATH="src/dlt/gateway/keys/ca.crt" +#fi + +# Uncomment to activate QKD App +# To manage QKD Apps, "service" requires "qkd_app" to be deployed +# before "service", thus we "hack" the TFS_COMPONENTS environment variable prepending the +# "qkd_app" only if "service" is already in TFS_COMPONENTS, and re-export it. +#if [[ "$TFS_COMPONENTS" == *"service"* ]]; then +# BEFORE="${TFS_COMPONENTS% service*}" +# AFTER="${TFS_COMPONENTS#* service}" +# export TFS_COMPONENTS="${BEFORE} qkd_app service ${AFTER}" +#fi + + +# 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" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" + +# Uncomment to monitor performance of components +#export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/servicemonitors.yaml" + +# Uncomment when deploying Optical CyberSecurity +#export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/cachingservice.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 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" + +# 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" + +# Set NATS installation mode to 'single'. This option is convenient for development and testing. +# See ./deploy/all.sh or ./deploy/nats.sh for additional details +export NATS_DEPLOY_MODE="single" + +# Disable flag for re-deploying NATS from scratch. +export NATS_REDEPLOY="" + + +# ----- QuestDB ---------------------------------------------------------------- + +# 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" + +# 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="" + + +# ----- K8s Observability ------------------------------------------------------ + +# Set the external port Prometheus Mgmt HTTP GUI interface will be exposed to. +export PROM_EXT_PORT_HTTP="9090" + +# Set the external port Grafana HTTP Dashboards will be exposed to. +export GRAF_EXT_PORT_HTTP="3000" + + +# ----- Apache Kafka ----------------------------------------------------------- + +# Set the namespace where Apache Kafka will be deployed. +export KFK_NAMESPACE="kafka" + +# Set the port Apache Kafka server will be exposed to. +export KFK_SERVER_PORT="9092" + +# Set the flag to YES for redeploying of Apache Kafka +export KFK_REDEPLOY="" diff --git a/src/tests/ofc25-camara-e2e-controller/redeploy-tfs.sh b/src/tests/ofc25-camara-e2e-controller/redeploy-tfs.sh new file mode 100755 index 0000000000000000000000000000000000000000..d030596d707c5f49c14bc0f43a654bda3159b688 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/redeploy-tfs.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +source ~/tfs-ctrl/src/tests/ofc25-camara-e2e-controller/deploy_specs.sh +./deploy/all.sh diff --git a/src/tests/ofc25-camara-e2e-controller/requirements.in b/src/tests/ofc25-camara-e2e-controller/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..1bdaec9997da4b83fa89c1bf0d00d4c3a73558a4 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/requirements.in @@ -0,0 +1,30 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +deepdiff==6.7.* +requests==2.27.* + +coverage==6.3 +grpcio==1.47.* +grpcio-health-checking==1.47.* +grpcio-reflection==1.47.* +grpcio-tools==1.47.* +grpclib==0.4.4 +prettytable==3.5.0 +prometheus-client==0.13.0 +protobuf==3.20.* +pytest==6.2.5 +pytest-benchmark==3.4.1 +python-dateutil==2.8.2 +pytest-depends==1.0.1 diff --git a/src/tests/ofc25-camara-e2e-controller/scripts/run-e2e-ietf-slice-operations.sh b/src/tests/ofc25-camara-e2e-controller/scripts/run-e2e-ietf-slice-operations.sh new file mode 100755 index 0000000000000000000000000000000000000000..6c41a85db2d5363971531c814283edbf3ebc0d62 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/scripts/run-e2e-ietf-slice-operations.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +source /var/teraflow/tfs_runtime_env_vars.sh +export PYTHONPATH=/var/teraflow +pytest --verbose --log-level=INFO \ + --junitxml=/opt/results/report_e2e_ietf_slice_operations.xml \ + /var/teraflow/tests/ofc25-camara-e2e-controller/tests/test_e2e_ietf_slice_operations.py diff --git a/src/tests/ofc25-camara-e2e-controller/scripts/run-onboarding.sh b/src/tests/ofc25-camara-e2e-controller/scripts/run-onboarding.sh new file mode 100755 index 0000000000000000000000000000000000000000..397d98d4b4db7952b2b879dcd1977c1a43cc30a2 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/scripts/run-onboarding.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +source /var/teraflow/tfs_runtime_env_vars.sh +export PYTHONPATH=/var/teraflow +pytest --verbose --log-level=INFO \ + --junitxml=/opt/results/report_onboarding.xml \ + /var/teraflow/tests/ofc25-camara-e2e-controller/tests/test_onboarding.py diff --git a/src/tests/ofc25-camara-e2e-controller/tests/Fixtures.py b/src/tests/ofc25-camara-e2e-controller/tests/Fixtures.py new file mode 100644 index 0000000000000000000000000000000000000000..15978851faae668339fa4eed6db8ab7e1be2eb5e --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/tests/Fixtures.py @@ -0,0 +1,43 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (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 pytest +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from monitoring.client.MonitoringClient import MonitoringClient +from service.client.ServiceClient import ServiceClient + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient() + yield _client + _client.close() + +@pytest.fixture(scope='session') +def device_client(): + _client = DeviceClient() + yield _client + _client.close() + +@pytest.fixture(scope='session') +def monitoring_client(): + _client = MonitoringClient() + yield _client + _client.close() + +@pytest.fixture(scope='session') +def service_client(): + _client = ServiceClient() + yield _client + _client.close() diff --git a/src/tests/ofc25-camara-e2e-controller/tests/Tools.py b/src/tests/ofc25-camara-e2e-controller/tests/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..cd2add49edd23f4c169b5fdc3c5123b2b31daa8d --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/tests/Tools.py @@ -0,0 +1,109 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (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 enum, logging, requests +from typing import Any, Dict, List, Optional, Set, Union +from common.Constants import ServiceNameEnum +from common.Settings import get_service_host, get_service_port_http + +NBI_ADDRESS = get_service_host(ServiceNameEnum.NBI) +NBI_PORT = get_service_port_http(ServiceNameEnum.NBI) +NBI_USERNAME = 'admin' +NBI_PASSWORD = 'admin' +NBI_BASE_URL = '' + +class RestRequestMethod(enum.Enum): + GET = 'get' + POST = 'post' + PUT = 'put' + PATCH = 'patch' + DELETE = 'delete' + +EXPECTED_STATUS_CODES : Set[int] = { + requests.codes['OK' ], + requests.codes['CREATED' ], + requests.codes['ACCEPTED' ], + requests.codes['NO_CONTENT'], +} + +def do_rest_request( + method : RestRequestMethod, url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + request_url = 'http://{:s}:{:s}@{:s}:{:d}{:s}{:s}'.format( + NBI_USERNAME, NBI_PASSWORD, NBI_ADDRESS, NBI_PORT, str(NBI_BASE_URL), url + ) + + if logger is not None: + msg = 'Request: {:s} {:s}'.format(str(method.value).upper(), str(request_url)) + if body is not None: msg += ' body={:s}'.format(str(body)) + logger.warning(msg) + reply = requests.request(method.value, request_url, headers={'Content-Type': 'application/json'}, timeout=timeout, json=body, allow_redirects=allow_redirects) + if logger is not None: + logger.warning('Reply: {:s}'.format(str(reply.text))) + assert reply.status_code in expected_status_codes, 'Reply failed with status code {:d}'.format(reply.status_code) + + if reply.content and len(reply.content) > 0: return reply.json() + return None + +def do_rest_get_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.GET, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_post_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.POST, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_put_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.PUT, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_patch_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.PATCH, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_delete_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Optional[Union[Dict, List]]: + return do_rest_request( + RestRequestMethod.DELETE, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) diff --git a/src/tests/ofc25-camara-e2e-controller/tests/__init__.py b/src/tests/ofc25-camara-e2e-controller/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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/ofc25-camara-e2e-controller/tests/test_e2e_ietf_slice_operations.py b/src/tests/ofc25-camara-e2e-controller/tests/test_e2e_ietf_slice_operations.py new file mode 100644 index 0000000000000000000000000000000000000000..cb991edbf1f3cc0768d006ef58725e621ac83de9 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/tests/test_e2e_ietf_slice_operations.py @@ -0,0 +1,478 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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, os +import requests +from deepdiff import DeepDiff + + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +HEADERS = {"Content-Type": "application/json"} + +POST_NETWORK_SLICE1 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "post_network_slice1.json", +) +POST_NETWORK_SLICE2 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "post_network_slice2.json", +) +POST_CONNECTION_GROUP_TO_NETWORK_SLICE1 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "post_connection_group_to_network_slice1.json", +) +POST_CONNECTION_GROUP_TO_NETWORK_SLICE2 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "post_connection_group_to_network_slice2.json", +) +POST_MATCH_CRITERIA_TO_SDP1_IN_SLICE1 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "post_match_criteria_to_sdp1_in_slice1.json", +) +POST_MATCH_CRITERIA_TO_SDP1_IN_SLICE2 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "post_match_criteria_to_sdp1_in_slice2.json", +) +POST_SDP_TO_NETWORK_SLICE1 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "post_sdp_to_network_slice1.json", +) +POST_SDP_TO_NETWORK_SLICE2 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "post_sdp_to_network_slice2.json", +) +TARGET_NCE_APP_FLOWS = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "target-nce-app-flows.json", +) +TARGET_NCE_APPS = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "target-nce-apps.json", +) +TARGET_FULL_IETF_SLICE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "slice", + "target-full-ietf-slice.json", +) +TARGET_FULL_IETF_SLICE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "target-full-ietf-slice.json", +) +TARGET_IETF_SLICE_POSTED_SLICES = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "target-ietf-slice-posted-slices.json", +) +TARGET_IETF_SLICE_PUT_CONNECTION_GROUPS = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "data", + "target-ietf-slice-put-connection-groups.json", +) + +NBI_ADDRESS = "localhost" +NBI_PORT = "80" +NBI_USERNAME = "admin" +NBI_PASSWORD = "admin" + +NCE_ADDRESS = "localhost" +NCE_PORT = 9090 + +AGG_TFS_ADDRESS = "localhost" +AGG_TFS_PORT = 9091 + +BASE_IETF_SLICE_URL = f"http://{NBI_ADDRESS}:{NBI_PORT}/restconf/data/ietf-network-slice-service:network-slice-services" +NCE_APP_DATA_URL = f"http://{NCE_ADDRESS}:{NCE_PORT}/restconf/v1/data/app-flows/apps" +NCE_APP_FLOW_DATA_URL = f"http://{NCE_ADDRESS}:{NCE_PORT}/restconf/v1/data/app-flows" +AGG_TFS_IETF_SLICE_URL = f"http://{AGG_TFS_ADDRESS}:{AGG_TFS_PORT}/restconf/data/ietf-network-slice-service:network-slice-services" + + +# pylint: disable=redefined-outer-name, unused-argument +def test_ietf_slice_creation_removal(): + # Issue service creation request + with open(POST_NETWORK_SLICE1, "r", encoding="UTF-8") as f: + post_network_slice1 = json.load(f) + with open(POST_NETWORK_SLICE2, "r", encoding="UTF-8") as f: + post_network_slice2 = json.load(f) + with open(POST_CONNECTION_GROUP_TO_NETWORK_SLICE1, "r", encoding="UTF-8") as f: + post_connection_group_to_network_slice1 = json.load(f) + with open(POST_CONNECTION_GROUP_TO_NETWORK_SLICE2, "r", encoding="UTF-8") as f: + post_connection_group_to_network_slice2 = json.load(f) + with open(POST_MATCH_CRITERIA_TO_SDP1_IN_SLICE1, "r", encoding="UTF-8") as f: + post_match_criteria_to_sdp1_in_slice1 = json.load(f) + with open(POST_MATCH_CRITERIA_TO_SDP1_IN_SLICE2, "r", encoding="UTF-8") as f: + post_match_criteria_to_sdp1_in_slice2 = json.load(f) + with open(POST_SDP_TO_NETWORK_SLICE1, "r", encoding="UTF-8") as f: + post_sdp_to_network_slice1 = json.load(f) + with open(POST_SDP_TO_NETWORK_SLICE2, "r", encoding="UTF-8") as f: + post_sdp_to_network_slice2 = json.load(f) + with open(TARGET_NCE_APPS, "r", encoding="UTF-8") as f: + target_nce_apps = json.load(f) + with open(TARGET_NCE_APP_FLOWS, "r", encoding="UTF-8") as f: + target_nce_app_flows = json.load(f) + with open(TARGET_FULL_IETF_SLICE, "r", encoding="UTF-8") as f: + target_full_ietf_slice = json.load(f) + with open(TARGET_IETF_SLICE_POSTED_SLICES, "r", encoding="UTF-8") as f: + target_ietf_slice_posted_slices = json.load(f) + with open(TARGET_IETF_SLICE_PUT_CONNECTION_GROUPS, "r", encoding="UTF-8") as f: + target_ietf_slice_put_connection_groups = json.load(f) + + # op 1 + URL = BASE_IETF_SLICE_URL + requests.post(URL, headers=HEADERS, json=post_network_slice1) + + URL = NCE_APP_DATA_URL + apps_response = requests.get(URL).json() + URL = NCE_APP_FLOW_DATA_URL + app_flows_response = requests.get(URL).json() + URL = AGG_TFS_IETF_SLICE_URL + ietf_slice_services = requests.get(URL).json() + URL = ( + AGG_TFS_IETF_SLICE_URL + + "/slice-service=dummy/connection-groups/connection-group=dummy" + ) + ietf_slice_connection_groups = requests.get(URL).json() + + app_name = "App_Flow_2_1_slice1" + apps_diff = DeepDiff(apps_response[app_name], target_nce_apps[app_name]) + app_flows_diff = DeepDiff( + app_flows_response[app_name], + target_nce_app_flows[app_name], + exclude_regex_paths=r"root\['app-flow'\]\[\d+\]\['user-id'\]", + ) + assert not apps_diff + assert not app_flows_diff + assert len(apps_response) == 1 and len(app_flows_response) == 1 + + assert len(ietf_slice_connection_groups) == 0 + assert len(ietf_slice_services) == 1 + slice_diff = DeepDiff( + ietf_slice_services["slice1"], target_ietf_slice_posted_slices[0] + ) + assert not slice_diff + + # op 2 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1/sdps" + requests.post(URL, headers=HEADERS, json=post_sdp_to_network_slice1) + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1/connection-groups" + requests.post(URL, headers=HEADERS, json=post_connection_group_to_network_slice1) + URL = ( + BASE_IETF_SLICE_URL + "/slice-service=slice1/sdps/sdp=1/service-match-criteria" + ) + requests.post(URL, headers=HEADERS, json=post_match_criteria_to_sdp1_in_slice1) + + URL = NCE_APP_DATA_URL + apps_response = requests.get(URL).json() + URL = NCE_APP_FLOW_DATA_URL + app_flows_response = requests.get(URL).json() + URL = AGG_TFS_IETF_SLICE_URL + ietf_slice_services = requests.get(URL).json() + URL = ( + AGG_TFS_IETF_SLICE_URL + + "/slice-service=dummy/connection-groups/connection-group=dummy" + ) + ietf_slice_connection_groups = requests.get(URL).json() + + app_name = "App_Flow_3_1_slice1" + apps_diff = DeepDiff(apps_response[app_name], target_nce_apps[app_name]) + app_flows_diff = DeepDiff( + app_flows_response[app_name], + target_nce_app_flows[app_name], + exclude_regex_paths=r"root\['app-flow'\]\[\d+\]\['user-id'\]", + ) + assert not apps_diff + assert not app_flows_diff + assert len(apps_response) == 2 and len(app_flows_response) == 2 + + assert len(ietf_slice_connection_groups) == 1 + assert len(ietf_slice_services) == 1 + connection_group_diff = DeepDiff( + ietf_slice_connection_groups[0], target_ietf_slice_put_connection_groups[0] + ) + assert not connection_group_diff + + # op 3 + URL = BASE_IETF_SLICE_URL + requests.post(URL, headers=HEADERS, json=post_network_slice2) + + URL = NCE_APP_DATA_URL + apps_response = requests.get(URL).json() + URL = NCE_APP_FLOW_DATA_URL + app_flows_response = requests.get(URL).json() + URL = AGG_TFS_IETF_SLICE_URL + ietf_slice_services = requests.get(URL).json() + URL = ( + AGG_TFS_IETF_SLICE_URL + + "/slice-service=dummy/connection-groups/connection-group=dummy" + ) + ietf_slice_connection_groups = requests.get(URL).json() + + app_name = "App_Flow_2_1_slice2" + apps_diff = DeepDiff(apps_response[app_name], target_nce_apps[app_name]) + app_flows_diff = DeepDiff( + app_flows_response[app_name], + target_nce_app_flows[app_name], + exclude_regex_paths=r"root\['app-flow'\]\[\d+\]\['user-id'\]", + ) + assert not apps_diff + assert not app_flows_diff + assert len(apps_response) == 3 and len(app_flows_response) == 3 + + assert len(ietf_slice_connection_groups) == 1 + assert len(ietf_slice_services) == 2 + slice_diff = DeepDiff( + ietf_slice_services["slice2"], target_ietf_slice_posted_slices[1] + ) + assert not slice_diff + + # op 4 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2/sdps" + requests.post(URL, headers=HEADERS, json=post_sdp_to_network_slice2) + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2/connection-groups" + requests.post(URL, headers=HEADERS, json=post_connection_group_to_network_slice2) + URL = ( + BASE_IETF_SLICE_URL + "/slice-service=slice2/sdps/sdp=1/service-match-criteria" + ) + requests.post(URL, headers=HEADERS, json=post_match_criteria_to_sdp1_in_slice2) + + URL = NCE_APP_DATA_URL + apps_response = requests.get(URL).json() + URL = NCE_APP_FLOW_DATA_URL + app_flows_response = requests.get(URL).json() + URL = AGG_TFS_IETF_SLICE_URL + ietf_slice_services = requests.get(URL).json() + URL = ( + AGG_TFS_IETF_SLICE_URL + + "/slice-service=dummy/connection-groups/connection-group=dummy" + ) + ietf_slice_connection_groups = requests.get(URL).json() + + app_name = "App_Flow_3_1_slice2" + apps_diff = DeepDiff(apps_response[app_name], target_nce_apps[app_name]) + app_flows_diff = DeepDiff( + app_flows_response[app_name], + target_nce_app_flows[app_name], + exclude_regex_paths=r"root\['app-flow'\]\[\d+\]\['user-id'\]", + ) + assert not apps_diff + assert not app_flows_diff + assert len(apps_response) == 4 and len(app_flows_response) == 4 + + assert len(ietf_slice_connection_groups) == 2 + assert len(ietf_slice_services) == 2 + connection_group_diff = DeepDiff( + ietf_slice_connection_groups[1], target_ietf_slice_put_connection_groups[1] + ) + assert not connection_group_diff + + # op 5 + ietf_slices_full_retrieved = requests.get(BASE_IETF_SLICE_URL).json() + ietf_slice_data = DeepDiff(ietf_slices_full_retrieved, target_full_ietf_slice) + assert not ietf_slice_data + + # op 6 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1/sdps/sdp=2" + requests.delete(URL) + URL = ( + BASE_IETF_SLICE_URL + + "/slice-service=slice1/sdps/sdp=1/service-match-criteria/match-criterion=1" + ) + requests.delete(URL) + URL = ( + BASE_IETF_SLICE_URL + + "/slice-service=slice1/connection-groups/connection-group=line1" + ) + requests.delete(URL) + + URL = NCE_APP_DATA_URL + apps_response = requests.get(URL).json() + URL = NCE_APP_FLOW_DATA_URL + app_flows_response = requests.get(URL).json() + URL = AGG_TFS_IETF_SLICE_URL + ietf_slice_services = requests.get(URL).json() + URL = ( + AGG_TFS_IETF_SLICE_URL + + "/slice-service=dummy/connection-groups/connection-group=dummy" + ) + ietf_slice_connection_groups = requests.get(URL).json() + + app_name = "App_Flow_2_1_slice1" + assert app_name not in apps_response + assert app_name not in app_flows_response + assert len(apps_response) == 3 and len(app_flows_response) == 3 + + assert len(ietf_slice_connection_groups) == 3 + assert len(ietf_slice_services) == 2 + connection_group_diff = DeepDiff( + ietf_slice_connection_groups[2], target_ietf_slice_put_connection_groups[2] + ) + assert not connection_group_diff + + # op 7 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1/sdps/sdp=3" + requests.delete(URL) + URL = ( + BASE_IETF_SLICE_URL + + "/slice-service=slice1/sdps/sdp=1/service-match-criteria/match-criterion=2" + ) + requests.delete(URL) + URL = ( + BASE_IETF_SLICE_URL + + "/slice-service=slice1/connection-groups/connection-group=line2" + ) + requests.delete(URL) + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1/sdps/sdp=1" + + URL = NCE_APP_DATA_URL + apps_response = requests.get(URL).json() + URL = NCE_APP_FLOW_DATA_URL + app_flows_response = requests.get(URL).json() + URL = AGG_TFS_IETF_SLICE_URL + ietf_slice_services = requests.get(URL).json() + URL = ( + AGG_TFS_IETF_SLICE_URL + + "/slice-service=dummy/connection-groups/connection-group=dummy" + ) + ietf_slice_connection_groups = requests.get(URL).json() + + requests.delete(URL) + URL = BASE_IETF_SLICE_URL + "/slice-service=slice1" + requests.delete(URL) + + app_name = "App_Flow_3_1_slice1" + assert app_name not in apps_response + assert app_name not in app_flows_response + assert len(apps_response) == 2 and len(app_flows_response) == 2 + + assert len(ietf_slice_connection_groups) == 3 + assert len(ietf_slice_services) == 1 + assert "slice1" not in ietf_slice_services + + # op 8 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2/sdps/sdp=2" + requests.delete(URL) + URL = ( + BASE_IETF_SLICE_URL + + "/slice-service=slice2/sdps/sdp=1/service-match-criteria/match-criterion=1" + ) + requests.delete(URL) + URL = ( + BASE_IETF_SLICE_URL + + "/slice-service=slice2/connection-groups/connection-group=line1" + ) + requests.delete(URL) + + URL = NCE_APP_DATA_URL + apps_response = requests.get(URL).json() + URL = NCE_APP_FLOW_DATA_URL + app_flows_response = requests.get(URL).json() + URL = AGG_TFS_IETF_SLICE_URL + ietf_slice_services = requests.get(URL).json() + URL = ( + AGG_TFS_IETF_SLICE_URL + + "/slice-service=dummy/connection-groups/connection-group=dummy" + ) + ietf_slice_connection_groups = requests.get(URL).json() + + app_name = "App_Flow_2_1_slice2" + assert app_name not in apps_response + assert app_name not in app_flows_response + assert len(apps_response) == 1 and len(app_flows_response) == 1 + + assert len(ietf_slice_connection_groups) == 4 + assert len(ietf_slice_services) == 1 + connection_group_diff = DeepDiff( + ietf_slice_connection_groups[3], target_ietf_slice_put_connection_groups[3] + ) + assert not connection_group_diff + + # op 9 + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2/sdps/sdp=3" + requests.delete(URL) + URL = ( + BASE_IETF_SLICE_URL + + "/slice-service=slice2/sdps/sdp=1/service-match-criteria/match-criterion=2" + ) + requests.delete(URL) + URL = ( + BASE_IETF_SLICE_URL + + "/slice-service=slice2/connection-groups/connection-group=line2" + ) + requests.delete(URL) + + URL = NCE_APP_DATA_URL + apps_response = requests.get(URL).json() + URL = NCE_APP_FLOW_DATA_URL + app_flows_response = requests.get(URL).json() + URL = AGG_TFS_IETF_SLICE_URL + ietf_slice_services = requests.get(URL).json() + URL = ( + AGG_TFS_IETF_SLICE_URL + + "/slice-service=dummy/connection-groups/connection-group=dummy" + ) + ietf_slice_connection_groups = requests.get(URL).json() + + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2/sdps/sdp=1" + requests.delete(URL) + URL = BASE_IETF_SLICE_URL + "/slice-service=slice2" + requests.delete(URL) + + app_name = "App_Flow_3_1_slice2" + assert app_name not in apps_response + assert app_name not in app_flows_response + assert len(apps_response) == 0 and len(app_flows_response) == 0 + + assert len(ietf_slice_connection_groups) == 4 + assert len(ietf_slice_services) == 0 + + # op 10 + ietf_slices_full_retrieved = requests.get(BASE_IETF_SLICE_URL).json() + empty_ietf_slices = {"network-slice-services": {"slice-service": []}} + ietf_slice_data = DeepDiff(ietf_slices_full_retrieved, empty_ietf_slices) + assert not ietf_slice_data diff --git a/src/tests/ofc25-camara-e2e-controller/tests/test_onboarding.py b/src/tests/ofc25-camara-e2e-controller/tests/test_onboarding.py new file mode 100644 index 0000000000000000000000000000000000000000..05e031da7b3ab88a8ee3f3c80fdddb92d9f26913 --- /dev/null +++ b/src/tests/ofc25-camara-e2e-controller/tests/test_onboarding.py @@ -0,0 +1,67 @@ +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (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, os, time +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import ContextId, DeviceOperationalStatusEnum, Empty +from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results, validate_empty_scenario +from common.tools.object_factory.Context import json_context_id +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from .Fixtures import context_client, device_client # pylint: disable=unused-import + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'data', 'camara-e2e-topology.json') +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) + +def test_scenario_onboarding( + context_client : ContextClient, # pylint: disable=redefined-outer-name + device_client : DeviceClient, # pylint: disable=redefined-outer-name +) -> None: + validate_empty_scenario(context_client) + + descriptor_loader = DescriptorLoader( + descriptors_file=DESCRIPTOR_FILE, context_client=context_client, device_client=device_client) + results = descriptor_loader.process() + check_descriptor_load_results(results, descriptor_loader) + # descriptor_loader.validate() + + # Verify the scenario has no services/slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.service_ids) == 0 + assert len(response.slice_ids) == 0 + +def test_scenario_devices_enabled( + context_client : ContextClient, # pylint: disable=redefined-outer-name +) -> None: + """ + This test validates that the devices are enabled. + """ + DEVICE_OP_STATUS_ENABLED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED + + num_devices = -1 + num_devices_enabled, num_retry = 0, 0 + while (num_devices != num_devices_enabled) and (num_retry < 10): + time.sleep(1.0) + response = context_client.ListDevices(Empty()) + num_devices = len(response.devices) + num_devices_enabled = 0 + for device in response.devices: + if device.device_operational_status != DEVICE_OP_STATUS_ENABLED: continue + num_devices_enabled += 1 + LOGGER.info('Num Devices enabled: {:d}/{:d}'.format(num_devices_enabled, num_devices)) + num_retry += 1 + assert num_devices_enabled == num_devices diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/Dockerfile b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b2ac55af45ba673cd7c119f19a5245d065d02ea3 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/Dockerfile @@ -0,0 +1,37 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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-folders, and copy content +RUN mkdir -p /var/teraflow/mock_ietf_l3vpn_sdn_ctrl +WORKDIR /var/teraflow/mock_ietf_l3vpn_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 + +RUN python3 -m pip list + +# Start the service +ENTRYPOINT ["python", "MockIetfL3VPNSdnCtrl.py"] diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/L3VPNServices.py b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/L3VPNServices.py new file mode 100644 index 0000000000000000000000000000000000000000..1e4d7c6195a3dd0b0cb6c124f788c2efa5fe7927 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/L3VPNServices.py @@ -0,0 +1,44 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# REST-API resource implementing minimal support for "IETF YANG Data Model for Transport Network Client Signals". +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html + +from flask import jsonify, make_response, request +from flask_restful import Resource + +VPN_SERVICES = {} + + +class L3VPNServices(Resource): + def get(self): + return make_response(jsonify(VPN_SERVICES), 200) + + def post(self): + json_request = request.get_json() + name = json_request["ietf-l3vpn-svc:l3vpn-svc"]["vpn-services"]["vpn-service"][0]["vpn-id"] + VPN_SERVICES[name] = json_request + return make_response(jsonify({}), 201) + + +class L3VPNService(Resource): + def put(self, vpn_id: str): + json_request = request.get_json() + VPN_SERVICES[vpn_id] = json_request + return make_response(jsonify({}), 200) + + def delete(self, vpn_id: str): + slice = VPN_SERVICES.pop(vpn_id, None) + data, status = ({}, 404) if slice is None else (slice, 204) + return make_response(jsonify(data), status) diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/MockIetfL3VPNSdnCtrl.py b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/MockIetfL3VPNSdnCtrl.py new file mode 100644 index 0000000000000000000000000000000000000000..49685aa19902c619cdc0259b5cb0573dbf77bd63 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/MockIetfL3VPNSdnCtrl.py @@ -0,0 +1,77 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 IETF ACTN SDN controller +# ----------------------------- +# REST server implementing minimal support for: +# - IETF YANG Data Model for Transport Network Client Signals +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html +# - IETF YANG Data Model for Traffic Engineering Tunnels, Label Switched Paths and Interfaces +# Ref: https://www.ietf.org/archive/id/draft-ietf-teas-yang-te-34.html + + +import functools, logging, sys, time +from flask import Flask, request +from flask_restful import Api + +from L3VPNServices import L3VPNService, L3VPNServices + +BIND_ADDRESS = "0.0.0.0" +BIND_PORT = 8443 +BASE_URL = "/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" +STR_ENDPOINT = "http://{:s}:{:s}{:s}".format( + str(BIND_ADDRESS), str(BIND_PORT), str(BASE_URL) +) +LOG_LEVEL = logging.DEBUG + +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 + + +def main(): + LOGGER.info("Starting...") + + app = Flask(__name__) + app.after_request(functools.partial(log_request, LOGGER)) + + api = Api(app, prefix=BASE_URL) + api.add_resource(L3VPNServices, "") + api.add_resource(L3VPNService, "/vpn-service=<string:vpn_id>") + + LOGGER.info("Listening on {:s}...".format(str(STR_ENDPOINT))) + app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT) + + LOGGER.info("Bye") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/README.md b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ed9d744f7641e20ddd39b51f837ea68e47fe64c0 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/README.md @@ -0,0 +1,31 @@ +# Mock IETF L3VPN SDN Controller + +This REST server implements very basic support for the following YANG data models: +- YANG Data Model for L3VPN Service Delivery + - Ref: https://datatracker.ietf.org/doc/html/rfc8049 + +The aim of this server is to enable testing ietf netowrk slice service handler, ietf l3vpn service handler, and ietf l3vpn driver + + +## 1. Install requirements for the Mock IETF Network Slice SDN controller +__NOTE__: if you run the Mock IETF L3VPN SDN controller from the PyEnv used for developing on the TeraFlowSDN +framework and you followed the official steps in +[Development Guide > Configure Environment > Python](https://labs.etsi.org/rep/tfs/controller/-/wikis/2.-Development-Guide/2.1.-Configure-Environment/2.1.1.-Python), +all the requirements are already in place. Install them only if you execute it in a separate/standalone environment. + +Install the required dependencies as follows: +```bash +pip install -r src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/requirements.in +``` + +Run the Mock IETF L3VPN SDN Controller as follows: +```bash +python src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/MockIetfL3VPNSdnCtrl.py +``` + + +## 2. Run the Mock IETF L3VPN SDN controller +Run the Mock IETF L3VPN SDN Controller as follows: +```bash +python src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/MockIetfL3VPNSdnCtrl.py +``` diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/__init__.py b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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/tools/mock_ietf_l3vpn_sdn_ctrl/build.sh b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..e5f12798556b0c9c044d86c0f175aeec9d0cff23 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/build.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# Make folder containing the script the root folder for its execution +cd $(dirname $0) + +docker build -t mock-ietf-l3vpn-sdn-ctrl:test -f Dockerfile . +docker tag mock-ietf-l3vpn-sdn-ctrl:test localhost:32000/tfs/mock-ietf-l3vpn-sdn-ctrl:test +docker push localhost:32000/tfs/mock-ietf-l3vpn-sdn-ctrl:test diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/deploy.sh b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..1c6e68deb33de588cffab91fda550aafe222ce37 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/deploy.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 mocks +kubectl --namespace mocks apply -f mock-ietf-l3vpn-sdn-ctrl.yaml diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/mock-ietf-network-slice-sdn-ctrl.yaml b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/mock-ietf-network-slice-sdn-ctrl.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ace79810f0cbfbd6605ef049c71c6c75cf5c0317 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/mock-ietf-network-slice-sdn-ctrl.yaml @@ -0,0 +1,64 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +kind: Namespace +apiVersion: v1 +metadata: + name: mocks +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mock-ietf-l3vpn-sdn-ctrl +spec: + selector: + matchLabels: + app: mock-ietf-l3vpn-sdn-ctrl + replicas: 1 + template: + metadata: + annotations: + config.linkerd.io/skip-inbound-ports: "8443" + labels: + app: mock-ietf-l3vpn-sdn-ctrl + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: localhost:32000/tfs/mock-ietf-l3vpn-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-ietf-l3vpn-sdn-ctrl + labels: + app: mock-ietf-l3vpn-sdn-ctrl +spec: + type: ClusterIP + selector: + app: mock-ietf-l3vpn-sdn-ctrl + ports: + - name: http + port: 8443 + targetPort: 8443 diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/requirements.in b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..dbb8e95d65cfd0f1ee60d9bb4f840393ac893725 --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/requirements.in @@ -0,0 +1,22 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 +werkzeug==2.3.7 diff --git a/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/run.sh b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..3c0ddc2b06f8f3af6ed3645a65fcea31888c82de --- /dev/null +++ b/src/tests/tools/mock_ietf_l3vpn_sdn_ctrl/run.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# Make folder containing the script the root folder for its execution +cd $(dirname $0) + +python MockIetfL3VPNSdnCtrl.py diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/Dockerfile b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..a624152de7d9188067a5828b4a8958b8d3418694 --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/Dockerfile @@ -0,0 +1,37 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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-folders, and copy content +RUN mkdir -p /var/teraflow/mock_ietf_network_slice_sdn_ctrl +WORKDIR /var/teraflow/mock_ietf_network_slice_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 + +RUN python3 -m pip list + +# Start the service +ENTRYPOINT ["python", "MockIetfNetworkSliceSdnCtrl.py"] diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/MockIetfNetworkSliceSdnCtrl.py b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/MockIetfNetworkSliceSdnCtrl.py new file mode 100644 index 0000000000000000000000000000000000000000..8d13f1bafcd98c28f20033ab8859bc6cc4b207dd --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/MockIetfNetworkSliceSdnCtrl.py @@ -0,0 +1,81 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 IETF ACTN SDN controller +# ----------------------------- +# REST server implementing minimal support for: +# - IETF YANG Data Model for Transport Network Client Signals +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html +# - IETF YANG Data Model for Traffic Engineering Tunnels, Label Switched Paths and Interfaces +# Ref: https://www.ietf.org/archive/id/draft-ietf-teas-yang-te-34.html + + +import functools, logging, sys, time +from flask import Flask, request +from flask_restful import Api +from ResourceNetworkSlices import NetworkSliceService, NetworkSliceServices +from ResourceConnectionGroups import ConnectionGroup + +BIND_ADDRESS = "0.0.0.0" +BIND_PORT = 8443 +BASE_URL = "/restconf/data/ietf-network-slice-service:network-slice-services" +STR_ENDPOINT = "http://{:s}:{:s}{:s}".format( + str(BIND_ADDRESS), str(BIND_PORT), str(BASE_URL) +) +LOG_LEVEL = logging.DEBUG + +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 + + +def main(): + LOGGER.info("Starting...") + + app = Flask(__name__) + app.after_request(functools.partial(log_request, LOGGER)) + + api = Api(app, prefix=BASE_URL) + api.add_resource(NetworkSliceServices, "") + api.add_resource(NetworkSliceService, "/slice-service=<string:slice_id>") + api.add_resource( + ConnectionGroup, + "/slice-service=<string:slice_id>/connection-groups/connection-group=<string:connection_group_id>", + ) + + LOGGER.info("Listening on {:s}...".format(str(STR_ENDPOINT))) + app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT) + + LOGGER.info("Bye") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/README.md b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/README.md new file mode 100644 index 0000000000000000000000000000000000000000..493fe37362dbbb8e8dbdf1a9621cb81a15c64691 --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/README.md @@ -0,0 +1,31 @@ +# Mock IETF Network Slice SDN Controller + +This REST server implements very basic support for the following YANG data models: +- IETF YANG Data Model for Transport Network Client Signals (draft-ietf-ccamp-client-signal-yang-10) + - Ref: https://datatracker.ietf.org/doc/draft-ietf-teas-ietf-network-slice-nbi-yang/ + +The aim of this server is to enable testing ietf netowrk slice service driver and ietf slice service service handler + + +## 1. Install requirements for the Mock IETF Network Slice SDN controller +__NOTE__: if you run the Mock IETF Network Slice SDN controller from the PyEnv used for developing on the TeraFlowSDN +framework and you followed the official steps in +[Development Guide > Configure Environment > Python](https://labs.etsi.org/rep/tfs/controller/-/wikis/2.-Development-Guide/2.1.-Configure-Environment/2.1.1.-Python), +all the requirements are already in place. Install them only if you execute it in a separate/standalone environment. + +Install the required dependencies as follows: +```bash +pip install -r src/tests/tools/mock_ietf_network_slice_sdn_ctrl/requirements.in +``` + +Run the Mock IETF Network Slice SDN Controller as follows: +```bash +python src/tests/tools/mock_ietf_network_slice_sdn_ctrl/MockIetfNetworkSliceSdnCtrl.py +``` + + +## 2. Run the Mock IETF Network Slice SDN controller +Run the Mock IETF Network Slice SDN Controller as follows: +```bash +python src/tests/tools/mock_ietf_network_slice_sdn_ctrl/MockIetfNetworkSliceSdnCtrl.py +``` diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/ResourceConnectionGroups.py b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/ResourceConnectionGroups.py new file mode 100644 index 0000000000000000000000000000000000000000..19c84e181a97354940fd3dca8f3d6396c0a50068 --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/ResourceConnectionGroups.py @@ -0,0 +1,31 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# REST-API resource implementing minimal support for "IETF YANG Data Model for Transport Network Client Signals". +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html + +from flask import jsonify, make_response, request +from flask_restful import Resource + +CONNECTION_GROUPS = [] + + +class ConnectionGroup(Resource): + def get(self, slice_id: str, connection_group_id: str): + return make_response(jsonify(CONNECTION_GROUPS), 200) + + def put(self, slice_id: str, connection_group_id: str): + json_request = request.get_json() + CONNECTION_GROUPS.append(json_request) + return make_response(jsonify({}), 200) diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/ResourceNetworkSlices.py b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/ResourceNetworkSlices.py new file mode 100644 index 0000000000000000000000000000000000000000..80840f8da13e00af689c7b84e9577e952d9e2adf --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/ResourceNetworkSlices.py @@ -0,0 +1,39 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# REST-API resource implementing minimal support for "IETF YANG Data Model for Transport Network Client Signals". +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html + +from flask import jsonify, make_response, request +from flask_restful import Resource + +NETWORK_SLICES = {} + + +class NetworkSliceServices(Resource): + def get(self): + return make_response(jsonify(NETWORK_SLICES), 200) + + def post(self): + json_request = request.get_json() + name = json_request["network-slice-services"]["slice-service"][0]["id"] + NETWORK_SLICES[name] = json_request + return make_response(jsonify({}), 201) + + +class NetworkSliceService(Resource): + def delete(self, slice_id: str): + slice = NETWORK_SLICES.pop(slice_id, None) + data, status = ({}, 404) if slice is None else (slice, 204) + return make_response(jsonify(data), status) diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/__init__.py b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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/tools/mock_ietf_network_slice_sdn_ctrl/build.sh b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..8e4dda34d7c83ef76c9944bcf52475de05d5238d --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/build.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# Make folder containing the script the root folder for its execution +cd $(dirname $0) + +docker build -t mock-ietf-network-slice-sdn-ctrl:test -f Dockerfile . +docker tag mock-ietf-network-slice-sdn-ctrl:test localhost:32000/tfs/mock-ietf-network-slice-sdn-ctrl:test +docker push localhost:32000/tfs/mock-ietf-network-slice-sdn-ctrl:test diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/deploy.sh b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..6db45be588e4d538e04ecb8922b3350432439a70 --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/deploy.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 mocks +kubectl --namespace mocks apply -f mock-ietf-network-slice-sdn-ctrl.yaml diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/mock-ietf-network-slice-sdn-ctrl.yaml b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/mock-ietf-network-slice-sdn-ctrl.yaml new file mode 100644 index 0000000000000000000000000000000000000000..cd5734a5f3087dc36d9a103a75e90f3eb902865d --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/mock-ietf-network-slice-sdn-ctrl.yaml @@ -0,0 +1,64 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +kind: Namespace +apiVersion: v1 +metadata: + name: mocks +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mock-ietf-network-slice-sdn-ctrl +spec: + selector: + matchLabels: + app: mock-ietf-network-slice-sdn-ctrl + replicas: 1 + template: + metadata: + annotations: + config.linkerd.io/skip-inbound-ports: "8443" + labels: + app: mock-ietf-network-slice-sdn-ctrl + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: localhost:32000/tfs/mock-ietf-network-slice-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-ietf-network-slice-sdn-ctrl + labels: + app: mock-ietf-network-slice-sdn-ctrl +spec: + type: ClusterIP + selector: + app: mock-ietf-network-slice-sdn-ctrl + ports: + - name: http + port: 8443 + targetPort: 8443 diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/requirements.in b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..dbb8e95d65cfd0f1ee60d9bb4f840393ac893725 --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/requirements.in @@ -0,0 +1,22 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 +werkzeug==2.3.7 diff --git a/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/run.sh b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..f52b6061b3d89b75504f2b80d77696573a09814d --- /dev/null +++ b/src/tests/tools/mock_ietf_network_slice_sdn_ctrl/run.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# Make folder containing the script the root folder for its execution +cd $(dirname $0) + +python MockIetfNetworkSliceSdnCtrl.py diff --git a/src/tests/tools/mock_nce_ctrl/Dockerfile b/src/tests/tools/mock_nce_ctrl/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..ae9dde4eb469a951c1ccf3f78a79a9ab2d07c122 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/Dockerfile @@ -0,0 +1,37 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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-folders, and copy content +RUN mkdir -p /var/teraflow/mock_nce_ctrl +WORKDIR /var/teraflow/mock_nce_ctrl +COPY . . + +# Get specific Python packages +RUN pip-compile --quiet --output-file=requirements.txt requirements.in +RUN python3 -m pip install -r requirements.txt + +RUN python3 -m pip list + +# Start the service +ENTRYPOINT ["python", "MockNCECtrl.py"] diff --git a/src/tests/tools/mock_nce_ctrl/MockNCECtrl.py b/src/tests/tools/mock_nce_ctrl/MockNCECtrl.py new file mode 100644 index 0000000000000000000000000000000000000000..e58a4dc445e74e6186267e7b43b2872b05cb114e --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/MockNCECtrl.py @@ -0,0 +1,79 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 IETF ACTN SDN controller +# ----------------------------- +# REST server implementing minimal support for: +# - IETF YANG Data Model for Transport Network Client Signals +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html +# - IETF YANG Data Model for Traffic Engineering Tunnels, Label Switched Paths and Interfaces +# Ref: https://www.ietf.org/archive/id/draft-ietf-teas-yang-te-34.html + + +import functools, logging, sys, time +from flask import Flask, jsonify, make_response, request +from flask_restful import Api, Resource +from ResourceApps import Apps, App +from ResourceAppFlows import AppFlows, AppFlow + +BIND_ADDRESS = "0.0.0.0" +BIND_PORT = 8443 +BASE_URL = "/restconf/v1/data/app-flows" +STR_ENDPOINT = "http://{:s}:{:s}{:s}".format( + str(BIND_ADDRESS), str(BIND_PORT), str(BASE_URL) +) +LOG_LEVEL = logging.DEBUG + +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 + + +def main(): + LOGGER.info("Starting...") + + app = Flask(__name__) + app.after_request(functools.partial(log_request, LOGGER)) + + api = Api(app, prefix=BASE_URL) + api.add_resource(Apps, "/apps") + api.add_resource(App, "/apps/application=<string:app_name>") + api.add_resource(AppFlows, "") + api.add_resource(AppFlow, "/app-flow=<string:app_name>") + + LOGGER.info("Listening on {:s}...".format(str(STR_ENDPOINT))) + app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT) + + LOGGER.info("Bye") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/tests/tools/mock_nce_ctrl/README.md b/src/tests/tools/mock_nce_ctrl/README.md new file mode 100644 index 0000000000000000000000000000000000000000..407f26425bd70b22e1426cc13dede15993925258 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/README.md @@ -0,0 +1,29 @@ +# Mock NCE Controller + +This REST server implements very basic support for the NCE access controller. + +The aim of this server is to enable testing IETF Network Slice NBI, NCE driver and NCE service handler. + + +## 1. Install requirements for the Mock NCE controller +__NOTE__: if you run the Mock NCE controller from the PyEnv used for developing on the TeraFlowSDN +framework and you followed the official steps in +[Development Guide > Configure Environment > Python](https://labs.etsi.org/rep/tfs/controller/-/wikis/2.-Development-Guide/2.1.-Configure-Environment/2.1.1.-Python), +all the requirements are already in place. Install them only if you execute it in a separate/standalone environment. + +Install the required dependencies as follows: +```bash +pip install -r src/tests/tools/mock_nce_ctrl/requirements.in +``` + +Run the Mock NCE Controller as follows: +```bash +python src/tests/tools/mock_nce_ctrl/MockNCECtrl.py +``` + + +## 2. Run the Mock NCE controller +Run the Mock NCE Controller as follows: +```bash +python src/tests/tools/mock_nce_ctrl/MockNCECtrl.py +``` diff --git a/src/tests/tools/mock_nce_ctrl/ResourceAppFlows.py b/src/tests/tools/mock_nce_ctrl/ResourceAppFlows.py new file mode 100644 index 0000000000000000000000000000000000000000..9f7a8df41497293af912bcf4f77aa27eabdbf095 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/ResourceAppFlows.py @@ -0,0 +1,39 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# REST-API resource implementing minimal support for "IETF YANG Data Model for Transport Network Client Signals". +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html + +from flask import jsonify, make_response, request +from flask_restful import Resource + +APP_FLOWS = {} + + +class AppFlows(Resource): + def get(self): + return make_response(jsonify(APP_FLOWS), 200) + + def post(self): + json_request = request.get_json() + name = json_request["app-flow"][0]["app-name"] + APP_FLOWS[name] = json_request + return make_response(jsonify({}), 201) + + +class AppFlow(Resource): + def delete(self, app_name: str): + app_flow = APP_FLOWS.pop(app_name, None) + data, status = ({}, 404) if app_flow is None else (app_flow, 204) + return make_response(jsonify(data), status) diff --git a/src/tests/tools/mock_nce_ctrl/ResourceApps.py b/src/tests/tools/mock_nce_ctrl/ResourceApps.py new file mode 100644 index 0000000000000000000000000000000000000000..163d08fc2fed7fd3c21806418de06bf7ef08741a --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/ResourceApps.py @@ -0,0 +1,39 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# REST-API resource implementing minimal support for "IETF YANG Data Model for Transport Network Client Signals". +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html + +from flask import jsonify, make_response, request +from flask_restful import Resource + +APPS = {} + + +class Apps(Resource): + def get(self): + return make_response(jsonify(APPS), 200) + + def post(self): + json_request = request.get_json() + name = json_request["application"][0]["name"] + APPS[name] = json_request + return make_response(jsonify({}), 201) + + +class App(Resource): + def delete(self, app_name: str): + app_flow = APPS.pop(app_name, None) + data, status = ({}, 404) if app_flow is None else (app_flow, 204) + return make_response(jsonify(data), status) diff --git a/src/tests/tools/mock_nce_ctrl/__init__.py b/src/tests/tools/mock_nce_ctrl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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/tools/mock_nce_ctrl/build.sh b/src/tests/tools/mock_nce_ctrl/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..766d2c1dc5d3a890c5a74d88c22055792b089de4 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/build.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# Make folder containing the script the root folder for its execution +cd $(dirname $0) + +docker build -t mock-nce-ctrl:test -f Dockerfile . +docker tag mock-nce-ctrl:test localhost:32000/tfs/mock-nce-ctrl:test +docker push localhost:32000/tfs/mock-nce-ctrl:test diff --git a/src/tests/tools/mock_nce_ctrl/deploy.sh b/src/tests/tools/mock_nce_ctrl/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..0e5faf26d435ee928f550106e36f85e65109ac66 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/deploy.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (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 mocks +kubectl --namespace mocks apply -f mock-nce-ctrl.yaml diff --git a/src/tests/tools/mock_nce_ctrl/mock-nce-ctrl.yaml b/src/tests/tools/mock_nce_ctrl/mock-nce-ctrl.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4a1512f238970afb0867a3cb79824df4ac60d5c9 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/mock-nce-ctrl.yaml @@ -0,0 +1,64 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +kind: Namespace +apiVersion: v1 +metadata: + name: mocks +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mock-nce-ctrl +spec: + selector: + matchLabels: + app: mock-nce-ctrl + replicas: 1 + template: + metadata: + annotations: + config.linkerd.io/skip-inbound-ports: "8443" + labels: + app: mock-nce-ctrl + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: localhost:32000/tfs/mock-nce-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-nce-ctrl + labels: + app: mock-nce-ctrl +spec: + type: ClusterIP + selector: + app: mock-nce-ctrl + ports: + - name: https + port: 8443 + targetPort: 8443 diff --git a/src/tests/tools/mock_nce_ctrl/requirements.in b/src/tests/tools/mock_nce_ctrl/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..dbb8e95d65cfd0f1ee60d9bb4f840393ac893725 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/requirements.in @@ -0,0 +1,22 @@ +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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 +werkzeug==2.3.7 diff --git a/src/tests/tools/mock_nce_ctrl/run.sh b/src/tests/tools/mock_nce_ctrl/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..6aa1149a4a9571bc1e52ab66ca0a36391edc6f54 --- /dev/null +++ b/src/tests/tools/mock_nce_ctrl/run.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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. + +# Make folder containing the script the root folder for its execution +cd $(dirname $0) + +python MockNCECtrl.py diff --git a/src/webui/service/slice/routes.py b/src/webui/service/slice/routes.py index 922f8af96dc916f0ca5658e120cdf3dfa4ec9b21..f56c50f46d8c9df083a943a91704e7d16352e5a2 100644 --- a/src/webui/service/slice/routes.py +++ b/src/webui/service/slice/routes.py @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json +from typing import Dict, Optional, Tuple import grpc from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for -from common.proto.context_pb2 import IsolationLevelEnum, Slice, SliceId, SliceStatusEnum +from common.method_wrappers.ServiceExceptions import InvalidArgumentsException +from common.proto.context_pb2 import IsolationLevelEnum, Slice, SliceId, SliceStatusEnum, EndPointId, SliceConfig, ConfigRule from common.tools.context_queries.Context import get_context from common.tools.context_queries.EndPoint import get_endpoint_names -from common.tools.context_queries.Slice import get_slice_by_uuid +from common.tools.context_queries.Slice import get_slice_by_uuid, get_uuid_from_string +from common.Constants import DEFAULT_CONTEXT_NAME from context.client.ContextClient import ContextClient from slice.client.SliceClient import SliceClient @@ -26,6 +30,94 @@ slice = Blueprint('slice', __name__, url_prefix='/slice') context_client = ContextClient() slice_client = SliceClient() + +RUNNING_RESOURCE_KEY = "running_ietf_slice" +CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" + + +class ConfigRuleNotFoundError(Exception): + ... + +def get_custom_config_rule( + slice_config: SliceConfig, resource_key: str +) -> Optional[ConfigRule]: + """ + Retrieve the custom config rule with the given resource_key from a ServiceConfig. + """ + for cr in slice_config.config_rules: + if ( + cr.WhichOneof("config_rule") == "custom" + and cr.custom.resource_key == resource_key + ): + return cr + return None + + +def get_ietf_data_from_config(slice_request: Slice, resource_key: str) -> Dict: + """ + Retrieve the IETF data (as a Python dict) from a slice's config rule for the specified resource_key. + Raises an exception if not found. + """ + config_rule = get_custom_config_rule(slice_request.slice_config, resource_key) + if not config_rule: + raise ConfigRuleNotFoundError(f"IETF data not found for resource_key: {resource_key}") + return json.loads(config_rule.custom.resource_value) + + +def endpoint_get_uuid( + endpoint_id : EndPointId, endpoint_name : str = '', allow_random : bool = False +) -> Tuple[str, str, str]: + device_uuid = endpoint_id.device_id.device_uuid.uuid + topology_uuid = endpoint_id.topology_id.topology_uuid.uuid + raw_endpoint_uuid = endpoint_id.endpoint_uuid.uuid + + if len(raw_endpoint_uuid) > 0: + prefix_for_name = '{:s}/{:s}'.format(topology_uuid, device_uuid) + return topology_uuid, device_uuid, get_uuid_from_string(raw_endpoint_uuid, prefix_for_name=prefix_for_name) + if len(endpoint_name) > 0: + prefix_for_name = '{:s}/{:s}'.format(topology_uuid, device_uuid) + return topology_uuid, device_uuid, get_uuid_from_string(endpoint_name, prefix_for_name=prefix_for_name) + + raise InvalidArgumentsException([ + ('endpoint_id.endpoint_uuid.uuid', raw_endpoint_uuid), + ('name', endpoint_name), + ], extra_details=['At least one is required to produce a EndPoint UUID']) + +def get_slice_endpoints(slice_obj: Slice) -> list[EndPointId]: + ''' + Get the list of endpoint ids for a slice. + If the slice has a `running_ietf_slice` config rule, return the list of endpoint ids from the config rule, + otherwise return the slice's list of endpoint ids. + ''' + try: + first_slice_endpoint_id = slice_obj.slice_endpoint_ids[0] + topology_uuid = first_slice_endpoint_id.topology_id.topology_uuid.uuid + context_uuid = slice_obj.slice_id.context_id.context_uuid.uuid + running_ietf_data = get_ietf_data_from_config(slice_obj, RUNNING_RESOURCE_KEY) + slice_service = running_ietf_data["network-slice-services"]["slice-service"][0] + slice_sdps = slice_service["sdps"]["sdp"] + list_endpoint_ids = [] + for sdp in slice_sdps: + endpoint = EndPointId() + endpoint.topology_id.context_id.context_uuid.uuid = context_uuid + endpoint.topology_id.topology_uuid.uuid = topology_uuid + device_uuid = get_uuid_from_string(sdp["node-id"]) + endpoint.device_id.device_uuid.uuid = device_uuid + attachment_circuits = sdp["attachment-circuits"]["attachment-circuit"] + endpoint_name = attachment_circuits[0]["ac-tp-id"] + endpoint.endpoint_uuid.uuid = endpoint_name + _, _, endpoint_uuid = endpoint_get_uuid(endpoint) + endpoint.endpoint_uuid.uuid = endpoint_uuid + list_endpoint_ids.append(endpoint) + del slice_obj.slice_endpoint_ids[:] + slice_obj.slice_endpoint_ids.extend(list_endpoint_ids) + + except ConfigRuleNotFoundError: + # The slice does not have `running_ietf_slice` config rule, return slice's list of endpoint ids + list_endpoint_ids = slice_obj.slice_endpoint_ids + + return list_endpoint_ids + @slice.get('/') def home(): if 'context_uuid' not in session or 'topology_uuid' not in session: @@ -50,7 +142,8 @@ def home(): else: endpoint_ids = list() for slice_ in slices: - endpoint_ids.extend(slice_.slice_endpoint_ids) + slice_endpoint_ids = get_slice_endpoints(slice_) + endpoint_ids.extend(slice_endpoint_ids) device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) context_client.close() @@ -81,7 +174,8 @@ def detail(slice_uuid: str): flash('Context({:s})/Slice({:s}) not found'.format(str(context_uuid), str(slice_uuid)), 'danger') slice_obj = Slice() else: - device_names, endpoints_data = get_endpoint_names(context_client, slice_obj.slice_endpoint_ids) + slice_endpoint_ids = get_slice_endpoints(slice_obj) + device_names, endpoints_data = get_endpoint_names(context_client, slice_endpoint_ids) context_client.close() @@ -111,5 +205,5 @@ def delete(slice_uuid: str): flash('Slice "{:s}" deleted successfully!'.format(slice_uuid), 'success') except Exception as e: flash('Problem deleting slice "{:s}": {:s}'.format(slice_uuid, str(e.details())), 'danger') - current_app.logger.exception(e) + current_app.logger.exception(e) return redirect(url_for('slice.home')) diff --git a/src/webui/service/static/topology_icons/ietf-slice.png b/src/webui/service/static/topology_icons/ietf-slice.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2232e8223a39eb0d829e0e50975a697b0660fc Binary files /dev/null and b/src/webui/service/static/topology_icons/ietf-slice.png differ diff --git a/src/webui/service/static/topology_icons/nce.png b/src/webui/service/static/topology_icons/nce.png new file mode 100644 index 0000000000000000000000000000000000000000..0c9af8f37fbb7249fbe570a920e0bcd281582655 Binary files /dev/null and b/src/webui/service/static/topology_icons/nce.png differ