diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ca970101fd46bdf0b281d57b585089c790bc3dd8..2856f9fed0a1c5cf59140c6d9c3c9e00d331726f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -57,6 +57,7 @@ include: - local: '/src/e2e_orchestrator/.gitlab-ci.yml' - local: '/src/ztp_server/.gitlab-ci.yml' - local: '/src/osm_client/.gitlab-ci.yml' + - local: '/src/simap_connector/.gitlab-ci.yml' # This should be last one: end-to-end integration tests - local: '/src/tests/.gitlab-ci.yml' diff --git a/deploy/build-only.sh b/deploy/build-only.sh new file mode 100755 index 0000000000000000000000000000000000000000..5db9e6e5d7fe1918bbdd8dae618ba4611e4470f5 --- /dev/null +++ b/deploy/build-only.sh @@ -0,0 +1,143 @@ +#!/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. + + +######################################################################################################################## +# Read deployment settings +######################################################################################################################## + + +# ----- TeraFlowSDN ------------------------------------------------------------ + +# If not already set, set the URL of the Docker registry where the images will be uploaded to. +# By default, assume internal MicroK8s registry is used. +export TFS_REGISTRY_IMAGES=${TFS_REGISTRY_IMAGES:-"http://localhost:32000/tfs/"} + +# If not already set, set the list of components, separated by spaces, you want to build images for, and deploy. +# By default, only basic components are deployed +export TFS_COMPONENTS=${TFS_COMPONENTS:-"context device pathcomp service slice nbi webui load_generator"} + +# If not already set, set the tag you want to use for your images. +export TFS_IMAGE_TAG=${TFS_IMAGE_TAG:-"dev"} + + +######################################################################################################################## +# Automated steps start here +######################################################################################################################## + +# Create a tmp folder for files modified during the deployment +TMP_LOGS_FOLDER="./tmp/build" +mkdir -p $TMP_LOGS_FOLDER + +DOCKER_BUILD="docker build" +DOCKER_MAJOR_VERSION=$(docker --version | grep -o -E "Docker version [0-9]+\." | grep -o -E "[0-9]+" | cut -c 1-3) +if [[ $DOCKER_MAJOR_VERSION -ge 23 ]]; then + # If Docker version >= 23, build command was migrated to docker-buildx + # In Ubuntu, in practice, means to install package docker-buildx together with docker.io + # Check if docker-buildx plugin is installed + docker buildx version 1>/dev/null 2>/dev/null + if [[ $? -ne 0 ]]; then + echo "Docker buildx command is not installed. Check: https://docs.docker.com/build/architecture/#install-buildx" + echo "If you installed docker through APT package docker.io, consider installing also package docker-buildx" + exit 1; + fi + DOCKER_BUILD="docker buildx build" +fi + +for COMPONENT in $TFS_COMPONENTS; do + echo "Processing '$COMPONENT' component..." + + echo " Building Docker image..." + BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}.log" + + if [ "$COMPONENT" == "ztp" ] || [ "$COMPONENT" == "policy" ]; then + $DOCKER_BUILD -t "$COMPONENT:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/Dockerfile ./src/"$COMPONENT"/ > "$BUILD_LOG" + elif [ "$COMPONENT" == "pathcomp" ] || [ "$COMPONENT" == "telemetry" ] || [ "$COMPONENT" == "analytics" ]; then + BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}-frontend.log" + $DOCKER_BUILD -t "$COMPONENT-frontend:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/frontend/Dockerfile . > "$BUILD_LOG" + + BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}-backend.log" + $DOCKER_BUILD -t "$COMPONENT-backend:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/backend/Dockerfile . > "$BUILD_LOG" + if [ "$COMPONENT" == "pathcomp" ]; then + # next command is redundant, but helpful to keep cache updated between rebuilds + IMAGE_NAME="$COMPONENT-backend:$TFS_IMAGE_TAG-builder" + $DOCKER_BUILD -t "$IMAGE_NAME" --target builder -f ./src/"$COMPONENT"/backend/Dockerfile . >> "$BUILD_LOG" + fi + elif [ "$COMPONENT" == "dlt" ]; then + BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}-connector.log" + $DOCKER_BUILD -t "$COMPONENT-connector:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/connector/Dockerfile . > "$BUILD_LOG" + + BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}-gateway.log" + $DOCKER_BUILD -t "$COMPONENT-gateway:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/gateway/Dockerfile . > "$BUILD_LOG" + else + $DOCKER_BUILD -t "$COMPONENT:$TFS_IMAGE_TAG" -f ./src/"$COMPONENT"/Dockerfile . > "$BUILD_LOG" + fi + + echo " Pushing Docker image to '$TFS_REGISTRY_IMAGES'..." + + if [ "$COMPONENT" == "pathcomp" ] || [ "$COMPONENT" == "telemetry" ] || [ "$COMPONENT" == "analytics" ] ; then + IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-frontend:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g') + + TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}-frontend.log" + docker tag "$COMPONENT-frontend:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG" + + PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}-frontend.log" + docker push "$IMAGE_URL" > "$PUSH_LOG" + + IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-backend:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g') + + TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}-backend.log" + docker tag "$COMPONENT-backend:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG" + + PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}-backend.log" + docker push "$IMAGE_URL" > "$PUSH_LOG" + elif [ "$COMPONENT" == "dlt" ]; then + IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-connector:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g') + + TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}-connector.log" + docker tag "$COMPONENT-connector:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG" + + PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}-connector.log" + docker push "$IMAGE_URL" > "$PUSH_LOG" + + IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-gateway:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g') + + TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}-gateway.log" + docker tag "$COMPONENT-gateway:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG" + + PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}-gateway.log" + docker push "$IMAGE_URL" > "$PUSH_LOG" + else + IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g') + + TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}.log" + docker tag "$COMPONENT:$TFS_IMAGE_TAG" "$IMAGE_URL" > "$TAG_LOG" + + PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}.log" + docker push "$IMAGE_URL" > "$PUSH_LOG" + fi + + printf "\n" +done + +echo "Pruning Docker Images..." +docker image prune --force +printf "\n\n" + +if [ "$DOCKER_BUILD" == "docker buildx build" ]; then + echo "Pruning Docker Buildx Cache..." + docker buildx prune --force + printf "\n\n" +fi diff --git a/manifests/nginx_ingress_http.yaml b/manifests/nginx_ingress_http.yaml index e45ca65f0cb63dedd1c9ff3cc22200f3bf1c9fa1..6d7badaa256ff545250cccd8b75505a4d05bc5b6 100644 --- a/manifests/nginx_ingress_http.yaml +++ b/manifests/nginx_ingress_http.yaml @@ -57,6 +57,13 @@ spec: name: webuiservice port: number: 3000 + - path: /()(.well-known/.*) + pathType: Prefix + backend: + service: + name: nbiservice + port: + number: 8080 - path: /()(restconf/.*) pathType: Prefix backend: diff --git a/manifests/simap_connectorservice.yaml b/manifests/simap_connectorservice.yaml new file mode 100644 index 0000000000000000000000000000000000000000..09796f6f8bc0fafe9867add81974859d1d1575a7 --- /dev/null +++ b/manifests/simap_connectorservice.yaml @@ -0,0 +1,100 @@ +# 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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simap-connectorservice +spec: + selector: + matchLabels: + app: simap-connectorservice + replicas: 1 + template: + metadata: + labels: + app: simap-connectorservice + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: labs.etsi.org:5050/tfs/controller/simap_connector:latest + imagePullPolicy: Always + ports: + - containerPort: 9090 + - containerPort: 9192 + env: + - name: LOG_LEVEL + value: "INFO" + - name: SIMAP_SERVER_SCHEME + value: "http" + - name: SIMAP_SERVER_ADDRESS + # Assuming SIMAP Server is deployed in a local Docker container, as per: + # - ./src/tests/tools/simap_server/build.sh + # - ./src/tests/tools/simap_server/deploy.sh + value: "172.17.0.1" + - name: SIMAP_SERVER_PORT + # Assuming SIMAP Server is deployed in a local Docker container, as per: + # - ./src/tests/tools/simap_server/build.sh + # - ./src/tests/tools/simap_server/deploy.sh + value: "8080" + - name: SIMAP_SERVER_USERNAME + value: "admin" + - name: SIMAP_SERVER_PASSWORD + value: "admin" + - name: CRDB_DATABASE + value: "tfs_simap_connector" + envFrom: + - secretRef: + name: crdb-data + - secretRef: + name: kfk-kpi-data + startupProbe: + grpc: + port: 9090 + failureThreshold: 30 + periodSeconds: 1 + readinessProbe: + grpc: + port: 9090 + livenessProbe: + grpc: + port: 9090 + resources: + requests: + cpu: 250m + memory: 128Mi + limits: + cpu: 1000m + memory: 1024Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: simap-connectorservice + labels: + app: simap-connectorservice +spec: + type: ClusterIP + selector: + app: simap-connectorservice + ports: + - name: grpc + protocol: TCP + port: 9090 + targetPort: 9090 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 diff --git a/my_deploy.sh b/my_deploy.sh index f4f8d203e718af3655bd27f72edd1b1af629b895..97b65d11685b808878f5341053e63ba7cc9ff29f 100644 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -86,6 +86,9 @@ export TFS_COMPONENTS="context device pathcomp service slice nbi webui" # export TFS_COMPONENTS="${BEFORE} qkd_app service ${AFTER}" #fi +# Uncomment to activate SIMAP Connector +#export TFS_COMPONENTS="${TFS_COMPONENTS} simap_connector" + # Uncomment to activate Load Generator #export TFS_COMPONENTS="${TFS_COMPONENTS} load_generator" diff --git a/nfvsdn22 b/nfvsdn22 deleted file mode 120000 index ac93a84be42e09c11106c5e0836bb4e51cc1fa1a..0000000000000000000000000000000000000000 --- a/nfvsdn22 +++ /dev/null @@ -1 +0,0 @@ -src/tests/nfvsdn22/ \ No newline at end of file diff --git a/oeccpsc22 b/oeccpsc22 deleted file mode 120000 index 4f55befad3e8730c8b7eb1a4cf2fbc7600d1878b..0000000000000000000000000000000000000000 --- a/oeccpsc22 +++ /dev/null @@ -1 +0,0 @@ -src/tests/oeccpsc22/ \ No newline at end of file diff --git a/proto/context.proto b/proto/context.proto index b33750e80b07b0300cf2aa8b526597bfea9a9ee5..4e52774388ff24517286411e9095fe86ddae53d3 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -78,7 +78,8 @@ service ContextService { rpc RemoveConnection (ConnectionId ) returns ( Empty ) {} rpc GetConnectionEvents(Empty ) returns (stream ConnectionEvent ) {} - + rpc GetAllEvents (Empty ) returns (stream AnyEvent ) {} + // ------------------------------ Experimental ----------------------------- rpc GetOpticalConfig (Empty ) returns (OpticalConfigList) {} rpc SetOpticalConfig (OpticalConfig ) returns (OpticalConfigId ) {} @@ -118,6 +119,18 @@ message Event { EventTypeEnum event_type = 2; } +message AnyEvent { + oneof event { + ContextEvent context = 1; + TopologyEvent topology = 2; + DeviceEvent device = 3; + LinkEvent link = 4; + ServiceEvent service = 5; + SliceEvent slice = 6; + ConnectionEvent connection = 7; + } +} + // ----- Context ------------------------------------------------------------------------------------------------------- message ContextId { Uuid context_uuid = 1; @@ -268,17 +281,19 @@ message LinkId { } enum LinkTypeEnum { - LINKTYPE_UNKNOWN = 0; - LINKTYPE_COPPER = 1; - LINKTYPE_FIBER = 2; - LINKTYPE_RADIO = 3; - LINKTYPE_VIRTUAL = 4; + LINKTYPE_UNKNOWN = 0; + LINKTYPE_COPPER = 1; + LINKTYPE_FIBER = 2; + LINKTYPE_RADIO = 3; + LINKTYPE_VIRTUAL = 4; LINKTYPE_MANAGEMENT = 5; + LINKTYPE_REMOTE = 6; // imported from remote topology } message LinkAttributes { - float total_capacity_gbps = 1; - float used_capacity_gbps = 2; + bool is_bidirectional = 1; + float total_capacity_gbps = 2; + float used_capacity_gbps = 3; } message Link { diff --git a/proto/device.proto b/proto/device.proto index a4233d33de2346d4d706a1fd1d468c13c6297004..265c96191e8a1d3adfb02841e2d2ae171f84dec3 100644 --- a/proto/device.proto +++ b/proto/device.proto @@ -19,11 +19,12 @@ import "context.proto"; import "monitoring.proto"; // to be migrated to: "kpi_manager.proto" service DeviceService { - rpc AddDevice (context.Device ) returns (context.DeviceId ) {} - rpc ConfigureDevice (context.Device ) returns (context.DeviceId ) {} - rpc DeleteDevice (context.DeviceId ) returns (context.Empty ) {} - rpc GetInitialConfig(context.DeviceId ) returns (context.DeviceConfig) {} - rpc MonitorDeviceKpi(MonitoringSettings) returns (context.Empty ) {} + rpc AddDevice (context.Device ) returns (context.DeviceId ) {} + rpc ConfigureDevice (context.Device ) returns (context.DeviceId ) {} + rpc DeleteDevice (context.DeviceId ) returns (context.Empty ) {} + rpc GetInitialConfig (context.DeviceId ) returns (context.DeviceConfig ) {} + rpc MonitorDeviceKpi (MonitoringSettings ) returns (context.Empty ) {} + rpc SSETelemetrySubscribe(monitoring.SSEMonitoringSubscriptionConfig) returns (monitoring.SSEMonitoringSubscriptionResponse ) {} } message MonitoringSettings { diff --git a/proto/monitoring.proto b/proto/monitoring.proto index d027b792b6b7da6b4c71ac0d754b314d7a3d6dc9..11dac60bf0cf2ea97f0d2c9ebf4bacb570d04c36 100644 --- a/proto/monitoring.proto +++ b/proto/monitoring.proto @@ -172,3 +172,21 @@ message AlarmResponse { message AlarmList { repeated AlarmDescriptor alarm_descriptor = 1; } + +message SSEMonitoringSubscriptionConfig { + enum ConfigType { + Subscribe = 0; + Unsubscribe = 1; + GetTelemetry = 2; + } + context.DeviceId device_id = 1; + ConfigType config_type = 2; + string uri = 3; + string sampling_interval = 4; // in seconds + string identifier = 5; +} + +message SSEMonitoringSubscriptionResponse { + string identifier = 1; + string uri = 2; +} diff --git a/proto/simap_connector.proto b/proto/simap_connector.proto new file mode 100644 index 0000000000000000000000000000000000000000..498c871b4ceebe99a32116aaacb8d637d9559265 --- /dev/null +++ b/proto/simap_connector.proto @@ -0,0 +1,43 @@ +// 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. + +syntax = "proto3"; +package simap_connector; + +import "context.proto"; + +// Subscription handling according to https://datatracker.ietf.org/doc/html/rfc8641 + +service SimapConnectorService { + rpc EstablishSubscription (Subscription ) returns (SubscriptionId) {} + rpc DeleteSubscription (SubscriptionId) returns (context.Empty ) {} + rpc AffectSampleSynthesizer(Affectation ) returns (context.Empty ) {} +} + +message SubscriptionId { + uint64 subscription_id = 1; +} + +message Subscription { + string datastore = 1; + string xpath_filter = 2; + float period = 3; +} + +message Affectation { + string network_id = 1; + string link_id = 2; + float bandwidth_factor = 3; + float latency_factor = 4; +} diff --git a/scripts/run_tests_locally-nbi-all.sh b/scripts/run_tests_locally-nbi-all.sh index 35c755b387f81efdcb152d1efbfb5b063154215f..83e2b02e2eb0c3bafec5a7acc2ef5979522f4774 100755 --- a/scripts/run_tests_locally-nbi-all.sh +++ b/scripts/run_tests_locally-nbi-all.sh @@ -59,10 +59,12 @@ docker run --name nbi -d \ --env "KFK_SERVER_ADDRESS=${KAFKA_IP}:9092" \ nbi:latest -while ! docker logs nbi 2>&1 | grep -q 'Initialization completed'; do - printf "." - sleep 1; -done +# Wait until any worker logs "Initialization completed" (from the start of logs) +# -m1 makes grep exit as soon as the line appears. +# With set -o pipefail, docker logs will get SIGPIPE when grep exits; +# `|| true` neutralizes that so the pipeline’s status reflects grep’s success. +(docker logs -f $IMAGE_NAME || true) 2>&1 | grep -m1 -Fi 'Initialization completed' + printf "\n" sleep 5 # Give extra time to NBI to get ready diff --git a/scripts/show_logs_simap_connector.sh b/scripts/show_logs_simap_connector.sh new file mode 100755 index 0000000000000000000000000000000000000000..20e5a5d3ed9684a246fed742e714121a9f24f1df --- /dev/null +++ b/scripts/show_logs_simap_connector.sh @@ -0,0 +1,27 @@ +#!/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. + +######################################################################################################################## +# Define your deployment settings here +######################################################################################################################## + +# If not already set, set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} + +######################################################################################################################## +# Automated steps start here +######################################################################################################################## + +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/simap-connectorservice -c server diff --git a/src/common/Constants.py b/src/common/Constants.py index 2cb7c3787be234a6b624dca5ab1bdaf3010c9ea1..21ac64a00037c7fc2d03d07fa7c6ad17197947ce 100644 --- a/src/common/Constants.py +++ b/src/common/Constants.py @@ -47,6 +47,7 @@ class ServiceNameEnum(Enum): MONITORING = 'monitoring' DLT = 'dlt' NBI = 'nbi' + SIMAP_CONNECTOR = 'simap-connector' CYBERSECURITY = 'cybersecurity' INTERDOMAIN = 'interdomain' PATHCOMP = 'pathcomp' @@ -90,6 +91,7 @@ DEFAULT_SERVICE_GRPC_PORTS = { ServiceNameEnum.POLICY .value : 6060, ServiceNameEnum.MONITORING .value : 7070, ServiceNameEnum.DLT .value : 8080, + ServiceNameEnum.SIMAP_CONNECTOR .value : 9090, ServiceNameEnum.L3_CAD .value : 10001, ServiceNameEnum.L3_AM .value : 10002, ServiceNameEnum.DBSCANSERVING .value : 10008, diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py index 7698097f8c8ff205b8ee78050ad98282b934d666..008cb448d1cc849dddf67bf314e0dccec0cd81ae 100644 --- a/src/common/DeviceTypes.py +++ b/src/common/DeviceTypes.py @@ -21,7 +21,10 @@ class DeviceTypeEnum(Enum): # Emulated device types EMULATED_CLIENT = 'emu-client' + EMULATED_COMPUTER = 'emu-computer' EMULATED_DATACENTER = 'emu-datacenter' + EMULATED_VIRTUAL_MACHINE = 'emu-virtual-machine' + EMULATED_IP_SDN_CONTROLLER = 'emu-ip-sdn-controller' EMULATED_MICROWAVE_RADIO_SYSTEM = 'emu-microwave-radio-system' EMULATED_OPEN_LINE_SYSTEM = 'emu-open-line-system' @@ -41,9 +44,13 @@ class DeviceTypeEnum(Enum): NCE = 'nce' MICROWAVE_RADIO_SYSTEM = 'microwave-radio-system' OPEN_LINE_SYSTEM = 'open-line-system' + OPTICAL_OLT = 'optical-olt' + OPTICAL_ONT = 'optical-ont' + OPTICAL_FGOTN = 'optical-fgotn' OPTICAL_ROADM = 'optical-roadm' OPTICAL_TRANSPONDER = 'optical-transponder' P4_SWITCH = 'p4-switch' + PACKET_POP = 'packet-pop' PACKET_RADIO_ROUTER = 'packet-radio-router' PACKET_ROUTER = 'packet-router' PACKET_SWITCH = 'packet-switch' diff --git a/src/common/tools/context_queries/Device.py b/src/common/tools/context_queries/Device.py index b972fd511efe931ede9994327d34e92c9d2989ad..d11bdb1be66930567c128b50c6742d7c842bfb13 100644 --- a/src/common/tools/context_queries/Device.py +++ b/src/common/tools/context_queries/Device.py @@ -50,6 +50,10 @@ def get_existing_device_uuids(context_client : ContextClient) -> Set[str]: existing_device_uuids = {device_id.device_uuid.uuid for device_id in existing_device_ids.device_ids} return existing_device_uuids +def get_devices(context_client : ContextClient) -> List[Device]: + devices = context_client.ListDevices(Empty()) + return [d for d in devices.devices] + def add_device_to_topology( context_client : ContextClient, context_id : ContextId, topology_uuid : str, device_uuid : str ) -> bool: diff --git a/src/common/tools/context_queries/Link.py b/src/common/tools/context_queries/Link.py index 5a96d9654b370eaf1d4d6f6223d27dd5a9a05898..144ec7534b94188bcbd0b7588151cbef7d4469ed 100644 --- a/src/common/tools/context_queries/Link.py +++ b/src/common/tools/context_queries/Link.py @@ -37,6 +37,10 @@ def get_existing_link_uuids(context_client : ContextClient) -> Set[str]: existing_link_uuids = {link_id.link_uuid.uuid for link_id in existing_link_ids.link_ids} return existing_link_uuids +def get_links(context_client : ContextClient) -> List[Link]: + links = context_client.ListLinks(Empty()) + return [l for l in links.links] + def add_link_to_topology( context_client : ContextClient, context_id : ContextId, topology_uuid : str, link_uuid : str ) -> bool: diff --git a/src/common/tools/context_queries/Service.py b/src/common/tools/context_queries/Service.py index da017412f36a58155de2c096870d418e49dd5095..8b7ca7312703ff3510ea56d9fefce4434bf4d68c 100644 --- a/src/common/tools/context_queries/Service.py +++ b/src/common/tools/context_queries/Service.py @@ -13,9 +13,9 @@ # limitations under the License. import grpc, logging -from typing import Optional +from typing import List, Optional from common.Constants import DEFAULT_CONTEXT_NAME -from common.proto.context_pb2 import Service, ServiceFilter, ServiceId +from common.proto.context_pb2 import ContextId, Service, ServiceFilter, ServiceId from context.client.ContextClient import ContextClient LOGGER = logging.getLogger(__name__) @@ -55,3 +55,11 @@ def get_service_by_uuid( return get_service_by_id( context_client, service_id, rw_copy=rw_copy, include_endpoint_ids=include_endpoint_ids, include_constraints=include_constraints, include_config_rules=include_config_rules) + +def get_services( + context_client : ContextClient, context_uuid : str = DEFAULT_CONTEXT_NAME + ) -> List[Service]: + context_id = ContextId() + context_id.context_uuid.uuid = context_uuid + services = context_client.ListServices(context_id) + return [s for s in services.services] diff --git a/src/common/tools/context_queries/Topology.py b/src/common/tools/context_queries/Topology.py index 5df396feb6ac184a74724ca0faf030438b86b51c..29e4b8051d46bf8b6ef4d37e9df8c8a89b1cebfa 100644 --- a/src/common/tools/context_queries/Topology.py +++ b/src/common/tools/context_queries/Topology.py @@ -62,6 +62,14 @@ def get_topology( #LOGGER.exception('Unable to get topology({:s} / {:s})'.format(str(context_uuid), str(topology_uuid))) return None +def get_topologies( + context_client : ContextClient, context_uuid : str = DEFAULT_CONTEXT_NAME + ) -> List[Topology]: + context_id = ContextId() + context_id.context_uuid.uuid = context_uuid + topologies = context_client.ListTopologies(context_id) + return [t for t in topologies.topologies] + def get_topology_details( context_client : ContextClient, topology_uuid : str, context_uuid : str = DEFAULT_CONTEXT_NAME, rw_copy : bool = False diff --git a/src/common/tools/grpc/BaseEventCollector.py b/src/common/tools/grpc/BaseEventCollector.py index 1cf370426f979d7a67edb62ec050a050dc3be694..637fa59795e6320719017e14c37cc88ecd544dce 100644 --- a/src/common/tools/grpc/BaseEventCollector.py +++ b/src/common/tools/grpc/BaseEventCollector.py @@ -47,7 +47,13 @@ class CollectorThread(threading.Thread): if self._log_events_received: str_event = grpc_message_to_json_string(event) LOGGER.info('[_collect] event: {:s}'.format(str_event)) - timestamp = event.event.timestamp.timestamp + object_name = str(event.__class__.__name__).lower().replace('event', '') + if object_name == 'any': + object_name = event.WhichOneof('event') + _event = getattr(event, object_name) + timestamp = _event.event.timestamp.timestamp + else: + timestamp = event.event.timestamp.timestamp self._events_queue.put_nowait((timestamp, event)) except grpc.RpcError as e: if e.code() == grpc.StatusCode.UNAVAILABLE: # pylint: disable=no-member diff --git a/src/common/tools/grpc/BaseEventDispatcher.py b/src/common/tools/grpc/BaseEventDispatcher.py index e654b4c737c26c5339b02f486abfb2b75a28b0ed..dc4d7a0a8627b1cc89fc85766faf778d4f763b4f 100644 --- a/src/common/tools/grpc/BaseEventDispatcher.py +++ b/src/common/tools/grpc/BaseEventDispatcher.py @@ -44,7 +44,9 @@ class BaseEventDispatcher(threading.Thread): def _get_dispatcher(self, event : Any) -> Optional[Callable]: object_name = str(event.__class__.__name__).lower().replace('event', '') - event_type = EventTypeEnum.Name(event.event.event_type).lower().replace('eventtype_', '') + event_type = event.event.event_type + event_type = EventTypeEnum.Name(event_type) + event_type = event_type.lower().replace('eventtype_', '') method_name = 'dispatch_{:s}_{:s}'.format(object_name, event_type) dispatcher = getattr(self, method_name, None) @@ -65,6 +67,16 @@ class BaseEventDispatcher(threading.Thread): event = self._get_event() if event is None: continue + object_name = str(event.__class__.__name__) + object_name = object_name.lower().replace('event', '') + if object_name == 'any': + field_name = event.WhichOneof('event') + event = getattr(event, field_name) + + event_type = event.event.event_type + event_type = EventTypeEnum.Name(event_type) + event_type = event_type.lower().replace('eventtype_', '') + dispatcher = self._get_dispatcher(event) if dispatcher is None: MSG = 'No dispatcher available for Event({:s})' diff --git a/src/common/tools/kafka/Variables.py b/src/common/tools/kafka/Variables.py index 7bb131dd6c76789b07c44c9568ede038af1e4d45..b9d52e33101980964136dd0a566bff7c3ecee20c 100644 --- a/src/common/tools/kafka/Variables.py +++ b/src/common/tools/kafka/Variables.py @@ -16,6 +16,7 @@ import logging, time from enum import Enum #from confluent_kafka.admin import AdminClient, NewTopic from kafka.admin import KafkaAdminClient, NewTopic +from kafka.errors import TopicAlreadyExistsError from common.Settings import get_setting @@ -88,32 +89,36 @@ class KafkaTopic(Enum): #create_topic_future_map = kafka_admin_client.create_topics(missing_topics, request_timeout=5*60) #LOGGER.debug('create_topic_future_map: {:s}'.format(str(create_topic_future_map))) - topics_result = kafka_admin_client.create_topics( - new_topics=missing_topics, timeout_ms=KAFKA_TOPIC_CREATE_REQUEST_TIMEOUT, - validate_only=False - ) - LOGGER.debug('topics_result={:s}'.format(str(topics_result))) - - failed_topic_creations = set() - #for topic, future in create_topic_future_map.items(): - # try: - # LOGGER.info('Waiting for Topic({:s})...'.format(str(topic))) - # future.result() # Blocks until topic is created or raises an exception - # LOGGER.info('Topic({:s}) successfully created.'.format(str(topic))) - # except: # pylint: disable=bare-except - # LOGGER.exception('Failed to create Topic({:s})'.format(str(topic))) - # failed_topic_creations.add(topic) - for topic_name, error_code, error_message in topics_result.topic_errors: - if error_code == 0 and error_message is None: - MSG = 'Topic({:s}) successfully created.' - LOGGER.info(MSG.format(str(topic_name))) - else: - MSG = 'Failed to create Topic({:s}): error_code={:s} error_message={:s}' - LOGGER.error(MSG.format(str(topic_name), str(error_code), str(error_message))) - failed_topic_creations.add(topic_name) - - if len(failed_topic_creations) > 0: return False - LOGGER.debug('All topics created.') + try: + topics_result = kafka_admin_client.create_topics( + new_topics=missing_topics, timeout_ms=KAFKA_TOPIC_CREATE_REQUEST_TIMEOUT, + validate_only=False + ) + LOGGER.debug('topics_result={:s}'.format(str(topics_result))) + + failed_topic_creations = set() + #for topic, future in create_topic_future_map.items(): + # try: + # LOGGER.info('Waiting for Topic({:s})...'.format(str(topic))) + # future.result() # Blocks until topic is created or raises an exception + # LOGGER.info('Topic({:s}) successfully created.'.format(str(topic))) + # except: # pylint: disable=bare-except + # LOGGER.exception('Failed to create Topic({:s})'.format(str(topic))) + # failed_topic_creations.add(topic) + for topic_name, error_code, error_message in topics_result.topic_errors: + if error_code == 0 and error_message is None: + MSG = 'Topic({:s}) successfully created.' + LOGGER.info(MSG.format(str(topic_name))) + else: + MSG = 'Failed to create Topic({:s}): error_code={:s} error_message={:s}' + LOGGER.error(MSG.format(str(topic_name), str(error_code), str(error_message))) + failed_topic_creations.add(topic_name) + + if len(failed_topic_creations) > 0: return False + LOGGER.debug('All topics created.') + + except TopicAlreadyExistsError: + LOGGER.debug('Some topics already exists.') # Wait until topics appear in metadata desired_topics = {topic.value for topic in KafkaTopic} diff --git a/src/service/service/service_handlers/l3nm_ietf_actn/__init__.py b/src/common/tools/kafka/__init__.py similarity index 100% rename from src/service/service/service_handlers/l3nm_ietf_actn/__init__.py rename to src/common/tools/kafka/__init__.py diff --git a/src/service/service/service_handlers/l3nm_nce/__init__.py b/src/common/tools/rest_api/__init__.py similarity index 100% rename from src/service/service/service_handlers/l3nm_nce/__init__.py rename to src/common/tools/rest_api/__init__.py diff --git a/src/common/tools/client/RestClient.py b/src/common/tools/rest_api/client/RestApiClient.py similarity index 88% rename from src/common/tools/client/RestClient.py rename to src/common/tools/rest_api/client/RestApiClient.py index 89717dbc08857c5ae0adc0ebf77cb27fd71d0b27..68977a60e5f0fb3f48f111057730d043c085f023 100644 --- a/src/common/tools/client/RestClient.py +++ b/src/common/tools/rest_api/client/RestApiClient.py @@ -12,10 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. + import enum, logging, requests from requests.auth import HTTPBasicAuth from typing import Any, Optional, Set + class RestRequestMethod(enum.Enum): GET = 'get' POST = 'post' @@ -23,6 +25,7 @@ class RestRequestMethod(enum.Enum): PATCH = 'patch' DELETE = 'delete' + EXPECTED_STATUS_CODES : Set[int] = { requests.codes['OK' ], # 200 - OK requests.codes['CREATED' ], # 201 - Created @@ -30,7 +33,6 @@ EXPECTED_STATUS_CODES : Set[int] = { requests.codes['NO_CONTENT'], # 204 - No Content } -URL_TEMPLATE = '{:s}://{:s}:{:d}/{:s}' def compose_basic_auth( username : Optional[str] = None, password : Optional[str] = None @@ -38,19 +40,24 @@ def compose_basic_auth( if username is None or password is None: return None return HTTPBasicAuth(username, password) + class SchemeEnum(enum.Enum): HTTP = 'http' HTTPS = 'https' + def check_scheme(scheme : str) -> str: str_scheme = str(scheme).lower() enm_scheme = SchemeEnum._value2member_map_[str_scheme] return enm_scheme.value -class RestClient: +TEMPLATE_URL = '{:s}://{:s}:{:d}/{:s}' + + +class RestApiClient: def __init__( - self, address : str, port : int, scheme : str = 'http', + self, address : str, port : int = 8080, scheme : str = 'http', base_url : str = '', username : Optional[str] = None, password : Optional[str] = None, timeout : int = 30, verify_certs : bool = True, allow_redirects : bool = True, logger : Optional[logging.Logger] = None @@ -58,15 +65,13 @@ class RestClient: self._address = address self._port = int(port) self._scheme = check_scheme(scheme) + self._base_url = base_url self._auth = compose_basic_auth(username=username, password=password) self._timeout = int(timeout) self._verify_certs = verify_certs self._allow_redirects = allow_redirects self._logger = logger - def _compose_url(self, endpoint : str) -> str: - endpoint = endpoint.lstrip('/') - return URL_TEMPLATE.format(self._scheme, self._address, self._port, endpoint) def _log_msg_request( self, method : RestRequestMethod, request_url : str, body : Optional[Any], @@ -77,6 +82,7 @@ class RestClient: if self._logger is not None: self._logger.log(log_level, msg) return msg + def _log_msg_check_reply( self, method : RestRequestMethod, request_url : str, body : Optional[Any], reply : requests.Response, expected_status_codes : Set[int], @@ -91,15 +97,23 @@ class RestClient: str(method.value).upper(), str(request_url), str(body), str(http_status_code), str(reply.text) ) - self._logger.error(msg) + if self._logger is not None: self._logger.error(msg) raise Exception(msg) + def _do_rest_request( self, method : RestRequestMethod, endpoint : str, body : Optional[Any] = None, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES ) -> Optional[Any]: - request_url = self._compose_url(endpoint) + candidate_schemes = tuple(['{:s}://'.format(m).lower() for m in SchemeEnum.__members__]) + if endpoint.lower().startswith(candidate_schemes): + request_url = endpoint.lstrip('/') + else: + endpoint = str(self._base_url + '/' + endpoint).replace('//', '/').lstrip('/') + request_url = TEMPLATE_URL.format(self._scheme, self._address, self._port, endpoint) + self._log_msg_request(method, request_url, body) + try: headers = {'accept': 'application/json'} reply = requests.request( @@ -110,12 +124,15 @@ class RestClient: except Exception as e: MSG = 'Request failed. method={:s} url={:s} body={:s}' msg = MSG.format(str(method.value).upper(), request_url, str(body)) - self._logger.exception(msg) + if self._logger is not None: self._logger.exception(msg) raise Exception(msg) from e + self._log_msg_check_reply(method, request_url, body, reply, expected_status_codes) + if reply.content and len(reply.content) > 0: return reply.json() return None + def get( self, endpoint : str, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES @@ -125,6 +142,7 @@ class RestClient: expected_status_codes=expected_status_codes ) + def post( self, endpoint : str, body : Optional[Any] = None, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES @@ -134,6 +152,7 @@ class RestClient: expected_status_codes=expected_status_codes ) + def put( self, endpoint : str, body : Optional[Any] = None, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES @@ -143,6 +162,7 @@ class RestClient: expected_status_codes=expected_status_codes ) + def patch( self, endpoint : str, body : Optional[Any] = None, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES @@ -152,6 +172,7 @@ class RestClient: expected_status_codes=expected_status_codes ) + def delete( self, endpoint : str, body : Optional[Any] = None, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES diff --git a/src/service/service/service_handlers/l3slice_ietfslice/__init__.py b/src/common/tools/rest_api/client/__init__.py similarity index 100% rename from src/service/service/service_handlers/l3slice_ietfslice/__init__.py rename to src/common/tools/rest_api/client/__init__.py diff --git a/src/common/tools/service/GenericRestServer.py b/src/common/tools/rest_api/server/GenericRestServer.py similarity index 94% rename from src/common/tools/service/GenericRestServer.py rename to src/common/tools/rest_api/server/GenericRestServer.py index 17f629be182b7d899e0e5ab55c276c17748c980c..0266ceb72981b0aa7f2e2f0f5cdac04670cee12b 100644 --- a/src/common/tools/service/GenericRestServer.py +++ b/src/common/tools/rest_api/server/GenericRestServer.py @@ -29,8 +29,8 @@ def log_request(logger, response): class GenericRestServer(threading.Thread): def __init__( - self, bind_port : Union[str, int], base_url : str, bind_address : Optional[str] = None, - cls_name : str = __name__ + self, bind_port : Union[str, int], base_url : Optional[str] = None, + bind_address : Optional[str] = None, cls_name : str = __name__ ) -> None: threading.Thread.__init__(self, daemon=True) self.logger = logging.getLogger(cls_name) diff --git a/src/common/tools/rest_api/server/__init__.py b/src/common/tools/rest_api/server/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/common/tools/rest_api/server/__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/common/tools/rest_conf/__init__.py b/src/common/tools/rest_conf/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/common/tools/rest_conf/__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/common/tools/rest_conf/client/README.md b/src/common/tools/rest_conf/client/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9605fc751ecd44c0404cfd4586012c91544890d5 --- /dev/null +++ b/src/common/tools/rest_conf/client/README.md @@ -0,0 +1,5 @@ +# Generic RESTCONF Client + +This server implements a basic RESTCONF Client that can be potentially used for any case. + +See a simple working example in folder `src/tests/tools/simap_server` diff --git a/src/common/tools/rest_conf/client/RestConfClient.py b/src/common/tools/rest_conf/client/RestConfClient.py new file mode 100644 index 0000000000000000000000000000000000000000..088bb4ae14c4308d11c07f4efb6e6ed1817995eb --- /dev/null +++ b/src/common/tools/rest_conf/client/RestConfClient.py @@ -0,0 +1,123 @@ +# 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 logging, requests +from typing import Any, Dict, Optional, Set +from common.tools.rest_api.client.RestApiClient import RestApiClient + + +HOST_META_URL = '{:s}://{:s}:{:d}/.well-known/host-meta' + + +class RestConfClient(RestApiClient): + def __init__( + self, address : str, port : int = 8080, scheme : str = 'http', + username : Optional[str] = None, password : Optional[str] = None, + restconf_version : Optional[str] = None, + timeout : int = 30, verify_certs : bool = True, allow_redirects : bool = True, + logger : Optional[logging.Logger] = None + ) -> None: + super().__init__( + address, port=port, scheme=scheme, username=username, password=password, + timeout=timeout, verify_certs=verify_certs, allow_redirects=allow_redirects, + logger=logger + ) + self._restconf_version = restconf_version + self._discover_base_url() + + + def _discover_base_url(self) -> None: + host_meta_url = HOST_META_URL.format(self._scheme, self._address, self._port) + host_meta : Dict = super().get(host_meta_url, expected_status_codes={requests.codes['OK']}) + + links = host_meta.get('links') + if links is None: raise AttributeError('Missing attribute "links" in host-meta reply') + if not isinstance(links, list): raise AttributeError('Attribute "links" must be a list') + if len(links) != 1: raise AttributeError('Attribute "links" is expected to have exactly 1 item') + + link = links[0] + if not isinstance(link, dict): raise AttributeError('Attribute "links[0]" must be a dict') + + rel = link.get('rel') + if rel is None: raise AttributeError('Missing attribute "links[0].rel" in host-meta reply') + if not isinstance(rel, str): raise AttributeError('Attribute "links[0].rel" must be a str') + if rel != 'restconf': raise AttributeError('Attribute "links[0].rel" != "restconf"') + + href = link.get('href') + if href is None: raise AttributeError('Missing attribute "links[0]" in host-meta reply') + if not isinstance(href, str): raise AttributeError('Attribute "links[0].href" must be a str') + + self._base_url = str(href).replace('//', '/') + if self._restconf_version is not None: + self._base_url += '/{:s}'.format(self._restconf_version) + + + def get( + self, endpoint : str, + expected_status_codes : Set[int] = {requests.codes['OK']} + ) -> Optional[Any]: + return super().get( + ('/data/{:s}'.format(endpoint)).replace('//', '/'), + expected_status_codes=expected_status_codes + ) + + + def post( + self, endpoint : str, body : Optional[Any] = None, + expected_status_codes : Set[int] = {requests.codes['CREATED']} + ) -> Optional[Any]: + return super().post( + ('/data/{:s}'.format(endpoint)).replace('//', '/'), body=body, + expected_status_codes=expected_status_codes + ) + + + def put( + self, endpoint : str, body : Optional[Any] = None, + expected_status_codes : Set[int] = {requests.codes['CREATED'], requests.codes['NO_CONTENT']} + ) -> Optional[Any]: + return super().put( + ('/data/{:s}'.format(endpoint)).replace('//', '/'), body=body, + expected_status_codes=expected_status_codes + ) + + + def patch( + self, endpoint : str, body : Optional[Any] = None, + expected_status_codes : Set[int] = {requests.codes['NO_CONTENT']} + ) -> Optional[Any]: + return super().patch( + ('/data/{:s}'.format(endpoint)).replace('//', '/'), body=body, + expected_status_codes=expected_status_codes + ) + + + def delete( + self, endpoint : str, body : Optional[Any] = None, + expected_status_codes : Set[int] = {requests.codes['NO_CONTENT']} + ) -> Optional[Any]: + return super().delete( + ('/data/{:s}'.format(endpoint)).replace('//', '/'), body=body, + expected_status_codes=expected_status_codes + ) + + + def rpc( + self, endpoint : str, body : Optional[Any] = None, + expected_status_codes : Set[int] = {requests.codes['OK'], requests.codes['NO_CONTENT']} + ) -> Optional[Any]: + return super().post( + ('/operations/{:s}'.format(endpoint)).replace('//', '/'), body=body, + expected_status_codes=expected_status_codes + ) diff --git a/src/common/tools/rest_conf/client/__init__.py b/src/common/tools/rest_conf/client/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/common/tools/rest_conf/client/__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/common/tools/rest_conf/server/Dockerfile b/src/common/tools/rest_conf/server/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..1dbe96c0abe3b632829809d6c0f0e8ea0b5df365 --- /dev/null +++ b/src/common/tools/rest_conf/server/Dockerfile @@ -0,0 +1,66 @@ +# 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 git build-essential cmake libpcre2-dev python3-dev python3-cffi && \ + rm -rf /var/lib/apt/lists/* + +# Download, build and install libyang. Note that APT package is outdated +# - Ref: https://github.com/CESNET/libyang +# - Ref: https://github.com/CESNET/libyang-python/ +RUN mkdir -p /var/libyang +RUN git clone https://github.com/CESNET/libyang.git /var/libyang +WORKDIR /var/libyang +RUN git fetch +RUN git checkout v2.1.148 +RUN mkdir -p /var/libyang/build +WORKDIR /var/libyang/build +RUN cmake -D CMAKE_BUILD_TYPE:String="Release" .. +RUN make +RUN make install +RUN ldconfig + +# 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, get specific Python packages +RUN mkdir -p /var/teraflow/restconf_server/ +WORKDIR /var/teraflow/restconf_server/ +COPY ./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 +COPY ./yang/*.yang ./yang/ +COPY ./restconf_server/*.py ./restconf_server/ +COPY ./startup.json ./startup.json + +# Configure RESTCONF Server +ENV RESTCONF_PREFIX="/restconf" +ENV YANG_SEARCH_PATH="./yang" +ENV STARTUP_FILE="./startup.json" + +# Configure Flask for production +ENV FLASK_ENV="production" + +# Start the service +ENTRYPOINT ["gunicorn", "--workers", "1", "--worker-class", "eventlet", "--bind", "0.0.0.0:8080", "restconf_server.app:app"] diff --git a/src/common/tools/rest_conf/server/README.md b/src/common/tools/rest_conf/server/README.md new file mode 100644 index 0000000000000000000000000000000000000000..542d836173a11bf65cdfb5dc52a8fdf8657725c5 --- /dev/null +++ b/src/common/tools/rest_conf/server/README.md @@ -0,0 +1,31 @@ +# Generic Mock RESTCONF Server + +This server implements a basic RESTCONF Server that can load, potentially, any YANG data model. +Just copy this file structure, drop in fodler `./yang` your YANG data models. +Hierarchical folder structures are also supported. +YangModelDiscoverer will parse the models, identify imports and dependencies, and sort the models before loading. + +The server can be configured using the following environment variables: +- `RESTCONF_PREFIX`, defaults to `"/restconf"` +- `YANG_SEARCH_PATH`, defaults to `"./yang"` +- `STARTUP_FILE`, defaults to `"./startup.json"` +- `SECRET_KEY`, defaults to `secrets.token_hex(64)` + + +See a simple working example in folder `src/tests/tools/simap_server` + + +## Build the RESTCONF Server Docker image +```bash +./build.sh +``` + +## Deploy the RESTCONF Server +```bash +./deploy.sh +``` + +## Destroy the RESTCONF Server +```bash +./destroy.sh +``` diff --git a/src/common/tools/rest_conf/server/__init__.py b/src/common/tools/rest_conf/server/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/common/tools/rest_conf/server/__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/common/tools/rest_conf/server/build.sh b/src/common/tools/rest_conf/server/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..e0645d65de5151b45d09602c98545de18b9d877f --- /dev/null +++ b/src/common/tools/rest_conf/server/build.sh @@ -0,0 +1,23 @@ +#!/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 buildx build -t mock-restconf-server:test -f Dockerfile . +#docker tag mock-restconf-server:test localhost:32000/tfs/mock-restconf-server:test +#docker push localhost:32000/tfs/mock-restconf-server:test diff --git a/src/common/tools/rest_conf/server/deploy.sh b/src/common/tools/rest_conf/server/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..03a33895daa87ec94d81ad7498b2ecee1e2f6776 --- /dev/null +++ b/src/common/tools/rest_conf/server/deploy.sh @@ -0,0 +1,32 @@ +#!/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. + + +# Cleanup +docker rm --force mock-restconf-server + + +# Create Mock RESTCONF Server +docker run --detach --name mock-restconf-server --publish 8080:8080 mock-restconf-server:test + + +sleep 2 + + +# Dump Mock RESTCONF Server Docker container +docker ps -a + + +echo "Bye!" diff --git a/src/common/tools/rest_conf/server/destroy.sh b/src/common/tools/rest_conf/server/destroy.sh new file mode 100755 index 0000000000000000000000000000000000000000..ecc2af6864b4230b4f704d793161e3b9b149881c --- /dev/null +++ b/src/common/tools/rest_conf/server/destroy.sh @@ -0,0 +1,25 @@ +#!/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. + + +# Cleanup +docker rm --force mock-restconf-server + + +# Dump Docker containers +docker ps -a + + +echo "Bye!" diff --git a/src/common/tools/rest_conf/server/requirements.in b/src/common/tools/rest_conf/server/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..f29c2d1800bc0a19468f10b5b1d0d36d8cf3b8ae --- /dev/null +++ b/src/common/tools/rest_conf/server/requirements.in @@ -0,0 +1,26 @@ +# 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 +deepdiff==6.7.* +eventlet==0.39.0 +Flask-HTTPAuth==4.5.0 +Flask-RESTful==0.3.9 +Flask==2.1.3 +gunicorn==23.0.0 +jsonschema==4.4.0 +libyang==2.8.4 +pyopenssl==23.0.0 +requests==2.27.1 +werkzeug==2.3.7 diff --git a/src/common/tools/rest_conf/server/restconf_server/Callbacks.py b/src/common/tools/rest_conf/server/restconf_server/Callbacks.py new file mode 100644 index 0000000000000000000000000000000000000000..e3e4d0f452434509f8caa083fb39d529e4f7efbd --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/Callbacks.py @@ -0,0 +1,179 @@ +# 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 logging, re +from typing import Dict, List, Optional, Union + + +LOGGER = logging.getLogger(__name__) + + +class _Callback: + def __init__(self, path_pattern : Union[str, re.Pattern]) -> None: + ''' + Initialize a Callback + @param path_pattern: A regular expression (string or compiled `re.Pattern`) + ''' + if isinstance(path_pattern, str): + path_pattern = re.compile('^{:s}/?$'.format(path_pattern)) + self._path_pattern = path_pattern + + def match(self, path : str) -> Optional[re.Match]: + ''' + Match method used to check if this callback should be executed. + @param path: A RESTCONF request path to test + @returns `re.Match` object if pattern fully matches `path`, otherwise `None` + ''' + return self._path_pattern.fullmatch(path) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + ''' + Execute the callback action for a matched data path. + This method should be implemented for each specific callback. + @param match: `re.Match` object returned by `match()`. + @param path: Original request path that was matched. + @param old_data: Resource representation before change, if applicable, otherwise `None` + @param new_data: Resource representation after change, if applicable, otherwise `None` + @returns boolean indicating whether additional callbacks should be executed, defaults to False + ''' + MSG = 'match={:s}, path={:s}, old_data={:s}, new_data={:s}' + msg = MSG.format(match.groupdict(), path, old_data, new_data) + raise NotImplementedError(msg) + + def execute_operation( + self, match : re.Match, path : str, input_data : Optional[Dict] + ) -> Optional[Dict]: + ''' + Execute the callback action for a matched operation path. + This method should be implemented for each specific callback. + @param match: `re.Match` object returned by `match()`. + @param path: Original request path that was matched. + @param input_data: Input data, if applicable, otherwise `None` + @returns Optional[Dict] containing output data, defaults to None + ''' + MSG = 'match={:s}, path={:s}, input_data={:s}' + msg = MSG.format(match.groupdict(), path, input_data) + raise NotImplementedError(msg) + + +class CallbackDispatcher: + def __init__(self): + self._callbacks : List[_Callback] = list() + + def register(self, callback : _Callback) -> None: + self._callbacks.append(callback) + + def dispatch_data( + self, path : str, old_data : Optional[Dict] = None, new_data : Optional[Dict] = None + ) -> None: + LOGGER.warning('[dispatch_data] Checking Callbacks for path={:s}'.format(str(path))) + for callback in self._callbacks: + match = callback.match(path) + if match is None: continue + keep_running_callbacks = callback.execute_data(match, path, old_data, new_data) + if not keep_running_callbacks: break + + def dispatch_operation( + self, path : str, input_data : Optional[Dict] = None + ) -> Optional[Dict]: + LOGGER.warning('[dispatch_operation] Checking Callbacks for path={:s}'.format(str(path))) + + # First matching callback is executed, and its output returned. + for callback in self._callbacks: + match = callback.match(path) + if match is None: continue + output_data = callback.execute_operation(match, path, input_data) + return output_data + + # If no callback found, raise NotImplemented exception + MSG = 'Callback for operation ({:s}) not defined' + raise NotImplementedError(MSG.format(str(path))) + + +# ===== EXAMPLE ========================================================================================== + +class CallbackOnNetwork(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/data' + pattern += r'/ietf-network:networks/network=(?P[^/]+)' + super().__init__(pattern) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + print('[on_network]', match.groupdict(), path, old_data, new_data) + return False + +class CallbackOnNode(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/data' + pattern += r'/ietf-network:networks/network=(?P[^/]+)' + pattern += r'/node=(?P[^/]+)' + super().__init__(pattern) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + print('[on_node]', match.groupdict(), path, old_data, new_data) + return False + +class CallbackOnLink(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/data' + pattern += r'/ietf-network:networks/network=(?P[^/]+)' + pattern += r'/ietf-network-topology:link=(?P[^/]+)' + super().__init__(pattern) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + print('[on_link]', match.groupdict(), path, old_data, new_data) + return False + +class CallbackShutdown(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/operations' + pattern += r'/shutdown' + super().__init__(pattern) + + def execute_operation( + self, match : re.Match, path : str, input_data : Optional[Dict] + ) -> bool: + print('[shutdown]', match.groupdict(), path, input_data) + return {'state': 'processing'} + +def main() -> None: + callbacks = CallbackDispatcher() + callbacks.register(CallbackOnNetwork()) + callbacks.register(CallbackOnNode()) + callbacks.register(CallbackOnLink()) + callbacks.register(CallbackShutdown()) + + callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin') + callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/node=P-PE2') + callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L6') + callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/') + callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/node=P-PE1/') + callbacks.dispatch_data('/restconf/data/ietf-network:networks/network=admin/ietf-network-topology:link=L4/') + callbacks.dispatch_operation('/restconf/operations/shutdown/') + +if __name__ == '__main__': + main() diff --git a/src/common/tools/rest_conf/server/restconf_server/Config.py b/src/common/tools/rest_conf/server/restconf_server/Config.py new file mode 100644 index 0000000000000000000000000000000000000000..f0a47aac5b62e927dd7c7a49ea47abd626692b6b --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/Config.py @@ -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. + + +import os, secrets + + +RESTCONF_PREFIX = os.environ.get('RESTCONF_PREFIX', '/restconf' ) +YANG_SEARCH_PATH = os.environ.get('YANG_SEARCH_PATH', './yang' ) +STARTUP_FILE = os.environ.get('STARTUP_FILE', './startup.json') +SECRET_KEY = os.environ.get('SECRET_KEY', secrets.token_hex(64)) diff --git a/src/common/tools/rest_conf/server/restconf_server/DispatchData.py b/src/common/tools/rest_conf/server/restconf_server/DispatchData.py new file mode 100644 index 0000000000000000000000000000000000000000..89cb8206e9d2126e6e5ef78b9a9fa89a940cf038 --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/DispatchData.py @@ -0,0 +1,179 @@ +# 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 deepdiff, json, logging +from flask import Response, abort, jsonify, request +from flask_restful import Resource +from .Callbacks import CallbackDispatcher +from .HttpStatusCodesEnum import HttpStatusCodesEnum +from .YangHandler import YangHandler + +LOGGER = logging.getLogger(__name__) + +class RestConfDispatchData(Resource): + def __init__( + self, yang_handler : YangHandler, callback_dispatcher : CallbackDispatcher + ) -> None: + super().__init__() + self._yang_handler = yang_handler + self._callback_dispatcher = callback_dispatcher + + def get(self, subpath : str = '/') -> Response: + data = self._yang_handler.get(subpath) + if data is None: + abort( + HttpStatusCodesEnum.CLI_ERR_NOT_FOUND.value, + description='Path({:s}) not found'.format(str(subpath)) + ) + + LOGGER.info('[GET] {:s} => {:s}'.format(subpath, str(data))) + + response = jsonify(json.loads(data)) + response.status_code = HttpStatusCodesEnum.SUCCESS_OK.value + return response + + def post(self, subpath : str) -> Response: + # TODO: client should not provide identifier of element to be created, add it to subpath + try: + payload = request.get_json(force=True) + except Exception: + LOGGER.exception('Invalid JSON') + abort(HttpStatusCodesEnum.CLI_ERR_BAD_REQUEST.value, desctiption='Invalid JSON') + + data = self._yang_handler.get(subpath) + if data is not None: + abort( + HttpStatusCodesEnum.CLI_ERR_CONFLICT.value, + description='Path({:s}) already exists'.format(str(subpath)) + ) + + try: + json_data = self._yang_handler.create(subpath, payload) + except Exception as e: + LOGGER.exception('Create failed') + abort( + HttpStatusCodesEnum.CLI_ERR_NOT_ACCEPTABLE.value, + description=str(e) + ) + + LOGGER.info('[POST] {:s} {:s} => {:s}'.format(subpath, str(payload), str(json_data))) + + self._callback_dispatcher.dispatch_data( + '/restconf/data/' + subpath, old_data=None, new_data=json_data + ) + + response = jsonify({'status': 'created'}) + response.status_code = HttpStatusCodesEnum.SUCCESS_CREATED.value + return response + + def put(self, subpath : str) -> Response: + # NOTE: client should provide identifier of element to be created/replaced + try: + payload = request.get_json(force=True) + except Exception: + LOGGER.exception('Invalid JSON') + abort(HttpStatusCodesEnum.CLI_ERR_BAD_REQUEST.value, desctiption='Invalid JSON') + + old_data = self._yang_handler.get(subpath) + + try: + new_data = self._yang_handler.update(subpath, payload) + except Exception as e: + LOGGER.exception('Update failed') + abort( + HttpStatusCodesEnum.CLI_ERR_NOT_ACCEPTABLE.value, + description=str(e) + ) + + LOGGER.info('[PUT] {:s} {:s} => {:s}'.format(subpath, str(payload), str(new_data))) + + diff_data = deepdiff.DeepDiff(old_data, new_data) + updated = len(diff_data) > 0 + + self._callback_dispatcher.dispatch_data( + '/restconf/data/' + subpath, old_data=old_data, new_data=new_data + ) + + response = jsonify({'status': ( + 'updated' if updated else 'created' + )}) + response.status_code = ( + HttpStatusCodesEnum.SUCCESS_NO_CONTENT.value + if updated else + HttpStatusCodesEnum.SUCCESS_CREATED.value + ) + return response + + def patch(self, subpath : str) -> Response: + # NOTE: client should provide identifier of element to be patched + try: + payload = request.get_json(force=True) + except Exception: + LOGGER.exception('Invalid JSON') + abort(HttpStatusCodesEnum.CLI_ERR_BAD_REQUEST.value, desctiption='Invalid JSON') + + old_data = self._yang_handler.get(subpath) + + try: + new_data = self._yang_handler.update(subpath, payload) + except Exception as e: + LOGGER.exception('Update failed') + abort( + HttpStatusCodesEnum.CLI_ERR_NOT_ACCEPTABLE.value, + description=str(e) + ) + + LOGGER.info('[PATCH] {:s} {:s} => {:s}'.format(subpath, str(payload), str(new_data))) + + #diff_data = deepdiff.DeepDiff(old_data, new_data) + #updated = len(diff_data) > 0 + + self._callback_dispatcher.dispatch_data( + '/restconf/data/' + subpath, old_data=old_data, new_data=new_data + ) + + response = jsonify({'status': 'patched'}) + response.status_code = HttpStatusCodesEnum.SUCCESS_NO_CONTENT.value + return response + + def delete(self, subpath : str) -> Response: + # NOTE: client should provide identifier of element to be patched + + old_data = self._yang_handler.get(subpath) + + try: + deleted_node = self._yang_handler.delete(subpath) + except Exception as e: + LOGGER.exception('Delete failed') + abort( + HttpStatusCodesEnum.CLI_ERR_NOT_ACCEPTABLE.value, + description=str(e) + ) + + LOGGER.info('[DELETE] {:s} => {:s}'.format(subpath, str(deleted_node))) + + if deleted_node is None: + abort( + HttpStatusCodesEnum.CLI_ERR_NOT_FOUND.value, + description='Path({:s}) not found'.format(str(subpath)) + ) + + self._callback_dispatcher.dispatch_data( + '/restconf/data/' + subpath, old_data=old_data, new_data=None + ) + + response = jsonify({}) + response.status_code = HttpStatusCodesEnum.SUCCESS_NO_CONTENT.value + return response diff --git a/src/common/tools/rest_conf/server/restconf_server/DispatchOperations.py b/src/common/tools/rest_conf/server/restconf_server/DispatchOperations.py new file mode 100644 index 0000000000000000000000000000000000000000..7e5bdd13a3a2fd1e1b676c017d32d6fc210b0ca3 --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/DispatchOperations.py @@ -0,0 +1,48 @@ +# 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 logging +from flask import Response, abort, jsonify, request +from flask_restful import Resource +from .Callbacks import CallbackDispatcher +from .HttpStatusCodesEnum import HttpStatusCodesEnum +from .YangHandler import YangHandler + +LOGGER = logging.getLogger(__name__) + +class RestConfDispatchOperations(Resource): + def __init__( + self, yang_handler : YangHandler, callback_dispatcher : CallbackDispatcher + ) -> None: + super().__init__() + self._yang_handler = yang_handler + self._callback_dispatcher = callback_dispatcher + + def post(self, subpath : str) -> Response: + try: + payload = request.get_json(force=True) + except Exception: + LOGGER.exception('Invalid JSON') + abort(HttpStatusCodesEnum.CLI_ERR_BAD_REQUEST.value, desctiption='Invalid JSON') + + output_data = self._callback_dispatcher.dispatch_operation( + '/restconf/operations/' + subpath, input_data=payload + ) + + LOGGER.info('[POST] {:s} {:s} => {:s}'.format(subpath, str(payload), str(output_data))) + + response = jsonify(output_data) + response.status_code = HttpStatusCodesEnum.SUCCESS_OK.value + return response diff --git a/src/common/tools/rest_conf/server/restconf_server/HostMeta.py b/src/common/tools/rest_conf/server/restconf_server/HostMeta.py new file mode 100644 index 0000000000000000000000000000000000000000..95ef34b19045a8bfbaf8e6fd1ec3174ed3a42918 --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/HostMeta.py @@ -0,0 +1,50 @@ +# 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 xml.etree.ElementTree as ET +from flask import abort, jsonify, make_response, request +from flask_restful import Resource +from .HttpStatusCodesEnum import HttpStatusCodesEnum + +XRD_NS = 'http://docs.oasis-open.org/ns/xri/xrd-1.0' +ET.register_namespace('', XRD_NS) + +class HostMeta(Resource): + def __init__(self, restconf_prefix : str) -> None: + super().__init__() + self._restconf_prefix = restconf_prefix + + def get(self): + best = request.accept_mimetypes.best_match([ + 'application/xrd+xml', 'application/json' + ], default='application/xrd+xml') + + if best == 'application/xrd+xml': + xrd = ET.Element('{{{:s}}}XRD'.format(str(XRD_NS))) + ET.SubElement(xrd, '{{{:s}}}Link'.format(str(XRD_NS)), attrib={ + 'rel': 'restconf', 'href': self._restconf_prefix + }) + xml_string = ET.tostring(xrd, encoding='utf-8', xml_declaration=True).decode() + response = make_response(str(xml_string)) + response.status_code = 200 + response.content_type = best + return response + elif best == 'application/json': + response = jsonify({'links': [{'rel': 'restconf', 'href': self._restconf_prefix}]}) + response.status_code = 200 + response.content_type = best + return response + else: + abort(HttpStatusCodesEnum.CLI_ERR_NOT_ACCEPTABLE) diff --git a/src/common/tools/rest_conf/server/restconf_server/HttpStatusCodesEnum.py b/src/common/tools/rest_conf/server/restconf_server/HttpStatusCodesEnum.py new file mode 100644 index 0000000000000000000000000000000000000000..c44d135c09400973a041cafd693491db1d076fa2 --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/HttpStatusCodesEnum.py @@ -0,0 +1,27 @@ +# 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 enum + +class HttpStatusCodesEnum(enum.IntEnum): + SUCCESS_OK = 200 + SUCCESS_CREATED = 201 + SUCCESS_ACCEPTED = 202 + SUCCESS_NO_CONTENT = 204 + CLI_ERR_BAD_REQUEST = 400 + CLI_ERR_NOT_FOUND = 404 + CLI_ERR_NOT_ACCEPTABLE = 406 + CLI_ERR_CONFLICT = 409 + SVR_ERR_NOT_IMPLEMENTED = 501 diff --git a/src/common/tools/rest_conf/server/restconf_server/RestConfServerApplication.py b/src/common/tools/rest_conf/server/restconf_server/RestConfServerApplication.py new file mode 100644 index 0000000000000000000000000000000000000000..58384299cba8dfbee37dc776863d180153e83b45 --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/RestConfServerApplication.py @@ -0,0 +1,117 @@ +# 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, time +from typing import Any, Dict, Tuple, Type +from flask import Flask, request +from flask_restful import Api, Resource +from .Callbacks import CallbackDispatcher +from .Config import RESTCONF_PREFIX, SECRET_KEY, STARTUP_FILE, YANG_SEARCH_PATH +from .DispatchData import RestConfDispatchData +from .DispatchOperations import RestConfDispatchOperations +from .HostMeta import HostMeta +from .YangHandler import YangHandler +from .YangModelDiscoverer import YangModuleDiscoverer + + +logging.basicConfig( + level=logging.INFO, + format='[Worker-%(process)d][%(asctime)s] %(levelname)s:%(name)s:%(message)s', +) + + +LOGGER = logging.getLogger(__name__) + + +def log_request(response): + timestamp = time.strftime('[%Y-%b-%d %H:%M]') + LOGGER.info( + '%s %s %s %s %s', timestamp, request.remote_addr, request.method, + request.full_path, response.status + ) + return response + + +class RestConfServerApplication: + def __init__(self) -> None: + self._ymd = YangModuleDiscoverer(YANG_SEARCH_PATH) + self._yang_module_names = self._ymd.run(do_log_order=True) + + with open(STARTUP_FILE, mode='r', encoding='UTF-8') as fp: + self._yang_startup_data = json.loads(fp.read()) + + self._yang_handler = YangHandler( + YANG_SEARCH_PATH, self._yang_module_names, self._yang_startup_data + ) + + self._callback_dispatcher = CallbackDispatcher() + + self._app = Flask(__name__) + self._app.config['SECRET_KEY'] = SECRET_KEY + self._app.after_request(log_request) + self._api = Api(self._app) + + @property + def callback_dispatcher(self): return self._callback_dispatcher + + def get_startup_data(self) -> None: + return self._yang_startup_data + + def register_host_meta(self) -> None: + self._api.add_resource( + HostMeta, + '/.well-known/host-meta', + resource_class_args=(RESTCONF_PREFIX,) + ) + + def register_restconf(self) -> None: + self._api.add_resource( + RestConfDispatchData, + RESTCONF_PREFIX + '/data/', + RESTCONF_PREFIX + '/data//', + resource_class_args=(self._yang_handler, self._callback_dispatcher) + ) + self._api.add_resource( + RestConfDispatchOperations, + RESTCONF_PREFIX + '/operations/', + RESTCONF_PREFIX + '/operations//', + resource_class_args=(self._yang_handler, self._callback_dispatcher) + ) + + def register_custom( + self, resource_class : Type[Resource], + *urls : str, add_prefix_to_urls : bool = True, + resource_class_args : Tuple[Any, ...] = tuple(), + resource_class_kwargs : Dict[str, Any] = dict() + ) -> None: + if add_prefix_to_urls: + urls = [RESTCONF_PREFIX + u for u in urls] + self._api.add_resource( + resource_class, *urls, + resource_class_args=resource_class_args, + resource_class_kwargs=resource_class_kwargs + ) + + def get_flask_app(self) -> Flask: + return self._app + + def get_flask_api(self) -> Api: + return self._api + + def dump_configuration(self) -> None: + LOGGER.info('Available RESTCONF paths:') + restconf_paths = self._yang_handler.get_schema_paths() + for restconf_path in sorted(restconf_paths): + LOGGER.info('- {:s}'.format(str(restconf_path))) diff --git a/src/common/tools/rest_conf/server/restconf_server/YangHandler.py b/src/common/tools/rest_conf/server/restconf_server/YangHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..9df57528f93a9e599da259d30d9fb88f618346a9 --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/YangHandler.py @@ -0,0 +1,226 @@ +# 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, libyang, logging +import urllib.parse +from typing import Dict, List, Optional, Set + + +LOGGER = logging.getLogger(__name__) + + +def walk_schema(node : libyang.SNode, path : str = '') -> Set[str]: + current_path = f'{path}/{node.name()}' + schema_paths : Set[str] = {current_path} + for child in node.children(): + if isinstance(child, (libyang.SLeaf, libyang.SLeafList)): continue + schema_paths.update(walk_schema(child, current_path)) + return schema_paths + +def extract_schema_paths(yang_module : libyang.Module) -> Set[str]: + schema_paths : Set[str] = set() + for node in yang_module.children(): + schema_paths.update(walk_schema(node)) + return schema_paths + +class YangHandler: + def __init__( + self, yang_search_path : str, yang_module_names : List[str], + yang_startup_data : Dict + ) -> None: + self._yang_context = libyang.Context(yang_search_path) + self._loaded_modules : Set[str] = set() + self._schema_paths : Set[str] = set() + for yang_module_name in yang_module_names: + LOGGER.info('Loading module: {:s}'.format(str(yang_module_name))) + yang_module = self._yang_context.load_module(yang_module_name) + yang_module.feature_enable_all() + self._loaded_modules.add(yang_module_name) + self._schema_paths.update(extract_schema_paths(yang_module)) + + self._datastore = self._yang_context.parse_data_mem( + json.dumps(yang_startup_data), fmt='json' + ) + + def destroy(self) -> None: + self._yang_context.destroy() + + def get_schema_paths(self) -> Set[str]: + return self._schema_paths + + def get(self, path : str) -> Optional[str]: + path = self._normalize_path(path) + data = self._datastore.find_path(path) + if data is None: return None + json_data = data.print_mem( + fmt='json', with_siblings=False, pretty=True, + keep_empty_containers=False, include_implicit_defaults=True + ) + return json_data + + def get_xpath(self, xpath : str) -> List[str]: + if not xpath.startswith('/'): xpath = '/' + xpath + items = self._datastore.find_all(xpath) + result = list() + for item in items: + result.append(item.print_mem( + fmt='json', with_siblings=False, pretty=True, + keep_empty_containers=False, include_implicit_defaults=True + )) + return result + + def create(self, path : str, payload : Dict) -> str: + path = self._normalize_path(path) + # TODO: client should not provide identifier of element to be created, add it to subpath + dnode_parsed : Optional[libyang.DNode] = self._yang_context.parse_data_mem( + json.dumps(payload), 'json', strict=True, parse_only=False, + validate_present=True, validate_multi_error=True + ) + if dnode_parsed is None: raise Exception('Unable to parse Data({:s})'.format(str(payload))) + + dnode : Optional[libyang.DNode] = self._yang_context.create_data_path( + path, parent=self._datastore, value=dnode_parsed, update=False + ) + self._datastore.merge(dnode_parsed, with_siblings=True, defaults=True) + + json_data = dnode.print_mem( + fmt='json', with_siblings=True, pretty=True, + keep_empty_containers=True, include_implicit_defaults=True + ) + return json_data + + def update(self, path : str, payload : Dict) -> str: + path = self._normalize_path(path) + # NOTE: client should provide identifier of element to be updated + dnode_parsed : Optional[libyang.DNode] = self._yang_context.parse_data_mem( + json.dumps(payload), 'json', strict=True, parse_only=False, + validate_present=True, validate_multi_error=True + ) + if dnode_parsed is None: raise Exception('Unable to parse Data({:s})'.format(str(payload))) + + dnode = self._yang_context.create_data_path( + path, parent=self._datastore, value=dnode_parsed, update=True + ) + self._datastore.merge(dnode_parsed, with_siblings=True, defaults=True) + + json_data = dnode.print_mem( + fmt='json', with_siblings=True, pretty=True, + keep_empty_containers=True, include_implicit_defaults=True + ) + return json_data + + def delete(self, path : str) -> Optional[str]: + path = self._normalize_path(path) + + # NOTE: client should provide identifier of element to be deleted + + node : libyang.DNode = self._datastore.find_path(path) + if node is None: return None + + LOGGER.info('node = {:s}'.format(str(node))) + json_data = str(node.print_mem( + fmt='json', with_siblings=True, pretty=True, + keep_empty_containers=True, include_implicit_defaults=True + )) + LOGGER.info('json_data = {:s}'.format(json_data)) + + node.unlink() + node.free() + + return json_data + + def _normalize_path(self, path : str) -> str: + """ + Normalize RESTCONF path segments using the standard `list=` + syntax into the libyang bracketed predicate form expected by + the datastore (e.g. `network="admin"` -> `network[network-id="admin"]`). + + This implementation looks up the schema node for the list and + uses its key leaf names to build the proper predicates. If the + schema information is unavailable, it falls back to using the + list name as the key name. + """ + + # URL-decode each path segment so escaped characters like `%22` + # (double quotes) are properly handled when parsing list keys. + parts = [urllib.parse.unquote(p) for p in path.strip('/').split('/') if p != ''] + schema_path = '' + out_parts: List[str] = [] + + for part in parts: + if '=' in part: + # split into name and value (value may contain commas/quotes) + name, val = part.split('=', 1) + # keep original name (may include prefix) for output, but + # use local name (without module prefix) to lookup schema + local_name = name.split(':', 1)[1] if ':' in name else name + schema_path = schema_path + '/' + local_name if schema_path else '/' + local_name + schema_nodes = list(self._yang_context.find_path(schema_path)) + if len(schema_nodes) != 1: + MSG = 'No/Multiple SchemaNodes({:s}) for SchemaPath({:s})' + raise Exception(MSG.format( + str([repr(sn) for sn in schema_nodes]), schema_path + )) + schema_node = schema_nodes[0] + + # parse values splitting on commas outside quotes + values = [] + cur = '' + in_quotes = False + for ch in val: + if ch == '"': + in_quotes = not in_quotes + cur += ch + elif ch == ',' and not in_quotes: + values.append(cur) + cur = '' + else: + cur += ch + if cur != '': + values.append(cur) + + # determine key names from schema_node if possible + key_names = None + if isinstance(schema_node, libyang.SList): + key_names = [k.name() for k in schema_node.keys()] + #if isinstance(keys, (list, tuple)): + # key_names = keys + #elif isinstance(keys, str): + # key_names = [kn for kn in k.split() if kn] + #else: + # MSG = 'Unsupported keys format: {:s} / {:s}' + # raise Exception(MSG.format(str(type(keys)), str(keys))) + #elif hasattr(schema_node, 'key'): + # k = schema_node.key() + # if isinstance(k, str): + # key_names = [kn for kn in k.split() if kn] + + if not key_names: + # fallback: use the local list name as the single key + key_names = [local_name] + + # build predicate(s) + preds = [] + for idx, kn in enumerate(key_names): + kv = values[idx] if idx < len(values) else values[0] + preds.append(f'[{kn}="{kv}"]') + + out_parts.append(name + ''.join(preds)) + else: + local_part = part.split(':', 1)[1] if ':' in part else part + schema_path = schema_path + '/' + local_part if schema_path else '/' + local_part + out_parts.append(part) + + return '/' + '/'.join(out_parts) diff --git a/src/common/tools/rest_conf/server/restconf_server/YangModelDiscoverer.py b/src/common/tools/rest_conf/server/restconf_server/YangModelDiscoverer.py new file mode 100644 index 0000000000000000000000000000000000000000..f31305280e45cf2ec00756cb4c2c4116e869246f --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/YangModelDiscoverer.py @@ -0,0 +1,195 @@ +# 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 logging, re +from collections import defaultdict +from graphlib import TopologicalSorter, CycleError +from pathlib import Path +from typing import Dict, List, Optional, Set, Tuple + + +COMMENT_SINGLE_RE = re.compile(r"//.*?$", re.MULTILINE) +COMMENT_MULTI_RE = re.compile(r"/\*.*?\*/", re.DOTALL) + +# module / submodule name +MODNAME_RE = re.compile(r"\b(module|submodule)\s+([A-Za-z0-9_.-]+)\s*\{") + +# import foo { ... } (most common form) +IMPORT_BLOCK_RE = re.compile(r"\bimport\s+([A-Za-z0-9_.-]+)\s*\{", re.IGNORECASE) + +# import foo; (very rare, but we’ll support it) +IMPORT_SEMI_RE = re.compile(r"\bimport\s+([A-Za-z0-9_.-]+)\s*;", re.IGNORECASE) + + +def _parse_yang_file(path: Path) -> Tuple[Optional[str], Set[str]]: + path_stem = path.stem # file name without extension + expected_module_name = path_stem.split('@', 1)[0] + + try: + data = path.read_text(encoding='utf-8', errors='ignore') + except Exception: + data = path.read_bytes().decode('utf-8', errors='ignore') + + data = COMMENT_MULTI_RE.sub('', data) + data = COMMENT_SINGLE_RE.sub('', data) + + match = MODNAME_RE.search(data) + if match is None: + return None, set() + module_name = match.group(2) + if module_name != expected_module_name: + MSG = 'Module({:s}) mismatches its FileName({:s})' + raise Exception(MSG.format(str(module_name), str(expected_module_name))) + + module_imports = set() + if module_name is not None: + module_imports.update(IMPORT_BLOCK_RE.findall(data)) + module_imports.update(IMPORT_SEMI_RE.findall(data)) + + # ignore modules importing themselves, just in case + module_imports.discard(module_name) + + return module_name, module_imports + + +class YangModuleDiscoverer: + def __init__(self, yang_search_path : str) -> None: + self._yang_search_path = yang_search_path + + self._module_to_paths : Dict[str, List[Path]] = defaultdict(list) + self._module_to_imports : Dict[str, Set[str]] = defaultdict(set) + self._ordered_module_names : Optional[List[str]] = None + + + def run( + self, do_print_order : bool = False, do_log_order : bool = False, + logger : Optional[logging.Logger] = None, level : int = logging.INFO + ) -> List[str]: + if self._ordered_module_names is None: + self._scan_modules() + self._sort_modules() + + if do_print_order: + self.print_order() + + if do_log_order: + if logger is None: logger = logging.getLogger(__name__) + self.log_order(logger, level=level) + + return self._ordered_module_names + + def _scan_modules(self) -> None: + yang_root = Path(self._yang_search_path).resolve() + if not yang_root.exists(): + MSG = 'Path({:s}) not found' + raise Exception(MSG.format(str(self._yang_search_path))) + + for yang_path in yang_root.rglob('*.yang'): + module_name, module_imports = _parse_yang_file(yang_path) + if module_name is None: continue + self._module_to_paths[module_name].append(yang_path) + self._module_to_imports[module_name] = module_imports + + if len(self._module_to_paths) == 0: + MSG = 'No modules found in Path({:s})' + raise Exception(MSG.format(str(self._yang_search_path))) + + self._check_duplicated_module_declaration() + self._check_missing_modules() + + + def _check_duplicated_module_declaration(self) -> None: + duplicate_module_declarations : List[str] = list() + for module_name, paths in self._module_to_paths.items(): + if len(paths) == 1: continue + str_paths = [str(p) for p in paths] + duplicate_module_declarations.append( + ' {:s} => {:s}'.format(module_name, str_paths) + ) + + if len(duplicate_module_declarations) > 0: + MSG = 'Duplicate module declarations:\n{:s}' + str_dup_mods = '\n'.join(duplicate_module_declarations) + raise Exception(MSG.format(str_dup_mods)) + + + def _check_missing_modules(self) -> None: + local_module_names = set(self._module_to_imports.keys()) + missing_modules : List[str] = list() + for module_name, imported_modules in self._module_to_imports.items(): + missing = imported_modules.difference(local_module_names) + if len(missing) == 0: continue + missing_modules.append( + ' {:s} => {:s}'.format(module_name, str(missing)) + ) + + if len(missing_modules) > 0: + MSG = 'Missing modules:\n{:s}' + str_mis_mods = '\n'.join(missing_modules) + raise Exception(MSG.format(str_mis_mods)) + + + def _sort_modules(self) -> None: + ts = TopologicalSorter() + for module_name, imported_modules in self._module_to_imports.items(): + ts.add(module_name, *imported_modules) + + try: + self._ordered_module_names = list(ts.static_order()) # raises CycleError on cycles + except CycleError as e: + cycle = list(dict.fromkeys(e.args[1])) # de-dup while preserving order + MSG = 'Circular dependencies between modules: {:s}' + raise Exception(MSG.format(str(cycle))) # pylint: disable=raise-missing-from + + + def dump_order(self) -> List[Tuple[int, str, List[str]]]: + if self._ordered_module_names is None: + raise Exception('First process the YANG Modules running method .run()') + + module_order : List[Tuple[int, str, List[str]]] = list() + for i, module_name in enumerate(self._ordered_module_names, 1): + module_imports = sorted(self._module_to_imports[module_name]) + module_order.append((i, module_name, module_imports)) + + return module_order + + + def print_order(self) -> None: + print('Ordered Modules:') + for i, module_name, module_imports in self.dump_order(): + MSG = '{:2d} : {:s} => {:s}' + print(MSG.format(i, module_name, str(module_imports))) + + + def log_order(self, logger : logging.Logger, level : int = logging.INFO) -> None: + logger.log(level, 'Ordered Modules:') + for i, module_name, module_imports in self.dump_order(): + MSG = '{:2d} : {:s} => {:s}' + logger.log(level, MSG.format(i, module_name, str(module_imports))) + + +def main() -> None: + logging.basicConfig(level=logging.INFO) + + ymd = YangModuleDiscoverer('./yang') + ordered_module_names = ymd.run( + do_print_order=True, + do_log_order=True + ) + print('ordered_module_names', ordered_module_names) + + +if __name__ == '__main__': + main() diff --git a/src/common/tools/rest_conf/server/restconf_server/__init__.py b/src/common/tools/rest_conf/server/restconf_server/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/__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/common/tools/rest_conf/server/restconf_server/__main__.py b/src/common/tools/rest_conf/server/restconf_server/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..2c84d92efd7e33d44237e3a8791771a371e12f3f --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/__main__.py @@ -0,0 +1,26 @@ +# 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 .app import app + +BIND_ADDRESS = '0.0.0.0' +BIND_PORT = 8080 + +if __name__ == '__main__': + # Only used to run it locally during development stage; + # otherwise, app is directly launched by gunicorn. + app.run( + host=BIND_ADDRESS, port=BIND_PORT, debug=True, use_reloader=False + ) diff --git a/src/common/tools/rest_conf/server/restconf_server/app.py b/src/common/tools/rest_conf/server/restconf_server/app.py new file mode 100644 index 0000000000000000000000000000000000000000..de35b25240487d9213ae1aa33c0691c1fc399dca --- /dev/null +++ b/src/common/tools/rest_conf/server/restconf_server/app.py @@ -0,0 +1,35 @@ +# 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 logging +from .RestConfServerApplication import RestConfServerApplication + + +logging.basicConfig( + level=logging.INFO, + format='[Worker-%(process)d][%(asctime)s] %(levelname)s:%(name)s:%(message)s', +) +LOGGER = logging.getLogger(__name__) + +LOGGER.info('Starting...') +rcs_app = RestConfServerApplication() +rcs_app.register_host_meta() +rcs_app.register_restconf() +LOGGER.info('All connectors registered') + +rcs_app.dump_configuration() +app = rcs_app.get_flask_app() + +LOGGER.info('Initialization completed!') diff --git a/src/common/tools/rest_conf/server/run_server_gunicorn.sh b/src/common/tools/rest_conf/server/run_server_gunicorn.sh new file mode 100755 index 0000000000000000000000000000000000000000..af7a1c8e03e347f95029ba7d6b2368c3acaabbb5 --- /dev/null +++ b/src/common/tools/rest_conf/server/run_server_gunicorn.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. + +# Make folder containing the script the root folder for its execution +cd $(dirname $0) + +export FLASK_ENV=development +gunicorn -w 1 --worker-class eventlet -b 0.0.0.0:8080 --log-level DEBUG restconf_server.app:app diff --git a/src/common/tools/rest_conf/server/run_server_standalone.sh b/src/common/tools/rest_conf/server/run_server_standalone.sh new file mode 100755 index 0000000000000000000000000000000000000000..4ce7966dde9b685f2c60f422145ed04fb0eada10 --- /dev/null +++ b/src/common/tools/rest_conf/server/run_server_standalone.sh @@ -0,0 +1,19 @@ +#!/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) + +python -m restconf_server diff --git a/src/common/tools/rest_conf/server/startup.json b/src/common/tools/rest_conf/server/startup.json new file mode 100644 index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93 --- /dev/null +++ b/src/common/tools/rest_conf/server/startup.json @@ -0,0 +1 @@ +{} diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py index e41b0d0d36442703fd649a5afe9d61eae7dbb6d5..7fb6831a45d47810166b7cb121f9764391ab54bc 100644 --- a/src/common/type_checkers/Assertions.py +++ b/src/common/type_checkers/Assertions.py @@ -511,7 +511,9 @@ def validate_component(component): def validate_link_attributes(link_attributes): assert isinstance(link_attributes, dict) - assert len(link_attributes.keys()) == 2 + assert len(link_attributes.keys()) == 3 + assert 'is_bidirectional' in link_attributes + assert isinstance(link_attributes['is_bidirectional'], bool) assert 'total_capacity_gbps' in link_attributes assert isinstance(link_attributes['total_capacity_gbps'], (int, float)) assert 'used_capacity_gbps' in link_attributes diff --git a/src/context/client/ContextClient.py b/src/context/client/ContextClient.py index 565be7fefb562eb9e305c6267952b2ee27ae2a9f..cb6f9ca3e6ff7ecb91a97520d68972563ec6f265 100644 --- a/src/context/client/ContextClient.py +++ b/src/context/client/ContextClient.py @@ -19,7 +19,7 @@ from common.Settings import get_service_host, get_service_port_grpc from common.tools.client.RetryDecorator import retry, delay_exponential from common.tools.grpc.Tools import grpc_message_to_json_string from common.proto.context_pb2 import ( - Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, + AnyEvent, Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId, ContextIdList, ContextList, Device, DeviceEvent, DeviceFilter, DeviceId, DeviceIdList, DeviceList, Empty, EndPointIdList, EndPointNameList, @@ -61,6 +61,13 @@ class ContextClient: self.stub = None self.policy_stub = None + @RETRY_DECORATOR + def GetAllEvents(self, request: Empty) -> Iterator[AnyEvent]: + LOGGER.debug('GetAllEvents request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.GetAllEvents(request) + LOGGER.debug('GetAllEvents result: {:s}'.format(grpc_message_to_json_string(response))) + return response + @RETRY_DECORATOR def ListContextIds(self, request: Empty) -> ContextIdList: LOGGER.debug('ListContextIds request: {:s}'.format(grpc_message_to_json_string(request))) diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index 73dc32bd034092566141278fb2e80a21c4a68bb2..2873c598e3e427070fc1f290ae9fcbd226b24166 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -16,7 +16,7 @@ import grpc, logging, sqlalchemy from typing import Iterator from common.message_broker.MessageBroker import MessageBroker from common.proto.context_pb2 import ( - Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, + AnyEvent, Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId, ContextIdList, ContextList, Device, DeviceEvent, DeviceFilter, DeviceId, DeviceIdList, DeviceList, Empty, EndPointIdList, EndPointNameList, @@ -41,7 +41,7 @@ from .database.Device import ( device_delete, device_get, device_list_ids, device_list_objs, device_select, device_set ) from .database.EndPoint import endpoint_list_names -from .database.Events import EventTopicEnum, consume_events +from .database.Events import EventTopicEnum, consume_all_events, consume_events from .database.Link import ( link_delete, link_get, link_list_ids, link_list_objs, link_set ) @@ -83,6 +83,11 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer def _get_metrics(self) -> MetricsPool: return METRICS_POOL + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def GetAllEvents(self, request : Empty, context : grpc.ServicerContext) -> Iterator[AnyEvent]: + for message in consume_all_events(self.messagebroker): yield message + + # ----- Context ---------------------------------------------------------------------------------------------------- @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) diff --git a/src/context/service/database/Events.py b/src/context/service/database/Events.py index 4667de549837ee11be6d30512691c03f92f25c15..398352e20fac7a1ed4083e197a02bf794bdcb7c0 100644 --- a/src/context/service/database/Events.py +++ b/src/context/service/database/Events.py @@ -17,7 +17,7 @@ from typing import Dict, Iterator, Set from common.message_broker.Message import Message from common.message_broker.MessageBroker import MessageBroker from common.proto.context_pb2 import ( - ConnectionEvent, ContextEvent, DeviceEvent, EventTypeEnum, LinkEvent, + AnyEvent, ConnectionEvent, ContextEvent, DeviceEvent, EventTypeEnum, LinkEvent, ServiceEvent, SliceEvent, TopologyEvent, OpticalConfigEvent ) @@ -31,6 +31,7 @@ class EventTopicEnum(enum.Enum): SLICE = 'slice' TOPOLOGY = 'topology' OPTICALCONFIG = 'optical-config' + ALL = 'all' TOPIC_TO_EVENTCLASS = { @@ -49,13 +50,18 @@ CONSUME_TIMEOUT = 0.5 # seconds LOGGER = logging.getLogger(__name__) +# NOTE: Forced to use a single "ALL" topic so that we ensure all messages are kept ordered. +# Consumer filters appropriate ones while delivering. +# TODO: Upgrade this schema with proper in-topic filters to enhance performance. + def notify_event( messagebroker : MessageBroker, topic_enum : EventTopicEnum, event_type : EventTypeEnum, fields : Dict[str, str] ) -> None: event = {'event': {'timestamp': {'timestamp': time.time()}, 'event_type': event_type}} for field_name, field_value in fields.items(): event[field_name] = field_value - messagebroker.publish(Message(topic_enum.value, json.dumps(event))) + #messagebroker.publish(Message(topic_enum.value, json.dumps(event))) + messagebroker.publish(Message(EventTopicEnum.ALL.value, json.dumps(event))) def notify_event_context(messagebroker : MessageBroker, event_type : EventTypeEnum, context_id : Dict) -> None: notify_event(messagebroker, EventTopicEnum.CONTEXT, event_type, {'context_id': context_id}) @@ -87,11 +93,67 @@ def notify_event_policy_rule(messagebroker : MessageBroker, event_type : EventTy def consume_events( messagebroker : MessageBroker, topic_enums : Set[EventTopicEnum], consume_timeout : float = CONSUME_TIMEOUT ) -> Iterator: - topic_names = [topic_enum.value for topic_enum in topic_enums] + #topic_names = [topic_enum.value for topic_enum in topic_enums] + topic_names = [EventTopicEnum.ALL.value] + for message in messagebroker.consume(topic_names, consume_timeout=consume_timeout): + #event_class = TOPIC_TO_EVENTCLASS.get(message.topic) + #if event_class is None: + # MSG = 'No EventClass defined for Topic({:s}). Ignoring...' + # LOGGER.warning(MSG.format(str(message.topic))) + # continue + data = json.loads(message.content) + if 'context_id' in data: + if EventTopicEnum.CONTEXT not in topic_enums: continue + yield ContextEvent(**data) + elif 'topology_id' in data: + if EventTopicEnum.TOPOLOGY not in topic_enums: continue + yield TopologyEvent(**data) + elif 'device_id' in data: + if EventTopicEnum.DEVICE not in topic_enums: continue + yield DeviceEvent(**data) + elif 'opticalconfig_id' in data: + if EventTopicEnum.OPTICALCONFIG not in topic_enums: continue + yield DeviceEvent(**data) + elif 'link_id' in data: + if EventTopicEnum.LINK not in topic_enums: continue + yield LinkEvent(**data) + elif 'service_id' in data: + if EventTopicEnum.SERVICE not in topic_enums: continue + yield ServiceEvent(**data) + elif 'slice_id' in data: + if EventTopicEnum.SLICE not in topic_enums: continue + yield SliceEvent(**data) + elif 'connection_id' in data: + if EventTopicEnum.CONNECTION not in topic_enums: continue + yield ConnectionEvent(**data) + else: + MSG = 'Unable to identify EventClass for Message({:s}). Ignoring...' + LOGGER.warning(MSG.format(str(message))) + continue + +def consume_all_events( + messagebroker : MessageBroker, consume_timeout : float = CONSUME_TIMEOUT +) -> Iterator[AnyEvent]: + topic_names = [EventTopicEnum.ALL.value] for message in messagebroker.consume(topic_names, consume_timeout=consume_timeout): - event_class = TOPIC_TO_EVENTCLASS.get(message.topic) - if event_class is None: - MSG = 'No EventClass defined for Topic({:s}). Ignoring...' - LOGGER.warning(MSG.format(str(message.topic))) + data = json.loads(message.content) + if 'context_id' in data: + yield AnyEvent(context=data) + elif 'topology_id' in data: + yield AnyEvent(topology=data) + elif 'device_id' in data: + yield AnyEvent(device=data) + elif 'opticalconfig_id' in data: + yield AnyEvent(device=data) + elif 'link_id' in data: + yield AnyEvent(link=data) + elif 'service_id' in data: + yield AnyEvent(service=data) + elif 'slice_id' in data: + yield AnyEvent(slice=data) + elif 'connection_id' in data: + yield AnyEvent(connection=data) + else: + MSG = 'Unable to identify EventClass for Message({:s}). Ignoring...' + LOGGER.warning(MSG.format(str(message))) continue - yield event_class(**json.loads(message.content)) diff --git a/src/context/service/database/Link.py b/src/context/service/database/Link.py index 8176873dee99b5856df70e5ce38e4f53412e1a61..68462b3f1da959ba699f64d942a05bdc27bfee2d 100644 --- a/src/context/service/database/Link.py +++ b/src/context/service/database/Link.py @@ -105,12 +105,16 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link) topology_uuids.add(endpoint_topology_uuid) total_capacity_gbps, used_capacity_gbps = None, None + is_bidirectional = False if request.HasField('attributes'): attributes = request.attributes # In proto3, HasField() does not work for scalar fields, using ListFields() instead. attribute_names = set([field.name for field,_ in attributes.ListFields()]) + if 'is_bidirectional' in attribute_names: + is_bidirectional = attributes.is_bidirectional + if 'total_capacity_gbps' in attribute_names: total_capacity_gbps = attributes.total_capacity_gbps @@ -125,6 +129,7 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link) 'link_type' : link_type, 'total_capacity_gbps' : total_capacity_gbps, 'used_capacity_gbps' : used_capacity_gbps, + 'is_bidirectional' : is_bidirectional, 'created_at' : now, 'updated_at' : now, }] @@ -138,6 +143,7 @@ def link_set(db_engine : Engine, messagebroker : MessageBroker, request : Link) link_type = stmt.excluded.link_type, total_capacity_gbps = stmt.excluded.total_capacity_gbps, used_capacity_gbps = stmt.excluded.used_capacity_gbps, + is_bidirectional = stmt.excluded.is_bidirectional, updated_at = stmt.excluded.updated_at, ) ) diff --git a/src/context/service/database/models/LinkModel.py b/src/context/service/database/models/LinkModel.py index 77a9d9fa4e22693971b8f6fa24a16e826d9cdf36..ff95df2886dc5af8b65d50b5f1b46434262968f3 100644 --- a/src/context/service/database/models/LinkModel.py +++ b/src/context/service/database/models/LinkModel.py @@ -14,7 +14,8 @@ import operator from sqlalchemy import ( - CheckConstraint, Column, DateTime, Enum, Float, ForeignKey, Integer, String + CheckConstraint, Boolean, Column, DateTime, Enum, Float, ForeignKey, + Integer, String ) from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship @@ -31,6 +32,7 @@ class LinkModel(_Base): link_type = Column(Enum(ORM_LinkTypeEnum), nullable=False) total_capacity_gbps = Column(Float, nullable=True) used_capacity_gbps = Column(Float, nullable=True) + is_bidirectional = Column(Boolean, default=False) created_at = Column(DateTime, nullable=False) updated_at = Column(DateTime, nullable=False) @@ -57,11 +59,11 @@ class LinkModel(_Base): } if self.link_type is None: self.link_type = LinkTypeEnum.LINKTYPE_UNKNOWN + attributes : Dict = result.setdefault('attributes', dict()) + attributes.setdefault('is_bidirectional', self.is_bidirectional) if self.total_capacity_gbps is not None: - attributes : Dict = result.setdefault('attributes', dict()) attributes.setdefault('total_capacity_gbps', self.total_capacity_gbps) if self.used_capacity_gbps is not None: - attributes : Dict = result.setdefault('attributes', dict()) attributes.setdefault('used_capacity_gbps', self.used_capacity_gbps) return result diff --git a/src/context/service/database/models/enums/LinkType.py b/src/context/service/database/models/enums/LinkType.py index 6571b19a98b5b2ba00f42b0bf0ab5ac1fb5213b7..01e77f248a173b827248a6e096e371c38059cc51 100644 --- a/src/context/service/database/models/enums/LinkType.py +++ b/src/context/service/database/models/enums/LinkType.py @@ -28,6 +28,7 @@ class ORM_LinkTypeEnum(enum.Enum): RADIO = LinkTypeEnum.LINKTYPE_RADIO VIRTUAL = LinkTypeEnum.LINKTYPE_VIRTUAL MANAGEMENT = LinkTypeEnum.LINKTYPE_MANAGEMENT + REMOTE = LinkTypeEnum.LINKTYPE_REMOTE grpc_to_enum__link_type_enum = functools.partial( grpc_to_enum, LinkTypeEnum, ORM_LinkTypeEnum diff --git a/src/device/client/DeviceClient.py b/src/device/client/DeviceClient.py index fe3ac0f02174c9e9f3580962401ebf2757b3a4fe..69d697cb1d27ea96e07e898954ddcffb6969dfaa 100644 --- a/src/device/client/DeviceClient.py +++ b/src/device/client/DeviceClient.py @@ -21,6 +21,7 @@ from common.proto.context_pb2 import ( ) from common.proto.device_pb2 import MonitoringSettings from common.proto.device_pb2_grpc import DeviceServiceStub +from common.proto.monitoring_pb2 import SSEMonitoringSubscriptionConfig, SSEMonitoringSubscriptionResponse from common.proto.optical_device_pb2_grpc import OpenConfigServiceStub from common.tools.client.RetryDecorator import retry, delay_exponential from common.tools.grpc.Tools import grpc_message_to_json_string @@ -105,3 +106,10 @@ class DeviceClient: response = self.openconfig_stub.DisableOpticalDevice(request) LOGGER.debug('DisableOpticalDevice result: {:s}'.format(grpc_message_to_json_string(response))) return response + + @RETRY_DECORATOR + def SSETelemetrySubscribe(self, request : SSEMonitoringSubscriptionConfig) -> SSEMonitoringSubscriptionResponse: + LOGGER.debug('SSETelemetrySubscribe request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.SSETelemetrySubscribe(request) + LOGGER.debug('SSETelemetrySubscribe result: {:s}'.format(grpc_message_to_json_string(response))) + return response diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py index d5cb2f61fbab5640c3d7dfaf380f1d23809bcef5..80ec6ae4d859f3d5628c79ace2138df308745ff3 100644 --- a/src/device/service/DeviceServiceServicerImpl.py +++ b/src/device/service/DeviceServiceServicerImpl.py @@ -13,7 +13,7 @@ # limitations under the License. import grpc, logging, os, time -from typing import Dict +from typing import Dict, List from prometheus_client import Histogram from common.Constants import ServiceNameEnum from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, get_env_var_name @@ -25,6 +25,7 @@ from common.proto.context_pb2 import ( ) from common.proto.device_pb2 import MonitoringSettings from common.proto.device_pb2_grpc import DeviceServiceServicer +from common.proto.monitoring_pb2 import SSEMonitoringSubscriptionConfig, SSEMonitoringSubscriptionResponse from common.tools.context_queries.Device import get_device from common.tools.mutex_queues.MutexQueues import MutexQueues from context.client.ContextClient import ContextClient @@ -108,7 +109,8 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): # (which controller is in charge of which sub-device). new_sub_devices : Dict[str, Device] = dict() new_sub_links : Dict[str, Link] = dict() - + sorted_sub_device_uuids : List[str] = list() + #----- Experimental ------------ new_optical_configs : Dict[str, OpticalConfig] = dict() @@ -116,8 +118,8 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): t5 = time.time() # created from request, populate endpoints using driver errors.extend(populate_endpoints( - device, driver, self.monitoring_loops, new_sub_devices, new_sub_links, - new_optical_configs + device, driver, self.monitoring_loops, new_sub_devices, sorted_sub_device_uuids, + new_sub_links, new_optical_configs )) t6 = time.time() t_pop_endpoints = t6 - t5 @@ -164,7 +166,8 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): t10 = time.time() - for sub_device in new_sub_devices.values(): + for sub_device_uuid in sorted_sub_device_uuids: + sub_device = new_sub_devices[sub_device_uuid] context_client.SetDevice(sub_device) t11 = time.time() @@ -400,3 +403,35 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): return Empty() finally: self.mutex_queues.signal_done(device_uuid) + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def SSETelemetrySubscribe( + self, request: SSEMonitoringSubscriptionConfig, context : grpc.ServicerContext + ) -> SSEMonitoringSubscriptionResponse: + device_id = request.device_id.device_uuid.uuid + config_type = request.config_type + context_client = ContextClient() + device = get_device( + context_client, device_id, rw_copy=True, include_endpoints=False, include_components=False, + include_config_rules=True) + if device is None: + raise NotFoundException('Device', device_id, extra_details='loading in ConfigureDevice') + driver : _Driver = get_driver(self.driver_instance_cache, device) + + if config_type == SSEMonitoringSubscriptionConfig.Subscribe: + r = driver.SubscribeState([(request.uri, 0, float(request.sampling_interval))]) + if len(r) != 1: + raise OperationFailedException( + 'SSETelemetrySubscribe', extra_details='Driver returned an unexpected number of responses: {:d}'.format(len(r)) + ) + sub_conf: dict = r[0] + return SSEMonitoringSubscriptionResponse(identifier=str(sub_conf['id']), uri=sub_conf['uri']) + + if config_type == SSEMonitoringSubscriptionConfig.Unsubscribe: + r = driver.UnsubscribeState([(str(request.identifier), 0, 0)]) + if len(r) != 1: + raise OperationFailedException( + 'SSETelemetrySubscribe', extra_details='Driver returned an unexpected number of responses: {:d}'.format(len(r)) + ) + return SSEMonitoringSubscriptionResponse() + diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index a62a0d702bdb50041f14e2fd462478fd05675692..384cfc9bb1d1c052f28ba5604e3ec8fae8b23d2b 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -12,18 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, logging -from typing import Any, Dict, List, Optional, Tuple, Union +import json, logging, re +from graphlib import TopologicalSorter, CycleError +from typing import Any, Dict, List, Optional, Set, Tuple, Union from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME from common.DeviceTypes import DeviceTypeEnum from common.method_wrappers.ServiceExceptions import InvalidArgumentException, NotFoundException from common.proto.context_pb2 import ( - ConfigActionEnum, ConfigRule_ACL, Device, DeviceConfig, EndPoint, Link, Location, OpticalConfig + ConfigActionEnum, ConfigRule, ConfigRule_ACL, Device, DeviceConfig, EndPoint, Link, Location, OpticalConfig ) from common.proto.device_pb2 import MonitoringSettings from common.proto.kpi_sample_types_pb2 import KpiSampleType from common.tools.grpc.ConfigRules import update_config_rule_custom from common.tools.grpc.Tools import grpc_message_to_json +from common.tools.object_factory.ConfigRule import json_config_rule_set from common.type_checkers.Checkers import chk_length, chk_type from .driver_api._Driver import _Driver, RESOURCE_ENDPOINTS from .monitoring.MonitoringLoops import MonitoringLoops @@ -104,8 +106,8 @@ def get_device_controller_uuid(device : Device) -> Optional[str]: def populate_endpoints( device : Device, driver : _Driver, monitoring_loops : MonitoringLoops, - new_sub_devices : Dict[str, Device], new_sub_links : Dict[str, Link], - new_optical_configs : Dict[str, OpticalConfig] + new_sub_devices : Dict[str, Device], sorted_sub_device_uuids : List[str], + new_sub_links : Dict[str, Link], new_optical_configs : Dict[str, OpticalConfig] ) -> List[str]: device_uuid = device.device_id.device_uuid.uuid device_name = device.name @@ -123,6 +125,9 @@ def populate_endpoints( add_mgmt_port = True break + devices_with_mgmt_endpoints : Set[str] = set() + mgmt_links : Set[Tuple[str, str]] = set() + if add_mgmt_port: # add mgmt port to main device device_mgmt_endpoint = device.device_endpoints.add() @@ -133,6 +138,9 @@ def populate_endpoints( device_mgmt_endpoint.name = 'mgmt' device_mgmt_endpoint.endpoint_type = 'mgmt' + devices_with_mgmt_endpoints.add(device_uuid) + devices_with_mgmt_endpoints.add(device_name) + errors : List[str] = list() for resource_data in results_getconfig: if len(resource_data) != 2: @@ -150,9 +158,11 @@ def populate_endpoints( if resource_key.startswith('/devices/device'): # create sub-device _sub_device_uuid = resource_value['uuid'] + _sub_device_name = resource_value['name'] + _sub_device_ctrl = resource_value.get('ctrl_uuid') _sub_device = Device() _sub_device.device_id.device_uuid.uuid = _sub_device_uuid # pylint: disable=no-member - _sub_device.name = resource_value['name'] + _sub_device.name = _sub_device_name _sub_device.device_type = resource_value['type'] _sub_device.device_operational_status = resource_value['status'] @@ -167,41 +177,69 @@ def populate_endpoints( MSG = 'Unsupported drivers definition in sub-device({:s}, {:s})' raise Exception(MSG.format(str(resource_key), str(resource_value))) - # Sub-devices should always have a controller associated. - _sub_device.controller_id.device_uuid.uuid = device_uuid + if _sub_device_ctrl is not None: + # Sub-device is managed by an underlying controller + _sub_device.controller_id.device_uuid.uuid = _sub_device_ctrl + else: + # Sub-devices should always have a controller associated. + _sub_device.controller_id.device_uuid.uuid = device_uuid new_sub_devices[_sub_device_uuid] = _sub_device # add mgmt port to sub-device - _sub_device_mgmt_endpoint = _sub_device.device_endpoints.add() # pylint: disable=no-member - _sub_device_mgmt_endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME - _sub_device_mgmt_endpoint.endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME - _sub_device_mgmt_endpoint.endpoint_id.device_id.device_uuid.uuid = _sub_device_uuid - _sub_device_mgmt_endpoint.endpoint_id.endpoint_uuid.uuid = 'mgmt' - _sub_device_mgmt_endpoint.name = 'mgmt' - _sub_device_mgmt_endpoint.endpoint_type = 'mgmt' + if ( + _sub_device_uuid not in devices_with_mgmt_endpoints and + _sub_device_name not in devices_with_mgmt_endpoints + ): + _sub_device_mgmt_endpoint = _sub_device.device_endpoints.add() # pylint: disable=no-member + _sub_device_mgmt_endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME + _sub_device_mgmt_endpoint.endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME + _sub_device_mgmt_endpoint.endpoint_id.device_id.device_uuid.uuid = _sub_device_uuid + _sub_device_mgmt_endpoint.endpoint_id.endpoint_uuid.uuid = 'mgmt' + _sub_device_mgmt_endpoint.name = 'mgmt' + _sub_device_mgmt_endpoint.endpoint_type = 'mgmt' + + devices_with_mgmt_endpoints.add(_sub_device_uuid) + devices_with_mgmt_endpoints.add(_sub_device_name) # add mgmt link - _mgmt_link_uuid = '{:s}/{:s}=={:s}/{:s}'.format(device_name, 'mgmt', _sub_device.name, 'mgmt') - _mgmt_link = Link() - _mgmt_link.link_id.link_uuid.uuid = _mgmt_link_uuid # pylint: disable=no-member - _mgmt_link.name = _mgmt_link_uuid - _mgmt_link.link_endpoint_ids.append(device_mgmt_endpoint.endpoint_id) # pylint: disable=no-member - _mgmt_link.link_endpoint_ids.append(_sub_device_mgmt_endpoint.endpoint_id) # pylint: disable=no-member - new_sub_links[_mgmt_link_uuid] = _mgmt_link + if ( + _sub_device_ctrl is None and + (device_name, _sub_device_name) not in mgmt_links + ): + _mgmt_link_uuid = '{:s}/{:s}=={:s}/{:s}'.format(device_name, 'mgmt', _sub_device_name, 'mgmt') + _mgmt_link = Link() + _mgmt_link.link_id.link_uuid.uuid = _mgmt_link_uuid # pylint: disable=no-member + _mgmt_link.name = _mgmt_link_uuid + _mgmt_link.link_endpoint_ids.append(device_mgmt_endpoint.endpoint_id) # pylint: disable=no-member + _mgmt_link.link_endpoint_ids.append(_sub_device_mgmt_endpoint.endpoint_id) # pylint: disable=no-member + new_sub_links[_mgmt_link_uuid] = _mgmt_link + + mgmt_links.add((device_name, _sub_device_name)) elif resource_key.startswith('/endpoints/endpoint'): endpoint_uuid = resource_value['uuid'] _device_uuid = resource_value.get('device_uuid') + endpoint_name = resource_value.get('name') + + if endpoint_uuid == 'mgmt' or endpoint_name == 'mgmt': + if _device_uuid is None: + if device_uuid in devices_with_mgmt_endpoints: + continue + else: + if _device_uuid in devices_with_mgmt_endpoints: + continue if _device_uuid is None: # add endpoint to current device device_endpoint = device.device_endpoints.add() device_endpoint.endpoint_id.device_id.device_uuid.uuid = device_uuid + device_config_rules = device.device_config.config_rules else: # add endpoint to specified device device_endpoint = new_sub_devices[_device_uuid].device_endpoints.add() device_endpoint.endpoint_id.device_id.device_uuid.uuid = _device_uuid + device_config_rules = new_sub_devices[_device_uuid].device_config.config_rules device_endpoint.endpoint_id.endpoint_uuid.uuid = endpoint_uuid @@ -225,7 +263,28 @@ def populate_endpoints( if location is not None: device_endpoint.endpoint_location.MergeFrom(Location(**location)) + settings = resource_value.get('settings', None) + if settings is not None: + device_config_rules.append(ConfigRule(**json_config_rule_set( + '/endpoints/endpoint[{:s}]'.format(str(endpoint_name)), settings + ))) + + if endpoint_uuid == 'mgmt' or endpoint_name == 'mgmt': + if _device_uuid is None: + devices_with_mgmt_endpoints.add(device_uuid) + devices_with_mgmt_endpoints.add(device_name) + else: + devices_with_mgmt_endpoints.add(_device_uuid) + devices_with_mgmt_endpoints.add(new_sub_devices[_device_uuid].name) + elif resource_key.startswith('/links/link'): + link_name = resource_value['name'] + mgmt_match = re.match(r'^([^\/]+)\/mgmt\=\=([^\/]+)\/mgmt$', link_name) + if mgmt_match is not None: + # is management link + src_dev_name, dst_dev_name = mgmt_match.groups() + if (src_dev_name, dst_dev_name) in mgmt_links: continue + # create sub-link _sub_link_uuid = resource_value['uuid'] _sub_link = Link() @@ -233,12 +292,15 @@ def populate_endpoints( _sub_link.name = resource_value['name'] new_sub_links[_sub_link_uuid] = _sub_link - for device_uuid,endpoint_uuid in resource_value['endpoints']: + for _device_uuid,_endpoint_uuid in resource_value['endpoints']: _sub_link_endpoint_id = _sub_link.link_endpoint_ids.add() # pylint: disable=no-member _sub_link_endpoint_id.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME _sub_link_endpoint_id.topology_id.topology_uuid.uuid = DEFAULT_TOPOLOGY_NAME - _sub_link_endpoint_id.device_id.device_uuid.uuid = device_uuid - _sub_link_endpoint_id.endpoint_uuid.uuid = endpoint_uuid + _sub_link_endpoint_id.device_id.device_uuid.uuid = _device_uuid + _sub_link_endpoint_id.endpoint_uuid.uuid = _endpoint_uuid + + if mgmt_match is not None: + mgmt_links.add((src_dev_name, dst_dev_name)) # ----------Experimental -------------- elif resource_key.startswith('/opticalconfigs/opticalconfig/'): @@ -247,6 +309,27 @@ def populate_endpoints( errors.append(ERROR_UNSUP_RESOURCE.format(device_uuid=device_uuid, resource_data=str(resource_data))) continue + # Topologically sort new_sub_devices so that controllers (those referenced by other + # devices via their controller_id) come before the devices that depend on them. + + graph : Dict[str, Set[str]] = dict() + for dev_uuid, sub_device in new_sub_devices.items(): + ctrl_uuid = get_device_controller_uuid(sub_device) + if ctrl_uuid is None: continue # sub_device has no controller + predecesors = graph.setdefault(dev_uuid, set()) + predecesors.add(ctrl_uuid) + + try: + ts = TopologicalSorter(graph) + sorted_sub_device_uuids.extend(list(ts.static_order())) + if device_uuid in sorted_sub_device_uuids: + sorted_sub_device_uuids.remove(device_uuid) + except CycleError: + MSG = 'Topological sort failed due to cycle among sub-devices({:s}), graph({:s})' + msg = MSG.format(str(new_sub_devices), str(graph)) + LOGGER.exception(msg) + errors.append(msg) + return errors def populate_endpoint_monitoring_resources(device_with_uuids : Device, monitoring_loops : MonitoringLoops) -> None: diff --git a/src/device/service/driver_api/DriverFactory.py b/src/device/service/driver_api/DriverFactory.py index 72583069b388c85e6ac01065e40d3b29c8f4e018..38ae0ac562bce400ce4fd57d06c93d09a682a3a0 100644 --- a/src/device/service/driver_api/DriverFactory.py +++ b/src/device/service/driver_api/DriverFactory.py @@ -12,77 +12,111 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, operator +import logging from enum import Enum -from typing import Any, Dict, Iterable, List, Set, Tuple +from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type from ._Driver import _Driver from .Exceptions import ( - UnsatisfiedFilterException, UnsupportedDriverClassException, UnsupportedFilterFieldException, - UnsupportedFilterFieldValueException) + AmbiguousFilterException, EmptyFilterFieldException, + UnsatisfiedFilterException, UnsupportedDriverClassException, + UnsupportedFilterFieldException, UnsupportedFilterFieldValueException +) from .FilterFields import FILTER_FIELD_ALLOWED_VALUES, FilterFieldEnum + LOGGER = logging.getLogger(__name__) +SUPPORTED_FILTER_FIELDS = set(FILTER_FIELD_ALLOWED_VALUES.keys()) + + +def sanitize_filter_fields( + filter_fields : Dict[FilterFieldEnum, Any], driver_name : Optional[str] = None +) -> Dict[FilterFieldEnum, Any]: + if len(filter_fields) == 0: + raise EmptyFilterFieldException( + filter_fields, driver_class_name=driver_name + ) + + unsupported_filter_fields = set(filter_fields.keys()).difference(SUPPORTED_FILTER_FIELDS) + if len(unsupported_filter_fields) > 0: + raise UnsupportedFilterFieldException( + unsupported_filter_fields, driver_class_name=driver_name + ) + + sanitized_filter_fields : Dict[FilterFieldEnum, Set[Any]] = dict() + for field_name, field_values in filter_fields.items(): + field_enum_values = FILTER_FIELD_ALLOWED_VALUES.get(field_name) + if not isinstance(field_values, Iterable) or isinstance(field_values, str): + field_values = [field_values] + + sanitized_field_values : Set[Any] = set() + for field_value in field_values: + if isinstance(field_value, Enum): field_value = field_value.value + if field_enum_values is not None and field_value not in field_enum_values: + raise UnsupportedFilterFieldValueException( + field_name, field_value, field_enum_values, + driver_class_name=driver_name + ) + sanitized_field_values.add(field_value) + + if len(sanitized_field_values) == 0: continue # do not add empty filters + sanitized_filter_fields[field_name] = sanitized_field_values + + return sanitized_filter_fields + + class DriverFactory: - def __init__(self, drivers : List[Tuple[type, List[Dict[FilterFieldEnum, Any]]]]) -> None: - self.__indices : Dict[str, Dict[str, Set[_Driver]]] = {} # Dict{field_name => Dict{field_value => Set{Driver}}} + def __init__( + self, drivers : List[Tuple[Type[_Driver], List[Dict[FilterFieldEnum, Any]]]] + ) -> None: + self.__drivers : List[Tuple[Type[_Driver], Dict[FilterFieldEnum, Any]]] = list() for driver_class,filter_field_sets in drivers: + #if not issubclass(driver_class, _Driver): + # raise UnsupportedDriverClassException(str(driver_class)) + driver_name = driver_class #.__name__ + for filter_fields in filter_field_sets: filter_fields = {k.value:v for k,v in filter_fields.items()} - self.register_driver_class(driver_class, **filter_fields) - - def register_driver_class(self, driver_class, **filter_fields): - if not issubclass(driver_class, _Driver): raise UnsupportedDriverClassException(str(driver_class)) - - driver_name = driver_class.__name__ - supported_filter_fields = set(FILTER_FIELD_ALLOWED_VALUES.keys()) - unsupported_filter_fields = set(filter_fields.keys()).difference(supported_filter_fields) - if len(unsupported_filter_fields) > 0: - raise UnsupportedFilterFieldException(unsupported_filter_fields, driver_class_name=driver_name) - - for field_name, field_values in filter_fields.items(): - field_indice = self.__indices.setdefault(field_name, dict()) - field_enum_values = FILTER_FIELD_ALLOWED_VALUES.get(field_name) - if not isinstance(field_values, Iterable) or isinstance(field_values, str): - field_values = [field_values] - for field_value in field_values: - if isinstance(field_value, Enum): field_value = field_value.value - if field_enum_values is not None and field_value not in field_enum_values: - raise UnsupportedFilterFieldValueException( - field_name, field_value, field_enum_values, driver_class_name=driver_name) - field_indice_drivers = field_indice.setdefault(field_value, set()) - field_indice_drivers.add(driver_class) - - def get_driver_class(self, **filter_fields) -> _Driver: - supported_filter_fields = set(FILTER_FIELD_ALLOWED_VALUES.keys()) - unsupported_filter_fields = set(filter_fields.keys()).difference(supported_filter_fields) - if len(unsupported_filter_fields) > 0: raise UnsupportedFilterFieldException(unsupported_filter_fields) - - candidate_driver_classes : Dict[_Driver, int] = None # number of filter hits per driver - for field_name, field_values in filter_fields.items(): - field_indice = self.__indices.get(field_name) - if field_indice is None: continue - field_enum_values = FILTER_FIELD_ALLOWED_VALUES.get(field_name) - if not isinstance(field_values, Iterable) or isinstance(field_values, str): - field_values = [field_values] - - field_candidate_driver_classes = set() - for field_value in field_values: - if field_enum_values is not None and field_value not in field_enum_values: - raise UnsupportedFilterFieldValueException(field_name, field_value, field_enum_values) - field_indice_drivers = field_indice.get(field_value) - if field_indice_drivers is None: continue - field_candidate_driver_classes = field_candidate_driver_classes.union(field_indice_drivers) - - if candidate_driver_classes is None: - if len(field_candidate_driver_classes) == 0: continue - candidate_driver_classes = {k:1 for k in field_candidate_driver_classes} - else: - for candidate_driver_class in candidate_driver_classes: - if candidate_driver_class not in field_candidate_driver_classes: continue - candidate_driver_classes[candidate_driver_class] += 1 - - if len(candidate_driver_classes) == 0: raise UnsatisfiedFilterException(filter_fields) - candidate_driver_classes = sorted(candidate_driver_classes.items(), key=operator.itemgetter(1), reverse=True) - return candidate_driver_classes[0][0] + filter_fields = sanitize_filter_fields( + filter_fields, driver_name=driver_name + ) + self.__drivers.append((driver_class, filter_fields)) + + + def is_driver_compatible( + self, driver_filter_fields : Dict[FilterFieldEnum, Any], + selection_filter_fields : Dict[FilterFieldEnum, Any] + ) -> bool: + # by construction empty driver_filter_fields are not allowed + # by construction empty selection_filter_fields are not allowed + for filter_field in SUPPORTED_FILTER_FIELDS: + driver_values = set(driver_filter_fields.get(filter_field, set())) + if driver_values is None : continue # means driver does not restrict + if len(driver_values) == 0: continue # means driver does not restrict + + selection_values = set(selection_filter_fields.get(filter_field, set())) + is_field_compatible = selection_values.issubset(driver_values) + if not is_field_compatible: return False + + return True + + + def get_driver_class(self, **selection_filter_fields) -> _Driver: + sanitized_filter_fields = sanitize_filter_fields(selection_filter_fields) + + compatible_drivers : List[Tuple[Type[_Driver], Dict[FilterFieldEnum, Any]]] = [ + driver_class + for driver_class,driver_filter_fields in self.__drivers + if self.is_driver_compatible(driver_filter_fields, sanitized_filter_fields) + ] + + MSG = '[get_driver_class] compatible_drivers={:s}' + LOGGER.debug(MSG.format(str(compatible_drivers))) + + num_compatible = len(compatible_drivers) + if num_compatible == 0: + raise UnsatisfiedFilterException(selection_filter_fields) + if num_compatible > 1: + raise AmbiguousFilterException(selection_filter_fields, compatible_drivers) + return compatible_drivers[0] diff --git a/src/device/service/driver_api/Exceptions.py b/src/device/service/driver_api/Exceptions.py index 1871fc2e0fbc0ff930b3d65003ffcbfc2c21cb25..8f33ebc57d040b3906997cfbc73a1d79a02dab41 100644 --- a/src/device/service/driver_api/Exceptions.py +++ b/src/device/service/driver_api/Exceptions.py @@ -17,11 +17,26 @@ class UnsatisfiedFilterException(Exception): msg = 'No Driver satisfies FilterFields({:s})' super().__init__(msg.format(str(filter_fields))) +class AmbiguousFilterException(Exception): + def __init__(self, filter_fields, compatible_drivers): + msg = 'Multiple Drivers satisfy FilterFields({:s}): {:s}' + super().__init__(msg.format(str(filter_fields), str(compatible_drivers))) + class UnsupportedDriverClassException(Exception): def __init__(self, driver_class_name): msg = 'Class({:s}) is not a subclass of _Driver' super().__init__(msg.format(str(driver_class_name))) +class EmptyFilterFieldException(Exception): + def __init__(self, filter_fields, driver_class_name=None): + if driver_class_name: + msg = 'Empty FilterField({:s}) specified by Driver({:s}) is not supported' + msg = msg.format(str(filter_fields), str(driver_class_name)) + else: + msg = 'Empty FilterField({:s}) is not supported' + msg = msg.format(str(filter_fields)) + super().__init__(msg) + class UnsupportedFilterFieldException(Exception): def __init__(self, unsupported_filter_fields, driver_class_name=None): if driver_class_name: diff --git a/src/device/service/driver_api/_Driver.py b/src/device/service/driver_api/_Driver.py index e8540b8728c902ec563822dd1780163a72bdcbd3..1b080bca664a96ab7fcd8bc6d4febebd375b3918 100644 --- a/src/device/service/driver_api/_Driver.py +++ b/src/device/service/driver_api/_Driver.py @@ -139,7 +139,7 @@ class _Driver: raise NotImplementedError() def SubscribeState(self, subscriptions: List[Tuple[str, float, float]]) -> \ - List[Union[bool, Exception]]: + List[Union[bool, dict[str, Any], Exception]]: """ Subscribe to state information of entire device or selected resources. Subscriptions are incremental. Driver should keep track of requested resources. diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py index 788b09edd71a472224a92c38923a83df4560d49b..99e18f0d6476dc36e758bbc33bc0aa8166d754a8 100644 --- a/src/device/service/drivers/__init__.py +++ b/src/device/service/drivers/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os + from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import DeviceDriverEnum from device.Config import LOAD_ALL_DEVICE_DRIVERS @@ -23,100 +23,66 @@ DRIVERS = [] from .emulated.EmulatedDriver import EmulatedDriver # pylint: disable=wrong-import-position DRIVERS.append( (EmulatedDriver, [ - # TODO: multi-filter is not working { - FilterFieldEnum.DEVICE_TYPE: [ - DeviceTypeEnum.EMULATED_DATACENTER, - DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM, - DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM, - DeviceTypeEnum.EMULATED_OPTICAL_ROADM, - DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER, - DeviceTypeEnum.EMULATED_P4_SWITCH, - DeviceTypeEnum.EMULATED_PACKET_ROUTER, - DeviceTypeEnum.EMULATED_PACKET_SWITCH, - - #DeviceTypeEnum.DATACENTER, - #DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, - #DeviceTypeEnum.OPEN_LINE_SYSTEM, - #DeviceTypeEnum.OPTICAL_ROADM, - #DeviceTypeEnum.OPTICAL_TRANSPONDER, - #DeviceTypeEnum.P4_SWITCH, - #DeviceTypeEnum.PACKET_ROUTER, - #DeviceTypeEnum.PACKET_SWITCH, - ], + FilterFieldEnum.DEVICE_TYPE: [], # any device type FilterFieldEnum.DRIVER: [ DeviceDriverEnum.DEVICEDRIVER_UNDEFINED, ], - }, - #{ - # # Emulated devices, all drivers => use Emulated - # FilterFieldEnum.DEVICE_TYPE: [ - # DeviceTypeEnum.EMULATED_DATACENTER, - # DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM, - # DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM, - # DeviceTypeEnum.EMULATED_OPTICAL_ROADM, - # DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER, - # DeviceTypeEnum.EMULATED_P4_SWITCH, - # DeviceTypeEnum.EMULATED_PACKET_ROUTER, - # DeviceTypeEnum.EMULATED_PACKET_SWITCH, - # ], - # FilterFieldEnum.DRIVER: [ - # DeviceDriverEnum.DEVICEDRIVER_UNDEFINED, - # DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, - # DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API, - # DeviceDriverEnum.DEVICEDRIVER_P4, - # DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY, - # DeviceDriverEnum.DEVICEDRIVER_ONF_TR_532, - # DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG, - # ], - #} - ])) - -from .ietf_l2vpn.IetfL2VpnDriver import IetfL2VpnDriver # pylint: disable=wrong-import-position -DRIVERS.append( - (IetfL2VpnDriver, [ - { - FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, - FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN, } ])) +if LOAD_ALL_DEVICE_DRIVERS: + from .ietf_l2vpn.IetfL2VpnDriver import IetfL2VpnDriver # pylint: disable=wrong-import-position + DRIVERS.append( + (IetfL2VpnDriver, [ + { + FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN, + } + ])) -from .ietf_l3vpn.IetfL3VpnDriver import IetfL3VpnDriver # pylint: disable=wrong-import-position -DRIVERS.append( - (IetfL3VpnDriver, [ - { - FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, - FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN, - } - ])) +if LOAD_ALL_DEVICE_DRIVERS: + from .ietf_l3vpn.IetfL3VpnDriver 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, [ - { - FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.OPEN_LINE_SYSTEM, - FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN, - } - ])) +if LOAD_ALL_DEVICE_DRIVERS: + from .ietf_actn.IetfActnDriver import IetfActnDriver # pylint: disable=wrong-import-position + DRIVERS.append( + (IetfActnDriver, [ + { + FilterFieldEnum.DEVICE_TYPE: [ + DeviceTypeEnum.OPEN_LINE_SYSTEM, + DeviceTypeEnum.NCE, + ], + FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN, + } + ])) -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, - } - ])) +if LOAD_ALL_DEVICE_DRIVERS: + from .ietf_slice.IetfSliceDriver import IetfSliceDriver # pylint: disable=wrong-import-position + DRIVERS.append( + (IetfSliceDriver, [ + { + FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + 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 .nce.NCEDriver 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 @@ -124,7 +90,10 @@ if LOAD_ALL_DEVICE_DRIVERS: (OpenConfigDriver, [ { # Real Packet Router, specifying OpenConfig Driver => use OpenConfigDriver - FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.PACKET_ROUTER, + FilterFieldEnum.DEVICE_TYPE: [ + DeviceTypeEnum.PACKET_POP, + DeviceTypeEnum.PACKET_ROUTER, + ], FilterFieldEnum.DRIVER : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG, } ])) @@ -135,7 +104,10 @@ if LOAD_ALL_DEVICE_DRIVERS: (GnmiOpenConfigDriver, [ { # Real Packet Router, specifying gNMI OpenConfig Driver => use GnmiOpenConfigDriver - FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.PACKET_ROUTER, + FilterFieldEnum.DEVICE_TYPE: [ + DeviceTypeEnum.PACKET_POP, + DeviceTypeEnum.PACKET_ROUTER, + ], FilterFieldEnum.DRIVER : DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG, } ])) @@ -193,6 +165,7 @@ if LOAD_ALL_DEVICE_DRIVERS: FilterFieldEnum.DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY, } ])) + if LOAD_ALL_DEVICE_DRIVERS: from .ryu.RyuDriver import RyuDriver DRIVERS.append( diff --git a/src/device/service/drivers/ietf_actn/IetfActnDriver.py b/src/device/service/drivers/ietf_actn/IetfActnDriver.py index 431674e4e87116834d5f945b324a91134320dc8c..12387e10153c1152a14ba8516e38908b54fd3359 100644 --- a/src/device/service/drivers/ietf_actn/IetfActnDriver.py +++ b/src/device/service/drivers/ietf_actn/IetfActnDriver.py @@ -12,41 +12,62 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, logging, requests, threading +import copy, json, logging, requests, threading from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.tools.rest_conf.client.RestConfClient import RestConfClient from common.type_checkers.Checkers import chk_string, chk_type from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES from .handlers.EthtServiceHandler import EthtServiceHandler from .handlers.OsuTunnelHandler import OsuTunnelHandler -from .handlers.RestApiClient import RestApiClient +from .handlers.NetworkTopologyHandler import NetworkTopologyHandler from .Tools import get_etht_services, get_osu_tunnels, parse_resource_key + LOGGER = logging.getLogger(__name__) + ALL_RESOURCE_KEYS = [ RESOURCE_ENDPOINTS, RESOURCE_SERVICES, ] + DRIVER_NAME = 'ietf_actn' METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME}) + class IetfActnDriver(_Driver): def __init__(self, address: str, port: int, **settings) -> None: super().__init__(DRIVER_NAME, address, port, **settings) self.__lock = threading.Lock() self.__started = threading.Event() self.__terminate = threading.Event() - self._rest_api_client = RestApiClient(address, port, settings=settings) - self._handler_osu_tunnel = OsuTunnelHandler(self._rest_api_client) - self._handler_etht_service = EthtServiceHandler(self._rest_api_client) + + restconf_v1_settings = copy.deepcopy(settings) + restconf_v1_settings.pop('base_url', None) + restconf_v1_settings.pop('import_topology', None) + restconf_v1_settings['logger'] = logging.getLogger(__name__ + '.RestConfClient_v1') + + self._rest_conf_v1_client = RestConfClient(address, port=port, **restconf_v1_settings) + self._handler_net_topology = NetworkTopologyHandler(self._rest_conf_v1_client, **settings) + + restconf_v2_settings = copy.deepcopy(settings) + restconf_v2_settings.pop('base_url', None) + restconf_v2_settings.pop('import_topology', None) + restconf_v2_settings['restconf_version'] = 'v2' + restconf_v2_settings['logger'] = logging.getLogger(__name__ + '.RestConfClient_v2') + + self._rest_conf_v2_client = RestConfClient(address, port=port, **restconf_v2_settings) + self._handler_etht_service = EthtServiceHandler(self._rest_conf_v2_client) + self._handler_osu_tunnel = OsuTunnelHandler(self._rest_conf_v2_client) def Connect(self) -> bool: with self.__lock: if self.__started.is_set(): return True try: - self._rest_api_client.get('Check Credentials', '') + self._rest_conf_v1_client._discover_base_url() + self._rest_conf_v2_client._discover_base_url() except requests.exceptions.Timeout: LOGGER.exception('Timeout exception checking connectivity') return False @@ -70,34 +91,29 @@ class IetfActnDriver(_Driver): @metered_subclass_method(METRICS_POOL) def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: chk_type('resources', resource_keys, list) - results = [] + results = list() with self.__lock: if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS for i, resource_key in enumerate(resource_keys): - chk_string('resource_key[#{:d}]'.format(i), resource_key, allow_empty=False) - + str_resource_name = 'resource_key[#{:d}]'.format(i) try: - _results = list() - + chk_string(str_resource_name, resource_key, allow_empty=False) if resource_key == RESOURCE_ENDPOINTS: - # Add mgmt endpoint by default - resource_key = '/endpoints/endpoint[mgmt]' - resource_value = {'uuid': 'mgmt', 'name': 'mgmt', 'type': 'mgmt'} - results.append((resource_key, resource_value)) + results.extend(self._handler_net_topology.get()) elif resource_key == RESOURCE_SERVICES: - get_osu_tunnels(self._handler_osu_tunnel, _results) - get_etht_services(self._handler_etht_service, _results) + get_osu_tunnels(self._handler_osu_tunnel, results) + get_etht_services(self._handler_etht_service, results) else: # check if resource key is for a specific OSU tunnel or ETHT service, and get them accordingly osu_tunnel_name, etht_service_name = parse_resource_key(resource_key) if osu_tunnel_name is not None: - get_osu_tunnels(self._handler_osu_tunnel, _results, osu_tunnel_name=osu_tunnel_name) + get_osu_tunnels(self._handler_osu_tunnel, results, osu_tunnel_name=osu_tunnel_name) if etht_service_name is not None: - get_etht_services(self._handler_etht_service, _results, etht_service_name=etht_service_name) - - results.extend(_results) + get_etht_services(self._handler_etht_service, results, etht_service_name=etht_service_name) except Exception as e: - results.append((resource_key, e)) + MSG = 'Error processing resource_key({:s}, {:s})' + LOGGER.exception(MSG.format(str_resource_name, str(resource_key))) + results.append((resource_key, e)) # if processing fails, store the exception return results diff --git a/src/device/service/drivers/ietf_actn/handlers/EthtServiceHandler.py b/src/device/service/drivers/ietf_actn/handlers/EthtServiceHandler.py index 6d8ec2f8ce4bd1c6739b2bc9ff9b0d36b0e7258d..00c9b664ebab105bc35ea6fa34c677d5036265ee 100644 --- a/src/device/service/drivers/ietf_actn/handlers/EthtServiceHandler.py +++ b/src/device/service/drivers/ietf_actn/handlers/EthtServiceHandler.py @@ -14,10 +14,12 @@ import enum, logging from typing import Dict, List, Optional, Tuple, Union -from .RestApiClient import HTTP_STATUS_CREATED, HTTP_STATUS_NO_CONTENT, HTTP_STATUS_OK, RestApiClient +from common.tools.rest_conf.client.RestConfClient import RestConfClient + LOGGER = logging.getLogger(__name__) + class BandwidthProfileTypeEnum(enum.Enum): MEF_10_BWP = 'ietf-eth-tran-types:mef-10-bwp' @@ -106,40 +108,24 @@ def compose_etht_service( 'optimizations': compose_optimizations(), }]}} + class EthtServiceHandler: - def __init__(self, rest_api_client : RestApiClient) -> None: - self._rest_api_client = rest_api_client - self._object_name = 'EthtService' + def __init__(self, rest_conf_client : RestConfClient) -> None: + self._rest_conf_client = rest_conf_client self._subpath_root = '/ietf-eth-tran-service:etht-svc' - self._subpath_item = self._subpath_root + '/etht-svc-instances="{etht_service_name:s}"' + self._subpath_item = self._subpath_root + '/etht-svc-instances={etht_service_name:s}' + - def _rest_api_get(self, etht_service_name : Optional[str] = None) -> Union[Dict, List]: + def get(self, etht_service_name : Optional[str] = None) -> Union[Dict, List]: if etht_service_name is None: subpath_url = self._subpath_root else: subpath_url = self._subpath_item.format(etht_service_name=etht_service_name) - return self._rest_api_client.get( - self._object_name, subpath_url, expected_http_status={HTTP_STATUS_OK} - ) - - def _rest_api_update(self, data : Dict) -> bool: - return self._rest_api_client.update( - self._object_name, self._subpath_root, data, expected_http_status={HTTP_STATUS_CREATED} - ) - def _rest_api_delete(self, etht_service_name : str) -> bool: - if etht_service_name is None: raise Exception('etht_service_name is None') - subpath_url = self._subpath_item.format(etht_service_name=etht_service_name) - return self._rest_api_client.delete( - self._object_name, subpath_url, expected_http_status={HTTP_STATUS_NO_CONTENT} - ) - - def get(self, etht_service_name : Optional[str] = None) -> Union[Dict, List]: - data = self._rest_api_get(etht_service_name=etht_service_name) + data = self._rest_conf_client.get(subpath_url) if not isinstance(data, dict): raise ValueError('data should be a dict') - if 'ietf-eth-tran-service:etht-svc' not in data: - raise ValueError('data does not contain key "ietf-eth-tran-service:etht-svc"') + if 'ietf-eth-tran-service:etht-svc' not in data: return list() data = data['ietf-eth-tran-service:etht-svc'] if 'etht-svc-instances' not in data: raise ValueError('data["ietf-eth-tran-service:etht-svc"] does not contain key "etht-svc-instances"') @@ -192,6 +178,7 @@ class EthtServiceHandler: return etht_services + def update(self, parameters : Dict) -> bool: name = parameters['name' ] service_type = parameters['service_type' ] @@ -214,8 +201,10 @@ class EthtServiceHandler: src_node_id, src_tp_id, src_vlan_tag, dst_node_id, dst_tp_id, dst_vlan_tag, src_static_routes=src_static_routes, dst_static_routes=dst_static_routes ) + return self._rest_conf_client.post(self._subpath_root, body=data) is not None - return self._rest_api_update(data) - def delete(self, etht_service_name : str) -> bool: - return self._rest_api_delete(etht_service_name) + def delete(self, etht_service_name : str) -> None: + if etht_service_name is None: raise Exception('etht_service_name is None') + subpath_url = self._subpath_item.format(etht_service_name=etht_service_name) + return self._rest_conf_client.delete(subpath_url) diff --git a/src/device/service/drivers/ietf_actn/handlers/NetworkTopologyHandler.py b/src/device/service/drivers/ietf_actn/handlers/NetworkTopologyHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..ef0246c061c5c40cf86b98cdc72b65f9d48635e6 --- /dev/null +++ b/src/device/service/drivers/ietf_actn/handlers/NetworkTopologyHandler.py @@ -0,0 +1,197 @@ +# 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 logging, re +from typing import Dict, List, Optional +from common.Constants import DEFAULT_TOPOLOGY_NAME +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import ( + DEVICEDRIVER_UNDEFINED, DEVICEOPERATIONALSTATUS_DISABLED, + DEVICEOPERATIONALSTATUS_ENABLED +) +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from device.service.driver_api.ImportTopologyEnum import ( + ImportTopologyEnum, get_import_topology +) + + +LOGGER = logging.getLogger(__name__) + + +class NetworkTopologyHandler: + def __init__(self, rest_conf_client : RestConfClient, **settings) -> None: + self._rest_conf_client = rest_conf_client + self._subpath_root = '/ietf-network:networks' + self._subpath_item = self._subpath_root + '/network={network_id:s}' + + # Options are: + # disabled --> just import endpoints as usual + # devices --> imports sub-devices but not links connecting them. + # (a remotely-controlled transport domain might exist between them) + # topology --> imports sub-devices and links connecting them. + # (not supported by XR driver) + self._import_topology = get_import_topology(settings, default=ImportTopologyEnum.TOPOLOGY) + + + def get(self, network_id : Optional[str] = None) -> List[Dict]: + if network_id is None: network_id = DEFAULT_TOPOLOGY_NAME + endpoint = self._subpath_item.format(network_id=network_id) + reply = self._rest_conf_client.get(endpoint) + + if 'ietf-network:network' not in reply: + raise Exception('Malformed reply. "ietf-network:network" missing') + networks = reply['ietf-network:network'] + + if len(networks) == 0: + MSG = '[get] Network({:s}) not found; returning' + LOGGER.debug(MSG.format(str(network_id))) + return list() + + if len(networks) > 1: + MSG = '[get] Multiple occurrences for Network({:s}); returning' + LOGGER.debug(MSG.format(str(network_id))) + return list() + + network = networks[0] + + MSG = '[get] import_topology={:s}' + LOGGER.debug(MSG.format(str(self._import_topology))) + + result = list() + if self._import_topology == ImportTopologyEnum.DISABLED: + LOGGER.debug('[get] abstract controller; returning') + return result + + device_type = DeviceTypeEnum.EMULATED_PACKET_SWITCH.value + endpoint_type = '' + if 'network-types' in network: + nnt = network['network-types'] + if 'ietf-te-topology:te-topology' in nnt: + nnt_tet = nnt['ietf-te-topology:te-topology'] + if 'ietf-otn-topology:otn-topology' in nnt_tet: + device_type = DeviceTypeEnum.OPTICAL_FGOTN.value + endpoint_type = 'optical' + elif 'ietf-eth-te-topology:eth-tran-topology' in nnt_tet: + device_type = DeviceTypeEnum.EMULATED_PACKET_SWITCH.value + endpoint_type = 'copper' + elif 'ietf-l3-unicast-topology:l3-unicast-topology' in nnt_tet: + device_type = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value + endpoint_type = 'copper' + + for node in network['node']: + node_id = node['node-id'] + + node_name = node_id + node_is_up = True + if 'ietf-te-topology:te' in node: + nte = node['ietf-te-topology:te'] + + if 'oper-status' in nte: + node_is_up = nte['oper-status'] == 'up' + + if 'te-node-attributes' in nte: + ntea = nte['te-node-attributes'] + if 'name' in ntea: + node_name = ntea['name'] + + device_url = '/devices/device[{:s}]'.format(node_id) + device_data = { + 'uuid': node_id, + 'name': node_name, + 'type': device_type, + 'status': DEVICEOPERATIONALSTATUS_ENABLED if node_is_up else DEVICEOPERATIONALSTATUS_DISABLED, + 'drivers': [DEVICEDRIVER_UNDEFINED], + } + result.append((device_url, device_data)) + + for tp in node['ietf-network-topology:termination-point']: + tp_id = tp['tp-id'] + + tp_name = tp_id + if 'ietf-te-topology:te' in tp: + tpte = tp['ietf-te-topology:te'] + if 'name' in tpte: + tp_name = tpte['name'] + + tp_ip_addr = '0.0.0.0' + if 'ietf-te-topology:te-tp-id' in tp: + tp_ip_addr = tp['ietf-te-topology:te-tp-id'] + + if node_name == 'O-PE1' and tp_name == '200': + site_location = 'access' + elif node_name == 'O-PE2' and tp_name == '200': + site_location = 'cloud' + else: + site_location = 'transport' + + endpoint_url = '/endpoints/endpoint[{:s}, {:s}]'.format(node_id, tp_id) + endpoint_settings = { + 'uuid' : tp_id, + 'name' : tp_name, + 'type' : endpoint_type, + 'address_ip' : tp_ip_addr, + 'address_prefix': '24', + 'mtu' : '1500', + 'site_location' : site_location, + } + + outer_tag_vlan_range : Optional[str] = ( + tp + .get('ietf-eth-te-topology:eth-svc', dict()) + .get('supported-classification', dict()) + .get('vlan-classification', dict()) + .get('outer-tag', dict()) + .get('vlan-range') + ) + if outer_tag_vlan_range is not None: + RE_NUMBER = re.compile(r'[0-9]+') + if RE_NUMBER.match(outer_tag_vlan_range) is not None: + endpoint_settings['vlan_tag'] = int(outer_tag_vlan_range) + + endpoint_data = { + 'device_uuid': node_id, + 'uuid': tp_id, + 'name': tp_name, + 'type': endpoint_type, + 'settings': endpoint_settings, + } + result.append((endpoint_url, endpoint_data)) + + if self._import_topology == ImportTopologyEnum.DEVICES: + LOGGER.debug('[get] devices only; returning') + return result + + for link in network['ietf-network-topology:link']: + link_uuid = link['link-id'] + link_src = link['source'] + link_dst = link['destination'] + link_src_dev_id = link_src['source-node'] + link_src_ep_id = link_src['source-tp'] + link_dst_dev_id = link_dst['dest-node'] + link_dst_ep_id = link_dst['dest-tp'] + + link_url = '/links/link[{:s}]'.format(link_uuid) + link_endpoint_ids = [ + (link_src_dev_id, link_src_ep_id), + (link_dst_dev_id, link_dst_ep_id), + ] + link_data = { + 'uuid': link_uuid, + 'name': link_uuid, + 'endpoints': link_endpoint_ids, + } + result.append((link_url, link_data)) + + LOGGER.debug('[get] topology; returning') + return result diff --git a/src/device/service/drivers/ietf_actn/handlers/OsuTunnelHandler.py b/src/device/service/drivers/ietf_actn/handlers/OsuTunnelHandler.py index d94e7d7c43da30d55892ff1908c648cb872061f2..5abc009b446c53f354600493ed7e4bddba08168c 100644 --- a/src/device/service/drivers/ietf_actn/handlers/OsuTunnelHandler.py +++ b/src/device/service/drivers/ietf_actn/handlers/OsuTunnelHandler.py @@ -14,10 +14,12 @@ import enum, logging from typing import Dict, List, Optional, Union -from .RestApiClient import HTTP_STATUS_CREATED, HTTP_STATUS_NO_CONTENT, HTTP_STATUS_OK, RestApiClient +from common.tools.rest_conf.client.RestConfClient import RestConfClient + LOGGER = logging.getLogger(__name__) + class EndpointProtectionRoleEnum(enum.Enum): WORK = 'work' @@ -80,39 +82,24 @@ def compose_osu_tunnel( 'protection': compose_osu_tunnel_protection(), }]} + class OsuTunnelHandler: - def __init__(self, rest_api_client : RestApiClient) -> None: - self._rest_api_client = rest_api_client - self._object_name = 'OsuTunnel' + def __init__(self, rest_conf_client : RestConfClient) -> None: + self._rest_conf_client = rest_conf_client self._subpath_root = '/ietf-te:te/tunnels' - self._subpath_item = self._subpath_root + '/tunnel="{osu_tunnel_name:s}"' + self._subpath_item = self._subpath_root + '/tunnel={osu_tunnel_name:s}' + - def _rest_api_get(self, osu_tunnel_name : Optional[str] = None) -> Union[Dict, List]: + def get(self, osu_tunnel_name : Optional[str] = None) -> Union[Dict, List]: if osu_tunnel_name is None: subpath_url = self._subpath_root else: subpath_url = self._subpath_item.format(osu_tunnel_name=osu_tunnel_name) - return self._rest_api_client.get( - self._object_name, subpath_url, expected_http_status={HTTP_STATUS_OK} - ) - - def _rest_api_update(self, data : Dict) -> bool: - return self._rest_api_client.update( - self._object_name, self._subpath_root, data, expected_http_status={HTTP_STATUS_CREATED} - ) - def _rest_api_delete(self, osu_tunnel_name : str) -> bool: - if osu_tunnel_name is None: raise Exception('osu_tunnel_name is None') - subpath_url = self._subpath_item.format(osu_tunnel_name=osu_tunnel_name) - return self._rest_api_client.delete( - self._object_name, subpath_url, expected_http_status={HTTP_STATUS_NO_CONTENT} - ) - - def get(self, osu_tunnel_name : Optional[str] = None) -> Union[Dict, List]: - data = self._rest_api_get(osu_tunnel_name=osu_tunnel_name) + data = self._rest_conf_client.get(subpath_url) if not isinstance(data, dict): raise ValueError('data should be a dict') - if 'ietf-te:tunnel' not in data: raise ValueError('data does not contain key "ietf-te:tunnel"') + if 'ietf-te:tunnel' not in data: return list() data = data['ietf-te:tunnel'] if not isinstance(data, list): raise ValueError('data[ietf-te:tunnel] should be a list') @@ -147,6 +134,7 @@ class OsuTunnelHandler: return osu_tunnels + def update(self, parameters : Dict) -> bool: name = parameters['name' ] @@ -169,8 +157,10 @@ class OsuTunnelHandler: name, src_node_id, src_tp_id, src_ttp_channel_name, dst_node_id, dst_tp_id, dst_ttp_channel_name, odu_type, osuflex_number, delay, bidirectional=bidirectional ) + return self._rest_conf_client.post(self._subpath_root, body=data) is not None - return self._rest_api_update(data) def delete(self, osu_tunnel_name : str) -> bool: - return self._rest_api_delete(osu_tunnel_name) + if osu_tunnel_name is None: raise Exception('osu_tunnel_name is None') + subpath_url = self._subpath_item.format(osu_tunnel_name=osu_tunnel_name) + return self._rest_conf_client.delete(subpath_url) diff --git a/src/device/service/drivers/ietf_actn/handlers/RestApiClient.py b/src/device/service/drivers/ietf_actn/handlers/RestApiClient.py deleted file mode 100644 index 2e369ab59acf93a6dea82233f7aead8fbac3b288..0000000000000000000000000000000000000000 --- a/src/device/service/drivers/ietf_actn/handlers/RestApiClient.py +++ /dev/null @@ -1,104 +0,0 @@ -# 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 copy, json, logging, requests -from requests.auth import HTTPBasicAuth -from typing import Any, Dict, List, Set, Union - -LOGGER = logging.getLogger(__name__) - -DEFAULT_BASE_URL = '/restconf/v2/data' -DEFAULT_SCHEME = 'https' -DEFAULT_TIMEOUT = 120 -DEFAULT_VERIFY = False - -HTTP_STATUS_OK = 200 -HTTP_STATUS_CREATED = 201 -HTTP_STATUS_ACCEPTED = 202 -HTTP_STATUS_NO_CONTENT = 204 - -HTTP_OK_CODES = { - HTTP_STATUS_OK, - HTTP_STATUS_CREATED, - HTTP_STATUS_ACCEPTED, - HTTP_STATUS_NO_CONTENT, -} - -class RestApiClient: - def __init__(self, address : str, port : int, settings : Dict[str, Any] = dict()) -> None: - username = settings.get('username') - password = settings.get('password') - self._auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None - - scheme = settings.get('scheme', DEFAULT_SCHEME ) - base_url = settings.get('base_url', DEFAULT_BASE_URL) - self._base_url = '{:s}://{:s}:{:d}{:s}'.format(scheme, address, int(port), base_url) - - self._timeout = int(settings.get('timeout', DEFAULT_TIMEOUT)) - self._verify = bool(settings.get('verify', DEFAULT_VERIFY)) - - def get( - self, object_name : str, url : str, - expected_http_status : Set[int] = {HTTP_STATUS_OK} - ) -> Union[Dict, List]: - MSG = 'Get {:s}({:s})' - LOGGER.info(MSG.format(str(object_name), str(url))) - response = requests.get( - self._base_url + url, - timeout=self._timeout, verify=self._verify, auth=self._auth - ) - LOGGER.info(' Response[{:s}]: {:s}'.format(str(response.status_code), str(response.content))) - - if response.status_code in expected_http_status: return json.loads(response.content) - - MSG = 'Could not get {:s}({:s}): status_code={:s} reply={:s}' - raise Exception(MSG.format(str(object_name), str(url), str(response.status_code), str(response))) - - def update( - self, object_name : str, url : str, data : Dict, headers : Dict[str, Any] = dict(), - expected_http_status : Set[int] = HTTP_OK_CODES - ) -> None: - headers = copy.deepcopy(headers) - if 'content-type' not in {header_name.lower() for header_name in headers.keys()}: - headers.update({'content-type': 'application/json'}) - - MSG = 'Create/Update {:s}({:s}, {:s})' - LOGGER.info(MSG.format(str(object_name), str(url), str(data))) - response = requests.post( - self._base_url + url, data=json.dumps(data), headers=headers, - timeout=self._timeout, verify=self._verify, auth=self._auth - ) - LOGGER.info(' Response[{:s}]: {:s}'.format(str(response.status_code), str(response.content))) - - if response.status_code in expected_http_status: return - - MSG = 'Could not create/update {:s}({:s}, {:s}): status_code={:s} reply={:s}' - raise Exception(MSG.format(str(object_name), str(url), str(data), str(response.status_code), str(response))) - - def delete( - self, object_name : str, url : str, - expected_http_status : Set[int] = HTTP_OK_CODES - ) -> None: - MSG = 'Delete {:s}({:s})' - LOGGER.info(MSG.format(str(object_name), str(url))) - response = requests.delete( - self._base_url + url, - timeout=self._timeout, verify=self._verify, auth=self._auth - ) - LOGGER.info(' Response[{:s}]: {:s}'.format(str(response.status_code), str(response.content))) - - if response.status_code in expected_http_status: return - - MSG = 'Could not delete {:s}({:s}): status_code={:s} reply={:s}' - raise Exception(MSG.format(str(object_name), str(url), str(response.status_code), str(response))) diff --git a/src/device/service/drivers/ietf_l2vpn/TfsApiClient.py b/src/device/service/drivers/ietf_l2vpn/TfsApiClient.py index cc124b02cf15103f1c807ea08e7307b2bf070b09..ac210671608f5f764e82c3a3288459f086d080ac 100644 --- a/src/device/service/drivers/ietf_l2vpn/TfsApiClient.py +++ b/src/device/service/drivers/ietf_l2vpn/TfsApiClient.py @@ -14,7 +14,7 @@ import logging, requests from typing import Dict, List, Optional -from common.tools.client.RestClient import RestClient +from common.tools.rest_api.client.RestApiClient import RestApiClient from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum GET_CONTEXT_IDS_URL = '/tfs-api/context_ids' @@ -51,7 +51,7 @@ MAPPING_DRIVER = { LOGGER = logging.getLogger(__name__) -class TfsApiClient(RestClient): +class TfsApiClient(RestApiClient): def __init__( self, address : str, port : int, scheme : str = 'http', username : Optional[str] = None, password : Optional[str] = None, diff --git a/src/device/service/drivers/ietf_l3vpn/IetfL3VpnDriver.py b/src/device/service/drivers/ietf_l3vpn/IetfL3VpnDriver.py index 79219e8959eb98e6b049bbd5e215d92bcabe0542..c1cf51536d7a4f09eefba26a836ef13d0e2f64c8 100644 --- a/src/device/service/drivers/ietf_l3vpn/IetfL3VpnDriver.py +++ b/src/device/service/drivers/ietf_l3vpn/IetfL3VpnDriver.py @@ -13,32 +13,39 @@ # limitations under the License. -import anytree, json, logging, re, requests, threading -from typing import Any, Iterator, List, Optional, Tuple, Union +import anytree, copy, json, logging, re, threading +from typing import Any, Dict, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.tools.rest_conf.client.RestConfClient import RestConfClient from common.type_checkers.Checkers import chk_length, chk_string, chk_type from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES 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 .handlers.SubscriptionHandler import ( + SubscribedNotificationsSchema, SubscriptionHandler, UnsubscribedNotificationsSchema +) 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$") +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) @@ -54,7 +61,13 @@ class IetfL3VpnDriver(_Driver): self.address, self.port, scheme=scheme, username=username, password=password, timeout=timeout ) - #self.__tfs_nbi_root = "{:s}://{:s}:{:d}".format(scheme, self.address, int(self.port)) + + restconf_settings = copy.deepcopy(settings) + restconf_settings.pop('base_url', None) + restconf_settings.pop('import_topology', None) + restconf_settings['logger'] = logging.getLogger(__name__ + '.RestConfClient') + self._rest_conf_client = RestConfClient(address, port=port, **restconf_settings) + self._handler_subscription = SubscriptionHandler(self._rest_conf_client) # Options are: # disabled --> just import endpoints as usual @@ -90,7 +103,7 @@ class IetfL3VpnDriver(_Driver): 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 + except Exception as e: LOGGER.exception( "Exception validating {:s}: {:s}".format( str_resource_name, str(resource_key) @@ -114,25 +127,23 @@ class IetfL3VpnDriver(_Driver): def Connect(self) -> bool: with self.__lock: if self.__started.is_set(): return True - try: - self.tac.check_credentials() - except: # pylint: disable=bare-except - LOGGER.exception('Exception checking credentials') - return False - else: - self.__started.set() - return True + checked = self.tac.check_credentials(raise_if_fail=False) + if checked: self.__started.set() + return checked + 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] = [] @@ -169,6 +180,7 @@ class IetfL3VpnDriver(_Driver): results.append((resource_key, e)) return results + @metered_subclass_method(METRICS_POOL) def SetConfig( self, resources : List[Tuple[str, Any]] @@ -176,95 +188,92 @@ class IetfL3VpnDriver(_Driver): 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))) + for i, resource in enumerate(resources): + str_resource_name = 'resource_key[#{:d}]'.format(i) + LOGGER.info('[SetConfig] resource = {:s}'.format(str(resource))) resource_key, resource_value = resource - if not RE_IETF_L3VPN_DATA.match(resource_key): - continue + + 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") + self.tac.create_connectivity_service(resource_value) 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) - ) - ) + except Exception as e: + MSG = 'Unhandled error processing SET {:s}: resource_key({:s})' + LOGGER.exception(MSG.format(str_resource_name, 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 + if len(resources) == 0: return results with self.__lock: - for resource in resources: - LOGGER.info("resource = {:s}".format(str(resource))) + for i, resource in enumerate(resources): + str_resource_name = 'resource_key[#{:d}]'.format(i) + LOGGER.info('[DeleteConfig] resource = {:s}'.format(str(resource))) resource_key, resource_value = resource - if not RE_IETF_L3VPN_DATA.match(resource_key): - continue + + 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) + #service_uuid = resource_value['ietf-l3vpn-svc:l3vpn-svc'][ + # 'vpn-services' + #]['vpn-service'][0]['vpn-id'] + service_uuid = resource_value['id'] + self.tac.delete_connectivity_service(service_uuid) 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) - ) - ) + except Exception as e: + MSG = 'Unhandled error processing DELETE {:s}: resource_key({:s})' + LOGGER.exception(MSG.format(str_resource_name, 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: does not support monitoring by now - return [False for _ in subscriptions] + ) -> List[Union[bool, Dict[str, Any], Exception]]: + if len(subscriptions) != 1: + raise ValueError('IETF L3VPN Driver supports only one subscription at a time') + s = subscriptions[0] + uri = s[0] + #sampling_duration = s[1] + sampling_interval = s[2] + s_data : SubscribedNotificationsSchema = { + 'ietf-subscribed-notifications:input': { + 'datastore': 'operational', + 'ietf-yang-push:datastore-xpath-filter': uri, + 'ietf-yang-push:periodic': {'ietf-yang-push:period': str(sampling_interval)}, + } + } + s_id = self._handler_subscription.subscribe(s_data) + return [s_id] + @metered_subclass_method(METRICS_POOL) def UnsubscribeState( self, subscriptions : List[Tuple[str, float, float]] - ) -> List[Union[bool, Exception]]: - # TODO: does not support monitoring by now - return [False for _ in subscriptions] + ) -> List[Union[bool, Dict[str, Any], Exception]]: + if len(subscriptions) != 1: + raise ValueError('IETF L3VPN Driver supports only one subscription at a time') + s = subscriptions[0] + identifier = s[0] + s_data : UnsubscribedNotificationsSchema = { + 'ietf-subscribed-notifications:input': { + 'id': int(identifier), + } + } + self._handler_subscription.unsubscribe(s_data) + return [True] + def GetState( self, blocking=False, terminate : Optional[threading.Event] = None ) -> Iterator[Tuple[float, str, Any]]: - # TODO: does not support monitoring by now return [] diff --git a/src/device/service/drivers/ietf_l3vpn/TfsApiClient.py b/src/device/service/drivers/ietf_l3vpn/TfsApiClient.py index f8a22d0a3d9c47ff69cd5f69f5cdd64e6f72caad..de695685cbf9b85d3e3f020df3f83d5acd0f1fdf 100644 --- a/src/device/service/drivers/ietf_l3vpn/TfsApiClient.py +++ b/src/device/service/drivers/ietf_l3vpn/TfsApiClient.py @@ -12,15 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, requests +import json, logging, requests from typing import Dict, List, Optional -from common.tools.client.RestClient import RestClient +from common.tools.rest_api.client.RestApiClient import RestApiClient from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum + GET_CONTEXT_IDS_URL = '/tfs-api/context_ids' GET_DEVICES_URL = '/tfs-api/devices' GET_LINKS_URL = '/tfs-api/links' -L3VPN_URL = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services' + + +IETF_L3VPN_ALL_URL = '/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services' +IETF_L3VPN_ONE_URL = IETF_L3VPN_ALL_URL + '/vpn-service={:s}' + MAPPING_STATUS = { 'DEVICEOPERATIONALSTATUS_UNDEFINED': 0, @@ -50,9 +55,11 @@ MAPPING_DRIVER = { 'DEVICEDRIVER_RYU' : 18, } + LOGGER = logging.getLogger(__name__) -class TfsApiClient(RestClient): + +class TfsApiClient(RestApiClient): def __init__( self, address : str, port : int, scheme : str = 'http', username : Optional[str] = None, password : Optional[str] = None, @@ -63,9 +70,26 @@ class TfsApiClient(RestClient): timeout=timeout, verify_certs=False, allow_redirects=True, logger=LOGGER ) - def check_credentials(self) -> None: - self.get(GET_CONTEXT_IDS_URL, expected_status_codes={requests.codes['OK']}) - LOGGER.info('Credentials checked') + + def check_credentials(self, raise_if_fail : bool = True) -> None: + try: + LOGGER.info('Checking credentials...') + self.get(GET_CONTEXT_IDS_URL, expected_status_codes={requests.codes['OK']}) + LOGGER.info('Credentials checked') + return True + except requests.exceptions.Timeout as e: + MSG = 'Timeout connecting {:s}' + msg = MSG.format(GET_CONTEXT_IDS_URL) + LOGGER.exception(msg) + if raise_if_fail: raise Exception(msg) from e + return False + except Exception as e: + MSG = 'Exception connecting credentials: {:s}' + msg = MSG.format(GET_CONTEXT_IDS_URL) + LOGGER.exception(msg) + if raise_if_fail: raise Exception(msg) from e + return False + def get_devices_endpoints( self, import_topology : ImportTopologyEnum = ImportTopologyEnum.DEVICES @@ -86,6 +110,10 @@ class TfsApiClient(RestClient): device_type : str = json_device['device_type'] #if not device_type.startswith('emu-'): device_type = 'emu-' + device_type device_status = json_device['device_operational_status'] + + ctrl_id : Dict[str, Dict] = json_device.get('controller_id', dict()) + ctrl_uuid : Optional[str] = ctrl_id.get('device_uuid', dict()).get('uuid') + device_url = '/devices/device[{:s}]'.format(device_uuid) device_data = { 'uuid': json_device['device_id']['device_uuid']['uuid'], @@ -97,17 +125,38 @@ class TfsApiClient(RestClient): for driver in json_device['device_drivers'] ], } + if ctrl_uuid is not None and len(ctrl_uuid) > 0: + device_data['ctrl_uuid'] = ctrl_uuid result.append((device_url, device_data)) + config_rule_list : List[Dict] = ( + json_device + .get('device_config', dict()) + .get('config_rules', list()) + ) + config_rule_dict = dict() + for cr in config_rule_list: + if cr['action'] != 'CONFIGACTION_SET': continue + if 'custom' not in cr: continue + cr_rk : str = cr['custom']['resource_key'] + if not cr_rk.startswith('/endpoints/endpoint['): continue + settings = json.loads(cr['custom']['resource_value']) + ep_name = settings['name'] + config_rule_dict[ep_name] = settings + for json_endpoint in json_device['device_endpoints']: endpoint_uuid = json_endpoint['endpoint_id']['endpoint_uuid']['uuid'] + endpoint_name = json_endpoint['name'] endpoint_url = '/endpoints/endpoint[{:s}]'.format(endpoint_uuid) endpoint_data = { 'device_uuid': device_uuid, 'uuid': endpoint_uuid, - 'name': json_endpoint['name'], + 'name': endpoint_name, 'type': json_endpoint['endpoint_type'], } + endpoint_settings = config_rule_dict.get(endpoint_name) + if endpoint_settings is not None: + endpoint_data['settings'] = endpoint_settings result.append((endpoint_url, endpoint_data)) if import_topology == ImportTopologyEnum.DEVICES: @@ -136,31 +185,41 @@ class TfsApiClient(RestClient): LOGGER.debug('[get_devices_endpoints] topology; returning') return result - def create_connectivity_service(self, l3vpn_data : dict) -> None: - MSG = '[create_connectivity_service] l3vpn_data={:s}' - LOGGER.debug(MSG.format(str(l3vpn_data))) + + def create_connectivity_service(self, data : Dict) -> None: + MSG = '[create_connectivity_service] data={:s}' + LOGGER.debug(MSG.format(str(data))) try: - self.post(L3VPN_URL, body=l3vpn_data) + MSG = '[create_connectivity_service] POST {:s}: {:s}' + LOGGER.info(MSG.format(str(IETF_L3VPN_ALL_URL), str(data))) + self.post(IETF_L3VPN_ALL_URL, body=data) except requests.exceptions.ConnectionError as e: - MSG = 'Failed to send POST request to TFS L3VPN NBI' + MSG = 'Failed to send POST request to TFS IETF L3VPN NBI' raise Exception(MSG) from e - def update_connectivity_service(self, l3vpn_data : dict) -> None: - MSG = '[update_connectivity_service] l3vpn_data={:s}' - LOGGER.debug(MSG.format(str(l3vpn_data))) - vpn_id = l3vpn_data['ietf-l3vpn-svc:l3vpn-svc']['vpn-services']['vpn-service'][0]['vpn-id'] + + def update_connectivity_service(self, data : Dict) -> None: + MSG = '[update_connectivity_service] data={:s}' + LOGGER.debug(MSG.format(str(data))) + vpn_id = data['ietf-l3vpn-svc:l3vpn-svc']['vpn-services']['vpn-service'][0]['vpn-id'] + url = IETF_L3VPN_ONE_URL.format(vpn_id) try: - self.put(L3VPN_URL + f'/vpn-service={vpn_id}', body=l3vpn_data) + MSG = '[update_connectivity_service] PUT {:s}: {:s}' + LOGGER.info(MSG.format(str(url), str(data))) + self.put(url, body=data) except requests.exceptions.ConnectionError as e: - MSG = 'Failed to send PUT request to TFS L3VPN NBI' + MSG = 'Failed to send PUT request to TFS IETF L3VPN NBI' raise Exception(MSG) from e + def delete_connectivity_service(self, service_uuid : str) -> None: - url = L3VPN_URL + f'/vpn-service={service_uuid}' - MSG = '[delete_connectivity_service] url={:s}' - LOGGER.debug(MSG.format(str(url))) + MSG = '[delete_connectivity_service] service_uuid={:s}' + LOGGER.debug(MSG.format(str(service_uuid))) + url = IETF_L3VPN_ONE_URL.format(service_uuid) try: + MSG = '[delete_connectivity_service] DELETE {:s}' + LOGGER.info(MSG.format(str(url))) self.delete(url) except requests.exceptions.ConnectionError as e: - MSG = 'Failed to send DELETE request to TFS L3VPN NBI' + MSG = 'Failed to send DELETE request to TFS IETF L3VPN NBI' raise Exception(MSG) from e diff --git a/src/device/service/drivers/ietf_l3vpn/handlers/SubscriptionHandler.py b/src/device/service/drivers/ietf_l3vpn/handlers/SubscriptionHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..0b7ffdfe1449df0c0a522bc267ced0eb9aec5895 --- /dev/null +++ b/src/device/service/drivers/ietf_l3vpn/handlers/SubscriptionHandler.py @@ -0,0 +1,82 @@ +# 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 logging, requests +from typing_extensions import TypedDict +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +LOGGER = logging.getLogger(__name__) + + +Periodic = TypedDict('Periodic', {'ietf-yang-push:period': str}) + +Input = TypedDict( + 'Input', + { + 'datastore': str, + 'ietf-yang-push:datastore-xpath-filter': str, + 'ietf-yang-push:periodic': Periodic, + }, +) + +SubscribedNotificationsSchema = TypedDict( + 'SubscribedNotificationsSchema', {'ietf-subscribed-notifications:input': Input} +) + +SubscriptionSchema = TypedDict('SubscriptionSchema', {'id': str}) + +UnsubscribedNotificationsSchema = TypedDict( + 'UnsubscribedNotificationsSchema', {'ietf-subscribed-notifications:input': SubscriptionSchema} +) + + +class SubscriptionId(TypedDict): + identifier: str + uri: str + + +class SubscriptionHandler: + def __init__(self, rest_conf_client : RestConfClient) -> None: + self._rest_conf_client = rest_conf_client + + def subscribe( + self, subscription_data : SubscribedNotificationsSchema + ) -> SubscriptionId: + MSG = '[subscribe] subscription_data={:s}' + LOGGER.debug(MSG.format(str(subscription_data))) + try: + url = '/subscriptions:establish-subscription' + LOGGER.debug('Subscribing to telemetry: {:s}'.format(str(subscription_data))) + reply = self._rest_conf_client.rpc(url, body=subscription_data) + LOGGER.debug('Subscription reply: {:s}'.format(str(reply))) + return reply + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send RPC request' + raise Exception(MSG) from e + + def unsubscribe( + self, unsubscription_data : UnsubscribedNotificationsSchema + ) -> SubscriptionId: + MSG = '[unsubscribe] unsubscription_data={:s}' + LOGGER.debug(MSG.format(str(unsubscription_data))) + try: + url = '/subscriptions:delete-subscription' + LOGGER.debug('Unsubscribing from telemetry: {:s}'.format(str(unsubscription_data))) + reply = self._rest_conf_client.rpc(url, body=unsubscription_data) + LOGGER.debug('Unsubscription reply: {:s}'.format(str(reply))) + return reply + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send RPC request' + raise Exception(MSG) from e diff --git a/src/device/service/drivers/ietf_l3vpn/handlers/__init__.py b/src/device/service/drivers/ietf_l3vpn/handlers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/device/service/drivers/ietf_l3vpn/handlers/__init__.py @@ -0,0 +1,13 @@ +# 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/device/service/drivers/ietf_slice/IetfSliceDriver.py b/src/device/service/drivers/ietf_slice/IetfSliceDriver.py new file mode 100644 index 0000000000000000000000000000000000000000..269ad3f0c9f9ac700ec218cd62a7609773afbacc --- /dev/null +++ b/src/device/service/drivers/ietf_slice/IetfSliceDriver.py @@ -0,0 +1,240 @@ +# 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 copy, json, logging, re, threading +from typing import Any, Dict, Iterator, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from common.type_checkers.Checkers import chk_string, chk_type +from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES +from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum, get_import_topology +from .handlers.SubscriptionHandler import ( + SubscribedNotificationsSchema, SubscriptionHandler, UnsubscribedNotificationsSchema +) +from .TfsApiClient import TfsApiClient + + +LOGGER = logging.getLogger(__name__) + + +ALL_RESOURCE_KEYS = [ + RESOURCE_ENDPOINTS, + RESOURCE_SERVICES, +] + + +RE_IETF_SLICE_DATA = re.compile(r'^\/service\[([^\]]+)\]\/IETFSlice$') + + +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() + + username = self.settings.get('username') + password = self.settings.get('password') + scheme = self.settings.get('scheme', 'http') + timeout = int(self.settings.get('timeout', 60)) + self.tac = TfsApiClient( + self.address, self.port, scheme=scheme, username=username, + password=password, timeout=timeout + ) + + restconf_settings = copy.deepcopy(settings) + restconf_settings.pop('base_url', None) + restconf_settings.pop('import_topology', None) + restconf_settings['logger'] = logging.getLogger(__name__ + '.RestConfClient') + self._rest_conf_client = RestConfClient(address, port=port, **restconf_settings) + self._handler_subscription = SubscriptionHandler(self._rest_conf_client) + + # Options are: + # disabled --> just import endpoints as usual + # devices --> imports sub-devices but not links connecting them. + # (a remotely-controlled transport domain might exist between them) + # topology --> imports sub-devices and links connecting them. + # (not supported by XR driver) + self.__import_topology = get_import_topology(self.settings, default=ImportTopologyEnum.DEVICES) + + + def Connect(self) -> bool: + with self.__lock: + if self.__started.is_set(): return True + checked = self.tac.check_credentials(raise_if_fail=False) + if checked: self.__started.set() + return checked + + + def Disconnect(self) -> bool: + with self.__lock: + self.__terminate.set() + return True + + + @metered_subclass_method(METRICS_POOL) + def GetInitialConfig(self) -> List[Tuple[str, Any]]: + with self.__lock: + return [] + + + @metered_subclass_method(METRICS_POOL) + def GetConfig( + self, resource_keys : List[str] = [] + ) -> List[Tuple[str, Union[Any, None, Exception]]]: + chk_type('resources', resource_keys, list) + results = [] + with self.__lock: + self.tac.check_credentials() + if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS + + for i, resource_key in enumerate(resource_keys): + str_resource_name = 'resource_key[#{:d}]'.format(i) + try: + chk_string(str_resource_name, resource_key, allow_empty=False) + if resource_key == RESOURCE_ENDPOINTS: + # return endpoints through TFS NBI API and list-devices method + results.extend(self.tac.get_devices_endpoints(self.__import_topology)) + elif resource_key == RESOURCE_SERVICES: + slices_data = self.tac.list_slices() + slices_list = ( + slices_data + .get('network-slice-services', dict()) + .get('slice-service', list()) + ) + for slice_data in slices_list: + slice_name = slice_data['id'] + slice_resource_key = '/service[{:s}]/IETFSlice'.format(str(slice_name)) + results.append((slice_resource_key, slice_data)) + else: + match_slice_data = RE_IETF_SLICE_DATA.match(resource_key) + if match_slice_data is not None: + slice_name = match_slice_data.groups()[0] + slices_data = self.tac.retrieve_slice(slice_name) + slices_list = ( + slices_data + .get('network-slice-services', dict()) + .get('slice-service', list()) + ) + for slice_data in slices_list: + slice_name = slice_data['id'] + slice_resource_key = '/service[{:s}]/IETFSlice'.format(str(slice_name)) + results.append((slice_resource_key, slice_data)) + else: + results.append((resource_key, None)) + except Exception as e: + MSG = 'Unhandled error processing {:s}: resource_key({:s})' + LOGGER.exception(MSG.format(str_resource_name, str(resource_key))) + results.append((resource_key, e)) + 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 i, resource in enumerate(resources): + str_resource_name = 'resource_key[#{:d}]'.format(i) + LOGGER.info('[SetConfig] 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) + self.tac.create_slice(resource_value) + results.append((resource_key, True)) + except Exception as e: + MSG = 'Unhandled error processing SET {:s}: resource_key({:s})' + LOGGER.exception(MSG.format(str_resource_name, 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 i, resource in enumerate(resources): + str_resource_name = 'resource_key[#{:d}]'.format(i) + LOGGER.info('[DeleteConfig] 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'] + self.tac.delete_slice(slice_name) + results.append((resource_key, True)) + except Exception as e: + MSG = 'Unhandled error processing DELETE {:s}: resource_key({:s})' + LOGGER.exception(MSG.format(str_resource_name, 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, Dict[str, Any], Exception]]: + if len(subscriptions) != 1: + raise ValueError('IETF Slice Driver supports only one subscription at a time') + s = subscriptions[0] + uri = s[0] + #sampling_duration = s[1] + sampling_interval = s[2] + s_data : SubscribedNotificationsSchema = { + 'ietf-subscribed-notifications:input': { + 'datastore': 'operational', + 'ietf-yang-push:datastore-xpath-filter': uri, + 'ietf-yang-push:periodic': {'ietf-yang-push:period': str(sampling_interval)}, + } + } + s_id = self._handler_subscription.subscribe(s_data) + return [s_id] + + + @metered_subclass_method(METRICS_POOL) + def UnsubscribeState( + self, subscriptions : List[Tuple[str, float, float]] + ) -> List[Union[bool, Dict[str, Any], Exception]]: + if len(subscriptions) != 1: + raise ValueError('IETF Slice Driver supports only one subscription at a time') + s = subscriptions[0] + identifier = s[0] + s_data : UnsubscribedNotificationsSchema = { + 'ietf-subscribed-notifications:input': { + 'id': int(identifier), + } + } + self._handler_subscription.unsubscribe(s_data) + return [True] + + + def GetState( + self, blocking=False, terminate : Optional[threading.Event] = None + ) -> Iterator[Tuple[float, str, Any]]: + return [] diff --git a/src/device/service/drivers/ietf_slice/TfsApiClient.py b/src/device/service/drivers/ietf_slice/TfsApiClient.py new file mode 100644 index 0000000000000000000000000000000000000000..08c9b78e06a6797bde1be175ba1dd9b2eeebc0a6 --- /dev/null +++ b/src/device/service/drivers/ietf_slice/TfsApiClient.py @@ -0,0 +1,251 @@ +# 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, requests +from typing import Dict, List, Optional +from common.tools.rest_api.client.RestApiClient import RestApiClient +from device.service.driver_api.ImportTopologyEnum import ImportTopologyEnum + + +GET_CONTEXT_IDS_URL = '/tfs-api/context_ids' +GET_DEVICES_URL = '/tfs-api/devices' +GET_LINKS_URL = '/tfs-api/links' + + +IETF_SLICE_ALL_URL = '/restconf/data/ietf-network-slice-service:network-slice-services' +IETF_SLICE_ONE_URL = IETF_SLICE_ALL_URL + '/slice-service={:s}' +IETF_SLICE_CG_URL = IETF_SLICE_ONE_URL + '/connection-groups/connection-group={:s}' + + +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, + 'DEVICEDRIVER_QKD' : 12, + 'DEVICEDRIVER_IETF_L3VPN' : 13, + 'DEVICEDRIVER_IETF_SLICE' : 14, + 'DEVICEDRIVER_NCE' : 15, + 'DEVICEDRIVER_SMARTNIC' : 16, + 'DEVICEDRIVER_MORPHEUS' : 17, + 'DEVICEDRIVER_RYU' : 18, +} + + +LOGGER = logging.getLogger(__name__) + + +class TfsApiClient(RestApiClient): + def __init__( + self, address : str, port : int, scheme : str = 'http', + username : Optional[str] = None, password : Optional[str] = None, + timeout : Optional[int] = 30 + ) -> None: + super().__init__( + address, port, scheme=scheme, username=username, password=password, + timeout=timeout, verify_certs=False, allow_redirects=True, logger=LOGGER + ) + + + def check_credentials(self, raise_if_fail : bool = True) -> None: + try: + LOGGER.info('Checking credentials...') + self.get(GET_CONTEXT_IDS_URL, expected_status_codes={requests.codes['OK']}) + LOGGER.info('Credentials checked') + return True + except requests.exceptions.Timeout as e: + MSG = 'Timeout connecting {:s}' + msg = MSG.format(GET_CONTEXT_IDS_URL) + LOGGER.exception(msg) + if raise_if_fail: raise Exception(msg) from e + return False + except Exception as e: + MSG = 'Exception connecting credentials: {:s}' + msg = MSG.format(GET_CONTEXT_IDS_URL) + LOGGER.exception(msg) + if raise_if_fail: raise Exception(msg) from e + return False + + + def get_devices_endpoints( + self, import_topology : ImportTopologyEnum = ImportTopologyEnum.DEVICES + ) -> List[Dict]: + LOGGER.debug('[get_devices_endpoints] begin') + MSG = '[get_devices_endpoints] import_topology={:s}' + LOGGER.debug(MSG.format(str(import_topology))) + + if import_topology == ImportTopologyEnum.DISABLED: + MSG = 'Unsupported import_topology mode: {:s}' + raise Exception(MSG.format(str(import_topology))) + + devices = self.get(GET_DEVICES_URL, expected_status_codes={requests.codes['OK']}) + + result = list() + for json_device in devices['devices']: + device_uuid : str = json_device['device_id']['device_uuid']['uuid'] + device_type : str = json_device['device_type'] + #if not device_type.startswith('emu-'): device_type = 'emu-' + device_type + device_status = json_device['device_operational_status'] + + ctrl_id : Dict[str, Dict] = json_device.get('controller_id', dict()) + ctrl_uuid : Optional[str] = ctrl_id.get('device_uuid', dict()).get('uuid') + + 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'] + ], + } + if ctrl_uuid is not None and len(ctrl_uuid) > 0: + device_data['ctrl_uuid'] = ctrl_uuid + result.append((device_url, device_data)) + + config_rule_list : List[Dict] = ( + json_device + .get('device_config', dict()) + .get('config_rules', list()) + ) + config_rule_dict = dict() + for cr in config_rule_list: + if cr['action'] != 'CONFIGACTION_SET': continue + if 'custom' not in cr: continue + cr_rk : str = cr['custom']['resource_key'] + if not cr_rk.startswith('/endpoints/endpoint['): continue + settings = json.loads(cr['custom']['resource_value']) + ep_name = settings['name'] + config_rule_dict[ep_name] = settings + + for json_endpoint in json_device['device_endpoints']: + endpoint_uuid = json_endpoint['endpoint_id']['endpoint_uuid']['uuid'] + endpoint_name = json_endpoint['name'] + endpoint_url = '/endpoints/endpoint[{:s}]'.format(endpoint_uuid) + endpoint_data = { + 'device_uuid': device_uuid, + 'uuid': endpoint_uuid, + 'name': endpoint_name, + 'type': json_endpoint['endpoint_type'], + } + endpoint_settings = config_rule_dict.get(endpoint_name) + if endpoint_settings is not None: + endpoint_data['settings'] = endpoint_settings + result.append((endpoint_url, endpoint_data)) + + if import_topology == ImportTopologyEnum.DEVICES: + LOGGER.debug('[get_devices_endpoints] devices only; returning') + return result + + links = self.get(GET_LINKS_URL, expected_status_codes={requests.codes['OK']}) + + for json_link in links['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_slice(self, data : Dict) -> None: + MSG = '[create_slice] data={:s}' + LOGGER.debug(MSG.format(str(data))) + try: + MSG = '[create_slice] POST {:s}: {:s}' + LOGGER.info(MSG.format(str(IETF_SLICE_ALL_URL), str(data))) + self.post(IETF_SLICE_ALL_URL, body=data) + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send POST request to TFS IETF Slice NBI' + raise Exception(MSG) from e + + + def list_slices(self) -> Dict: + try: + MSG = '[list_slices] GET {:s}' + LOGGER.info(MSG.format(str(IETF_SLICE_ALL_URL))) + return self.get(IETF_SLICE_ALL_URL) + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send GET request to TFS IETF Slice NBI' + raise Exception(MSG) from e + + + def retrieve_slice(self, slice_name : str) -> Dict: + MSG = '[retrieve_slice] slice_name={:s}' + LOGGER.debug(MSG.format(str(slice_name))) + url = IETF_SLICE_ONE_URL.format(slice_name) + try: + MSG = '[retrieve_slice] GET {:s}' + LOGGER.info(MSG.format(str(url))) + return self.get(url) + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send GET request to TFS IETF Slice NBI' + raise Exception(MSG) from e + + + def update_slice( + self, slice_name : str, connection_group_id : str, + updated_connection_group_data : Dict + ) -> None: + MSG = '[update_slice] slice_name={:s} connection_group_id={:s} updated_connection_group_data={:s}' + LOGGER.debug(MSG.format(str(slice_name), str(connection_group_id), str(updated_connection_group_data))) + url = IETF_SLICE_CG_URL.format(slice_name, connection_group_id) + try: + MSG = '[update_slice] PUT {:s}: {:s}' + LOGGER.info(MSG.format(str(url), str(updated_connection_group_data))) + self.put(url, body=updated_connection_group_data) + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send PUT request to TFS IETF Slice NBI' + raise Exception(MSG) from e + + + def delete_slice(self, slice_name : str) -> None: + MSG = '[delete_slice] slice_name={:s}' + LOGGER.debug(MSG.format(str(slice_name))) + url = IETF_SLICE_ONE_URL.format(slice_name) + try: + MSG = '[delete_slice] DELETE {:s}' + LOGGER.info(MSG.format(str(url))) + self.delete(url) + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send DELETE request to TFS IETF Slice NBI' + raise Exception(MSG) from e diff --git a/src/device/service/drivers/ietf_slice/handlers/SubscriptionHandler.py b/src/device/service/drivers/ietf_slice/handlers/SubscriptionHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..0b7ffdfe1449df0c0a522bc267ced0eb9aec5895 --- /dev/null +++ b/src/device/service/drivers/ietf_slice/handlers/SubscriptionHandler.py @@ -0,0 +1,82 @@ +# 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 logging, requests +from typing_extensions import TypedDict +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +LOGGER = logging.getLogger(__name__) + + +Periodic = TypedDict('Periodic', {'ietf-yang-push:period': str}) + +Input = TypedDict( + 'Input', + { + 'datastore': str, + 'ietf-yang-push:datastore-xpath-filter': str, + 'ietf-yang-push:periodic': Periodic, + }, +) + +SubscribedNotificationsSchema = TypedDict( + 'SubscribedNotificationsSchema', {'ietf-subscribed-notifications:input': Input} +) + +SubscriptionSchema = TypedDict('SubscriptionSchema', {'id': str}) + +UnsubscribedNotificationsSchema = TypedDict( + 'UnsubscribedNotificationsSchema', {'ietf-subscribed-notifications:input': SubscriptionSchema} +) + + +class SubscriptionId(TypedDict): + identifier: str + uri: str + + +class SubscriptionHandler: + def __init__(self, rest_conf_client : RestConfClient) -> None: + self._rest_conf_client = rest_conf_client + + def subscribe( + self, subscription_data : SubscribedNotificationsSchema + ) -> SubscriptionId: + MSG = '[subscribe] subscription_data={:s}' + LOGGER.debug(MSG.format(str(subscription_data))) + try: + url = '/subscriptions:establish-subscription' + LOGGER.debug('Subscribing to telemetry: {:s}'.format(str(subscription_data))) + reply = self._rest_conf_client.rpc(url, body=subscription_data) + LOGGER.debug('Subscription reply: {:s}'.format(str(reply))) + return reply + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send RPC request' + raise Exception(MSG) from e + + def unsubscribe( + self, unsubscription_data : UnsubscribedNotificationsSchema + ) -> SubscriptionId: + MSG = '[unsubscribe] unsubscription_data={:s}' + LOGGER.debug(MSG.format(str(unsubscription_data))) + try: + url = '/subscriptions:delete-subscription' + LOGGER.debug('Unsubscribing from telemetry: {:s}'.format(str(unsubscription_data))) + reply = self._rest_conf_client.rpc(url, body=unsubscription_data) + LOGGER.debug('Unsubscription reply: {:s}'.format(str(reply))) + return reply + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send RPC request' + raise Exception(MSG) from e diff --git a/src/device/service/drivers/ietf_slice/handlers/__init__.py b/src/device/service/drivers/ietf_slice/handlers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/device/service/drivers/ietf_slice/handlers/__init__.py @@ -0,0 +1,13 @@ +# 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/device/service/drivers/ietf_slice/driver.py b/src/device/service/drivers/ietf_slice/old/IetfSliceDriver.py similarity index 51% rename from src/device/service/drivers/ietf_slice/driver.py rename to src/device/service/drivers/ietf_slice/old/IetfSliceDriver.py index a657dc1e034b976615de5be9e5acff66d93251ba..dce1a6d266195e463997a19a9d210b040a2b5b15 100644 --- a/src/device/service/drivers/ietf_slice/driver.py +++ b/src/device/service/drivers/ietf_slice/old/IetfSliceDriver.py @@ -12,38 +12,19 @@ # 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 +import anytree, json, logging, re, threading +from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_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 device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES +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 .TfsApiClient import TfsApiClient from .Tools import compose_resource_endpoint + LOGGER = logging.getLogger(__name__) @@ -52,43 +33,38 @@ ALL_RESOURCE_KEYS = [ 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}) +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: + 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.__running = TreeNode('.') + username = self.settings.get('username') + password = self.settings.get('password') + scheme = self.settings.get('scheme', 'http') + timeout = int(self.settings.get('timeout', 60)) 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 + self.address, self.port, scheme=scheme, username=username, + password=password, timeout=timeout ) + + # Options are: + # disabled --> just import endpoints as usual + # devices --> imports sub-devices but not links connecting them. + # (a remotely-controlled transport domain might exist between them) + # topology --> imports sub-devices and links connecting them. + # (not supported by XR driver) + self.__import_topology = get_import_topology(self.settings, default=ImportTopologyEnum.DEVICES) + endpoints = self.settings.get("endpoints", []) endpoint_resources = [] for endpoint in endpoints: @@ -115,7 +91,7 @@ class IetfSliceDriver(_Driver): 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 + except Exception as e: LOGGER.exception( "Exception validating {:s}: {:s}".format( str_resource_name, str(resource_key) @@ -137,22 +113,11 @@ class IetfSliceDriver(_Driver): 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 + if self.__started.is_set(): return True + checked = self.tac.check_credentials(raise_if_fail=False) + if checked: self.__started.set() + return checked def Disconnect(self) -> bool: with self.__lock: @@ -166,141 +131,138 @@ class IetfSliceDriver(_Driver): @metered_subclass_method(METRICS_POOL) def GetConfig( - self, resource_keys: List[str] = [] + self, resource_keys : List[str] = [] ) -> List[Tuple[str, Union[Any, None, Exception]]]: - chk_type("resources", resource_keys, list) + chk_type('resources', resource_keys, list) + results = [] with self.__lock: + self.tac.check_credentials() if len(resource_keys) == 0: return dump_subtree(self.__running) - results = [] - resolver = anytree.Resolver(pathattr="name") + resolver = anytree.Resolver(pathattr='name') for i, resource_key in enumerate(resource_keys): - str_resource_name = "resource_key[#{:d}]".format(i) + 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) + if resource_key == RESOURCE_ENDPOINTS: + # return endpoints through TFS NBI API and list-devices method + results.extend(self.tac.get_devices_endpoints(self.__import_topology)) + else: + resource_key = SPECIAL_RESOURCE_MAPPINGS.get( + resource_key, 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 + resource_path = resource_key.split('/') + 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)) + except Exception as e: + MSG = 'Unhandled error processing {:s}: resource_key({:s})' + LOGGER.exception(MSG.format(str_resource_name, str(resource_key))) + results.append((resource_key, e)) + return results @metered_subclass_method(METRICS_POOL) def SetConfig( - self, resources: List[Tuple[str, Any]] + self, resources : List[Tuple[str, Any]] ) -> List[Union[bool, Exception]]: results = [] - - if len(resources) == 0: - return results - + if len(resources) == 0: return results with self.__lock: for resource in resources: resource_key, resource_value = resource if RE_IETF_SLICE_OPERATION.match(resource_key): - operation_type = json.loads(resource_value)["type"] + 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))) + raise Exception('operation type not found in resources') + + for i, resource in enumerate(resources): + str_resource_name = 'resource_key[#{:d}]'.format(i) + 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"] + slice_data = resource_value['network-slice-services'][ + 'slice-service' + ][0] + slice_name = slice_data['id'] - if operation_type == "create": + 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"] - + elif operation_type == 'update': + connection_groups = slice_data['connection-groups']['connection-group'] if len(connection_groups) != 1: - raise Exception("only one connection group is supported") - + MSG = 'Exactly one ConnectionGroup({:s}) is supported' + raise Exception(MSG.format(str(connection_groups))) connection_group = connection_groups[0] - self.tac.update_slice( - slice_name, connection_group["id"], connection_group + slice_name, connection_group['id'], connection_group ) - - elif operation_type == "delete": + elif operation_type == 'delete': self.tac.delete_slice(slice_name) + else: + MSG = 'OperationType({:s}) not supported' + raise Exception(MSG.format(str(operation_type))) 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) - ) - ) + except Exception as e: + MSG = 'Unhandled error processing {:s}: resource_key({:s})' + LOGGER.exception(MSG.format(str_resource_name, str(resource_key))) results.append((resource_key, e)) return results @metered_subclass_method(METRICS_POOL) def DeleteConfig( - self, resources: List[Tuple[str, Any]] + 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))) + for i, resource in enumerate(resources): + str_resource_name = 'resource_key[#{:d}]'.format(i) + 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'] + 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) - ) - ) + except Exception as e: + MSG = 'Unhandled error processing {:s}: resource_key({:s})' + LOGGER.exception(MSG.format(str_resource_name, str(resource_key))) results.append((resource_key, e)) return results @metered_subclass_method(METRICS_POOL) def SubscribeState( - self, subscriptions: List[Tuple[str, float, float]] + self, subscriptions : List[Tuple[str, float, float]] ) -> List[Union[bool, Exception]]: - # TODO: IETF Slice does not support monitoring by now + # TODO: 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]] + self, subscriptions : List[Tuple[str, float, float]] ) -> List[Union[bool, Exception]]: - # TODO: IETF Slice does not support monitoring by now + # TODO: does not support monitoring by now return [False for _ in subscriptions] def GetState( - self, blocking=False, terminate: Optional[threading.Event] = None + self, blocking=False, terminate : Optional[threading.Event] = None ) -> Iterator[Tuple[float, str, Any]]: - # TODO: IETF Slice does not support monitoring by now + # TODO: does not support monitoring by now return [] diff --git a/src/device/service/drivers/ietf_slice/Tools.py b/src/device/service/drivers/ietf_slice/old/Tools.py similarity index 99% rename from src/device/service/drivers/ietf_slice/Tools.py rename to src/device/service/drivers/ietf_slice/old/Tools.py index 9aed37fa421389177f11fc30fe78c547415f648b..270569b2b4824016bdb9b3d4346b741b3d22c5c5 100644 --- a/src/device/service/drivers/ietf_slice/Tools.py +++ b/src/device/service/drivers/ietf_slice/old/Tools.py @@ -20,7 +20,7 @@ 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 +from ..Constants import SPECIAL_RESOURCE_MAPPINGS LOGGER = logging.getLogger(__name__) 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 deleted file mode 100644 index d8869073e0b4fb13b64a8ea3b524ed30639c3187..0000000000000000000000000000000000000000 --- a/src/device/service/drivers/ietf_slice/tfs_slice_nbi_client.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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 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/NCEDriver.py b/src/device/service/drivers/nce/NCEDriver.py new file mode 100644 index 0000000000000000000000000000000000000000..4e40ab7afa802281d9be49189da05daebf1310dd --- /dev/null +++ b/src/device/service/drivers/nce/NCEDriver.py @@ -0,0 +1,267 @@ +# 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 anytree, copy, json, logging, re, requests, threading +from typing import Any, Iterator, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from common.type_checkers.Checkers import chk_length, chk_string, chk_type +from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES +from device.service.driver_api.AnyTreeTools import ( + TreeNode, dump_subtree, get_subnode, set_subnode_value, +) +from .handlers.AppFlowHandler import AppFlowHandler +from .handlers.NetworkTopologyHandler import NetworkTopologyHandler +from .handlers.SubscriptionHandler import ( + SubscribedNotificationsSchema, SubscriptionHandler, UnsubscribedNotificationsSchema +) +from .Constants import SPECIAL_RESOURCE_MAPPINGS +from .Tools import compose_resource_endpoint + + +ALL_RESOURCE_KEYS = [ + RESOURCE_ENDPOINTS, + RESOURCE_SERVICES, +] + + +LOGGER = logging.getLogger(__name__) + + +RE_NCE_APP_FLOW_DATA = re.compile(r'^\/service\[([^\]]+)\]\/AppFlow$') + + +DRIVER_NAME = 'nce' +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME}) + + +class NCEDriver(_Driver): + def __init__(self, address: str, port: int, **settings) -> None: + super().__init__(DRIVER_NAME, address, port, **settings) + self.__lock = threading.Lock() + self.__started = threading.Event() + self.__terminate = threading.Event() + + restconf_settings = copy.deepcopy(settings) + restconf_settings.pop('base_url', None) + restconf_settings.pop('import_topology', None) + restconf_settings['logger'] = logging.getLogger(__name__ + '.RestConfClient') + self._rest_conf_client = RestConfClient(address, port=port, **restconf_settings) + self._handler_net_topology = NetworkTopologyHandler(self._rest_conf_client, **settings) + self._handler_app_flow = AppFlowHandler(self._rest_conf_client) + self._handler_subscription = SubscriptionHandler(self._rest_conf_client) + + self.__running = TreeNode('.') + + 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: + 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: + self._rest_conf_client._discover_base_url() + except requests.exceptions.Timeout: + LOGGER.exception('Timeout exception checking connectivity') + return False + except Exception: + LOGGER.exception('Unhandled exception checking connectivity') + return False + else: + self.__started.set() + return True + + + def Disconnect(self) -> bool: + with self.__lock: + self.__terminate.set() + return True + + + @metered_subclass_method(METRICS_POOL) + def GetInitialConfig(self) -> List[Tuple[str, Any]]: + with self.__lock: + return [] + + + @metered_subclass_method(METRICS_POOL) + def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: + chk_type('resources', resource_keys, list) + results = list() + with self.__lock: + if len(resource_keys) == 0: + return dump_subtree(self.__running) + + 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) + if resource_key == RESOURCE_ENDPOINTS: + results.extend(self._handler_net_topology.get()) + elif resource_key == RESOURCE_SERVICES: + app_flows = self._handler_app_flow.retrieve() + app_flow_names = [ + app_flow['name'] + for app_flow in app_flows['huawei-nce-app-flow:app-flows']['app-flow'] + ] + if len(app_flow_names) == 1: + resource_key = '/service[{:s}]/AppFlow'.format(app_flow_names[0]) + results.append((resource_key, app_flows)) + elif len(app_flow_names) > 1: + raise Exception('Support for multiple app-flow retrieval not properly managed') + else: + resource_key = SPECIAL_RESOURCE_MAPPINGS.get(resource_key, resource_key) + resource_path = resource_key.split('/') + 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)) + except Exception as e: + MSG = 'Error processing resource_key({:s}, {:s})' + LOGGER.exception(MSG.format(str_resource_name, str(resource_key))) + results.append((resource_key, e)) # if processing fails, store the exception + continue + 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: + LOGGER.info('[SetConfig] 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) + self._handler_app_flow.create(resource_value) + results.append((resource_key, True)) + except Exception as e: # pylint: disable=broad-except + MSG = 'Unhandled error processing SET resource_key({:s})' + LOGGER.exception(MSG.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]]: + LOGGER.debug('[DeleteConfig] resources={:s}'.format(str(resources))) + + results = [] + if len(resources) == 0: return results + with self.__lock: + for resource in resources: + LOGGER.info('[DeleteConfig] 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) + self._handler_app_flow.delete(resource_value) + results.append((resource_key, True)) + except Exception as e: + MSG = 'Unhandled error processing DELETE resource_key({:s})' + LOGGER.exception(MSG.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, dict[str, Any], Exception]]: + if len(subscriptions) != 1: + raise ValueError('NCE driver supports only one subscription at a time') + s = subscriptions[0] + uri = s[0] + #sampling_duration = s[1] + sampling_interval = s[2] + s_data : SubscribedNotificationsSchema = { + 'ietf-subscribed-notifications:input': { + 'datastore': 'operational', + 'ietf-yang-push:datastore-xpath-filter': uri, + 'ietf-yang-push:periodic': {'ietf-yang-push:period': str(sampling_interval)}, + } + } + s_id = self._handler_subscription.subscribe(s_data) + return [s_id] + + + @metered_subclass_method(METRICS_POOL) + def UnsubscribeState( + self, subscriptions: List[Tuple[str, float, float]] + ) -> List[Union[bool, Exception]]: + if len(subscriptions) != 1: + raise ValueError('NCE driver supports only one subscription at a time') + s = subscriptions[0] + identifier = s[0] + s_data : UnsubscribedNotificationsSchema = { + 'ietf-subscribed-notifications:input': { + 'id': int(identifier), + } + } + self._handler_subscription.unsubscribe(s_data) + return [True] + + + def GetState( + self, blocking=False, terminate: Optional[threading.Event] = None + ) -> Iterator[Tuple[float, str, Any]]: + return [] diff --git a/src/device/service/drivers/nce/driver.py b/src/device/service/drivers/nce/driver.py deleted file mode 100644 index 2792f9e22f30698233379cd04fb0b3768112ce39..0000000000000000000000000000000000000000 --- a/src/device/service/drivers/nce/driver.py +++ /dev/null @@ -1,278 +0,0 @@ -# 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 -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/handlers/AppFlowHandler.py b/src/device/service/drivers/nce/handlers/AppFlowHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..251916fb9ad1ec283e515d2f7e89b777645a9799 --- /dev/null +++ b/src/device/service/drivers/nce/handlers/AppFlowHandler.py @@ -0,0 +1,166 @@ +# 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 logging, requests +from typing import Dict +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +LOGGER = logging.getLogger(__name__) + + +class AppFlowHandler: + def __init__(self, rest_conf_client : RestConfClient) -> None: + self._rest_conf_client = rest_conf_client + + self._url_qos_profile = '/huawei-nce-app-flow:qos-profiles' + self._url_qos_profile_item = self._url_qos_profile + '/qos-profile={:s}' + + self._url_application = '/huawei-nce-app-flow:applications' + self._url_application_item = self._url_application + '/application={:s}' + + self._url_app_flow = '/huawei-nce-app-flow:app-flows' + self._url_app_flow_item = self._url_app_flow + '/app-flow={:s}' + + + def create(self, data : Dict) -> None: + MSG = '[create] data={:s}' + LOGGER.debug(MSG.format(str(data))) + + try: + qos_profiles = ( + data + .get('huawei-nce-app-flow:app-flows', dict()) + .get('qos-profiles', dict()) + .get('qos-profile', list()) + ) + for qos_profile in qos_profiles: + request = {'huawei-nce-app-flow:qos-profiles': {'qos-profile': [qos_profile]}} + qos_profile_name = qos_profile['name'] + LOGGER.info('Creating QoS Profile: {:s}'.format(str(request))) + url = self._url_qos_profile_item.format(qos_profile_name) + self._rest_conf_client.put(url, body=request) + + applications = ( + data + .get('huawei-nce-app-flow:app-flows', dict()) + .get('applications', dict()) + .get('application', list()) + ) + for application in applications: + request = {'huawei-nce-app-flow:applications': {'application': [application]}} + application_name = application['name'] + LOGGER.info('Creating Application: {:s}'.format(str(request))) + url = self._url_application_item.format(application_name) + self._rest_conf_client.put(url, body=request) + + app_flows = ( + data + .get('huawei-nce-app-flow:app-flows', dict()) + .get('app-flow', list()) + ) + for app_flow in app_flows: + request = {'huawei-nce-app-flow:app-flows': {'app-flow': [app_flow]}} + app_flow_name = app_flow['name'] + LOGGER.info('Creating App Flow: {:s}'.format(str(request))) + url = self._url_app_flow_item.format(app_flow_name) + self._rest_conf_client.put(url, body=request) + + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send PUT requests to NCE FAN NBI' + raise Exception(MSG) from e + + + def retrieve(self) -> Dict: + try: + LOGGER.info('Retrieving QoS Profiles') + qos_profiles = self._rest_conf_client.get(self._url_qos_profile) + + LOGGER.info('Retrieving Applications') + applications = self._rest_conf_client.get(self._url_application) + + LOGGER.info('Retrieving App Flows') + app_flows = self._rest_conf_client.get(self._url_app_flow) + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send GET requests to NCE FAN NBI' + raise Exception(MSG) from e + + qos_profiles = ( + qos_profiles + .get('huawei-nce-app-flow:qos-profiles', dict()) + .get('qos-profile', list()) + ) + + applications = ( + applications + .get('huawei-nce-app-flow:applications', dict()) + .get('application', list()) + ) + + app_flows = ( + app_flows + .get('huawei-nce-app-flow:app-flows', dict()) + .get('app-flow', list()) + ) + + return {'huawei-nce-app-flow:app-flows': { + 'qos-profiles': {'qos-profile': qos_profiles}, + 'applications': {'application': applications}, + 'app-flow': app_flows, + }} + + + def delete(self, data : Dict) -> None: + MSG = '[delete] data={:s}' + LOGGER.debug(MSG.format(str(data))) + + try: + app_flows = ( + data + .get('huawei-nce-app-flow:app-flows', dict()) + .get('app-flow', list()) + ) + for app_flow in app_flows: + app_flow_name = app_flow['name'] + LOGGER.info('Deleting App Flow: {:s}'.format(str(app_flow_name))) + app_flow_url = self._url_app_flow_item.format(app_flow_name) + self._rest_conf_client.delete(app_flow_url) + + applications = ( + data + .get('huawei-nce-app-flow:app-flows', dict()) + .get('applications', dict()) + .get('application', list()) + ) + for application in applications: + application_name = application['name'] + LOGGER.info('Deleting Application: {:s}'.format(str(application_name))) + application_url = self._url_application_item.format(application_name) + self._rest_conf_client.delete(application_url) + + qos_profiles = ( + data + .get('huawei-nce-app-flow:app-flows', dict()) + .get('qos-profiles', dict()) + .get('qos-profile', list()) + ) + for qos_profile in qos_profiles: + qos_profile_name = qos_profile['name'] + LOGGER.info('Deleting QoS Profile: {:s}'.format(str(qos_profile_name))) + qos_profile_url = self._url_qos_profile_item.format(qos_profile_name) + self._rest_conf_client.delete(qos_profile_url) + + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send POST requests to NCE FAN NBI' + raise Exception(MSG) from e diff --git a/src/device/service/drivers/nce/handlers/NetworkTopologyHandler.py b/src/device/service/drivers/nce/handlers/NetworkTopologyHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..7ca1b73f6a3f51e0a993e7d5d1853308c0fbc5c0 --- /dev/null +++ b/src/device/service/drivers/nce/handlers/NetworkTopologyHandler.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 logging, re +from typing import Dict, List, Optional +from common.Constants import DEFAULT_TOPOLOGY_NAME +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import ( + DEVICEDRIVER_UNDEFINED, DEVICEOPERATIONALSTATUS_DISABLED, + DEVICEOPERATIONALSTATUS_ENABLED +) +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from device.service.driver_api.ImportTopologyEnum import ( + ImportTopologyEnum, get_import_topology +) + + +LOGGER = logging.getLogger(__name__) + + +class NetworkTopologyHandler: + def __init__(self, rest_conf_client : RestConfClient, **settings) -> None: + self._rest_conf_client = rest_conf_client + self._subpath_root = '/ietf-network:networks' + self._subpath_item = self._subpath_root + '/network={network_id:s}' + + # Options are: + # disabled --> just import endpoints as usual + # devices --> imports sub-devices but not links connecting them. + # (a remotely-controlled transport domain might exist between them) + # topology --> imports sub-devices and links connecting them. + # (not supported by XR driver) + self._import_topology = get_import_topology(settings, default=ImportTopologyEnum.TOPOLOGY) + + + def get(self, network_id : Optional[str] = None) -> List[Dict]: + if network_id is None: network_id = DEFAULT_TOPOLOGY_NAME + endpoint = self._subpath_item.format(network_id=network_id) + reply = self._rest_conf_client.get(endpoint) + + if 'ietf-network:network' not in reply: + raise Exception('Malformed reply. "ietf-network:network" missing') + networks = reply['ietf-network:network'] + + if len(networks) == 0: + MSG = '[get] Network({:s}) not found; returning' + LOGGER.debug(MSG.format(str(network_id))) + return list() + + if len(networks) > 1: + MSG = '[get] Multiple occurrences for Network({:s}); returning' + LOGGER.debug(MSG.format(str(network_id))) + return list() + + network = networks[0] + + MSG = '[get] import_topology={:s}' + LOGGER.debug(MSG.format(str(self._import_topology))) + + result = list() + if self._import_topology == ImportTopologyEnum.DISABLED: + LOGGER.debug('[get] abstract controller; returning') + return result + + device_type = DeviceTypeEnum.EMULATED_PACKET_SWITCH.value + endpoint_type = '' + if 'network-types' in network: + nnt = network['network-types'] + if 'ietf-te-topology:te-topology' in nnt: + nnt_tet = nnt['ietf-te-topology:te-topology'] + if 'ietf-otn-topology:otn-topology' in nnt_tet: + device_type = DeviceTypeEnum.OPTICAL_OLT.value + endpoint_type = 'optical' + elif 'ietf-eth-te-topology:eth-tran-topology' in nnt_tet: + device_type = DeviceTypeEnum.EMULATED_PACKET_SWITCH.value + endpoint_type = 'copper' + elif 'ietf-l3-unicast-topology:l3-unicast-topology' in nnt_tet: + device_type = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value + endpoint_type = 'copper' + + for node in network['node']: + node_id = node['node-id'] + + node_name = node_id + node_is_up = True + if 'ietf-te-topology:te' in node: + nte = node['ietf-te-topology:te'] + + if 'oper-status' in nte: + node_is_up = nte['oper-status'] == 'up' + + if 'te-node-attributes' in nte: + ntea = nte['te-node-attributes'] + if 'name' in ntea: + node_name = ntea['name'] + + if 'OLT' in node_id: + device_type = DeviceTypeEnum.OPTICAL_OLT.value + elif 'ONT' in node_id: + device_type = DeviceTypeEnum.OPTICAL_ONT.value + + device_url = '/devices/device[{:s}]'.format(node_id) + device_data = { + 'uuid': node_id, + 'name': node_name, + 'type': device_type, + 'status': DEVICEOPERATIONALSTATUS_ENABLED if node_is_up else DEVICEOPERATIONALSTATUS_DISABLED, + 'drivers': [DEVICEDRIVER_UNDEFINED], + } + result.append((device_url, device_data)) + + for tp in node['ietf-network-topology:termination-point']: + tp_id = tp['tp-id'] + + tp_name = tp_id + if 'ietf-te-topology:te' in tp: + tpte = tp['ietf-te-topology:te'] + if 'name' in tpte: + tp_name = tpte['name'] + + tp_ip_addr = '0.0.0.0' + if 'ietf-te-topology:te-tp-id' in tp: + tp_ip_addr = tp['ietf-te-topology:te-tp-id'] + + if node_name.startswith('ONT') and tp_name == '200': + site_location = 'user' + elif node_name.startswith('ONT') and tp_name == '500': + site_location = 'access' + elif node_name == 'OLT' and tp_name in {'200', '201'}: + site_location = 'access' + elif node_name == 'OLT' and tp_name in {'500', '501'}: + site_location = 'transport' + else: + site_location = 'access' + + endpoint_url = '/endpoints/endpoint[{:s}, {:s}]'.format(node_id, tp_id) + endpoint_settings = { + 'uuid' : tp_id, + 'name' : tp_name, + 'type' : endpoint_type, + 'address_ip' : tp_ip_addr, + 'address_prefix': '24', + 'mtu' : '1500', + 'site_location' : site_location, + } + + outer_tag_vlan_range : Optional[str] = ( + tp + .get('ietf-eth-te-topology:eth-svc', dict()) + .get('supported-classification', dict()) + .get('vlan-classification', dict()) + .get('outer-tag', dict()) + .get('vlan-range') + ) + if outer_tag_vlan_range is not None: + RE_NUMBER = re.compile(r'[0-9]+') + if RE_NUMBER.match(outer_tag_vlan_range) is not None: + endpoint_settings['vlan_tag'] = int(outer_tag_vlan_range) + + endpoint_data = { + 'device_uuid': node_id, + 'uuid': tp_id, + 'name': tp_name, + 'type': endpoint_type, + 'settings': endpoint_settings, + } + result.append((endpoint_url, endpoint_data)) + + if self._import_topology == ImportTopologyEnum.DEVICES: + LOGGER.debug('[get] devices only; returning') + return result + + for link in network['ietf-network-topology:link']: + link_uuid = link['link-id'] + link_src = link['source'] + link_dst = link['destination'] + link_src_dev_id = link_src['source-node'] + link_src_ep_id = link_src['source-tp'] + link_dst_dev_id = link_dst['dest-node'] + link_dst_ep_id = link_dst['dest-tp'] + + link_url = '/links/link[{:s}]'.format(link_uuid) + link_endpoint_ids = [ + (link_src_dev_id, link_src_ep_id), + (link_dst_dev_id, link_dst_ep_id), + ] + link_data = { + 'uuid': link_uuid, + 'name': link_uuid, + 'endpoints': link_endpoint_ids, + } + result.append((link_url, link_data)) + + LOGGER.debug('[get] topology; returning') + return result diff --git a/src/device/service/drivers/nce/handlers/SubscriptionHandler.py b/src/device/service/drivers/nce/handlers/SubscriptionHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..0b7ffdfe1449df0c0a522bc267ced0eb9aec5895 --- /dev/null +++ b/src/device/service/drivers/nce/handlers/SubscriptionHandler.py @@ -0,0 +1,82 @@ +# 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 logging, requests +from typing_extensions import TypedDict +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +LOGGER = logging.getLogger(__name__) + + +Periodic = TypedDict('Periodic', {'ietf-yang-push:period': str}) + +Input = TypedDict( + 'Input', + { + 'datastore': str, + 'ietf-yang-push:datastore-xpath-filter': str, + 'ietf-yang-push:periodic': Periodic, + }, +) + +SubscribedNotificationsSchema = TypedDict( + 'SubscribedNotificationsSchema', {'ietf-subscribed-notifications:input': Input} +) + +SubscriptionSchema = TypedDict('SubscriptionSchema', {'id': str}) + +UnsubscribedNotificationsSchema = TypedDict( + 'UnsubscribedNotificationsSchema', {'ietf-subscribed-notifications:input': SubscriptionSchema} +) + + +class SubscriptionId(TypedDict): + identifier: str + uri: str + + +class SubscriptionHandler: + def __init__(self, rest_conf_client : RestConfClient) -> None: + self._rest_conf_client = rest_conf_client + + def subscribe( + self, subscription_data : SubscribedNotificationsSchema + ) -> SubscriptionId: + MSG = '[subscribe] subscription_data={:s}' + LOGGER.debug(MSG.format(str(subscription_data))) + try: + url = '/subscriptions:establish-subscription' + LOGGER.debug('Subscribing to telemetry: {:s}'.format(str(subscription_data))) + reply = self._rest_conf_client.rpc(url, body=subscription_data) + LOGGER.debug('Subscription reply: {:s}'.format(str(reply))) + return reply + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send RPC request' + raise Exception(MSG) from e + + def unsubscribe( + self, unsubscription_data : UnsubscribedNotificationsSchema + ) -> SubscriptionId: + MSG = '[unsubscribe] unsubscription_data={:s}' + LOGGER.debug(MSG.format(str(unsubscription_data))) + try: + url = '/subscriptions:delete-subscription' + LOGGER.debug('Unsubscribing from telemetry: {:s}'.format(str(unsubscription_data))) + reply = self._rest_conf_client.rpc(url, body=unsubscription_data) + LOGGER.debug('Unsubscription reply: {:s}'.format(str(reply))) + return reply + except requests.exceptions.ConnectionError as e: + MSG = 'Failed to send RPC request' + raise Exception(MSG) from e diff --git a/src/device/service/drivers/nce/handlers/__init__.py b/src/device/service/drivers/nce/handlers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/device/service/drivers/nce/handlers/__init__.py @@ -0,0 +1,13 @@ +# 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/device/service/drivers/nce/nce_fan_client.py b/src/device/service/drivers/nce/nce_fan_client.py deleted file mode 100644 index 9805f9cf875caca4c8e74ad1013ce63a2fd2157a..0000000000000000000000000000000000000000 --- a/src/device/service/drivers/nce/nce_fan_client.py +++ /dev/null @@ -1,94 +0,0 @@ -# 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 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/service/drivers/optical_tfs/TfsApiClient.py b/src/device/service/drivers/optical_tfs/TfsApiClient.py index 854154be62f76099d548533834408f97db6205e5..79802fcf6d2708fe12fceccd68c0b1460c1e5ef8 100644 --- a/src/device/service/drivers/optical_tfs/TfsApiClient.py +++ b/src/device/service/drivers/optical_tfs/TfsApiClient.py @@ -16,7 +16,7 @@ import logging from typing import Dict, List, Optional, Tuple from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME from common.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum -from common.tools.client.RestClient import RestClient +from common.tools.rest_api.client.RestApiClient import RestApiClient from common.tools.object_factory.Constraint import json_constraint_custom from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Device import json_device_id @@ -59,7 +59,7 @@ MAPPING_DRIVER = { LOGGER = logging.getLogger(__name__) -class TfsApiClient(RestClient): +class TfsApiClient(RestApiClient): def __init__( self, address : str, port : int, scheme : str = 'http', username : Optional[str] = None, password : Optional[str] = None, diff --git a/src/device/service/drivers/optical_tfs/TfsOpticalClient.py b/src/device/service/drivers/optical_tfs/TfsOpticalClient.py index 07bf615c322e86349b3b4c38e2a99363c4a3e1ce..4a4f263439df7c4a39fb21e6405f7aad3cd479c6 100644 --- a/src/device/service/drivers/optical_tfs/TfsOpticalClient.py +++ b/src/device/service/drivers/optical_tfs/TfsOpticalClient.py @@ -15,7 +15,7 @@ import logging, requests from typing import Dict, List, Optional, Union -from common.tools.client.RestClient import RestClient +from common.tools.rest_api.client.RestApiClient import RestApiClient LOGGER = logging.getLogger(__name__) @@ -26,7 +26,7 @@ ADD_LIGHTPATH_URL = '/OpticalTFS/AddLightpath/{src_node:s}/{dst_node:s}/{bit DEL_LIGHTPATH_URL = '/OpticalTFS/DelLightpath/{flow_id:s}/{src_node:s}/{dst_node:s}/{bitrate:s}' -class TfsOpticalClient(RestClient): +class TfsOpticalClient(RestApiClient): def __init__( self, address : str, port : int, scheme : str = 'http', username : Optional[str] = None, password : Optional[str] = None, diff --git a/src/device/tests/test_unitary_ietf_actn.py b/src/device/tests/test_unitary_ietf_actn.py index b5c4a5966bfd13e20ac212c8b15f34794870cbf2..02d04277054ec88d807128bc4d2fdb5a246a3386 100644 --- a/src/device/tests/test_unitary_ietf_actn.py +++ b/src/device/tests/test_unitary_ietf_actn.py @@ -21,7 +21,7 @@ from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Device import ( json_device_connect_rules, json_device_id, json_device_ietf_actn_disabled ) -from common.tools.service.GenericRestServer import GenericRestServer +from common.tools.rest_api.server.GenericRestServer import GenericRestServer from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from device.service.DeviceService import DeviceService @@ -35,26 +35,24 @@ from .PrepareTestScenario import ( # pylint: disable=unused-import mock_service, device_service, context_client, device_client, monitoring_client, test_prepare_environment ) -DEVICE_UUID = 'DEVICE-IETF-ACTN' -DEVICE_ADDRESS = '127.0.0.1' -DEVICE_PORT = 8080 -DEVICE_USERNAME = 'admin' -DEVICE_PASSWORD = 'admin' -DEVICE_SCHEME = 'http' -DEVICE_BASE_URL = '/restconf/v2/data' -DEVICE_TIMEOUT = 120 -DEVICE_VERIFY = False +DEVICE_UUID = 'DEVICE-IETF-ACTN' +DEVICE_ADDRESS = '127.0.0.1' +DEVICE_PORT = 8080 +DEVICE_USERNAME = 'admin' +DEVICE_PASSWORD = 'admin' +DEVICE_SCHEME = 'http' +DEVICE_TIMEOUT = 120 +DEVICE_VERIFY_CERTS = False DEVICE_ID = json_device_id(DEVICE_UUID) DEVICE = json_device_ietf_actn_disabled(DEVICE_UUID) DEVICE_CONNECT_RULES = json_device_connect_rules(DEVICE_ADDRESS, DEVICE_PORT, { - 'scheme' : DEVICE_SCHEME, - 'username': DEVICE_USERNAME, - 'password': DEVICE_PASSWORD, - 'base_url': DEVICE_BASE_URL, - 'timeout' : DEVICE_TIMEOUT, - 'verify' : DEVICE_VERIFY, + 'scheme' : DEVICE_SCHEME, + 'username' : DEVICE_USERNAME, + 'password' : DEVICE_PASSWORD, + 'timeout' : DEVICE_TIMEOUT, + 'verify_certs': DEVICE_VERIFY_CERTS, }) DATA_FILE_CONFIG_RULES = 'device/tests/data/ietf_actn/config_rules.json' @@ -69,22 +67,44 @@ LOGGER.setLevel(logging.DEBUG) def ietf_actn_sdn_ctrl( device_service : DeviceService, # pylint: disable=redefined-outer-name ) -> Flask: - _rest_server = GenericRestServer(DEVICE_PORT, DEVICE_BASE_URL, bind_address=DEVICE_ADDRESS) + _rest_server = GenericRestServer(DEVICE_PORT, bind_address=DEVICE_ADDRESS) _rest_server.app.config['DEBUG' ] = True _rest_server.app.config['ENV' ] = 'development' _rest_server.app.config['SERVER_NAME'] = '{:s}:{:d}'.format(DEVICE_ADDRESS, DEVICE_PORT) _rest_server.app.config['TESTING' ] = True + class HostMeta(Resource): + def get(self): + host_meta = {'links': [{'rel': 'restconf', 'href': '/restconf'}]} + return make_response(jsonify(host_meta), 200) + class Root(Resource): def get(self): return make_response(jsonify({}), 200) + class Network(Resource): + def get(self, network_id : str): + network_topology = {'ietf-network:network': []} + return make_response(jsonify(network_topology), 200) + + RESTCONF_V1 = '/restconf/data' + RESTCONF_V2 = '/restconf/v2/data' + add_rsrc = _rest_server.add_resource - add_rsrc(Root, '/') - add_rsrc(OsuTunnels, '/ietf-te:te/tunnels') - add_rsrc(OsuTunnel, '/ietf-te:te/tunnels/tunnel=""') - add_rsrc(EthServices, '/ietf-eth-tran-service:etht-svc') - add_rsrc(EthService, '/ietf-eth-tran-service:etht-svc/etht-svc-instances=""') + add_rsrc(HostMeta, '/.well-known/host-meta', endpoint='well-known.host-meta') + add_rsrc(Root, RESTCONF_V1 + '/', endpoint='restconf.v1.root') + add_rsrc(Root, RESTCONF_V2 + '/', endpoint='restconf.v2.root') + + RESTCONF_NETWORK = RESTCONF_V1 + '/ietf-network:networks/network=' + add_rsrc(Network, RESTCONF_NETWORK) + + RESTCONF_TE_TUNNELS = RESTCONF_V2 + '/ietf-te:te/tunnels' + add_rsrc(OsuTunnels, RESTCONF_TE_TUNNELS) + add_rsrc(OsuTunnel, RESTCONF_TE_TUNNELS + '/tunnel=') + + RESTCONF_ETHT_SERVICES = RESTCONF_V2 + '/ietf-eth-tran-service:etht-svc' + add_rsrc(EthServices, RESTCONF_ETHT_SERVICES) + add_rsrc(EthService, RESTCONF_ETHT_SERVICES + '/etht-svc-instances=') _rest_server.start() time.sleep(1) # bring time for the server to start diff --git a/src/nbi/.gitlab-ci.yml b/src/nbi/.gitlab-ci.yml index 1a4bfe01b7f5a182c3ac18f41331c8ba2f45dd74..0a5810354d65a8d366d931f723e290a869b568b2 100644 --- a/src/nbi/.gitlab-ci.yml +++ b/src/nbi/.gitlab-ci.yml @@ -86,7 +86,6 @@ unit_test nbi: bitnamilegacy/kafka:latest - while ! docker logs kafka 2>&1 | grep -q 'Kafka Server started'; do sleep 1; done - sleep 5 # Give extra time to Kafka to get stabilized - - docker inspect kafka --format "{{.NetworkSettings.Networks}}" - KAFKA_IP=$(docker inspect kafka --format "{{.NetworkSettings.Networks.teraflowbridge.IPAddress}}") - echo $KAFKA_IP - > @@ -104,7 +103,11 @@ unit_test nbi: --env IETF_NETWORK_RENDERER=LIBYANG --env "KFK_SERVER_ADDRESS=${KAFKA_IP}:9092" $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG - - while ! docker logs $IMAGE_NAME 2>&1 | grep -q 'Initialization completed'; do sleep 1; done + # Wait until any worker logs "Initialization completed" (from the start of logs) + # -m1 makes grep exit as soon as the line appears. + # With set -o pipefail, docker logs will get SIGPIPE when grep exits; + # `|| true` neutralizes that so the pipeline’s status reflects grep’s success. + - (docker logs -f $IMAGE_NAME || true) 2>&1 | grep -m1 -Fi 'Initialization completed' - sleep 5 # Give extra time to NBI to get ready - docker ps -a - docker logs kafka diff --git a/src/nbi/Dockerfile b/src/nbi/Dockerfile index 63556432be46fae44552bb2ec191e3f7eab17a99..05e6f861adbabe617340088491c469c93d32ff4a 100644 --- a/src/nbi/Dockerfile +++ b/src/nbi/Dockerfile @@ -85,6 +85,8 @@ COPY src/qkd_app/__init__.py qkd_app/__init__.py COPY src/qkd_app/client/. qkd_app/client/ COPY src/qos_profile/__init__.py qos_profile/__init__.py COPY src/qos_profile/client/. qos_profile/client/ +COPY src/simap_connector/__init__.py simap_connector/__init__.py +COPY src/simap_connector/client/. simap_connector/client/ COPY src/vnt_manager/__init__.py vnt_manager/__init__.py COPY src/vnt_manager/client/. vnt_manager/client/ RUN mkdir -p /var/teraflow/tests/tools @@ -92,4 +94,4 @@ COPY src/tests/tools/mock_osm/. tests/tools/mock_osm/ # Start the service # NOTE: Configured single worker to prevent issues with multi-worker synchronization. To be invetsigated. -ENTRYPOINT ["gunicorn", "--workers", "1", "--worker-class", "eventlet", "--bind", "0.0.0.0:8080", "nbi.service.app:app"] +ENTRYPOINT ["gunicorn", "--workers", "8", "--worker-class", "eventlet", "--bind", "0.0.0.0:8080", "nbi.service.app:app"] diff --git a/src/nbi/service/_tools/HttpStatusCodes.py b/src/nbi/service/_tools/HttpStatusCodes.py index 19c56d7fbb392e74fed89eeca8eb2c3614177f6e..cb7fc76a6dfa6383fb4fc8002b07bc02aba85a8a 100644 --- a/src/nbi/service/_tools/HttpStatusCodes.py +++ b/src/nbi/service/_tools/HttpStatusCodes.py @@ -12,12 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -HTTP_OK = 200 -HTTP_CREATED = 201 -HTTP_ACCEPTED = 202 -HTTP_NOCONTENT = 204 -HTTP_BADREQUEST = 400 -HTTP_NOTFOUND = 404 -HTTP_UNSUPMEDIATYPE = 415 -HTTP_SERVERERROR = 500 -HTTP_GATEWAYTIMEOUT = 504 +HTTP_OK = 200 +HTTP_CREATED = 201 +HTTP_ACCEPTED = 202 +HTTP_NOCONTENT = 204 +HTTP_BADREQUEST = 400 +HTTP_NOTFOUND = 404 +HTTP_NOT_ACCEPTABLE = 406 +HTTP_CONFLICT = 409 +HTTP_UNSUPMEDIATYPE = 415 +HTTP_SERVERERROR = 500 +HTTP_NOT_IMPLEMENTED = 501 +HTTP_GATEWAYTIMEOUT = 504 diff --git a/src/nbi/service/app.py b/src/nbi/service/app.py index 2d6102a3492a8e29bf88682d1ae0cec0e327b8de..2ad81b1f3d32891d76c469e1ba2643cd8c669a08 100644 --- a/src/nbi/service/app.py +++ b/src/nbi/service/app.py @@ -43,6 +43,7 @@ from .restconf_root import register_restconf_root from .tfs_api import register_tfs_api #from .topology_updates import register_topology_updates from .vntm_recommend import register_vntm_recommend +from .sse_telemetry import register_telemetry_subscription from .well_known_meta import register_well_known @@ -97,6 +98,7 @@ register_ietf_acl (nbi_app) register_qkd_app (nbi_app) #register_topology_updates(nbi_app) # does not work; check if eventlet-grpc side effects register_vntm_recommend (nbi_app) +register_telemetry_subscription(nbi_app) register_camara_qod (nbi_app) LOGGER.info('All connectors registered') diff --git a/src/nbi/service/ietf_network/ComposeNetwork.py b/src/nbi/service/ietf_network/ComposeNetwork.py index 8d62525ad35cbcec97c1ecd387d2ec4b2e881bac..fb32e385b400bcc64ae27d9ad18cf69e64f8b3b4 100644 --- a/src/nbi/service/ietf_network/ComposeNetwork.py +++ b/src/nbi/service/ietf_network/ComposeNetwork.py @@ -24,10 +24,11 @@ from .NetworkTypeEnum import NetworkTypeEnum, get_network_topology_type LOGGER = logging.getLogger(__name__) IGNORE_DEVICE_TYPES = { - DeviceTypeEnum.CLIENT.value, - DeviceTypeEnum.DATACENTER.value, DeviceTypeEnum.EMULATED_CLIENT.value, + DeviceTypeEnum.EMULATED_COMPUTER.value, DeviceTypeEnum.EMULATED_DATACENTER.value, + DeviceTypeEnum.EMULATED_VIRTUAL_MACHINE.value, + DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM.value, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value, diff --git a/src/nbi/service/ietf_network_slice/ietf_slice_handler.py b/src/nbi/service/ietf_network_slice/ietf_slice_handler.py index 149d0cfa8fa2c7fa00a59e229288674f23bc6828..e3e8ea6f8a2ff4956d945f47f3d14909ed0e41d4 100644 --- a/src/nbi/service/ietf_network_slice/ietf_slice_handler.py +++ b/src/nbi/service/ietf_network_slice/ietf_slice_handler.py @@ -103,19 +103,65 @@ def build_constraints_from_connection_group(connection_group: dict) -> List[Cons ]["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"]) + metric_type = str(metric['metric-type']) + metric_type = metric_type.replace('ietf-network-slice-service:', 'ietf-nss:') + + if metric_type == 'ietf-nss:one-way-delay-maximum': + value = float(metric['bound']) + unit = str(metric['metric-unit']) + + if unit == 'nanoseconds': + factor = 1.0e6 + elif unit == 'microseconds': + factor = 1.0e3 + elif unit == 'milliseconds': + factor = 1.0 + else: + MSG = 'Unsupported unit({:s}) for metric({:s})' + raise Exception(MSG.format(unit, metric_type)) + + constraint = Constraint() + constraint.sla_latency.e2e_latency_ms = value / factor + constraints.append(constraint) + + elif metric_type == 'ietf-nss:one-way-bandwidth': + value = float(metric['bound']) + unit = str(metric['metric-unit']) + + if unit == 'bps': + factor = 1.0e9 + elif unit == 'Kbps': + factor = 1.0e6 + elif unit == 'Mbps': + factor = 1.0e3 + elif unit == 'Gbps': + factor = 1.0 + else: + MSG = 'Unsupported unit({:s}) for metric({:s})' + raise Exception(MSG.format(unit, metric_type)) + constraint = Constraint() - constraint.sla_latency.e2e_latency_ms = bound_value + constraint.sla_capacity.capacity_gbps = value / factor constraints.append(constraint) - elif metric_type == "ietf-nss:one-way-bandwidth": - bound_value = float(metric["bound"]) + + elif metric_type == "ietf-nss:two-way-packet-loss": + value = float(metric["percentile-value"]) + unit = str(metric['metric-unit']) + + if unit != 'percentage': + MSG = 'Unsupported unit({:s}) for metric({:s})' + raise Exception(MSG.format(unit, metric_type)) + constraint = Constraint() - # Convert from Mbps to Gbps if needed - constraint.sla_capacity.capacity_gbps = bound_value / 1.0e3 + constraint.sla_availability.num_disjoint_paths = 1 + constraint.sla_availability.all_active = True + constraint.sla_availability.availability = 100.0 - value constraints.append(constraint) + else: + MSG = 'Unsupported metric({:s})' + raise Exception(MSG.format(str(metric))) + return constraints @@ -301,18 +347,61 @@ class IETFSliceHandler: endpoint.endpoint_uuid.uuid = endpoint_uuid list_endpoints.append(endpoint) + match_criteria = sdp["service-match-criteria"]["match-criterion"] + if len(match_criteria) != 1: + raise Exception('Each SDP must have exactly 1 service-match-criteria/match-criterion') + match_criterion = match_criteria[0] + # Keep track of connection-group-id from each SDP connection_group_ids.add( - sdp["service-match-criteria"]["match-criterion"][0][ - "target-connection-group-id" - ] + match_criterion['target-connection-group-id'] + ) + + sdp_ip_addresses = sdp['sdp-ip-address'] + if len(sdp_ip_addresses) != 1: + raise Exception('Each SDP must have exactly 1 sdp-ip-address') + sdp_ip_address = sdp_ip_addresses[0] + + vlan_tag = None + match_type = match_criterion['match-type'] + for match_type_item in match_type: + item_type = match_type_item['type'] + if item_type != 'ietf-network-slice-service:vlan': continue + vlan_tag = int(match_type_item['value'][0]) + break + + update_config_rule_custom( + slice_request.slice_config.config_rules, + '/settings', + { + 'address_families': (['IPV4'], True), + 'mtu' : (1500, True), + } + ) + + static_routing_table = { + #'{:d}-{:s}/{:d}'.format(lan_tag, ip_range, ip_prefix_len): ( + # { + # 'vlan-id': lan_tag, + # 'ip-network': '{:s}/{:d}'.format(ip_range, ip_prefix_len), + # 'next-hop': next_hop + # }, + # True + #) + #for (ip_range, ip_prefix_len, lan_tag), next_hop in static_routing.items() + } + update_config_rule_custom( + slice_request.slice_config.config_rules, + '/static_routing', static_routing_table ) # Endpoint-specific config rule fields endpoint_config_rule_fields = { - "address_ip": (endpoint_uuid, RAISE_IF_DIFFERS), - "address_prefix": (ADDRESS_PREFIX, RAISE_IF_DIFFERS), + 'address_ip': (sdp_ip_address, RAISE_IF_DIFFERS), + 'address_prefix': (ADDRESS_PREFIX, RAISE_IF_DIFFERS), } + if vlan_tag is not None: + endpoint_config_rule_fields['vlan_tag'] = (vlan_tag, RAISE_IF_DIFFERS) endpoint_config_rules.append( ( f"/device[{device_uuid}]/endpoint[{endpoint_uuid}]/settings", @@ -335,9 +424,11 @@ class IETFSliceHandler: # 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 - ) + + # NOTE: not sure why this is needed + #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) diff --git a/src/nbi/service/sse_telemetry/AffectSampleSynthesizer.py b/src/nbi/service/sse_telemetry/AffectSampleSynthesizer.py new file mode 100644 index 0000000000000000000000000000000000000000..c201d233bbc348ba47da9a93c26ed3417bdcb270 --- /dev/null +++ b/src/nbi/service/sse_telemetry/AffectSampleSynthesizer.py @@ -0,0 +1,60 @@ +# 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 logging +from flask import jsonify, request +from flask_restful import Resource +from werkzeug.exceptions import BadRequest, UnsupportedMediaType +from common.proto.simap_connector_pb2 import Affectation +from simap_connector.client.SimapConnectorClient import SimapConnectorClient + + +LOGGER = logging.getLogger(__name__) + + +class AffectSampleSynthesizer(Resource): + # @HTTP_AUTH.login_required + def post(self): + if not request.is_json: + raise UnsupportedMediaType('JSON payload is required') + + request_data = request.json + LOGGER.debug('[post] Affectation request: {:s}'.format(str(request_data))) + + if 'network_id' not in request_data: + raise BadRequest('Missing field(network_id)') + network_id = str(request_data['network_id']) + + if 'link_id' not in request_data: + raise BadRequest('Missing field(link_id)') + link_id = str(request_data['link_id']) + + if 'bandwidth_factor' not in request_data: + raise BadRequest('Missing field(bandwidth_factor)') + bandwidth_factor = float(request_data['bandwidth_factor']) + + if 'latency_factor' not in request_data: + raise BadRequest('Missing field(latency_factor)') + latency_factor = float(request_data['latency_factor']) + + affectation = Affectation() + affectation.network_id = network_id + affectation.link_id = link_id + affectation.bandwidth_factor = bandwidth_factor + affectation.latency_factor = latency_factor + + simap_connector_client = SimapConnectorClient() + simap_connector_client.AffectSampleSynthesizer(affectation) + return jsonify({}) diff --git a/src/nbi/service/sse_telemetry/DeleteSubscription.py b/src/nbi/service/sse_telemetry/DeleteSubscription.py new file mode 100644 index 0000000000000000000000000000000000000000..dd60a45894a7924aef27b811934642de6a053cab --- /dev/null +++ b/src/nbi/service/sse_telemetry/DeleteSubscription.py @@ -0,0 +1,54 @@ +# 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 logging +from flask import jsonify, request +from flask_restful import Resource +from werkzeug.exceptions import BadRequest, UnsupportedMediaType +from common.proto.simap_connector_pb2 import SubscriptionId +from nbi.service._tools.Authentication import HTTP_AUTH +from simap_connector.client.SimapConnectorClient import SimapConnectorClient + + +LOGGER = logging.getLogger(__name__) + + +class DeleteSubscription(Resource): + @HTTP_AUTH.login_required + def post(self): + if not request.is_json: + raise UnsupportedMediaType('JSON payload is required') + + request_data = request.json + LOGGER.debug('[post] Unsubscription request: {:s}'.format(str(request_data))) + + if 'ietf-subscribed-notifications:input' not in request_data: + path = 'ietf-subscribed-notifications:input' + MSG = 'Missing field({:s})'.format(str(path)) + raise BadRequest(MSG) + input_data = request_data['ietf-subscribed-notifications:input'] + + subscription_id = SubscriptionId() + + if 'id' not in input_data: + path = 'ietf-subscribed-notifications:input/id' + MSG = 'Missing field({:s})'.format(str(path)) + raise BadRequest(MSG) + subscription_id.subscription_id = input_data['id'] + + simap_connector_client = SimapConnectorClient() + simap_connector_client.DeleteSubscription(subscription_id) + + return jsonify({}) diff --git a/src/nbi/service/sse_telemetry/EstablishSubscription.py b/src/nbi/service/sse_telemetry/EstablishSubscription.py new file mode 100644 index 0000000000000000000000000000000000000000..2f10fa43e73750f68554a0c586243079de6bfcfa --- /dev/null +++ b/src/nbi/service/sse_telemetry/EstablishSubscription.py @@ -0,0 +1,74 @@ +# 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 logging +from flask import jsonify, request, url_for +from flask_restful import Resource +from werkzeug.exceptions import BadRequest, UnsupportedMediaType +from common.proto.simap_connector_pb2 import Subscription +from nbi.service._tools.Authentication import HTTP_AUTH +from simap_connector.client.SimapConnectorClient import SimapConnectorClient + +LOGGER = logging.getLogger(__name__) + + +class EstablishSubscription(Resource): + @HTTP_AUTH.login_required + def post(self): + if not request.is_json: + raise UnsupportedMediaType('JSON payload is required') + + request_data = request.json + LOGGER.debug('[post] Subscription request: {:s}'.format(str(request_data))) + + if 'ietf-subscribed-notifications:input' not in request_data: + path = 'ietf-subscribed-notifications:input' + MSG = 'Missing field({:s})'.format(str(path)) + raise BadRequest(MSG) + input_data = request_data['ietf-subscribed-notifications:input'] + + subscription = Subscription() + + if 'datastore' not in input_data: + path = 'ietf-subscribed-notifications:input/datastore' + MSG = 'Missing field({:s})'.format(str(path)) + raise BadRequest(MSG) + subscription.datastore = input_data['datastore'] + + if 'ietf-yang-push:datastore-xpath-filter' not in input_data: + path = 'ietf-subscribed-notifications:input/ietf-yang-push:datastore-xpath-filter' + MSG = 'Missing field({:s})'.format(str(path)) + raise BadRequest(MSG) + subscription.xpath_filter = input_data['ietf-yang-push:datastore-xpath-filter'] + + if 'ietf-yang-push:periodic' not in input_data: + path = 'ietf-subscribed-notifications:input/ietf-yang-push:periodic' + MSG = 'Missing field({:s})'.format(str(path)) + raise BadRequest(MSG) + periodic = input_data['ietf-yang-push:periodic'] + + if 'ietf-yang-push:period' not in periodic: + path = 'ietf-subscribed-notifications:input/ietf-yang-push:periodic/ietf-yang-push:period' + MSG = 'Missing field({:s})'.format(str(path)) + raise BadRequest(MSG) + subscription.period = float(periodic['ietf-yang-push:period']) + + simap_connector_client = SimapConnectorClient() + subscription_id = simap_connector_client.EstablishSubscription(subscription) + subscription_id = subscription_id.subscription_id + + subscription_uri = url_for('sse.stream', subscription_id=subscription_id) + sub_id = {'id': subscription_id, 'uri': subscription_uri} + return jsonify(sub_id) diff --git a/src/nbi/service/sse_telemetry/SimapClient.py b/src/nbi/service/sse_telemetry/SimapClient.py new file mode 100644 index 0000000000000000000000000000000000000000..a300aca747b4a159d6436f9869c8f5ad2cf792e2 --- /dev/null +++ b/src/nbi/service/sse_telemetry/SimapClient.py @@ -0,0 +1,350 @@ +# 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, Optional, Tuple +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +class TerminationPoint: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}/node={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:termination-point={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str, tp_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tp_id = tp_id + + def create(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network-topology:termination-point'][0] + + def update(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + self._restconf_client.delete(endpoint) + + +class NodeTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/node={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + + def create( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class Node: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/node={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tps : Dict[str, TerminationPoint] = dict() + self._telemetry : Optional[NodeTelemetry] = None + + @property + def telemetry(self) -> NodeTelemetry: + if self._telemetry is None: + self._telemetry = NodeTelemetry(self._restconf_client, self._network_id, self._node_id) + return self._telemetry + + def termination_points(self) -> List[Dict]: + tps : Dict = self._restconf_client.get(TerminationPoint.ENDPOINT_NO_ID) + return tps['ietf-network-topology:termination-point'].get('termination-point', list()) + + def termination_point(self, tp_id : str) -> TerminationPoint: + _tp = self._tps.get(tp_id) + if _tp is not None: return _tp + _tp = TerminationPoint(self._restconf_client, self._network_id, self._node_id, tp_id) + return self._tps.setdefault(tp_id, _tp) + + def create( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network:node'][0] + + def update( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class LinkTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/ietf-network-topology:link={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + + def create( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': bandwidth_utilization, + 'latency' : latency, + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Link: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:link={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + self._telemetry : Optional[LinkTelemetry] = None + + @property + def telemetry(self) -> LinkTelemetry: + if self._telemetry is None: + self._telemetry = LinkTelemetry(self._restconf_client, self._network_id, self._link_id) + return self._telemetry + + def create( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link : Dict = self._restconf_client.get(endpoint) + return link['ietf-network-topology:link'][0] + + def update( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Network: + ENDPOINT_NO_ID = '/ietf-network:networks' + ENDPOINT_ID = ENDPOINT_NO_ID + '/network={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._nodes : Dict[str, Node] = dict() + self._links : Dict[str, Link] = dict() + + def nodes(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Node.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('node', list()) + + def links(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Link.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('ietf-network-topology:link', list()) + + def node(self, node_id : str) -> Node: + _node = self._nodes.get(node_id) + if _node is not None: return _node + _node = Node(self._restconf_client, self._network_id, node_id) + return self._nodes.setdefault(node_id, _node) + + def link(self, link_id : str) -> Link: + _link = self._links.get(link_id) + if _link is not None: return _link + _link = Link(self._restconf_client, self._network_id, link_id) + return self._links.setdefault(link_id, _link) + + def create(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + networks : Dict = self._restconf_client.get(endpoint) + return networks['ietf-network:network'][0] + + def update(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + self._restconf_client.delete(endpoint) + + +class SimapClient: + def __init__(self, restconf_client : RestConfClient) -> None: + self._restconf_client = restconf_client + self._networks : Dict[str, Network] = dict() + + def networks(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Network.ENDPOINT_NO_ID) + return reply['ietf-network:networks'].get('network', list()) + + def network(self, network_id : str) -> Network: + _network = self._networks.get(network_id) + if _network is not None: return _network + _network = Network(self._restconf_client, network_id) + return self._networks.setdefault(network_id, _network) diff --git a/src/nbi/service/sse_telemetry/StreamSubscription.py b/src/nbi/service/sse_telemetry/StreamSubscription.py new file mode 100644 index 0000000000000000000000000000000000000000..02b4dd0c9e04725ba3d889aa751d27514eed4882 --- /dev/null +++ b/src/nbi/service/sse_telemetry/StreamSubscription.py @@ -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. + + +import logging, time +from typing import Dict, List +from flask import Response +from flask_restful import Resource +from kafka import KafkaConsumer, TopicPartition +from kafka.admin import KafkaAdminClient, NewTopic +from kafka.consumer.fetcher import ConsumerRecord +from kafka.errors import TopicAlreadyExistsError +from common.tools.kafka.Variables import KafkaConfig +from nbi.service._tools.Authentication import HTTP_AUTH + + +LOGGER = logging.getLogger(__name__) + + +KAFKA_BOOT_SERVERS = KafkaConfig.get_kafka_address() + + +class StreamSubscription(Resource): + @HTTP_AUTH.login_required + def get(self, subscription_id : int): + LOGGER.debug('[get] begin') + + def event_stream(): + LOGGER.debug('[stream:event_stream] begin') + topic = 'subscription.{:s}'.format(str(subscription_id)) + + LOGGER.info('[stream:event_stream] Checking Topics...') + kafka_admin = KafkaAdminClient(bootstrap_servers=KAFKA_BOOT_SERVERS) + existing_topics = set(kafka_admin.list_topics()) + LOGGER.info('[stream:event_stream] existing_topics={:s}'.format(str(existing_topics))) + if topic not in existing_topics: + LOGGER.info('[stream:event_stream] Creating Topic...') + to_create = [NewTopic(topic, num_partitions=3, replication_factor=1)] + try: + kafka_admin.create_topics(to_create, validate_only=False) + LOGGER.info('[stream:event_stream] Topic Created') + except TopicAlreadyExistsError: + pass + + LOGGER.info('[stream:event_stream] Connecting Consumer...') + kafka_consumer = KafkaConsumer( + bootstrap_servers = KAFKA_BOOT_SERVERS, + group_id = None, # consumer dispatch all messages sent to subscribed topics + auto_offset_reset = 'latest', + ) + LOGGER.info('[stream:event_stream] Subscribing topic={:s}...'.format(str(topic))) + kafka_consumer.subscribe(topics=[topic]) + LOGGER.info('[stream:event_stream] Subscribed') + + while True: + #LOGGER.debug('[stream:event_stream] Waiting...') + topic_records : Dict[TopicPartition, List[ConsumerRecord]] = \ + kafka_consumer.poll(timeout_ms=1000, max_records=1) + if len(topic_records) == 0: + time.sleep(0.5) + continue # no pending records + + #LOGGER.info('[stream:event_stream] topic_records={:s}'.format(str(topic_records))) + for _topic, records in topic_records.items(): + if _topic.topic != topic: continue + for record in records: + #message_key = record.key.decode('utf-8') + message_value = record.value.decode('utf-8') + + #MSG = '[stream:event_stream] message_key={:s} message_value={:s}' + #LOGGER.debug(MSG.format(str(message_key), str(message_value))) + yield message_value + #LOGGER.debug('[stream:event_stream] Sent') + + LOGGER.info('[stream:event_stream] Closing...') + kafka_consumer.close() + + LOGGER.info('[stream] Ready to stream...') + return Response(event_stream(), mimetype='text/event-stream') diff --git a/src/nbi/service/sse_telemetry/__init__.py b/src/nbi/service/sse_telemetry/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c668cd94268dad0e7b03a489b58c78fb7e5125fa --- /dev/null +++ b/src/nbi/service/sse_telemetry/__init__.py @@ -0,0 +1,53 @@ +# 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. + + +# RFC 8639 - Subscription to YANG Notifications +# Ref: https://datatracker.ietf.org/doc/html/rfc8639 + +# RFC 8641 - Subscription to YANG Notifications for Datastore Updates +# Ref: https://datatracker.ietf.org/doc/html/rfc8641 + + +from nbi.service.NbiApplication import NbiApplication +from .AffectSampleSynthesizer import AffectSampleSynthesizer +from .EstablishSubscription import EstablishSubscription +from .DeleteSubscription import DeleteSubscription +from .StreamSubscription import StreamSubscription + +def register_telemetry_subscription(nbi_app: NbiApplication): + nbi_app.add_rest_api_resource( + EstablishSubscription, + '/restconf/operations/subscriptions:establish-subscription', + '/restconf/operations/subscriptions:establish-subscription/', + endpoint='sse.establish', + ) + nbi_app.add_rest_api_resource( + DeleteSubscription, + '/restconf/operations/subscriptions:delete-subscription', + '/restconf/operations/subscriptions:delete-subscription/', + endpoint='sse.delete', + ) + nbi_app.add_rest_api_resource( + StreamSubscription, + '/restconf/stream/', + '/restconf/stream//', + endpoint='sse.stream', + ) + nbi_app.add_rest_api_resource( + AffectSampleSynthesizer, + '/restconf/operations/affect_sample_synthesizer', + '/restconf/operations/affect_sample_synthesizer/', + endpoint='sse.affect_sample_synthesizer', + ) diff --git a/src/nbi/service/well_known_meta/Resources.py b/src/nbi/service/well_known_meta/Resources.py index 86004c165e06393685923aa5656d6b9a2060757b..e60f4230a966f1dc72da9e009ce11c6da086eea9 100644 --- a/src/nbi/service/well_known_meta/Resources.py +++ b/src/nbi/service/well_known_meta/Resources.py @@ -12,24 +12,56 @@ # See the License for the specific language governing permissions and # limitations under the License. + # RESTCONF .well-known endpoint (RFC 8040) -from flask import jsonify +from flask import abort, jsonify, make_response, request from flask_restful import Resource +from xml.sax.saxutils import escape +import xml.etree.ElementTree as ET +from .._tools.HttpStatusCodes import HTTP_NOT_ACCEPTABLE + +XRD_NS = 'http://docs.oasis-open.org/ns/xri/xrd-1.0' +ET.register_namespace('', XRD_NS) + +RESTCONF_PREFIX = '/restconf' class WellKnownHostMeta(Resource): def get(self): - response = { - 'restconf': { - 'capabilities': [ - 'urn:ietf:params:restconf:capability:defaults:1.0', - 'urn:ietf:params:restconf:capability:depth:1.0', - 'urn:ietf:params:restconf:capability:with-defaults:1.0' - ], - 'media-types': [ - 'application/yang-data+json', - 'application/yang-data+xml' + best = request.accept_mimetypes.best_match([ + 'application/xrd+xml', + 'application/json', + ], default='application/xrd+xml') + + if best == 'application/xrd+xml': + xrd = ET.Element('{{{:s}}}XRD'.format(str(XRD_NS))) + ET.SubElement(xrd, '{{{:s}}}Link'.format(str(XRD_NS)), attrib={ + 'rel': 'restconf', 'href': RESTCONF_PREFIX + }) + xml_string = ET.tostring(xrd, encoding='utf-8', xml_declaration=True).decode() + response = make_response(str(xml_string)) + response.status_code = 200 + response.content_type = best + return response + elif best == 'application/json': + response = jsonify({ + 'restconf': { + 'capabilities': [ + 'urn:ietf:params:restconf:capability:defaults:1.0', + 'urn:ietf:params:restconf:capability:depth:1.0', + 'urn:ietf:params:restconf:capability:with-defaults:1.0' + ], + 'media-types': [ + 'application/yang-data+json', + 'application/yang-data+xml' + ] + }, + 'links': [ + {'rel': 'restconf', 'href': RESTCONF_PREFIX} ] - } - } - return jsonify(response) + }) + response.status_code = 200 + response.content_type = best + return response + else: + abort(HTTP_NOT_ACCEPTABLE) diff --git a/src/osm_client/.gitlab-ci.yml b/src/osm_client/.gitlab-ci.yml index 2a0cfbb7e0adbffcb647ce6449ca8d8b3dea64b4..6c5ffd318fc6c3885fa5238f6d71291b44128008 100644 --- a/src/osm_client/.gitlab-ci.yml +++ b/src/osm_client/.gitlab-ci.yml @@ -43,80 +43,80 @@ build osm_client: - src/tests/.gitlab-ci.yml - .gitlab-ci.yml -# Apply unit test to the component -unit_test osm_client: - variables: - IMAGE_NAME: 'osm_client' # name of the microservice - MOCK_IMAGE_NAME: 'mock_osm_nbi' - IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) - stage: unit_test - needs: - - build osm_client - - build mock_osm_nbi - before_script: - # Do Docker cleanup - - docker ps --all --quiet | xargs --no-run-if-empty docker stop - - docker container prune --force - - docker ps --all --quiet | xargs --no-run-if-empty docker rm --force - - docker image prune --force - - docker network prune --force - - docker volume prune --all --force - - docker buildx prune --force - - # Login Docker repository - - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - script: - - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" - - docker pull "$CI_REGISTRY_IMAGE/mock-osm-nbi:test" - - docker network create -d bridge teraflowbridge - - > - docker run --name mock_osm_nbi -d - --network=teraflowbridge - --env LOG_LEVEL=DEBUG - --env FLASK_ENV=development - $CI_REGISTRY_IMAGE/mock-osm-nbi:test - - > - docker run --name $IMAGE_NAME -d -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" - --network=teraflowbridge - --env LOG_LEVEL=DEBUG - --env FLASK_ENV=development - --env OSM_ADDRESS=mock_osm_nbi - $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG - - while ! docker logs $IMAGE_NAME 2>&1 | grep -q 'Running...'; do sleep 1; done - - docker ps -a - - docker logs $IMAGE_NAME - - docker logs mock_osm_nbi - - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report_unitary.xml" - - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" - coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' - after_script: - - docker logs $IMAGE_NAME - - docker logs mock_osm_nbi - - # Do Docker cleanup - - docker ps --all --quiet | xargs --no-run-if-empty docker stop - - docker container prune --force - - docker ps --all --quiet | xargs --no-run-if-empty docker rm --force - - docker image prune --force - - docker network prune --force - - docker volume prune --all --force - - docker buildx prune --force - - 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"' - - changes: - - src/common/**/*.py - - proto/*.proto - - src/$IMAGE_NAME/**/*.{py,in,yml} - - src/$IMAGE_NAME/Dockerfile - - src/$IMAGE_NAME/tests/*.py - - manifests/${IMAGE_NAME}service.yaml - - src/tests/tools/mock_osm_nbi/**/*.{py,in,yml,yaml,yang,sh,json} - - src/tests/tools/mock_osm_nbi/Dockerfile - - src/tests/.gitlab-ci.yml - - .gitlab-ci.yml - artifacts: - when: always - reports: - junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report_*.xml +## Apply unit test to the component +#unit_test osm_client: +# variables: +# IMAGE_NAME: 'osm_client' # name of the microservice +# MOCK_IMAGE_NAME: 'mock_osm_nbi' +# IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) +# stage: unit_test +# needs: +# - build osm_client +# - build mock_osm_nbi +# before_script: +# # Do Docker cleanup +# - docker ps --all --quiet | xargs --no-run-if-empty docker stop +# - docker container prune --force +# - docker ps --all --quiet | xargs --no-run-if-empty docker rm --force +# - docker image prune --force +# - docker network prune --force +# - docker volume prune --all --force +# - docker buildx prune --force +# +# # Login Docker repository +# - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY +# script: +# - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" +# - docker pull "$CI_REGISTRY_IMAGE/mock-osm-nbi:test" +# - docker network create -d bridge teraflowbridge +# - > +# docker run --name mock_osm_nbi -d +# --network=teraflowbridge +# --env LOG_LEVEL=DEBUG +# --env FLASK_ENV=development +# $CI_REGISTRY_IMAGE/mock-osm-nbi:test +# - > +# docker run --name $IMAGE_NAME -d -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" +# --network=teraflowbridge +# --env LOG_LEVEL=DEBUG +# --env FLASK_ENV=development +# --env OSM_ADDRESS=mock_osm_nbi +# $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG +# - while ! docker logs $IMAGE_NAME 2>&1 | grep -q 'Running...'; do sleep 1; done +# - docker ps -a +# - docker logs $IMAGE_NAME +# - docker logs mock_osm_nbi +# - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report_unitary.xml" +# - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" +# coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' +# after_script: +# - docker logs $IMAGE_NAME +# - docker logs mock_osm_nbi +# +# # Do Docker cleanup +# - docker ps --all --quiet | xargs --no-run-if-empty docker stop +# - docker container prune --force +# - docker ps --all --quiet | xargs --no-run-if-empty docker rm --force +# - docker image prune --force +# - docker network prune --force +# - docker volume prune --all --force +# - docker buildx prune --force +# +# 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"' +# - changes: +# - src/common/**/*.py +# - proto/*.proto +# - src/$IMAGE_NAME/**/*.{py,in,yml} +# - src/$IMAGE_NAME/Dockerfile +# - src/$IMAGE_NAME/tests/*.py +# - manifests/${IMAGE_NAME}service.yaml +# - src/tests/tools/mock_osm_nbi/**/*.{py,in,yml,yaml,yang,sh,json} +# - src/tests/tools/mock_osm_nbi/Dockerfile +# - src/tests/.gitlab-ci.yml +# - .gitlab-ci.yml +# artifacts: +# when: always +# reports: +# junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report_*.xml diff --git a/src/pathcomp/.gitlab-ci.yml b/src/pathcomp/.gitlab-ci.yml index cb6e2d98bd45d96b3fda2fc031307f9927e2d38b..ff9da4fc32bd9be9c0fae873e74a418e493f9892 100644 --- a/src/pathcomp/.gitlab-ci.yml +++ b/src/pathcomp/.gitlab-ci.yml @@ -137,7 +137,7 @@ unit_test pathcomp-frontend: - docker logs ${IMAGE_NAME}-backend - > docker exec -i ${IMAGE_NAME}-frontend bash -c - "coverage run -m pytest --log-level=INFO --verbose --junitxml=/opt/results/${IMAGE_NAME}-frontend_report.xml $IMAGE_NAME/frontend/tests/test_unitary.py $IMAGE_NAME/frontend/tests/test_unitary_pathcomp_forecaster.py" + "coverage run -m pytest --log-level=DEBUG --verbose --junitxml=/opt/results/${IMAGE_NAME}-frontend_report.xml $IMAGE_NAME/frontend/tests/test_unitary.py $IMAGE_NAME/frontend/tests/test_unitary_pathcomp_forecaster.py" - docker exec -i ${IMAGE_NAME}-frontend bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' after_script: diff --git a/src/pathcomp/frontend/service/algorithms/KDisjointPathAlgorithm.py b/src/pathcomp/frontend/service/algorithms/KDisjointPathAlgorithm.py index e1da3db2c2c035384093e2943d026e50f6cf9c09..00dbea5be3984fc49167816e4c9730f43fc893ef 100644 --- a/src/pathcomp/frontend/service/algorithms/KDisjointPathAlgorithm.py +++ b/src/pathcomp/frontend/service/algorithms/KDisjointPathAlgorithm.py @@ -63,9 +63,9 @@ class KDisjointPathAlgorithm(_Algorithm): elif kind == 'endpoint_location': endpoint_id = constraint.endpoint_location.endpoint_id device_uuid = endpoint_id.device_id.device_uuid.uuid - device_uuid = self.device_name_mapping.get(device_uuid, device_uuid) + device_uuid = self.device_uuid_mapping.get(device_uuid, device_uuid) endpoint_uuid = endpoint_id.endpoint_uuid.uuid - endpoint_uuid = self.endpoint_name_mapping.get((device_uuid, endpoint_uuid), endpoint_uuid) + endpoint_uuid = self.endpoint_uuid_mapping.get((device_uuid, endpoint_uuid), endpoint_uuid) location_kind = constraint.endpoint_location.location.WhichOneof('location') if location_kind != 'region': MSG = 'Unsupported LocationType({:s}) in Constraint({:s})' @@ -76,9 +76,9 @@ class KDisjointPathAlgorithm(_Algorithm): elif kind == 'endpoint_priority': endpoint_id = constraint.endpoint_priority.endpoint_id device_uuid = endpoint_id.device_id.device_uuid.uuid - device_uuid = self.device_name_mapping.get(device_uuid, device_uuid) + device_uuid = self.device_uuid_mapping.get(device_uuid, device_uuid) endpoint_uuid = endpoint_id.endpoint_uuid.uuid - endpoint_uuid = self.endpoint_name_mapping.get((device_uuid, endpoint_uuid), endpoint_uuid) + endpoint_uuid = self.endpoint_uuid_mapping.get((device_uuid, endpoint_uuid), endpoint_uuid) priority = constraint.endpoint_priority.priority endpoints.setdefault((device_uuid, endpoint_uuid), dict())['priority'] = priority @@ -130,9 +130,11 @@ class KDisjointPathAlgorithm(_Algorithm): algorithm.sync_paths = True algorithm.device_list = self.device_list algorithm.device_name_mapping = self.device_name_mapping + algorithm.device_uuid_mapping = self.device_uuid_mapping algorithm.device_dict = self.device_dict algorithm.endpoint_dict = self.endpoint_dict algorithm.endpoint_name_mapping = self.endpoint_name_mapping + algorithm.endpoint_uuid_mapping = self.endpoint_uuid_mapping algorithm.link_list = self.link_list algorithm.link_dict = self.link_dict algorithm.endpoint_to_link_dict = self.endpoint_to_link_dict diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py index a5bfe1352c09f71929719d63a3a869f34eca8edc..5e1a5e3a610928fcd8b1e6180d54e2f60d907deb 100644 --- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py +++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py @@ -12,23 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json, logging, requests, uuid +import json, logging, re, requests, uuid from typing import Dict, List, Optional, Tuple, Union from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import ( - ConfigRule, Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum + ConfigActionEnum, ConfigRule, Connection, Device, DeviceList, EndPointId, + Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum, ) from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest +from common.tools.context_queries.Device import get_device +from common.tools.grpc.ConfigRules import update_config_rule_custom from common.tools.grpc.Tools import grpc_message_list_to_json +from context.client.ContextClient import ContextClient from pathcomp.frontend.Config import BACKEND_URL from .tools.EroPathToHops import eropath_to_hops from .tools.ComposeConfigRules import ( - compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules, - generate_neighbor_endpoint_config_rules + compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, + compose_tapi_config_rules, generate_neighbor_endpoint_config_rules, ) from .tools.ComposeRequest import compose_device, compose_link, compose_service from .tools.ComputeSubServices import ( - convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection) + convert_explicit_path_hops_to_connections, + convert_explicit_path_hops_to_plain_connection, +) SRC_END = 'src' DST_END = 'dst' @@ -48,8 +54,10 @@ class _Algorithm: self.device_list : List[Dict] = list() self.device_dict : Dict[str, Tuple[Dict, Device]] = dict() + self.device_uuid_mapping : Dict[str, str] = dict() self.device_name_mapping : Dict[str, str] = dict() self.endpoint_dict : Dict[str, Dict[str, Tuple[Dict, EndPointId]]] = dict() + self.endpoint_uuid_mapping : Dict[Tuple[str, str], str] = dict() self.endpoint_name_mapping : Dict[Tuple[str, str], str] = dict() self.link_list : List[Dict] = list() self.link_dict : Dict[str, Tuple[Dict, Link]] = dict() @@ -68,8 +76,10 @@ class _Algorithm: _device_uuid = grpc_device.device_id.device_uuid.uuid _device_name = grpc_device.name - self.device_name_mapping[_device_name] = _device_uuid - self.device_name_mapping[_device_uuid] = _device_uuid + self.device_uuid_mapping[_device_name] = _device_uuid + self.device_uuid_mapping[_device_uuid] = _device_uuid + self.device_name_mapping[_device_name] = _device_name + self.device_name_mapping[_device_uuid] = _device_name device_endpoint_dict : Dict[str, Tuple[Dict, EndPointId]] = dict() for json_endpoint,grpc_endpoint in zip(json_device['device_endpoints'], grpc_device.device_endpoints): @@ -79,10 +89,14 @@ class _Algorithm: _endpoint_uuid = grpc_endpoint.endpoint_id.endpoint_uuid.uuid _endpoint_name = grpc_endpoint.name - self.endpoint_name_mapping[(_device_uuid, _endpoint_name)] = _endpoint_uuid - self.endpoint_name_mapping[(_device_name, _endpoint_name)] = _endpoint_uuid - self.endpoint_name_mapping[(_device_uuid, _endpoint_uuid)] = _endpoint_uuid - self.endpoint_name_mapping[(_device_name, _endpoint_uuid)] = _endpoint_uuid + self.endpoint_uuid_mapping[(_device_uuid, _endpoint_name)] = _endpoint_uuid + self.endpoint_uuid_mapping[(_device_name, _endpoint_name)] = _endpoint_uuid + self.endpoint_uuid_mapping[(_device_uuid, _endpoint_uuid)] = _endpoint_uuid + self.endpoint_uuid_mapping[(_device_name, _endpoint_uuid)] = _endpoint_uuid + self.endpoint_name_mapping[(_device_uuid, _endpoint_name)] = _endpoint_name + self.endpoint_name_mapping[(_device_name, _endpoint_name)] = _endpoint_name + self.endpoint_name_mapping[(_device_uuid, _endpoint_uuid)] = _endpoint_name + self.endpoint_name_mapping[(_device_name, _endpoint_uuid)] = _endpoint_name self.endpoint_dict[device_uuid] = device_endpoint_dict @@ -176,16 +190,16 @@ class _Algorithm: service.service_id.context_id.context_uuid.uuid = context_uuid service.service_id.service_uuid.uuid = service_uuid service.service_type = service_type - rules_nb = len(config_rules) + + #rules_nb = len(config_rules) + rules_nb = 0 # NOTE: do we need to skip default rules if there are other rules? if service_type == ServiceTypeEnum.SERVICETYPE_L2NM and rules_nb == 0: compose_l2nm_config_rules(config_rules, service.service_config.config_rules) self.logger.info("Installing default rules for L2NM service") - pass elif service_type == ServiceTypeEnum.SERVICETYPE_L3NM and rules_nb == 0: compose_l3nm_config_rules(config_rules, service.service_config.config_rules) self.logger.info("Installing default rules for L3NM service") - pass elif service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE and rules_nb == 0: compose_tapi_config_rules(config_rules, service.service_config.config_rules) self.logger.info("Installing default rules for TAPI service") @@ -195,7 +209,58 @@ class _Algorithm: compose_device_config_rules( config_rules, service.service_config.config_rules, path_hops, - self.device_name_mapping, self.endpoint_name_mapping) + self.device_uuid_mapping, self.endpoint_uuid_mapping) + + context_client = ContextClient() + device_cache : Dict[str, Device] = dict() + for path_hop in path_hops: + path_hop_device_id = path_hop['device'] + path_hop_ingress_ep_id = path_hop['ingress_ep'] + path_hop_egress_ep_id = path_hop['egress_ep'] + + path_hop_device_uuid = self.device_uuid_mapping[path_hop_device_id] + if path_hop_device_uuid in device_cache: + path_hop_device = device_cache[path_hop_device_uuid] + else: + path_hop_device = get_device( + context_client, path_hop_device_uuid, include_components=False, + include_endpoints=False, include_config_rules=True + ) + device_cache[path_hop_device_uuid] = path_hop_device + + target_endpoint_ids = { + self.endpoint_uuid_mapping[(path_hop_device_id, path_hop_ingress_ep_id)], + self.endpoint_uuid_mapping[(path_hop_device_id, path_hop_egress_ep_id )], + self.endpoint_name_mapping[(path_hop_device_id, path_hop_ingress_ep_id)], + self.endpoint_name_mapping[(path_hop_device_id, path_hop_egress_ep_id )], + } + + path_hop_device_name = path_hop_device.name + + RE_ENDPOINT_SETTINGS = re.compile(r'\/endpoints\/endpoint\[([^\]]+)\](\/settings)?') + for config_rule in path_hop_device.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + resource_key = str(config_rule.custom.resource_key) + ep_match = RE_ENDPOINT_SETTINGS.match(resource_key) + if ep_match is None: continue + endpoint_id = ep_match.group(1) + if endpoint_id not in target_endpoint_ids: continue + endpoint_name = self.endpoint_name_mapping[(path_hop_device_id, endpoint_id)] + + resource_value : Dict = json.loads(config_rule.custom.resource_value) + address_ip = resource_value.pop('address_ip', '0.0.0.0') + if address_ip != '0.0.0.0': resource_value['address_ip'] = address_ip + + if len(resource_value) == 0: continue + field_updates = {name:(value, False) for name,value in resource_value.items()} + + resource_key = '/device[{:s}]/endpoint[{:s}]/settings'.format( + path_hop_device_name, endpoint_name + ) + update_config_rule_custom( + service.service_config.config_rules, resource_key, field_updates, + new_action=ConfigActionEnum.CONFIGACTION_SET + ) if path_hops is not None and len(path_hops) > 0: ingress_endpoint_id = service.service_endpoint_ids.add() @@ -249,7 +314,7 @@ class _Algorithm: path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict) json_generated_config_rules = generate_neighbor_endpoint_config_rules( - json_orig_config_rules, path_hops, self.device_name_mapping, self.endpoint_name_mapping + json_orig_config_rules, path_hops, self.device_uuid_mapping, self.endpoint_uuid_mapping ) json_extended_config_rules = list() json_extended_config_rules.extend(json_orig_config_rules) @@ -262,9 +327,13 @@ class _Algorithm: self.logger.debug('path_hops = {:s}'.format(str(path_hops))) device_types = {v[0]['device_type'] for k,v in self.device_dict.items()} DEVICES_BASIC_CONNECTION = { - DeviceTypeEnum.DATACENTER.value, DeviceTypeEnum.EMULATED_DATACENTER.value, - DeviceTypeEnum.CLIENT.value, DeviceTypeEnum.EMULATED_CLIENT.value, - DeviceTypeEnum.PACKET_ROUTER.value, DeviceTypeEnum.EMULATED_PACKET_ROUTER.value, + DeviceTypeEnum.EMULATED_CLIENT.value, + DeviceTypeEnum.EMULATED_COMPUTER.value, + DeviceTypeEnum.EMULATED_DATACENTER.value, + DeviceTypeEnum.EMULATED_VIRTUAL_MACHINE.value, + DeviceTypeEnum.EMULATED_PACKET_ROUTER.value, + DeviceTypeEnum.PACKET_POP.value, + DeviceTypeEnum.PACKET_ROUTER.value, } self.logger.debug('device_types = {:s}'.format(str(device_types))) self.logger.debug('DEVICES_BASIC_CONNECTION = {:s}'.format(str(DEVICES_BASIC_CONNECTION))) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py index 087b09754a01593cda80e9b29313095c14fe918f..497e4ce350cfae347df622e5250778adf274164c 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py @@ -119,6 +119,7 @@ def compose_link(grpc_link : Link) -> Dict: ] total_capacity_gbps, used_capacity_gbps = None, None + forwarding_direction = LinkForwardingDirection.UNIDIRECTIONAL.value if grpc_link.HasField('attributes'): attributes = grpc_link.attributes # In proto3, HasField() does not work for scalar fields, using ListFields() instead. @@ -130,11 +131,15 @@ def compose_link(grpc_link : Link) -> Dict: elif total_capacity_gbps is not None: used_capacity_gbps = total_capacity_gbps + if 'is_bidirectional' in attribute_names: + is_bidirectional = attributes.is_bidirectional + if is_bidirectional: + forwarding_direction = LinkForwardingDirection.BIDIRECTIONAL.value + if total_capacity_gbps is None: total_capacity_gbps = 100000 if used_capacity_gbps is None: used_capacity_gbps = 0 available_capacity_gbps = total_capacity_gbps - used_capacity_gbps - forwarding_direction = LinkForwardingDirection.UNIDIRECTIONAL.value total_potential_capacity = compose_capacity(total_capacity_gbps, CapacityUnit.GBPS.value) available_capacity = compose_capacity(available_capacity_gbps, CapacityUnit.GBPS.value) cost_characteristics = compose_cost_characteristics('linkcost', '1', '0') diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py index 3b8e4bbb13f4a6b3b7e7f131ac17f6f280608ef3..b92ccaf3467f279e5823c06324e0deff693e9301 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py @@ -114,6 +114,14 @@ def convert_explicit_path_hops_to_connections( elif prv_res_class[2] is None and res_class[2] is not None: # entering domain of a device controller, create underlying connection LOGGER.debug(' entering domain of a device controller, create underlying connection') + + if len(connection_stack.queue) == 0: + LOGGER.debug(' synthetic path ingress') + connection_entry = ConnectionEntry( + uuid=main_service_uuid, service_type=main_service_type, path_hops=[] + ) + connection_stack.put(connection_entry) + prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) @@ -123,6 +131,7 @@ def convert_explicit_path_hops_to_connections( LOGGER.debug(' leaving domain of a device controller, terminate underlying connection') connection = connection_stack.get() connections.append(connection) + connection_stack.queue[-1].dependencies.append(connection) connection_stack.queue[-1].path_hops.append(path_hop) elif prv_res_class[2] is not None and res_class[2] is not None: @@ -184,6 +193,13 @@ def convert_explicit_path_hops_to_connections( prv_device_uuid = device_uuid prv_res_class = res_class + + while len(connection_stack.queue) > 1: + LOGGER.debug(' synthetic path egress') + connection = connection_stack.get() + connections.append(connection) + connection_stack.queue[-1].dependencies.append(connection) + # path egress LOGGER.debug(' path egress') connections.append(connection_stack.get()) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py index 06b0f90e8f3b50fa38854b7fa0ec55d5bc6344ab..6a696501307313903b9ac5d242479dbb679ec19e 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py @@ -13,17 +13,17 @@ # limitations under the License. -import json from typing import Dict, Optional, Tuple from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import Device from common.tools.grpc.Tools import grpc_message_to_json_string + DEVICE_TYPE_TO_DEEPNESS = { - DeviceTypeEnum.EMULATED_DATACENTER.value : 90, - DeviceTypeEnum.DATACENTER.value : 90, DeviceTypeEnum.EMULATED_CLIENT.value : 90, - DeviceTypeEnum.CLIENT.value : 90, + DeviceTypeEnum.EMULATED_COMPUTER.value : 90, + DeviceTypeEnum.EMULATED_DATACENTER.value : 90, + DeviceTypeEnum.EMULATED_VIRTUAL_MACHINE.value : 90, DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value : 80, DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER.value : 80, @@ -33,6 +33,7 @@ DEVICE_TYPE_TO_DEEPNESS = { DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : 70, + DeviceTypeEnum.PACKET_POP.value : 70, DeviceTypeEnum.PACKET_ROUTER.value : 70, DeviceTypeEnum.EMULATED_PACKET_SWITCH.value : 60, @@ -49,28 +50,53 @@ DEVICE_TYPE_TO_DEEPNESS = { DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value : 30, DeviceTypeEnum.OPEN_LINE_SYSTEM.value : 30, - DeviceTypeEnum.EMULATED_PACKET_RADIO_ROUTER.value : 10, - DeviceTypeEnum.PACKET_RADIO_ROUTER.value : 10, - DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER.value : 10, - DeviceTypeEnum.OPTICAL_TRANSPONDER.value : 10, DeviceTypeEnum.EMULATED_OPTICAL_ROADM.value : 10, + DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER.value : 10, + DeviceTypeEnum.OPEN_ROADM.value : 10, + DeviceTypeEnum.OPTICAL_FGOTN.value : 10, + DeviceTypeEnum.OPTICAL_OLT.value : 10, + DeviceTypeEnum.OPTICAL_ONT.value : 10, DeviceTypeEnum.OPTICAL_ROADM.value : 10, + DeviceTypeEnum.OPTICAL_TRANSPONDER.value : 10, + + DeviceTypeEnum.EMULATED_PACKET_RADIO_ROUTER.value : 10, + DeviceTypeEnum.PACKET_RADIO_ROUTER.value : 10, DeviceTypeEnum.QKD_NODE.value : 10, - DeviceTypeEnum.OPEN_ROADM.value : 10, DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER.value : 0, DeviceTypeEnum.NETWORK.value : 0, # network out of our control; always delegate } + IGNORED_DEVICE_TYPES = {DeviceTypeEnum.EMULATED_OPTICAL_SPLITTER} REMOTEDOMAIN_DEVICE_TYPES = {DeviceTypeEnum.NETWORK} + +def get_device( + device_dict : Dict[str, Tuple[Dict, Device]], device_uuid : str, + fail_if_not_found : bool = True +) -> Tuple[Dict, Device]: + device_tuple = device_dict.get(device_uuid) + if device_tuple is None and fail_if_not_found: + MSG = 'Device({:s}) not found' + raise Exception(MSG.format(str(device_uuid))) + return device_tuple + + def get_device_controller_uuid( - device : Device + device : Device, device_dict : Dict[str, Tuple[Dict, Device]] ) -> Optional[str]: - controller_uuid = device.controller_id.device_uuid.uuid - if len(controller_uuid) > 0: return controller_uuid - return None + last_controller_uuid = None + while True: + controller_uuid = device.controller_id.device_uuid.uuid + if len(controller_uuid) == 0: return last_controller_uuid + controller_tuple = get_device(device_dict, controller_uuid, fail_if_not_found=False) + if controller_tuple is None: + MSG = 'Unable to find referenced Controller({:s})' + raise Exception(MSG.format(str(controller_uuid))) + last_controller_uuid = controller_uuid + _, device = controller_tuple + def _map_device_type(device : Device) -> DeviceTypeEnum: device_type = DeviceTypeEnum._value2member_map_.get(device.device_type) # pylint: disable=no-member @@ -79,24 +105,25 @@ def _map_device_type(device : Device) -> DeviceTypeEnum: raise Exception(MSG.format(str(device.device_type), grpc_message_to_json_string(device))) return device_type + def _map_resource_to_deepness(device_type : DeviceTypeEnum) -> int: deepness = DEVICE_TYPE_TO_DEEPNESS.get(device_type.value) if deepness is None: raise Exception('Unsupported DeviceType({:s})'.format(str(device_type.value))) return deepness + def get_device_type( device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_controller_uuid : Optional[str] ) -> DeviceTypeEnum: if device_controller_uuid is None: return _map_device_type(device) - device_controller_tuple = device_dict.get(device_controller_uuid) - if device_controller_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_controller_uuid))) - _,device = device_controller_tuple + _,device = get_device(device_dict, device_controller_uuid) return _map_device_type(device) + def get_resource_classification( device : Device, device_dict : Dict[str, Tuple[Dict, Device]] ) -> Tuple[int, DeviceTypeEnum, Optional[str]]: - device_controller_uuid = get_device_controller_uuid(device) + device_controller_uuid = get_device_controller_uuid(device, device_dict) device_type = get_device_type(device, device_dict, device_controller_uuid) resource_deepness = _map_resource_to_deepness(device_type) return resource_deepness, device_type, device_controller_uuid diff --git a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py index 2f55db0c668d653e08c9cdbcf3361f83d92cbded..5943bf1ba245f7fe3997492f88bcc5787dc830cb 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py @@ -24,6 +24,7 @@ PACKET_DEVICE_TYPES = { DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, DeviceTypeEnum.IETF_SLICE, DeviceTypeEnum.NCE, DeviceTypeEnum.IP_SDN_CONTROLLER, DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER, + DeviceTypeEnum.PACKET_POP, DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER, DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH, } @@ -47,8 +48,15 @@ SERVICE_TYPE_L3NM = {ServiceTypeEnum.SERVICETYPE_L3NM} SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM} SERVICE_TYPE_TAPI = {ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE} -def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum: - if device_type in PACKET_DEVICE_TYPES and prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type +def get_service_type( + device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum +) -> ServiceTypeEnum: + if device_type is DeviceTypeEnum.NCE: return ServiceTypeEnum.SERVICETYPE_L3NM + if device_type is DeviceTypeEnum.TERAFLOWSDN_CONTROLLER: return ServiceTypeEnum.SERVICETYPE_L3NM + if ( + device_type in PACKET_DEVICE_TYPES and + prv_service_type in SERVICE_TYPE_LXNM + ): return prv_service_type if device_type in L2_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_L2NM if device_type in OPTICAL_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE if device_type in NETWORK_DEVICE_TYPES: return prv_service_type diff --git a/src/service/service/service_handler_api/ServiceHandlerFactory.py b/src/service/service/service_handler_api/ServiceHandlerFactory.py index a5b3bed2ae80aa928d0d77285761a7b7078dcc09..96ab0ae06aac20bd9f866bd3203948baa40625f1 100644 --- a/src/service/service/service_handler_api/ServiceHandlerFactory.py +++ b/src/service/service/service_handler_api/ServiceHandlerFactory.py @@ -100,9 +100,6 @@ class ServiceHandlerFactory: candidate_service_handler_classes.items(), key=operator.itemgetter(1), reverse=True) return candidate_service_handler_classes[0][0] -def get_device_supported_drivers(device : Device) -> Set[int]: - return {device_driver for device_driver in device.device_drivers} - def get_common_device_drivers(drivers_per_device : List[Set[int]]) -> Set[int]: common_device_drivers = None for device_drivers in drivers_per_device: @@ -114,15 +111,16 @@ def get_common_device_drivers(drivers_per_device : List[Set[int]]) -> Set[int]: return common_device_drivers def get_service_handler_class( - service_handler_factory : ServiceHandlerFactory, service : Service, connection_devices : Dict[str, Device] + service_handler_factory : ServiceHandlerFactory, service : Service, + device_and_drivers: Dict[str, Tuple[Device, Set[int]]] ) -> Optional['_ServiceHandler']: str_service_key = grpc_message_to_json_string(service.service_id) # Assume all devices involved in the service's connection must support at least one driver in common common_device_drivers = get_common_device_drivers([ - get_device_supported_drivers(device) - for device in connection_devices.values() + device_drivers + for _,device_drivers in device_and_drivers.values() ]) filter_fields = { diff --git a/src/service/service/service_handler_api/SettingsHandler.py b/src/service/service/service_handler_api/SettingsHandler.py index b9b8b2950dcfff989861ce09e84cdb08ff628e31..5c9bc79a76215003a67d3fb7563b0102148b8e97 100644 --- a/src/service/service/service_handler_api/SettingsHandler.py +++ b/src/service/service/service_handler_api/SettingsHandler.py @@ -78,12 +78,18 @@ class SettingsHandler: for device_key in device_keys: for endpoint_key in endpoint_keys: + # should navigate from deepest to top-level + endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_key, endpoint_key) endpoint_settings = self.get(endpoint_settings_uri) if endpoint_settings is not None: return endpoint_settings + endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]'.format(device_key, endpoint_key) + endpoint_settings = self.get(endpoint_settings_uri) + if endpoint_settings is not None: return endpoint_settings + return None - + def get_endpoint_acls(self, device : Device, endpoint : EndPoint) -> List [Tuple]: endpoint_name = endpoint.name device_keys = device.device_id.device_uuid.uuid, device.name diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index 1aba88e303ccb99afec1995aa7e9ad5b35414377..2b06a1c3484131fa715ea1414a49d83e52c43f03 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -16,14 +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 .l3nm_ietfactn.L3NM_IETFACTN_ServiceHandler import L3NM_IETFACTN_ServiceHandler +from .l3nm_ietfl3vpn.L3NM_IETFL3VPN_ServiceHandler import L3NM_IETFL3VPN_ServiceHandler +from .l3nm_ietfslice.L3NM_IETFSlice_ServiceHandler import L3NM_IETFSlice_ServiceHandler +from .l3nm_ncefan.L3NM_NCEFAN_ServiceHandler import L3NM_NCEFAN_ServiceHandler +from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceHandler from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler from .p4_dummy_l1.p4_dummy_l1_service_handler import P4DummyL1ServiceHandler from .p4_fabric_tna_int.p4_fabric_tna_int_service_handler import P4FabricINTServiceHandler @@ -68,7 +68,7 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG, } ]), - (L3NMIetfActnServiceHandler, [ + (L3NM_IETFACTN_ServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L3NM, FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN, @@ -80,13 +80,13 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_L3VPN, } ]), - (L3NMNCEServiceHandler, [ + (L3NM_NCEFAN_ServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L3NM, FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_NCE, } ]), - (L3NMSliceIETFSliceServiceHandler, [ + (L3NM_IETFSlice_ServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_L3NM, FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_SLICE, diff --git a/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py b/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py index a1857c3b222fe577e9ca64c98615e2e9d23a9b1c..cf0eacab515d86e9ec9bc418adeb91f720d0b376 100644 --- a/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py +++ b/src/service/service/service_handlers/l3nm_gnmi_openconfig/ConfigRuleComposer.py @@ -225,7 +225,11 @@ class DeviceComposer: self.static_routes.setdefault(prefix, dict())[metric] = next_hop def get_config_rules(self, network_instance_name : str, delete : bool = False) -> List[Dict]: - SELECTED_DEVICES = {DeviceTypeEnum.PACKET_ROUTER.value, DeviceTypeEnum.EMULATED_PACKET_ROUTER.value} + SELECTED_DEVICES = { + DeviceTypeEnum.PACKET_POP.value, + DeviceTypeEnum.PACKET_ROUTER.value, + DeviceTypeEnum.EMULATED_PACKET_ROUTER.value + } if self.objekt.device_type not in SELECTED_DEVICES: return [] json_config_rule = json_config_rule_delete if delete else json_config_rule_set diff --git a/src/service/service/service_handlers/l3nm_ietf_actn/Constants.py b/src/service/service/service_handlers/l3nm_ietfactn/Constants.py similarity index 77% rename from src/service/service/service_handlers/l3nm_ietf_actn/Constants.py rename to src/service/service/service_handlers/l3nm_ietfactn/Constants.py index d09790bdf9eb40c6b58fd9e0c524bf408c19aa52..5f72bfbbc06ecc8e083c409e22b9688d09cc7d8c 100644 --- a/src/service/service/service_handlers/l3nm_ietf_actn/Constants.py +++ b/src/service/service/service_handlers/l3nm_ietfactn/Constants.py @@ -15,8 +15,8 @@ # These hardcoded values will be updated with proper logic in second phase of the PoC VPN_VLAN_TAGS_TO_SERVICE_NAME = { - (21, 101): ('osu_tunnel_1', 'etht_service_1'), - (31, 201): ('osu_tunnel_2', 'etht_service_2'), + (21, 201): ('osu_tunnel_1', 'etht_service_1'), + (31, 101): ('osu_tunnel_2', 'etht_service_2'), } OSU_TUNNEL_SETTINGS = { @@ -26,8 +26,8 @@ OSU_TUNNEL_SETTINGS = { 'bidirectional': True, 'delay': 20, 'ttp_channel_names': { - ('10.0.10.1', '200'): 'och:1-odu2:1-oduflex:1-osuflex:2', - ('10.0.30.1', '200'): 'och:1-odu2:1-oduflex:3-osuflex:1', + ('O-PE1', '200'): 'och:1-odu2:1-oduflex:1-osuflex:2', + ('O-PE2', '200'): 'och:1-odu2:1-oduflex:3-osuflex:1', } }, 'osu_tunnel_2': { @@ -36,8 +36,8 @@ OSU_TUNNEL_SETTINGS = { 'bidirectional': True, 'delay': 20, 'ttp_channel_names': { - ('10.0.10.1', '200'): 'och:1-odu2:1-oduflex:1-osuflex:2', - ('10.0.30.1', '200'): 'och:1-odu2:1-oduflex:3-osuflex:1', + ('O-PE1', '200'): 'och:1-odu2:1-oduflex:1-osuflex:2', + ('O-PE2', '200'): 'och:1-odu2:1-oduflex:3-osuflex:1', } }, } diff --git a/src/service/service/service_handlers/l3nm_ietf_actn/L3NMIetfActnServiceHandler.py b/src/service/service/service_handlers/l3nm_ietfactn/L3NM_IETFACTN_ServiceHandler.py similarity index 95% rename from src/service/service/service_handlers/l3nm_ietf_actn/L3NMIetfActnServiceHandler.py rename to src/service/service/service_handlers/l3nm_ietfactn/L3NM_IETFACTN_ServiceHandler.py index 6129d07de7ec7c4869a4abde48b4bd48706f9067..4b75e7ec203150c235c0b7567f3fdb7dee1a0320 100644 --- a/src/service/service/service_handlers/l3nm_ietf_actn/L3NMIetfActnServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_ietfactn/L3NM_IETFACTN_ServiceHandler.py @@ -30,7 +30,7 @@ LOGGER = logging.getLogger(__name__) METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_ietf_actn'}) -class L3NMIetfActnServiceHandler(_ServiceHandler): +class L3NM_IETFACTN_ServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings ) -> None: @@ -43,9 +43,19 @@ class L3NMIetfActnServiceHandler(_ServiceHandler): ) -> Tuple[Device, EndPoint, Dict]: device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint) device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + device_name = device_obj.name + device_prefix = '/device[{:s}]/'.format(device_name) + for config_rule in device_obj.device_config.config_rules: + if config_rule.WhichOneof('config_rule') != 'custom': continue + rw_config_rule = ConfigRule() + rw_config_rule.CopyFrom(config_rule) + resource_key = str(config_rule.custom.resource_key) + if resource_key.startswith('/endpoints/endpoint['): + resource_key = resource_key.replace('/endpoints/', device_prefix) + rw_config_rule.custom.resource_key = resource_key + self.__settings_handler.update_config_rule(rw_config_rule) endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) - device_name = device_obj.name endpoint_name = endpoint_obj.name if endpoint_settings is None: MSG = 'Settings not found for Endpoint(device=[uuid={:s}, name={:s}], endpoint=[uuid={:s}, name={:s}])' @@ -109,8 +119,7 @@ class L3NMIetfActnServiceHandler(_ServiceHandler): self, src_vlan_tag : int, dst_vlan_tag : int ) -> Tuple[List[Dict], List[Dict]]: static_routing = self.__settings_handler.get('/static_routing') - if static_routing is None: raise Exception('static_routing not found') - static_routing_dict : Dict = static_routing.value + static_routing_dict : Dict = dict() if static_routing is None else static_routing.value src_static_routes = list() dst_static_routes = list() for _, static_route in static_routing_dict.items(): @@ -256,8 +265,8 @@ class L3NMIetfActnServiceHandler(_ServiceHandler): ) del controller.device_config.config_rules[:] - controller.device_config.config_rules.append(osu_tunnel_config_rule) controller.device_config.config_rules.append(etht_service_config_rule) + controller.device_config.config_rules.append(osu_tunnel_config_rule) self.__task_executor.configure_device(controller) results.append(True) except Exception as e: # pylint: disable=broad-except diff --git a/src/service/service/service_handlers/l3nm_ietfactn/__init__.py b/src/service/service/service_handlers/l3nm_ietfactn/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfactn/__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/l3nm_ietfl3vpn/ConfigRules.py b/src/service/service/service_handlers/l3nm_ietfl3vpn/ConfigRules.py index c5638fc104c253be20ef1bbeb6c69a4392095ad2..3b537a4675db4d16c66756804eebe028a3f94791 100644 --- a/src/service/service/service_handlers/l3nm_ietfl3vpn/ConfigRules.py +++ b/src/service/service/service_handlers/l3nm_ietfl3vpn/ConfigRules.py @@ -259,10 +259,10 @@ def setup_config_rules( "/service[{:s}]/IETFL3VPN".format(service_uuid), l3_vpn_data_model, ), - json_config_rule_set( - "/service[{:s}]/IETFL3VPN/operation".format(service_uuid), - {"type": operation_type}, - ), + #json_config_rule_set( + # "/service[{:s}]/IETFL3VPN/operation".format(service_uuid), + # {"type": operation_type}, + #), ] return json_config_rules @@ -274,10 +274,10 @@ def teardown_config_rules(service_uuid: str) -> List[Dict]: "/service[{:s}]/IETFL3VPN".format(service_uuid), {"id": service_uuid}, ), - json_config_rule_delete( - "/service[{:s}]/IETFL3VPN/operation".format(service_uuid), - {}, - ), + #json_config_rule_delete( + # "/service[{:s}]/IETFL3VPN/operation".format(service_uuid), + # {}, + #), ] return json_config_rules 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 index 2a412aa5383130daff3ce23320e3fecaf000597b..7c6522ee2d0ccb5a1bb08a92c284e889dd3018bb 100644 --- a/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_ietfl3vpn/L3NM_IETFL3VPN_ServiceHandler.py @@ -27,10 +27,10 @@ from common.proto.context_pb2 import ( Service, ServiceConfig, ) +from common.tools.grpc.Tools import grpc_message_to_json_string 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, @@ -194,13 +194,18 @@ def get_endpoint_settings(device_obj: Device, endpoint_name: str) -> dict: raise ValueError(f"Endpoint settings not found for endpoint {endpoint_name}") +PACKET_SDN_CONTROLLERS = { + DeviceTypeEnum.IP_SDN_CONTROLLER.value, + DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER.value, + DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value, +} + 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 @@ -218,7 +223,7 @@ class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): 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: + if device_controller.device_type in PACKET_SDN_CONTROLLERS: src_device_uuid, src_endpoint_uuid = device_uuid, endpoint_uuid src_device_controller = device_controller break @@ -232,7 +237,7 @@ class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): 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: + if device_controller.device_type in PACKET_SDN_CONTROLLERS: dst_device_uuid, dst_endpoint_uuid = device_uuid, endpoint_uuid dst_device_controller = device_controller break @@ -320,6 +325,10 @@ class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): endpoints: List[Tuple[str, str, Optional[str]]], connection_uuid: Optional[str] = None, ) -> List[Union[bool, Exception]]: + LOGGER.debug('[SetEndpoint] service={:s}'.format(grpc_message_to_json_string(self.__service))) + LOGGER.debug('[SetEndpoint] endpoints={:s}'.format(str(endpoints))) + LOGGER.debug('[SetEndpoint] connection_uuid={:s}'.format(str(connection_uuid))) + chk_type("endpoints", endpoints, list) if len(endpoints) < 2: return [] @@ -424,8 +433,9 @@ class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): controller.device_config.config_rules.append(ConfigRule(**jcr)) self.__task_executor.configure_device(controller) except Exception as e: # pylint: disable=broad-except + str_service_id = grpc_message_to_json_string(self.__service.service_id) LOGGER.exception( - "Unable to SetEndpoint for Service({:s})".format(str(service_id)) + "Unable to SetEndpoint for Service({:s})".format(str(str_service_id)) ) results.append(e) @@ -487,60 +497,38 @@ class L3NM_IETFL3VPN_ServiceHandler(_ServiceHandler): 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))] + 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 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))] + 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 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 + chk_type('resources', resources, list) + if len(resources) == 0: return [] + MSG = '[SetConfig] Method not implemented. Resources({:s}) are being ignored.' + LOGGER.warning(MSG.format(str(resources))) + return [True for _ in resources] @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 + chk_type('resources', resources, list) + if len(resources) == 0: return [] + MSG = '[DeleteConfig] Method not implemented. Resources({:s}) are being ignored.' + LOGGER.warning(MSG.format(str(resources))) + return [True for _ in resources] diff --git a/src/service/service/service_handlers/l3nm_ietfl3vpn/old/ConfigRules.py b/src/service/service/service_handlers/l3nm_ietfl3vpn/old/ConfigRules.py new file mode 100644 index 0000000000000000000000000000000000000000..c5638fc104c253be20ef1bbeb6c69a4392095ad2 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfl3vpn/old/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/old/L3NM_IETFL3VPN_ServiceHandler.py b/src/service/service/service_handlers/l3nm_ietfl3vpn/old/L3NM_IETFL3VPN_ServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..b542779112d0c5caad284941870789c31fcc3146 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfl3vpn/old/L3NM_IETFL3VPN_ServiceHandler.py @@ -0,0 +1,524 @@ +# 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 +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.grpc.Tools import grpc_message_to_json_string +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.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 + + 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 + str_service_id = grpc_message_to_json_string(self.__service.service_id) + LOGGER.exception( + "Unable to SetEndpoint for Service({:s})".format(str(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 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 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 [] + MSG = '[SetConfig] Method not implemented. Resources({:s}) are being ignored.' + LOGGER.warning(MSG.format(str(resources))) + return [True for _ in resources] + + @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 [] + MSG = '[DeleteConfig] Method not implemented. Resources({:s}) are being ignored.' + LOGGER.warning(MSG.format(str(resources))) + return [True for _ in resources] diff --git a/src/service/service/service_handlers/l3nm_ietfslice/DataStoreDelta.py b/src/service/service/service_handlers/l3nm_ietfslice/DataStoreDelta.py new file mode 100644 index 0000000000000000000000000000000000000000..725de5c2547b96c643923a67bdeec070dcd54866 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfslice/DataStoreDelta.py @@ -0,0 +1,48 @@ +# 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 +from deepdiff import DeepDiff +from typing import Dict, Optional +from common.proto.context_pb2 import Service + + +RUNNING_RESOURCE_KEY = 'running_ietf_slice' +CANDIDATE_RESOURCE_KEY = 'candidate_ietf_slice' + + +class DataStoreDelta: + def __init__(self, service : Service): + self._service = service + self._service_config = service.service_config + self._candidate_data = self._get_datastore_data(CANDIDATE_RESOURCE_KEY) + self._running_data = self._get_datastore_data(RUNNING_RESOURCE_KEY ) + + def _get_datastore_data(self, resource_key : str) -> Optional[Dict]: + for cr in self._service_config.config_rules: + if cr.WhichOneof('config_rule') != 'custom': continue + if cr.custom.resource_key != resource_key: continue + resource_value = json.loads(cr.custom.resource_value) + return resource_value.get('network-slice-services', dict()).get('slice-service') + return None + + @property + def candidate_data(self): return self._candidate_data + + @property + def running_data(self): return self._running_data + + def get_diff(self) -> Dict: + return DeepDiff(self._running_data, self._candidate_data) diff --git a/src/service/service/service_handlers/l3nm_ietfslice/L3NM_IETFSlice_ServiceHandler.py b/src/service/service/service_handlers/l3nm_ietfslice/L3NM_IETFSlice_ServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..54a4a0d309bccf42b07c7e6db85ce01c8e9d6ab1 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfslice/L3NM_IETFSlice_ServiceHandler.py @@ -0,0 +1,253 @@ +# 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 ipaddress, logging +from typing import Any, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.proto.context_pb2 import ConfigRule, DeviceId, Service +from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set +from common.tools.object_factory.Device import json_device_id +from common.type_checkers.Checkers import chk_type +from service.service.service_handler_api._ServiceHandler import _ServiceHandler +from service.service.service_handler_api.Tools import get_device_endpoint_uuids +from service.service.task_scheduler.TaskExecutor import TaskExecutor +from .DataStoreDelta import DataStoreDelta +from .Tools import get_device_endpoint_name + + +LOGGER = logging.getLogger(__name__) + + +METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_ietfslice'}) + + +class L3NM_IETFSlice_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 + + @metered_subclass_method(METRICS_POOL) + def SetEndpoint( + self, + endpoints: List[Tuple[str, str, Optional[str]]], + connection_uuid: Optional[str] = None, + ) -> List[Union[bool, Exception]]: + LOGGER.debug('[SetEndpoint] service={:s}'.format(grpc_message_to_json_string(self.__service))) + LOGGER.debug('[SetEndpoint] endpoints={:s}'.format(str(endpoints))) + LOGGER.debug('[SetEndpoint] connection_uuid={:s}'.format(str(connection_uuid))) + + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + results = [] + try: + # 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_endpoint_name = get_device_endpoint_name(src_device_obj, src_endpoint_uuid) + 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_endpoint_name = get_device_endpoint_name(dst_device_obj, dst_endpoint_uuid) + dst_controller = self.__task_executor.get_device_controller(dst_device_obj) + + + # 2. Identify controller to be used + 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 + + + # 3. Load DataStore configuration + datastore_delta = DataStoreDelta(self.__service) + candidate_slice = datastore_delta.candidate_data + + if len(candidate_slice) != 1: + MSG = 'Unsupported number of Slices[{:d}]({:s})' + raise Exception(MSG.format(len(candidate_slice), str(candidate_slice))) + candidate_slice = candidate_slice[0] + + slice_name = candidate_slice['id'] + + conn_groups = candidate_slice.get('connection-groups', dict()) + + + # 4. Adapt SDPs + candidate_sdps = candidate_slice.get('sdps', dict()).get('sdp', list()) + if len(candidate_sdps) != 2: + MSG = 'Unsupported number of SDPs[{:d}]({:s})' + raise Exception(MSG.format(len(candidate_sdps), str(candidate_sdps))) + + sdp_dict = {sdp['node-id'] : sdp for sdp in candidate_sdps} + if src_device_name in sdp_dict and dst_device_name not in sdp_dict: + src_device_sdp = sdp_dict.get(src_device_name) + + other_device_names = set(sdp_dict.keys()) + other_device_names.remove(src_device_name) + unneeded_sdp_id = other_device_names.pop() + + dst_device_sdp = sdp_dict.get(unneeded_sdp_id) + dst_device_sdp['node-id'] = dst_device_name + + try: + dst_mgmt_ip_address = str(ipaddress.ip_address(dst_device_name)) + except ValueError: + dst_mgmt_ip_address = '0.0.0.0' + dst_device_sdp['sdp-ip-address'] = [dst_mgmt_ip_address] + + dst_ac = dst_device_sdp['attachment-circuits']['attachment-circuit'][0] + dst_ac['id'] = 'AC {:s}'.format(str(dst_device_name)) + dst_ac['description'] = 'AC {:s}'.format(str(dst_device_name)) + dst_ac['ac-node-id'] = dst_device_name + dst_ac['ac-tp-id'] = dst_endpoint_name + + elif dst_device_name in sdp_dict and src_device_name not in sdp_dict: + dst_device_sdp = sdp_dict.get(dst_device_name) + + other_device_names = set(sdp_dict.keys()) + other_device_names.remove(dst_device_name) + unneeded_sdp_id = other_device_names.pop() + + src_device_sdp = sdp_dict.get(unneeded_sdp_id) + src_device_sdp['node-id'] = src_device_name + + try: + src_mgmt_ip_address = str(ipaddress.ip_address(src_device_name)) + except ValueError: + src_mgmt_ip_address = '0.0.0.0' + src_device_sdp['sdp-ip-address'] = [src_mgmt_ip_address] + + src_ac = src_device_sdp['attachment-circuits']['attachment-circuit'][0] + src_ac['id'] = 'AC {:s}'.format(str(src_device_name)) + src_ac['description'] = 'AC {:s}'.format(str(src_device_name)) + src_ac['ac-node-id'] = src_device_name + src_ac['ac-tp-id'] = src_endpoint_name + + else: + MSG = 'Unsupported case: sdp_dict={:s} src_device_name={:s} dst_device_name={:s}' + raise Exception(MSG.format(str(sdp_dict), str(src_device_name), str(dst_device_name))) + + + + # 5. Compose slice and setup it + + slice_data_model = {'network-slice-services': {'slice-service': [{ + 'id': slice_name, + 'description': slice_name, + 'sdps': {'sdp': [src_device_sdp, dst_device_sdp]}, + 'connection-groups': conn_groups, + }]}} + + del controller.device_config.config_rules[:] + controller.device_config.config_rules.append(ConfigRule(**json_config_rule_set( + '/service[{:s}]/IETFSlice'.format(slice_name), slice_data_model + ))) + self.__task_executor.configure_device(controller) + except Exception as e: + LOGGER.exception('Unable to handle Slice Setup') + 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]]: + LOGGER.debug('[DeleteEndpoint] service={:s}'.format(grpc_message_to_json_string(self.__service))) + LOGGER.debug('[DeleteEndpoint] endpoints={:s}'.format(str(endpoints))) + LOGGER.debug('[DeleteEndpoint] connection_uuid={:s}'.format(str(connection_uuid))) + + 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) + + datastore_delta = DataStoreDelta(self.__service) + running_slice = datastore_delta.running_data + + if len(running_slice) != 1: + MSG = 'Unsupported number of Slices[{:d}]({:s})' + raise Exception(MSG.format(len(running_slice), str(running_slice))) + running_slice = running_slice[0] + slice_name = running_slice['id'] + + slice_data_model = {'network-slice-services': {'slice-service': [{ + 'id': slice_name, + }]}} + + del controller.device_config.config_rules[:] + controller.device_config.config_rules.append(ConfigRule(**json_config_rule_delete( + '/service[{:s}]/IETFSlice'.format(slice_name), slice_data_model + ))) + self.__task_executor.configure_device(controller) + results.append(True) + except Exception as e: + LOGGER.exception('Unable to handle Slice Tear Down') + 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 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 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 [] + MSG = '[SetConfig] Method not implemented. Resources({:s}) are being ignored.' + LOGGER.warning(MSG.format(str(resources))) + return [True for _ in resources] + + @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 [] + MSG = '[DeleteConfig] Method not implemented. Resources({:s}) are being ignored.' + LOGGER.warning(MSG.format(str(resources))) + return [True for _ in resources] diff --git a/src/service/service/service_handlers/l3nm_ietfslice/Tools.py b/src/service/service/service_handlers/l3nm_ietfslice/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..3e06d0b77c1eb94e674801133370339e8ef19cf4 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfslice/Tools.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. + + +from common.proto.context_pb2 import Device + + +def get_device_endpoint_name(device_obj : Device, 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 + + device_uuid = str(device_obj.device_id.device_uuid.uuid) + device_name = str(device_obj.name) + MSG = 'Device({:s},{:s})/Endpoint({:s}) not found' + raise Exception(MSG.format(device_uuid, device_name, str(endpoint_uuid))) diff --git a/src/service/service/service_handlers/l3nm_ietfslice/__init__.py b/src/service/service/service_handlers/l3nm_ietfslice/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_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/service_handlers/l3slice_ietfslice/ConfigRules.py b/src/service/service/service_handlers/l3nm_ietfslice/old/ConfigRules.py similarity index 100% rename from src/service/service/service_handlers/l3slice_ietfslice/ConfigRules.py rename to src/service/service/service_handlers/l3nm_ietfslice/old/ConfigRules.py diff --git a/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py b/src/service/service/service_handlers/l3nm_ietfslice/old/L3NM_IETFSlice_ServiceHandler.py similarity index 99% rename from src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py rename to src/service/service/service_handlers/l3nm_ietfslice/old/L3NM_IETFSlice_ServiceHandler.py index 0df8b56e3495dcf70dcfd78b8e3ea83bef93dc46..161b520e24075a24bee47092ca8a1e04c5710465 100644 --- a/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_ietfslice/old/L3NM_IETFSlice_ServiceHandler.py @@ -380,7 +380,7 @@ def _parse_item_added(diff: Dict) -> dict: return added_items -class L3NMSliceIETFSliceServiceHandler(_ServiceHandler): +class L3NM_IETFSlice_ServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service: Service, task_executor: TaskExecutor, **settings ) -> None: diff --git a/src/service/service/service_handlers/l3nm_ietfslice/old/Tools.py b/src/service/service/service_handlers/l3nm_ietfslice/old/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..7fd152cc47b04c2c8b2c468d985b52019eb94299 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ietfslice/old/Tools.py @@ -0,0 +1,47 @@ +# 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 dataclasses import dataclass +from typing import Dict + + +@dataclass +class Ipv4Info: + src_prefix : str = '' + dst_prefix : str = '' + src_port : str = '' + dst_port : str = '' + vlan : str = '' + + +def extract_match_criterion_ipv4_info(match_criterion : Dict) -> Ipv4Info: + ipv4_info = Ipv4Info() + + for type_value in match_criterion['match-type']: + match_type = type_value['type'] + value = type_value['value'][0] + + if match_type == 'ietf-network-slice-service:source-ip-prefix': + ipv4_info.src_prefix = value + elif match_type == 'ietf-network-slice-service:destination-ip-prefix': + ipv4_info.dst_prefix = value + elif match_type == 'ietf-network-slice-service:source-tcp-port': + ipv4_info.src_port = value + elif match_type == 'ietf-network-slice-service:destination-tcp-port': + ipv4_info.dst_port = value + elif match_type == 'ietf-network-slice-service:vlan': + ipv4_info.vlan = value + + return ipv4_info diff --git a/src/service/service/service_handlers/l3nm_nce/ConfigRules.py b/src/service/service/service_handlers/l3nm_ncefan/ConfigRules.py similarity index 69% rename from src/service/service/service_handlers/l3nm_nce/ConfigRules.py rename to src/service/service/service_handlers/l3nm_ncefan/ConfigRules.py index 0544d897606afe950725349bfeb68c365189aa21..592f2e53c7cd43695c092f4dd2743ab99587c848 100644 --- a/src/service/service/service_handlers/l3nm_nce/ConfigRules.py +++ b/src/service/service/service_handlers/l3nm_ncefan/ConfigRules.py @@ -20,7 +20,7 @@ from common.tools.object_factory.ConfigRule import ( ) -def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: +def setup_config_rules(service_name: 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"] @@ -41,9 +41,10 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: 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}" + qos_profile_name: str = f"AR_VR_Gaming_{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") @@ -52,7 +53,7 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: "user-id": app_flow_user_id, "app-name": app_flow_app_name, "max-online-users": app_flow_max_online_users, - "stas": app_flow_stas, + "stas": [app_flow_stas], "qos-profile": qos_profile_name, "service-profile": app_flow_service_profile, "duration": app_flow_duration, @@ -61,19 +62,19 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: "name": qos_profile_name, "max-latency": max_latency, "max-jitter": max_jitter, - "max-loss": max_loss, + "max-loss": str(max_loss), "upstream": { - "assure-bandwidth": upstream_assure_bw, - "max-bandwidth": upstream_max_bw, + "assure-bandwidth": str(int(upstream_assure_bw)), + "max-bandwidth": str(int(upstream_max_bw)), }, "downstream": { - "assure-bandwidth": downstream_assure_bw, - "max-bandwidth": downstream_max_bw, + "assure-bandwidth": str(int(downstream_assure_bw)), + "max-bandwidth": str(int(downstream_max_bw)), }, } application = { "name": app_flow_app_name, - "app-id": app_id, + "app-id": [app_id], "app-features": { "app-feature": [ { @@ -96,25 +97,43 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: } 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}, + "/service[{:s}]/AppFlow".format(service_name), app_flow_datamodel ), + #json_config_rule_set( + # "/service[{:s}]/AppFlow/operation".format(service_name), + # {"type": operation_type}, + #), ] return json_config_rules -def teardown_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]: +def teardown_config_rules(service_name: str, json_settings: Dict) -> List[Dict]: + app_flow_id : str = json_settings["app_flow_id"] + app_flow_app_name: str = f"App_Flow_{app_flow_id}" + app_flow_name : str = f"App_Flow_{app_flow_id}" + qos_profile_name : str = f"AR_VR_Gaming_{app_flow_id}" + + app_flow = {"name": app_flow_name } + qos_profile = {"name": qos_profile_name } + application = {"name": app_flow_app_name} + + 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_delete( - "/service[{:s}]/AppFlow".format(service_uuid), - {}, - ), - json_config_rule_delete( - "/service[{:s}]/AppFlow/operation".format(service_uuid), - {}, + "/service[{:s}]/AppFlow".format(service_name), + app_flow_datamodel ), + #json_config_rule_delete( + # "/service[{:s}]/AppFlow/operation".format(service_name), + # {}, + #), ] + return json_config_rules diff --git a/src/service/service/service_handlers/l3nm_ncefan/DataStoreDelta.py b/src/service/service/service_handlers/l3nm_ncefan/DataStoreDelta.py new file mode 100644 index 0000000000000000000000000000000000000000..725de5c2547b96c643923a67bdeec070dcd54866 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ncefan/DataStoreDelta.py @@ -0,0 +1,48 @@ +# 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 +from deepdiff import DeepDiff +from typing import Dict, Optional +from common.proto.context_pb2 import Service + + +RUNNING_RESOURCE_KEY = 'running_ietf_slice' +CANDIDATE_RESOURCE_KEY = 'candidate_ietf_slice' + + +class DataStoreDelta: + def __init__(self, service : Service): + self._service = service + self._service_config = service.service_config + self._candidate_data = self._get_datastore_data(CANDIDATE_RESOURCE_KEY) + self._running_data = self._get_datastore_data(RUNNING_RESOURCE_KEY ) + + def _get_datastore_data(self, resource_key : str) -> Optional[Dict]: + for cr in self._service_config.config_rules: + if cr.WhichOneof('config_rule') != 'custom': continue + if cr.custom.resource_key != resource_key: continue + resource_value = json.loads(cr.custom.resource_value) + return resource_value.get('network-slice-services', dict()).get('slice-service') + return None + + @property + def candidate_data(self): return self._candidate_data + + @property + def running_data(self): return self._running_data + + def get_diff(self) -> Dict: + return DeepDiff(self._running_data, self._candidate_data) diff --git a/src/service/service/service_handlers/l3nm_ncefan/L3NM_NCEFAN_ServiceHandler.py b/src/service/service/service_handlers/l3nm_ncefan/L3NM_NCEFAN_ServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..6fb709b406b96756600d28a80eba7010ad2ab1cb --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ncefan/L3NM_NCEFAN_ServiceHandler.py @@ -0,0 +1,582 @@ +# 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, 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.grpc.Tools import grpc_message_to_json_string +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.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 L3NM_NCEFAN_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 + + @metered_subclass_method(METRICS_POOL) + def SetEndpoint( + self, + endpoints: List[Tuple[str, str, Optional[str]]], + connection_uuid: Optional[str] = None, + ) -> List[Union[bool, Exception]]: + LOGGER.debug('[SetEndpoint] service={:s}'.format(grpc_message_to_json_string(self.__service))) + LOGGER.debug('[SetEndpoint] endpoints={:s}'.format(str(endpoints))) + LOGGER.debug('[SetEndpoint] connection_uuid={:s}'.format(str(connection_uuid))) + + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + results = [] + try: + context_client = ContextClient() + service_config = self.__service.service_config + + 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]]: + LOGGER.debug('[DeleteEndpoint] service={:s}'.format(grpc_message_to_json_string(self.__service))) + LOGGER.debug('[DeleteEndpoint] endpoints={:s}'.format(str(endpoints))) + LOGGER.debug('[DeleteEndpoint] connection_uuid={:s}'.format(str(connection_uuid))) + + chk_type("endpoints", endpoints, list) + if len(endpoints) == 0: + return [] + service_uuid = self.__service.service_id.service_uuid.uuid + results = [] + try: + context_client = ContextClient() + service_config = self.__service.service_config + + 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_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_service = running_resource_value_dict["network-slice-services"][ + "slice-service" + ][0] + service_name = slice_service["id"] + sdps = slice_service["sdps"]["sdp"] + 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] + break + else: + raise Exception("connection group id not found") + + resource_value_dict = { + "app_flow_id": f"{src_sdp_idx}_{dst_sdp_idx}_{service_name}", + } + json_config_rules = teardown_config_rules(service_name, resource_value_dict) + 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 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 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 [] + MSG = '[SetConfig] Method not implemented. Resources({:s}) are being ignored.' + LOGGER.warning(MSG.format(str(resources))) + return [True for _ in resources] + + @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 [] + MSG = '[DeleteConfig] Method not implemented. Resources({:s}) are being ignored.' + LOGGER.warning(MSG.format(str(resources))) + return [True for _ in resources] diff --git a/src/service/service/service_handlers/l3nm_ncefan/Tools.py b/src/service/service/service_handlers/l3nm_ncefan/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..3e06d0b77c1eb94e674801133370339e8ef19cf4 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ncefan/Tools.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. + + +from common.proto.context_pb2 import Device + + +def get_device_endpoint_name(device_obj : Device, 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 + + device_uuid = str(device_obj.device_id.device_uuid.uuid) + device_name = str(device_obj.name) + MSG = 'Device({:s},{:s})/Endpoint({:s}) not found' + raise Exception(MSG.format(device_uuid, device_name, str(endpoint_uuid))) diff --git a/src/service/service/service_handlers/l3nm_ncefan/__init__.py b/src/service/service/service_handlers/l3nm_ncefan/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/service/service/service_handlers/l3nm_ncefan/__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/l3nm_nce/L3NMNCEServiceHandler.py b/src/service/service/service_handlers/l3nm_ncefan/old/L3NMNCEServiceHandler.py similarity index 99% rename from src/service/service/service_handlers/l3nm_nce/L3NMNCEServiceHandler.py rename to src/service/service/service_handlers/l3nm_ncefan/old/L3NMNCEServiceHandler.py index 1317bd0615e4789d7ba76e8c0c6b0923d8f2dec7..9af1dfbbab0f6867f70501621998ed6a6b6bc790 100644 --- a/src/service/service/service_handlers/l3nm_nce/L3NMNCEServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_ncefan/old/L3NMNCEServiceHandler.py @@ -32,7 +32,7 @@ from service.service.service_handler_api.Tools import ( ) from service.service.task_scheduler.TaskExecutor import TaskExecutor -from .ConfigRules import setup_config_rules, teardown_config_rules +from ..ConfigRules import setup_config_rules, teardown_config_rules RUNNING_RESOURCE_KEY = "running_ietf_slice" CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice" diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index f0f9c78b46cb6d83d7831867372bf718ccb562fb..51b3cf00b949e15ca649d8ba14826136ad29fca0 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -14,7 +14,7 @@ import json, logging from enum import Enum -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple, Union from common.DeviceTypes import DeviceTypeEnum from common.method_wrappers.ServiceExceptions import NotFoundException from common.proto.qkd_app_pb2 import QKDAppStatusEnum @@ -59,12 +59,14 @@ CONTROLLER_DEVICE_TYPES = { DeviceTypeEnum.IETF_SLICE, DeviceTypeEnum.IP_SDN_CONTROLLER, DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, + DeviceTypeEnum.NCE, DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.OPENFLOW_RYU_CONTROLLER, DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, } EXPANSION_CONTROLLER_DEVICE_TYPES = { DeviceTypeEnum.IETF_SLICE, + DeviceTypeEnum.NCE, DeviceTypeEnum.OPENFLOW_RYU_CONTROLLER, } @@ -274,15 +276,18 @@ class TaskExecutor: controller_uuid = device.controller_id.device_uuid.uuid if len(controller_uuid) == 0: return None controller = self.get_device(DeviceId(**json_device_id(controller_uuid))) - controller_uuid = controller.device_id.device_uuid.uuid if controller is None: raise Exception('Device({:s}) not found'.format(str(controller_uuid))) - return controller + if len(controller.controller_id.device_uuid.uuid) == 0: + return controller + else: + # in case controller is an under-underlay controller + return self.get_device_controller(controller) def get_devices_from_connection( self, connection : Connection, exclude_managed_by_controller : bool = False ) -> Dict[DeviceTypeEnum, Dict[str, Device]]: devices : Dict[DeviceTypeEnum, Dict[str, Device]] = dict() - controllers : Dict[DeviceTypeEnum, Dict[str, Device]] = dict() + #controllers : Dict[DeviceTypeEnum, Dict[str, Device]] = dict() for endpoint_id in connection.path_hops_endpoint_ids: device = self.get_device(endpoint_id.device_id) device_uuid = endpoint_id.device_id.device_uuid.uuid @@ -291,10 +296,11 @@ class TaskExecutor: controller = self.get_device_controller(device) if controller is None: device_type = DeviceTypeEnum._value2member_map_[device.device_type] - if device_type in CONTROLLER_DEVICE_TYPES: - controllers.setdefault(device_type, dict())[device_uuid] = device - else: - devices.setdefault(device_type, dict())[device_uuid] = device + #if device_type in CONTROLLER_DEVICE_TYPES: + # controllers.setdefault(device_type, dict())[device_uuid] = device + #else: + # devices.setdefault(device_type, dict())[device_uuid] = device + devices.setdefault(device_type, dict())[device_uuid] = device else: # ===== Ryu original test ======================================================================== #if not exclude_managed_by_controller: @@ -316,14 +322,48 @@ class TaskExecutor: devices.setdefault(device_type, dict())[device_uuid] = device device_type = DeviceTypeEnum._value2member_map_[controller.device_type] - controllers.setdefault(device_type, dict())[controller.device_id.device_uuid.uuid] = controller + #controllers.setdefault(device_type, dict())[controller.device_id.device_uuid.uuid] = controller + devices.setdefault(device_type, dict())[controller.device_id.device_uuid.uuid] = controller LOGGER.debug('[get_devices_from_connection] devices = {:s}'.format(str(devices))) - LOGGER.debug('[get_devices_from_connection] controllers = {:s}'.format(str(controllers))) - if len(devices) == 0 and len(controllers) > 0: - return controllers - else: - return devices + #LOGGER.debug('[get_devices_from_connection] controllers = {:s}'.format(str(controllers))) + #if len(devices) == 0 and len(controllers) > 0: + # return controllers + #else: + # return devices + return devices + + + def get_device_type_drivers_for_connection( + self, connection : Connection + ) -> Dict[DeviceTypeEnum, Dict[str, Tuple[Device, Set[int]]]]: + + devices : Dict[DeviceTypeEnum, Dict[str, Tuple[Device, Set[int]]]] = dict() + + for endpoint_id in connection.path_hops_endpoint_ids: + device = self.get_device(endpoint_id.device_id) + device_uuid = endpoint_id.device_id.device_uuid.uuid + if device is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) + + controller = self.get_device_controller(device) + if controller is None: + device_type = DeviceTypeEnum._value2member_map_[device.device_type] + device_drivers = set(driver for driver in device.device_drivers) + devices.setdefault(device_type, dict())[device_uuid] = (device, device_drivers) + else: + # Controller device types for those underlying path is needed by service handler + device_type = DeviceTypeEnum._value2member_map_[controller.device_type] + controller_drivers = set(driver for driver in controller.device_drivers) + + if device_type not in EXPANSION_CONTROLLER_DEVICE_TYPES: + devices.setdefault(device_type, dict())[device_uuid] = (device, controller_drivers) + else: + controller_uuid = controller.device_id.device_uuid.uuid + devices.setdefault(device_type, dict())[controller_uuid] = (controller, controller_drivers) + + LOGGER.debug('[get_devices_from_connection] devices = {:s}'.format(str(devices))) + return devices + # ----- Service-related methods ------------------------------------------------------------------------------------ @@ -366,18 +406,22 @@ class TaskExecutor: #LOGGER.debug('connection_device_types_included = {:s}'.format(str(connection_device_types_included))) # ================================================================================================ - connection_device_types : Dict[DeviceTypeEnum, Dict[str, Device]] = self.get_devices_from_connection( - connection, exclude_managed_by_controller=False - ) + device_type_to_device_and_drivers : Dict[DeviceTypeEnum, Dict[str, Tuple[Device, Set[int]]]] = \ + self.get_device_type_drivers_for_connection(connection) + service_handlers : Dict[DeviceTypeEnum, Tuple['_ServiceHandler', Dict[str, Device]]] = dict() # ===== Ryu original test ======================================================================== #for device_type, connection_devices in connection_device_types_excluded.items(): # ================================================================================================ - for device_type, connection_devices in connection_device_types.items(): + for device_type, device_and_drivers in device_type_to_device_and_drivers.items(): try: service_handler_class = get_service_handler_class( - self._service_handler_factory, service, connection_devices + self._service_handler_factory, service, device_and_drivers ) + connection_devices = { + device_uuid : device + for device_uuid, (device, _) in device_and_drivers.items() + } # ===== Ryu original test ======================================================================== #LOGGER.debug('service_handler_class IN CONNECTION DEVICE TYPE EXCLUDED = {:s}'.format(str(service_handler_class.__name__))) #service_handler = service_handler_class(service, self, **service_handler_settings) @@ -394,11 +438,18 @@ class TaskExecutor: UnsupportedFilterFieldValueException ): dict_connection_devices = { - cd_data.name : (cd_uuid, cd_data.name, { - (device_driver, DeviceDriverEnum.Name(device_driver)) - for device_driver in cd_data.device_drivers - }) - for cd_uuid,cd_data in connection_devices.items() + cd_data.name : ( + cd_uuid, + cd_data.name, + { + (device_driver, DeviceDriverEnum.Name(device_driver)) + for device_driver in cd_data.device_drivers + }, { + (device_driver, DeviceDriverEnum.Name(device_driver)) + for device_driver in drivers + } + ) + for cd_uuid,(cd_data, drivers) in device_and_drivers.items() } MSG = 'Unable to select service handler. service={:s} connection={:s} connection_devices={:s}' LOGGER.exception(MSG.format( diff --git a/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py b/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py index da27191aa09be0c8eae00ef9416484d9dc505daf..10f32e81b0ce63e02534780316f0787d33551449 100644 --- a/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py +++ b/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py @@ -28,6 +28,11 @@ if TYPE_CHECKING: KEY_TEMPLATE = 'connection({connection_id:s}):configure' +CONTROLLER_DEVICE_TYPES = { + DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + DeviceTypeEnum.NCE, +} + class Task_ConnectionConfigure(_Task): def __init__(self, task_executor : TaskExecutor, connection_id : ConnectionId) -> None: super().__init__(task_executor) @@ -57,7 +62,7 @@ class Task_ConnectionConfigure(_Task): errors = list() for device_type, (service_handler, connection_devices) in service_handlers.items(): - if device_type == DeviceTypeEnum.TERAFLOWSDN_CONTROLLER: + if device_type in CONTROLLER_DEVICE_TYPES: _endpointids_to_set = endpointids_to_set else: _endpointids_to_set = [ diff --git a/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py b/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py index f75c7fe84916eacfd57ad2b832376ea15c6eb172..28086cdb139117e8ae23330934713c237609637b 100644 --- a/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py +++ b/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py @@ -28,6 +28,11 @@ if TYPE_CHECKING: KEY_TEMPLATE = 'connection({connection_id:s}):deconfigure' +CONTROLLER_DEVICE_TYPES = { + DeviceTypeEnum.TERAFLOWSDN_CONTROLLER, + DeviceTypeEnum.NCE, +} + class Task_ConnectionDeconfigure(_Task): def __init__(self, task_executor : TaskExecutor, connection_id : ConnectionId) -> None: super().__init__(task_executor) @@ -57,7 +62,7 @@ class Task_ConnectionDeconfigure(_Task): errors = list() for device_type, (service_handler, connection_devices) in service_handlers.items(): - if device_type == DeviceTypeEnum.TERAFLOWSDN_CONTROLLER: + if device_type in CONTROLLER_DEVICE_TYPES: _endpointids_to_delete = endpointids_to_delete else: _endpointids_to_delete = [ diff --git a/src/simap_connector/.gitlab-ci.yml b/src/simap_connector/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..02e15a16c2bff748dc46ace22b4d6703e4dee8d2 --- /dev/null +++ b/src/simap_connector/.gitlab-ci.yml @@ -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. + +# build, tag and push the Docker image to the gitlab registry +build simap_connector: + variables: + IMAGE_NAME: 'simap_connector' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: build + before_script: + - docker image prune --force + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + script: + - docker buildx build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile . + - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + after_script: + - docker image prune --force + 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"' + - changes: + - src/$IMAGE_NAME/**/*.{py,in,yml} + - src/$IMAGE_NAME/Dockerfile + - src/$IMAGE_NAME/tests/*.py + - src/$IMAGE_NAME/tests/Dockerfile + - manifests/${IMAGE_NAME}service.yaml + - .gitlab-ci.yml diff --git a/src/simap_connector/Config.py b/src/simap_connector/Config.py new file mode 100644 index 0000000000000000000000000000000000000000..656e9a87584e14ea73b0cee7e75f3dec11852d9d --- /dev/null +++ b/src/simap_connector/Config.py @@ -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. + + +from common.Settings import get_setting + +SIMAP_SERVER_SCHEME = str(get_setting('SIMAP_SERVER_SCHEME', default='http' )) +SIMAP_SERVER_ADDRESS = str(get_setting('SIMAP_SERVER_ADDRESS', default='127.0.0.1')) +SIMAP_SERVER_PORT = int(get_setting('SIMAP_SERVER_PORT', default='80' )) +SIMAP_SERVER_USERNAME = str(get_setting('SIMAP_SERVER_USERNAME', default='admin' )) +SIMAP_SERVER_PASSWORD = str(get_setting('SIMAP_SERVER_PASSWORD', default='admin' )) diff --git a/src/simap_connector/Dockerfile b/src/simap_connector/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..35dd3abfb9235df271fc74207e8076d68d80d36a --- /dev/null +++ b/src/simap_connector/Dockerfile @@ -0,0 +1,62 @@ +# 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 + +# 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/simap_connector +WORKDIR /var/teraflow/simap_connector +COPY src/simap_connector/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/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/simap_connector/. simap_connector/ + +# Start the service +ENTRYPOINT ["python", "-m", "simap_connector.service"] diff --git a/src/simap_connector/__init__.py b/src/simap_connector/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/__init__.py @@ -0,0 +1,13 @@ +# 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/simap_connector/client/SimapConnectorClient.py b/src/simap_connector/client/SimapConnectorClient.py new file mode 100644 index 0000000000000000000000000000000000000000..137f2fb5bfc9f20cd97cc6ede9de203ce7ce3585 --- /dev/null +++ b/src/simap_connector/client/SimapConnectorClient.py @@ -0,0 +1,71 @@ +# 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 grpc, logging +from common.Constants import ServiceNameEnum +from common.Settings import get_service_host, get_service_port_grpc +from common.proto.context_pb2 import Empty +from common.proto.simap_connector_pb2 import Affectation, Subscription, SubscriptionId +from common.proto.simap_connector_pb2_grpc import SimapConnectorServiceStub +from common.tools.client.RetryDecorator import delay_exponential, retry +from common.tools.grpc.Tools import grpc_message_to_json_string + +LOGGER = logging.getLogger(__name__) +MAX_RETRIES = 15 +DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0) +RETRY_DECORATOR = retry( + max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, + prepare_method_name='connect', +) + +class SimapConnectorClient: + def __init__(self, host=None, port=None): + if not host: host = get_service_host(ServiceNameEnum.SIMAP_CONNECTOR) + if not port: port = get_service_port_grpc(ServiceNameEnum.SIMAP_CONNECTOR) + self.endpoint = '{:s}:{:s}'.format(str(host), str(port)) + LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint))) + self.channel = None + self.stub = None + self.connect() + LOGGER.debug('Channel created') + + def connect(self): + self.channel = grpc.insecure_channel(self.endpoint) + self.stub = SimapConnectorServiceStub(self.channel) + + def close(self): + if self.channel is not None: self.channel.close() + self.channel = None + self.stub = None + + @RETRY_DECORATOR + def EstablishSubscription(self, request : Subscription) -> SubscriptionId: + LOGGER.debug('EstablishSubscription request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.EstablishSubscription(request) + LOGGER.debug('EstablishSubscription result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def DeleteSubscription(self, request : SubscriptionId) -> Empty: + LOGGER.debug('DeleteSubscription request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.DeleteSubscription(request) + LOGGER.debug('DeleteSubscription result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def AffectSampleSynthesizer(self, request : Affectation) -> Empty: + LOGGER.debug('AffectSampleSynthesizer request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.AffectSampleSynthesizer(request) + LOGGER.debug('AffectSampleSynthesizer result: {:s}'.format(grpc_message_to_json_string(response))) + return response diff --git a/src/simap_connector/client/__init__.py b/src/simap_connector/client/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/client/__init__.py @@ -0,0 +1,13 @@ +# 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/simap_connector/requirements.in b/src/simap_connector/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..167b3d99487ce7cee068c989ccedf92065cf95a9 --- /dev/null +++ b/src/simap_connector/requirements.in @@ -0,0 +1,20 @@ +# 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. + +kafka-python==2.0.* +psycopg2-binary==2.9.* +requests==2.27.* +sqlalchemy-cockroachdb==1.4.* +SQLAlchemy-Utils==0.38.* +SQLAlchemy==1.4.* diff --git a/src/simap_connector/service/SimapConnectorService.py b/src/simap_connector/service/SimapConnectorService.py new file mode 100644 index 0000000000000000000000000000000000000000..fc960f214416df7d58058fea5c3e189fabe38305 --- /dev/null +++ b/src/simap_connector/service/SimapConnectorService.py @@ -0,0 +1,43 @@ +# 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 logging, sqlalchemy +from common.Constants import ServiceNameEnum +from common.Settings import get_service_port_grpc +from common.proto.simap_connector_pb2 import DESCRIPTOR as SIMAP_CONNECTOR_DESCRIPTOR +from common.proto.simap_connector_pb2_grpc import add_SimapConnectorServiceServicer_to_server +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from common.tools.service.GenericGrpcService import GenericGrpcService +from .telemetry.TelemetryPool import TelemetryPool +from .SimapConnectorServiceServicerImpl import SimapConnectorServiceServicerImpl + + +LOGGER = logging.getLogger(__name__) + + +class SimapConnectorService(GenericGrpcService): + def __init__( + self, db_engine : sqlalchemy.engine.Engine, restconf_client : RestConfClient, + telemetry_pool : TelemetryPool, cls_name : str = __name__ + ) -> None: + port = get_service_port_grpc(ServiceNameEnum.SIMAP_CONNECTOR) + super().__init__(port, cls_name=cls_name) + self.simap_connector_servicer = SimapConnectorServiceServicerImpl( + db_engine, restconf_client, telemetry_pool + ) + + def install_servicers(self): + add_SimapConnectorServiceServicer_to_server(self.simap_connector_servicer, self.server) + self.add_reflection_service_name(SIMAP_CONNECTOR_DESCRIPTOR, 'SimapConnectorService') diff --git a/src/simap_connector/service/SimapConnectorServiceServicerImpl.py b/src/simap_connector/service/SimapConnectorServiceServicerImpl.py new file mode 100644 index 0000000000000000000000000000000000000000..8aafffc1aa79090c2ad7bda0b50020c924cae7cb --- /dev/null +++ b/src/simap_connector/service/SimapConnectorServiceServicerImpl.py @@ -0,0 +1,177 @@ +# 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 grpc, logging, sqlalchemy +from typing import Optional +from common.proto.context_pb2 import Empty +from common.proto.simap_connector_pb2 import Affectation, Subscription, SubscriptionId +from common.proto.simap_connector_pb2_grpc import SimapConnectorServiceServicer +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method +from device.client.DeviceClient import DeviceClient +from simap_connector.service.telemetry.worker.SynthesizerWorker import SynthesizerWorker +from simap_connector.service.telemetry.worker._Worker import WorkerTypeEnum +from .database.Subscription import subscription_get, subscription_set, subscription_delete +from .database.SubSubscription import ( + sub_subscription_list, sub_subscription_set, sub_subscription_delete +) +from .telemetry.worker.data.AggregationCache import AggregationCache +from .telemetry.TelemetryPool import TelemetryPool +from .Tools import ( + LinkDetails, create_kafka_topic, delete_kafka_topic, delete_underlay_subscription, + discover_link_details, establish_underlay_subscription, get_controller_id +) + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool('SimapConnector', 'RPC') + + +class SimapConnectorServiceServicerImpl(SimapConnectorServiceServicer): + def __init__( + self, db_engine : sqlalchemy.engine.Engine, restconf_client : RestConfClient, + telemetry_pool : TelemetryPool + ) -> None: + LOGGER.debug('Creating Servicer...') + self._db_engine = db_engine + self._restconf_client = restconf_client + self._telemetry_pool = telemetry_pool + LOGGER.debug('Servicer Created') + + + def _get_metrics(self) -> MetricsPool: return METRICS_POOL + + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def EstablishSubscription( + self, request : Subscription, context : grpc.ServicerContext + ) -> SubscriptionId: + datastore = request.datastore + xpath_filter = request.xpath_filter + period = request.period + link_details : LinkDetails = discover_link_details( + self._restconf_client, xpath_filter + ) + xpath_filter = link_details.link.get_xpath_filter(add_simap_telemetry=True) + + parent_subscription_uuid, parent_subscription_id = subscription_set( + self._db_engine, datastore, xpath_filter, period + ) + + aggregation_cache = AggregationCache() + + device_client = DeviceClient() + for supporting_link in link_details.supporting_links: + controller_id = get_controller_id(supporting_link.network_id) + sup_link_xpath_filter = supporting_link.get_xpath_filter(add_simap_telemetry=True) + + if controller_id is None: + collector_name = '{:d}:SIMAP:{:s}:{:s}'.format( + parent_subscription_id, str(supporting_link.network_id), + str(supporting_link.link_id) + ) + target_uri = sup_link_xpath_filter + underlay_subscription_id = 0 + else: + underlay_sub_id = establish_underlay_subscription( + device_client, controller_id, sup_link_xpath_filter, period + ) + collector_name = '{:d}:{:s}:{:s}'.format( + parent_subscription_id, controller_id, + str(underlay_sub_id.subscription_id) + ) + target_uri = underlay_sub_id.subscription_uri + underlay_subscription_id = underlay_sub_id.subscription_id + + self._telemetry_pool.start_collector( + collector_name, controller_id, supporting_link.network_id, + supporting_link.link_id, target_uri, aggregation_cache, period + ) + + sub_request = Subscription() + sub_request.datastore = datastore + sub_request.xpath_filter = sup_link_xpath_filter + sub_request.period = period + sub_subscription_set( + self._db_engine, parent_subscription_uuid, controller_id, datastore, + sup_link_xpath_filter, period, underlay_subscription_id, target_uri, + collector_name + ) + + topic = 'subscription.{:d}'.format(parent_subscription_id) + create_kafka_topic(topic) + + aggregator_name = str(parent_subscription_id) + network_id = link_details.link.network_id + link_id = link_details.link.link_id + self._telemetry_pool.start_aggregator( + aggregator_name, network_id, link_id, parent_subscription_id, + aggregation_cache, topic, period + ) + + return SubscriptionId(subscription_id=parent_subscription_id) + + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def DeleteSubscription( + self, request : SubscriptionId, context : grpc.ServicerContext + ) -> Empty: + parent_subscription_id = request.subscription_id + subscription = subscription_get(self._db_engine, parent_subscription_id) + if subscription is None: return Empty() + + aggregator_name = str(parent_subscription_id) + self._telemetry_pool.stop_worker(WorkerTypeEnum.AGGREGATOR, aggregator_name) + + device_client = DeviceClient() + parent_subscription_uuid = subscription['subscription_uuid'] + sub_subscriptions = sub_subscription_list(self._db_engine, parent_subscription_uuid) + for sub_subscription in sub_subscriptions: + sub_subscription_id = sub_subscription['sub_subscription_id'] + controller_id = sub_subscription['controller_uuid' ] + collector_name = sub_subscription['collector_name' ] + + self._telemetry_pool.stop_worker(WorkerTypeEnum.COLLECTOR, collector_name) + + if controller_id is not None and len(controller_id) > 0: + delete_underlay_subscription(device_client, controller_id, sub_subscription_id) + + sub_subscription_delete(self._db_engine, parent_subscription_uuid, sub_subscription_id) + + topic = 'subscription.{:d}'.format(parent_subscription_id) + delete_kafka_topic(topic) + + subscription_delete(self._db_engine, parent_subscription_id) + return Empty() + + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def AffectSampleSynthesizer( + self, request : Affectation, context : grpc.ServicerContext + ) -> Empty: + network_id = request.network_id + link_id = request.link_id + bandwidth_factor = request.bandwidth_factor + latency_factor = request.latency_factor + + synthesizer_name = '{:s}:{:s}'.format(network_id, link_id) + synthesizer : Optional[SynthesizerWorker] = self._telemetry_pool.get_worker( + WorkerTypeEnum.SYNTHESIZER, synthesizer_name + ) + if synthesizer is None: + MSG = 'Synthesizer({:s}) not found' + raise Exception(MSG.format(synthesizer_name)) + synthesizer.change_resources(bandwidth_factor, latency_factor) + return Empty() diff --git a/src/simap_connector/service/Tools.py b/src/simap_connector/service/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..024f8d70896d9555a0eb51f2730e6b208726ddb6 --- /dev/null +++ b/src/simap_connector/service/Tools.py @@ -0,0 +1,175 @@ +# 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 logging, re +from dataclasses import dataclass, field +from kafka.admin import KafkaAdminClient, NewTopic +from kafka.errors import BrokerResponseError +from typing import List, Optional +from common.proto.monitoring_pb2 import ( + SSEMonitoringSubscriptionConfig, SSEMonitoringSubscriptionResponse +) +from common.tools.kafka.Variables import KafkaConfig +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from device.client.DeviceClient import DeviceClient + + +LOGGER = logging.getLogger(__name__) + + +XPATH_LINK_TEMPLATE = '/ietf-network:networks/network={:s}/ietf-network-topology:link={:s}' +SIMAP_TELEMETRY_SUFFIX = '/simap-telemetry:simap-telemetry' + +RE_XPATH_LINK = re.compile( + r'^/ietf-network:networks/network=([^/]+)/ietf-network-topology:link=([^/]+)/?.*$' +) + + +@dataclass +class Link: + network_id : str + link_id : str + + def get_xpath_filter(self, add_simap_telemetry : bool = True) -> str: + xpath_filter = XPATH_LINK_TEMPLATE.format(self.network_id, self.link_id) + if add_simap_telemetry: xpath_filter += SIMAP_TELEMETRY_SUFFIX + return xpath_filter + + +@dataclass +class LinkDetails: + link : Link + supporting_links : List[Link] = field(default_factory=list) + + +def discover_link_details(restconf_client : RestConfClient, xpath_filter : str) -> LinkDetails: + link_xpath_match = RE_XPATH_LINK.match(xpath_filter) + if link_xpath_match is None: + raise Exception('Unsupported xpath_filter({:s})'.format(str(xpath_filter))) + + network_id, link_id = link_xpath_match.groups() + link_details = LinkDetails(Link(network_id, link_id)) + + xpath_filter = link_details.link.get_xpath_filter(add_simap_telemetry=False) + xpath_data = restconf_client.get(xpath_filter) + if not xpath_data: + raise Exception('Resource({:s}) not found in SIMAP Server'.format(str(xpath_filter))) + + links = xpath_data.get('ietf-network-topology:link', list()) + if len(links) == 0: + raise Exception('Link({:s}) not found'.format(str(xpath_filter))) + if len(links) > 1: + raise Exception('Multiple occurrences for Link({:s})'.format(str(xpath_filter))) + link = links[0] + if link['link-id'] != link_id: + MSG = 'Retieved Link({:s}) does not match xpath_filter({:s})' + raise Exception(MSG.format(str(link), str(xpath_filter))) + supporting_links = link.get('supporting-link', list()) + if len(supporting_links) == 0: + MSG = 'No supporting links found for Resource({:s}, {:s})' + raise Exception(MSG.format(str(xpath_filter), str(xpath_data))) + + for sup_link in supporting_links: + link_details.supporting_links.append(Link( + sup_link['network-ref'], sup_link['link-ref'] + )) + return link_details + + +#def compose_establish_subscription(datastore : str, xpath_filter : str, period : float) -> Dict: +# return { +# 'ietf-subscribed-notifications:input': { +# 'datastore': datastore, +# 'ietf-yang-push:datastore-xpath-filter': xpath_filter, +# 'ietf-yang-push:periodic': { +# 'ietf-yang-push:period': period, +# } +# } +# } + + +CONTROLLER_MAP = { + 'e2e' : 'TFS-E2E', + 'agg' : 'TFS-AGG', + 'trans-pkt': 'TFS-IP', + 'trans-opt': 'NCE-T', + 'access' : 'NCE-FAN', + 'admin' : None, # controller-less +} + +def get_controller_id(network_id : str) -> Optional[str]: + # TODO: Future improvement: infer controller based on topology data + if network_id not in CONTROLLER_MAP: + MSG = 'Unable to identify controller for SimapNetwork({:s})' + raise Exception(MSG.format(str(network_id))) + return CONTROLLER_MAP[network_id] + + +@dataclass +class UnderlaySubscriptionId: + subscription_id : int + subscription_uri : str + + @classmethod + def from_reply(cls, sse_sub_rep : SSEMonitoringSubscriptionResponse) -> 'UnderlaySubscriptionId': + return cls( + subscription_id = sse_sub_rep.identifier, + subscription_uri = sse_sub_rep.uri, + ) + +def establish_underlay_subscription( + device_client : DeviceClient, controller_uuid : str, xpath_filter : str, + sampling_interval : float +) -> UnderlaySubscriptionId: + sse_sub_req = SSEMonitoringSubscriptionConfig() + sse_sub_req.device_id.device_uuid.uuid = controller_uuid + sse_sub_req.config_type = SSEMonitoringSubscriptionConfig.Subscribe + sse_sub_req.uri = xpath_filter + sse_sub_req.sampling_interval = str(sampling_interval) + sse_sub_rep = device_client.SSETelemetrySubscribe(sse_sub_req) + return UnderlaySubscriptionId.from_reply(sse_sub_rep) + +def delete_underlay_subscription( + device_client : DeviceClient, controller_uuid : str, subscription_id : int +) -> None: + sse_unsub_req = SSEMonitoringSubscriptionConfig() + sse_unsub_req.device_id.device_uuid.uuid = controller_uuid + sse_unsub_req.config_type = SSEMonitoringSubscriptionConfig.Unsubscribe + sse_unsub_req.identifier = str(subscription_id) + device_client.SSETelemetrySubscribe(sse_unsub_req) + + +KAFKA_BOOT_SERVERS = KafkaConfig.get_kafka_address() + +def create_kafka_topic(topic : str) -> None: + try: + kafka_admin = KafkaAdminClient(bootstrap_servers=KAFKA_BOOT_SERVERS) + existing_topics = set(kafka_admin.list_topics()) + if topic in existing_topics: return + to_create = [NewTopic(topic, num_partitions=3, replication_factor=1)] + kafka_admin.create_topics(to_create, validate_only=False) + except BrokerResponseError: + MSG = 'Error creating Topic({:s})' + LOGGER.exception(MSG.format(str(topic))) + +def delete_kafka_topic(topic : str) -> None: + try: + kafka_admin = KafkaAdminClient(bootstrap_servers=KAFKA_BOOT_SERVERS) + existing_topics = set(kafka_admin.list_topics()) + if topic not in existing_topics: return + kafka_admin.delete_topics([topic]) + except BrokerResponseError: + MSG = 'Error deleting Topic({:s})' + LOGGER.exception(MSG.format(str(topic))) diff --git a/src/simap_connector/service/__init__.py b/src/simap_connector/service/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/service/__init__.py @@ -0,0 +1,13 @@ +# 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/simap_connector/service/__main__.py b/src/simap_connector/service/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..e52768f8e7808572368d4839549c1f98a6508bcb --- /dev/null +++ b/src/simap_connector/service/__main__.py @@ -0,0 +1,107 @@ +# 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 logging, signal, sys, threading +from prometheus_client import start_http_server +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from common.Constants import ServiceNameEnum +from common.Settings import ( + ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, + get_log_level, get_metrics_port, wait_for_environment_variables +) +from simap_connector.Config import ( + SIMAP_SERVER_SCHEME, SIMAP_SERVER_ADDRESS, SIMAP_SERVER_PORT, + SIMAP_SERVER_USERNAME, SIMAP_SERVER_PASSWORD, +) +from .database.Engine import Engine +from .database.models._Base import rebuild_database +from .simap_updater.SimapClient import SimapClient +from .simap_updater.SimapUpdater import SimapUpdater +from .telemetry.TelemetryPool import TelemetryPool +from .SimapConnectorService import SimapConnectorService + + +TERMINATE = threading.Event() + +LOG_LEVEL = get_log_level() +logging.basicConfig(level=LOG_LEVEL, format='[%(asctime)s] %(levelname)s:%(name)s:%(message)s') +logging.getLogger('RestConfClient').setLevel(logging.WARN) + +LOGGER = logging.getLogger(__name__) + + +def signal_handler(signal, frame): # pylint: disable=redefined-outer-name + LOGGER.warning('Terminate signal received') + TERMINATE.set() + + +def main(): + wait_for_environment_variables([ + get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST ), + get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC), + ]) + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + LOGGER.info('Starting...') + + # Start metrics server + metrics_port = get_metrics_port() + start_http_server(metrics_port) + + # Get Database Engine instance and initialize database, if needed + LOGGER.info('Getting SQLAlchemy DB Engine...') + db_engine = Engine.get_engine() + if db_engine is None: + LOGGER.error('Unable to get SQLAlchemy DB Engine...') + return -1 + + try: + Engine.create_database(db_engine) + except: # pylint: disable=bare-except # pragma: no cover + LOGGER.exception('Failed to check/create the database: {:s}'.format(str(db_engine.url))) + + rebuild_database(db_engine) + + restconf_client = RestConfClient( + scheme=SIMAP_SERVER_SCHEME, address=SIMAP_SERVER_ADDRESS, + port=SIMAP_SERVER_PORT, username=SIMAP_SERVER_USERNAME, + password=SIMAP_SERVER_PASSWORD, + ) + + simap_client = SimapClient(restconf_client) + telemetry_pool = TelemetryPool(simap_client, terminate=TERMINATE) + + grpc_service = SimapConnectorService(db_engine, restconf_client, telemetry_pool) + grpc_service.start() + + simap_updater = SimapUpdater(simap_client, telemetry_pool, TERMINATE) + simap_updater.start() + + LOGGER.info('Running...') + # Wait for Ctrl+C or termination signal + while not TERMINATE.wait(timeout=1.0): pass + + LOGGER.info('Terminating...') + simap_updater.stop() + telemetry_pool.stop_all() + grpc_service.stop() + + LOGGER.info('Bye') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/simap_connector/service/database/Engine.py b/src/simap_connector/service/database/Engine.py new file mode 100644 index 0000000000000000000000000000000000000000..43690382e513093feda4dfa89ee75ef85c08b651 --- /dev/null +++ b/src/simap_connector/service/database/Engine.py @@ -0,0 +1,55 @@ +# 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 logging, sqlalchemy, sqlalchemy_utils +from common.Settings import get_setting + +LOGGER = logging.getLogger(__name__) + +APP_NAME = 'tfs' +ECHO = False # true: dump SQL commands and transactions executed +CRDB_URI_TEMPLATE = 'cockroachdb://{:s}:{:s}@cockroachdb-public.{:s}.svc.cluster.local:{:s}/{:s}?sslmode={:s}' + +class Engine: + @staticmethod + def get_engine() -> sqlalchemy.engine.Engine: + crdb_uri = get_setting('CRDB_URI', default=None) + if crdb_uri is None: + CRDB_NAMESPACE = get_setting('CRDB_NAMESPACE') + CRDB_SQL_PORT = get_setting('CRDB_SQL_PORT') + CRDB_DATABASE = get_setting('CRDB_DATABASE') + CRDB_USERNAME = get_setting('CRDB_USERNAME') + CRDB_PASSWORD = get_setting('CRDB_PASSWORD') + CRDB_SSLMODE = get_setting('CRDB_SSLMODE') + crdb_uri = CRDB_URI_TEMPLATE.format( + CRDB_USERNAME, CRDB_PASSWORD, CRDB_NAMESPACE, CRDB_SQL_PORT, CRDB_DATABASE, CRDB_SSLMODE) + + try: + engine = sqlalchemy.create_engine( + crdb_uri, connect_args={'application_name': APP_NAME}, echo=ECHO, future=True) + except: # pylint: disable=bare-except # pragma: no cover + LOGGER.exception('Failed to connect to database: {:s}'.format(str(crdb_uri))) + return None + + return engine + + @staticmethod + def create_database(engine : sqlalchemy.engine.Engine) -> None: + if not sqlalchemy_utils.database_exists(engine.url): + sqlalchemy_utils.create_database(engine.url) + + @staticmethod + def drop_database(engine : sqlalchemy.engine.Engine) -> None: + if sqlalchemy_utils.database_exists(engine.url): + sqlalchemy_utils.drop_database(engine.url) diff --git a/src/simap_connector/service/database/SubSubscription.py b/src/simap_connector/service/database/SubSubscription.py new file mode 100644 index 0000000000000000000000000000000000000000..4c19a6c64d8440a8178de5f123b857d05aba2788 --- /dev/null +++ b/src/simap_connector/service/database/SubSubscription.py @@ -0,0 +1,119 @@ +# 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 datetime, logging +from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.engine import Engine +from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy_cockroachdb import run_transaction +from typing import Dict, List, Optional, Tuple +from common.method_wrappers.ServiceExceptions import NotFoundException +from .models.SubSubscriptionModel import SubSubscriptionModel + + +LOGGER = logging.getLogger(__name__) + + +def sub_subscription_list(db_engine : Engine, parent_subscription_uuid : str) -> List[Dict]: + def callback(session : Session) -> List[Dict]: + obj_list : List[SubSubscriptionModel] = ( + session + .query(SubSubscriptionModel) + .filter_by(parent=parent_subscription_uuid) + .all() + ) + return [obj.dump() for obj in obj_list] + return run_transaction(sessionmaker(bind=db_engine), callback) + + +def sub_subscription_get( + db_engine : Engine, parent_subscription_uuid : str, sub_subscription_id : int +) -> Dict: + def callback(session : Session) -> Optional[Dict]: + obj : Optional[SubSubscriptionModel] = ( + session + .query(SubSubscriptionModel) + .filter_by(parent=parent_subscription_uuid, sub_subscription_id=sub_subscription_id) + .one_or_none() + ) + return None if obj is None else obj.dump() + obj = run_transaction(sessionmaker(bind=db_engine), callback) + if obj is None: + sub_sub_key = '{:s}/{:s}'.format(str(parent_subscription_uuid), str(sub_subscription_id)) + raise NotFoundException('SubSubscription', sub_sub_key) + return obj + + +def sub_subscription_set( + db_engine : Engine, parent_subscription_uuid : str, controller_uuid : str, datastore : str, + xpath_filter : str, period : float, sub_subscription_id : int, sub_subscription_uri : str, + collector_name : str +) -> str: + now = datetime.datetime.now(datetime.timezone.utc) + if controller_uuid is None: controller_uuid = '' + sub_subscription_data = { + 'parent' : parent_subscription_uuid, + 'controller_uuid' : controller_uuid, + 'datastore' : datastore, + 'xpath_filter' : xpath_filter, + 'period' : period, + 'sub_subscription_id' : sub_subscription_id, + 'sub_subscription_uri': sub_subscription_uri, + 'collector_name' : collector_name, + 'created_at' : now, + 'updated_at' : now, + } + + def callback(session : Session) -> Tuple[bool, str]: + stmt = insert(SubSubscriptionModel).values([sub_subscription_data]) + stmt = stmt.on_conflict_do_update( + index_elements=[ + SubSubscriptionModel.parent, + SubSubscriptionModel.sub_subscription_uuid, + ], + set_=dict( + controller_uuid = stmt.excluded.controller_uuid, + datastore = stmt.excluded.datastore, + xpath_filter = stmt.excluded.xpath_filter, + period = stmt.excluded.period, + sub_subscription_id = stmt.excluded.sub_subscription_id, + sub_subscription_uri = stmt.excluded.sub_subscription_uri, + collector_name = stmt.excluded.collector_name, + updated_at = stmt.excluded.updated_at, + ) + ) + stmt = stmt.returning( + SubSubscriptionModel.created_at, SubSubscriptionModel.updated_at, + SubSubscriptionModel.sub_subscription_uuid + ) + return_values = session.execute(stmt).fetchone() + created_at,updated_at,subscription_uuid = return_values + return updated_at > created_at, subscription_uuid + + _, subscription_uuid = run_transaction(sessionmaker(bind=db_engine), callback) + return subscription_uuid + + +def sub_subscription_delete( + db_engine : Engine, parent_subscription_uuid : str, sub_subscription_id : int +) -> bool: + def callback(session : Session) -> bool: + num_deleted = ( + session + .query(SubSubscriptionModel) + .filter_by(parent=parent_subscription_uuid, sub_subscription_id=sub_subscription_id) + .delete() + ) + return num_deleted > 0 + return run_transaction(sessionmaker(bind=db_engine), callback) diff --git a/src/simap_connector/service/database/Subscription.py b/src/simap_connector/service/database/Subscription.py new file mode 100644 index 0000000000000000000000000000000000000000..6081fc4853461efd88261a870351b263a6b4f5b0 --- /dev/null +++ b/src/simap_connector/service/database/Subscription.py @@ -0,0 +1,87 @@ +# 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 datetime, logging +from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.engine import Engine +from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy_cockroachdb import run_transaction +from typing import Dict, Optional, Tuple +from common.method_wrappers.ServiceExceptions import NotFoundException +from .models.SubscriptionModel import SubscriptionModel + + +LOGGER = logging.getLogger(__name__) + + +def subscription_get(db_engine : Engine, subscription_id : int) -> Dict: + def callback(session : Session) -> Optional[Dict]: + obj : Optional[SubscriptionModel] = ( + session + .query(SubscriptionModel) + .filter_by(subscription_id=subscription_id) + .one_or_none() + ) + return None if obj is None else obj.dump() + obj = run_transaction(sessionmaker(bind=db_engine), callback) + if obj is None: + raise NotFoundException('Subscription', str(subscription_id)) + return obj + + +def subscription_set( + db_engine : Engine, datastore : str, xpath_filter : str, period : float +) -> Tuple[str, int]: + now = datetime.datetime.now(datetime.timezone.utc) + subscription_data = { + 'datastore' : datastore, + 'xpath_filter' : xpath_filter, + 'period' : period, + 'created_at' : now, + 'updated_at' : now, + } + + def callback(session : Session) -> Tuple[bool, str, int]: + stmt = insert(SubscriptionModel).values([subscription_data]) + stmt = stmt.on_conflict_do_update( + index_elements=[SubscriptionModel.subscription_uuid], + set_=dict( + datastore = stmt.excluded.datastore, + xpath_filter = stmt.excluded.xpath_filter, + period = stmt.excluded.period, + updated_at = stmt.excluded.updated_at, + ) + ) + stmt = stmt.returning( + SubscriptionModel.created_at, SubscriptionModel.updated_at, + SubscriptionModel.subscription_uuid, SubscriptionModel.subscription_id + ) + return_values = session.execute(stmt).fetchone() + created_at,updated_at,subscription_uuid,subscription_id = return_values + return updated_at > created_at, subscription_uuid, subscription_id + + _, subscription_uuid, subscription_id = run_transaction(sessionmaker(bind=db_engine), callback) + return subscription_uuid, subscription_id + + +def subscription_delete(db_engine : Engine, subscription_id : int) -> bool: + def callback(session : Session) -> bool: + num_deleted = ( + session + .query(SubscriptionModel) + .filter_by(subscription_id=subscription_id) + .delete() + ) + return num_deleted > 0 + return run_transaction(sessionmaker(bind=db_engine), callback) diff --git a/src/simap_connector/service/database/__init__.py b/src/simap_connector/service/database/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/service/database/__init__.py @@ -0,0 +1,13 @@ +# 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/simap_connector/service/database/models/SubSubscriptionModel.py b/src/simap_connector/service/database/models/SubSubscriptionModel.py new file mode 100644 index 0000000000000000000000000000000000000000..e05182457f5b9ad2e8a75b52214165e41e1fe53b --- /dev/null +++ b/src/simap_connector/service/database/models/SubSubscriptionModel.py @@ -0,0 +1,50 @@ +# 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 sqlalchemy import BigInteger, Column, DateTime, Float, ForeignKey, String, text +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship +from typing import Dict +from ._Base import _Base + + +class SubSubscriptionModel(_Base): + __tablename__ = 'sub_subscription' + + parent = Column(ForeignKey('subscription.subscription_uuid', ondelete='CASCADE'), primary_key=True) + sub_subscription_uuid = Column(UUID(as_uuid=False), primary_key=True, server_default=text('gen_random_uuid()')) + controller_uuid = Column(String, nullable=False, unique=False) + datastore = Column(String, nullable=False, unique=False) + xpath_filter = Column(String, nullable=False, unique=False) + period = Column(Float, nullable=False, unique=False) + sub_subscription_id = Column(BigInteger, nullable=False, unique=False) + sub_subscription_uri = Column(String, nullable=False, unique=False) + collector_name = Column(String, nullable=False, unique=False) + created_at = Column(DateTime, nullable=False) + updated_at = Column(DateTime, nullable=False) + + subscription = relationship('SubscriptionModel', back_populates='sub_subscriptions') + + def dump(self) -> Dict: + return { + 'parent_subscription_uuid' : self.parent, + 'sub_subscription_uuid' : self.sub_subscription_uuid, + 'controller_uuid' : self.controller_uuid, + 'datastore' : self.datastore, + 'xpath_filter' : self.xpath_filter, + 'period' : self.period, + 'sub_subscription_id' : self.sub_subscription_id, + 'sub_subscription_uri' : self.sub_subscription_uri, + 'collector_name' : self.collector_name, + } diff --git a/src/simap_connector/service/database/models/SubscriptionModel.py b/src/simap_connector/service/database/models/SubscriptionModel.py new file mode 100644 index 0000000000000000000000000000000000000000..ad4ac5e7820981cc3732615fb0e5168ec5b45016 --- /dev/null +++ b/src/simap_connector/service/database/models/SubscriptionModel.py @@ -0,0 +1,43 @@ +# 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 sqlalchemy import Column, DateTime, String +from sqlalchemy import Column, Float, BigInteger, String, text +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship +from typing import Dict +from ._Base import _Base + + +class SubscriptionModel(_Base): + __tablename__ = 'subscription' + + subscription_uuid = Column(UUID(as_uuid=False), primary_key=True, server_default=text('gen_random_uuid()')) + subscription_id = Column(BigInteger, nullable=False, unique=True, server_default=text('unique_rowid()')) + datastore = Column(String, nullable=False, unique=False) + xpath_filter = Column(String, nullable=False, unique=False) + period = Column(Float, nullable=False, unique=False) + created_at = Column(DateTime, nullable=False) + updated_at = Column(DateTime, nullable=False) + + sub_subscriptions = relationship('SubSubscriptionModel') + + def dump(self) -> Dict: + return { + 'subscription_uuid': self.subscription_uuid, + 'subscription_id' : self.subscription_id, + 'datastore' : self.datastore, + 'xpath_filter' : self.xpath_filter, + 'period' : self.period, + } diff --git a/src/simap_connector/service/database/models/_Base.py b/src/simap_connector/service/database/models/_Base.py new file mode 100644 index 0000000000000000000000000000000000000000..a67909275cbb96d8e71a856503e87b0a7d0358dc --- /dev/null +++ b/src/simap_connector/service/database/models/_Base.py @@ -0,0 +1,42 @@ +# 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 sqlalchemy +#from typing import Any, List +#from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy.orm import declarative_base +#from sqlalchemy.sql import text +#from sqlalchemy_cockroachdb import run_transaction + +_Base = declarative_base() + +#def create_performance_enhancers(db_engine : sqlalchemy.engine.Engine) -> None: +# def index_storing( +# index_name : str, table_name : str, index_fields : List[str], storing_fields : List[str] +# ) -> Any: +# str_index_fields = ','.join(['"{:s}"'.format(index_field) for index_field in index_fields]) +# str_storing_fields = ','.join(['"{:s}"'.format(storing_field) for storing_field in storing_fields]) +# INDEX_STORING = 'CREATE INDEX IF NOT EXISTS {:s} ON "{:s}" ({:s}) STORING ({:s});' +# return text(INDEX_STORING.format(index_name, table_name, str_index_fields, str_storing_fields)) +# +# statements = [ +# ] +# def callback(session : Session) -> bool: +# for stmt in statements: session.execute(stmt) +# run_transaction(sessionmaker(bind=db_engine), callback) + +def rebuild_database(db_engine : sqlalchemy.engine.Engine, drop_if_exists : bool = False): + if drop_if_exists: _Base.metadata.drop_all(db_engine) + _Base.metadata.create_all(db_engine) + #create_performance_enhancers(db_engine) diff --git a/src/simap_connector/service/database/models/__init__.py b/src/simap_connector/service/database/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/service/database/models/__init__.py @@ -0,0 +1,13 @@ +# 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/device/service/drivers/ietf_slice/Constants.py b/src/simap_connector/service/simap_updater/AllowedLinks.py similarity index 68% rename from src/device/service/drivers/ietf_slice/Constants.py rename to src/simap_connector/service/simap_updater/AllowedLinks.py index 70ce2da174ae30efb9363601aee129a2f956ac66..4758eebbf343f55e5bafba7f8adf568eb8dfb8ea 100644 --- a/src/device/service/drivers/ietf_slice/Constants.py +++ b/src/simap_connector/service/simap_updater/AllowedLinks.py @@ -12,14 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from device.service.driver_api._Driver import ( - RESOURCE_ENDPOINTS, - RESOURCE_INTERFACES, - RESOURCE_NETWORK_INSTANCES, -) - -SPECIAL_RESOURCE_MAPPINGS = { - RESOURCE_ENDPOINTS: "/endpoints", - RESOURCE_INTERFACES: "/interfaces", - RESOURCE_NETWORK_INSTANCES: "/net-instances", +ALLOWED_LINKS_PER_CONTROLLER = { + 'e2e' : {'L1', 'L2', 'L3', 'L4'}, + 'agg' : {'L7ab', 'L7ba', 'L8ab', 'L8ba', 'L11ab', 'L11ba', + 'L12ab', 'L12ba', 'L13', 'L14'}, + 'trans-pkt': {'L5', 'L6', 'L9', 'L10'}, } diff --git a/src/simap_connector/service/simap_updater/MockSimaps.py b/src/simap_connector/service/simap_updater/MockSimaps.py new file mode 100644 index 0000000000000000000000000000000000000000..37526d0199c823e99c1fbf253972f8829948a809 --- /dev/null +++ b/src/simap_connector/service/simap_updater/MockSimaps.py @@ -0,0 +1,143 @@ +# 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 logging +from .SimapClient import SimapClient + + +LOGGER = logging.getLogger(__name__) + + +def set_simap_e2e_net(simap_client : SimapClient) -> None: + simap = simap_client.network('e2e') + simap.update(supporting_network_ids=['admin', 'agg']) + + node_a = simap.node('sdp1') + node_a.update(supporting_node_ids=[('admin', 'ONT1')]) + node_a.termination_point('200').update(supporting_termination_point_ids=[('admin', 'ONT1', '200')]) + node_a.termination_point('500').update(supporting_termination_point_ids=[('admin', 'ONT1', '500')]) + + node_b = simap.node('sdp2') + node_b.update(supporting_node_ids=[('admin', 'POP2')]) + node_b.termination_point('200').update(supporting_termination_point_ids=[('admin', 'POP2', '200')]) + node_b.termination_point('201').update(supporting_termination_point_ids=[('admin', 'POP2', '201')]) + node_b.termination_point('500').update(supporting_termination_point_ids=[('admin', 'POP2', '500')]) + + link = simap.link('E2E-L1') + link.update( + 'sdp1', '500', 'sdp2', '500', + supporting_link_ids=[ + ('admin', 'L1'), ('admin', 'L3'), ('agg', 'AggNet-L1') + ] + ) + + +def delete_simap_e2e_net(simap_client : SimapClient) -> None: + simap = simap_client.network('e2e') + simap.update(supporting_network_ids=['admin', 'agg']) + + link = simap.link('E2E-L1') + link.delete() + + +def set_simap_agg_net(simap_client : SimapClient) -> None: + simap = simap_client.network('agg') + simap.update(supporting_network_ids=['admin', 'trans-pkt']) + + node_a = simap.node('sdp1') + node_a.update(supporting_node_ids=[('admin', 'OLT')]) + node_a.termination_point('200').update(supporting_termination_point_ids=[('admin', 'OLT', '200')]) + node_a.termination_point('201').update(supporting_termination_point_ids=[('admin', 'OLT', '201')]) + node_a.termination_point('500').update(supporting_termination_point_ids=[('admin', 'OLT', '500')]) + node_a.termination_point('501').update(supporting_termination_point_ids=[('admin', 'OLT', '501')]) + + node_b = simap.node('sdp2') + node_b.update(supporting_node_ids=[('admin', 'POP2')]) + node_b.termination_point('200').update(supporting_termination_point_ids=[('admin', 'POP2', '200')]) + node_b.termination_point('201').update(supporting_termination_point_ids=[('admin', 'POP2', '201')]) + node_b.termination_point('500').update(supporting_termination_point_ids=[('admin', 'POP2', '500')]) + + link = simap.link('AggNet-L1') + link.update( + 'sdp1', '500', 'sdp2', '500', + supporting_link_ids=[ + ('trans-pkt', 'Trans-L1'), ('admin', 'L13') + ] + ) + + +def delete_simap_agg_net(simap_client : SimapClient) -> None: + simap = simap_client.network('agg') + simap.update(supporting_network_ids=['admin', 'trans-pkt']) + + link = simap.link('AggNet-L1') + link.delete() + + +def set_simap_trans_pkt(simap_client : SimapClient) -> None: + simap = simap_client.network('trans-pkt') + simap.update(supporting_network_ids=['admin']) + + node_a = simap.node('site1') + node_a.update(supporting_node_ids=[('admin', 'P-PE1')]) + node_a.termination_point('200').update(supporting_termination_point_ids=[('admin', 'P-PE1', '200')]) + node_a.termination_point('500').update(supporting_termination_point_ids=[('admin', 'P-PE1', '500')]) + node_a.termination_point('501').update(supporting_termination_point_ids=[('admin', 'P-PE1', '501')]) + + node_b = simap.node('site2') + node_b.update(supporting_node_ids=[('admin', 'P-PE2')]) + node_b.termination_point('200').update(supporting_termination_point_ids=[('admin', 'P-PE2', '200')]) + node_b.termination_point('500').update(supporting_termination_point_ids=[('admin', 'P-PE2', '500')]) + node_b.termination_point('501').update(supporting_termination_point_ids=[('admin', 'P-PE2', '501')]) + + link = simap.link('Trans-L1') + link.update( + 'site1', '500', 'site2', '500', + supporting_link_ids=[ + ('admin', 'L5'), ('admin', 'L9') + ] + ) + + +def delete_simap_trans_pkt(simap_client : SimapClient) -> None: + simap = simap_client.network('trans-pkt') + simap.update(supporting_network_ids=['admin']) + + link = simap.link('Trans-L1') + link.delete() + + +def set_mock_simap(simap_client : SimapClient, domain_name : str) -> None: + if domain_name == 'trans-pkt': + set_simap_trans_pkt(simap_client) + elif domain_name == 'agg': + set_simap_agg_net(simap_client) + elif domain_name == 'e2e': + set_simap_e2e_net(simap_client) + else: + MSG = 'Unsupported Topology({:s}) to set SIMAP' + LOGGER.warning(MSG.format(str(domain_name))) + + +def delete_mock_simap(simap_client : SimapClient, domain_name : str) -> None: + if domain_name == 'trans-pkt': + delete_simap_trans_pkt(simap_client) + elif domain_name == 'agg': + delete_simap_agg_net(simap_client) + elif domain_name == 'e2e': + delete_simap_e2e_net(simap_client) + else: + MSG = 'Unsupported Topology({:s}) to delete SIMAP' + LOGGER.warning(MSG.format(str(domain_name))) diff --git a/src/simap_connector/service/simap_updater/ObjectCache.py b/src/simap_connector/service/simap_updater/ObjectCache.py new file mode 100644 index 0000000000000000000000000000000000000000..d8b04f8d4dd8b07d9bf4d6c3ef01e5190c350aaa --- /dev/null +++ b/src/simap_connector/service/simap_updater/ObjectCache.py @@ -0,0 +1,201 @@ +# 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 logging +from enum import Enum +from typing import Any, Dict, List, Optional, Tuple +from common.tools.context_queries.Device import get_device, get_devices +from common.tools.context_queries.Link import get_link, get_links +from common.tools.context_queries.Topology import get_topology, get_topologies +from common.tools.context_queries.Service import get_service_by_uuid, get_services +from context.client.ContextClient import ContextClient + + +LOGGER = logging.getLogger(__name__) + + +class CachedEntities(Enum): + TOPOLOGY = 'topology' + DEVICE = 'device' + ENDPOINT = 'endpoint' + LINK = 'link' + SERVICE = 'service' + CONNECTION = 'connection' + + +KEY_LENGTHS = { + CachedEntities.TOPOLOGY : 1, + CachedEntities.DEVICE : 1, + CachedEntities.ENDPOINT : 2, + CachedEntities.LINK : 1, + CachedEntities.SERVICE : 1, + CachedEntities.CONNECTION : 3, +} + + +def compose_object_key(entity : CachedEntities, *object_uuids : str) -> Tuple[str, ...]: + expected_length = KEY_LENGTHS.get(entity) + entity_name = str(entity.value) + if expected_length is None: + MSG = 'Unsupported ({:s}, {:s})' + raise Exception(MSG.format(entity_name.title(), str(object_uuids))) + + if len(object_uuids) == expected_length: + return (entity_name, *object_uuids) + + MSG = 'Invalid Key ({:s}, {:s})' + raise Exception(MSG.format(entity_name.title(), str(object_uuids))) + + +class ObjectCache: + def __init__(self, context_client : ContextClient): + self._context_client = context_client + self._object_cache : Dict[Tuple[str, str], Any] = dict() + + def get( + self, entity : CachedEntities, *object_uuids : str, + auto_retrieve : bool = True, force_update : bool = False + ) -> Optional[Any]: + if force_update: self._update(entity, *object_uuids) + + object_key = compose_object_key(entity, *object_uuids) + if object_key in self._object_cache: + return self._object_cache[object_key] + + if not auto_retrieve: return None + return self._update(entity, *object_uuids) + + def get_all( + self, entity : CachedEntities, fresh : bool = False + ) -> List[Any]: + if fresh: self._update_all(entity) + entity_name = str(entity.value) + return [ + obj + for obj_key, obj in self._object_cache.items() + if obj_key[0] == entity_name + ] + + def set(self, entity : CachedEntities, object_inst : Any, *object_uuids : str) -> None: + object_key = compose_object_key(entity, *object_uuids) + self._object_cache[object_key] = object_inst + + def _update(self, entity : CachedEntities, *object_uuids : str) -> Optional[Any]: + if entity == CachedEntities.TOPOLOGY: + object_inst = get_topology( + self._context_client, object_uuids[0], rw_copy=False + ) + elif entity == CachedEntities.DEVICE: + object_inst = get_device( + self._context_client, object_uuids[0], rw_copy=False, include_endpoints=True, + include_components=False, include_config_rules=False, + ) + elif entity == CachedEntities.ENDPOINT: + # Endpoints are only updated when updating a Device + return None + elif entity == CachedEntities.LINK: + object_inst = get_link( + self._context_client, object_uuids[0], rw_copy=False + ) + elif entity == CachedEntities.SERVICE: + object_inst = get_service_by_uuid( + self._context_client, object_uuids[0], rw_copy=False + ) + else: + MSG = 'Not Supported ({:s}, {:s})' + LOGGER.warning(MSG.format(str(entity.value).title(), str(object_uuids))) + return None + + if object_inst is None: + MSG = 'Not Found ({:s}, {:s})' + LOGGER.warning(MSG.format(str(entity.value).title(), str(object_uuids))) + return None + + self.set(entity, object_inst, object_uuids[0]) + self.set(entity, object_inst, object_inst.name) + + if entity == CachedEntities.DEVICE: + device_uuid = object_inst.device_id.device_uuid.uuid + device_name = object_inst.name + + for endpoint in object_inst.device_endpoints: + endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + if device_uuid != endpoint_device_uuid: + MSG = 'DeviceUUID({:s}) != Endpoint.DeviceUUID({:s})' + raise Exception(str(device_uuid), str(endpoint_device_uuid)) + + endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid + endpoint_name = endpoint.name + self.set(CachedEntities.ENDPOINT, endpoint, device_uuid, endpoint_uuid) + self.set(CachedEntities.ENDPOINT, endpoint, device_uuid, endpoint_name) + self.set(CachedEntities.ENDPOINT, endpoint, device_name, endpoint_uuid) + self.set(CachedEntities.ENDPOINT, endpoint, device_name, endpoint_name) + + return object_inst + + def _update_all(self, entity : CachedEntities) -> None: + if entity == CachedEntities.TOPOLOGY: + objects = get_topologies(self._context_client) + objects = { + (t.topology_id.topology_uuid.uuid, t.name) : t + for t in objects + } + elif entity == CachedEntities.DEVICE: + objects = get_devices(self._context_client) + objects = { + (d.device_id.device_uuid.uuid, d.name) : d + for d in objects + } + elif entity == CachedEntities.ENDPOINT: + # Endpoints are only updated when updating a Device + return None + elif entity == CachedEntities.LINK: + objects = get_links(self._context_client) + objects = { + (l.link_id.link_uuid.uuid, l.name) : l + for l in objects + } + elif entity == CachedEntities.SERVICE: + objects = get_services(self._context_client) + objects = { + (s.service_id.service_uuid.uuid, s.name) : s + for s in objects + } + else: + MSG = 'Not Supported ({:s})' + LOGGER.warning(MSG.format(str(entity.value).title())) + return None + + for (object_uuid, object_name), object_inst in objects.items(): + self.set(entity, object_inst, object_uuid) + self.set(entity, object_inst, object_name) + + if entity == CachedEntities.DEVICE: + for endpoint in object_inst.device_endpoints: + endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + if object_uuid != endpoint_device_uuid: + MSG = 'DeviceUUID({:s}) != Endpoint.DeviceUUID({:s})' + raise Exception(str(object_uuid), str(endpoint_device_uuid)) + + endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid + endpoint_name = endpoint.name + self.set(CachedEntities.ENDPOINT, endpoint, object_uuid, endpoint_uuid) + self.set(CachedEntities.ENDPOINT, endpoint, object_uuid, endpoint_name) + self.set(CachedEntities.ENDPOINT, endpoint, object_name, endpoint_uuid) + self.set(CachedEntities.ENDPOINT, endpoint, object_name, endpoint_name) + + def delete(self, entity : CachedEntities, *object_uuids : str) -> None: + object_key = compose_object_key(entity, *object_uuids) + self._object_cache.pop(object_key, None) diff --git a/src/simap_connector/service/simap_updater/SimapClient.py b/src/simap_connector/service/simap_updater/SimapClient.py new file mode 100644 index 0000000000000000000000000000000000000000..725b08bd47e0bd127cf0f7c4131cb744313b149d --- /dev/null +++ b/src/simap_connector/service/simap_updater/SimapClient.py @@ -0,0 +1,350 @@ +# 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, Optional, Tuple +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +class TerminationPoint: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}/node={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:termination-point={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str, tp_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tp_id = tp_id + + def create(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network-topology:termination-point'][0] + + def update(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + self._restconf_client.delete(endpoint) + + +class NodeTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/node={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + + def create( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class Node: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/node={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tps : Dict[str, TerminationPoint] = dict() + self._telemetry : Optional[NodeTelemetry] = None + + @property + def telemetry(self) -> NodeTelemetry: + if self._telemetry is None: + self._telemetry = NodeTelemetry(self._restconf_client, self._network_id, self._node_id) + return self._telemetry + + def termination_points(self) -> List[Dict]: + tps : Dict = self._restconf_client.get(TerminationPoint.ENDPOINT_NO_ID) + return tps['ietf-network-topology:termination-point'].get('termination-point', list()) + + def termination_point(self, tp_id : str) -> TerminationPoint: + _tp = self._tps.get(tp_id) + if _tp is not None: return _tp + _tp = TerminationPoint(self._restconf_client, self._network_id, self._node_id, tp_id) + return self._tps.setdefault(tp_id, _tp) + + def create( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network:node'][0] + + def update( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class LinkTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/ietf-network-topology:link={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + + def create( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Link: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:link={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + self._telemetry : Optional[LinkTelemetry] = None + + @property + def telemetry(self) -> LinkTelemetry: + if self._telemetry is None: + self._telemetry = LinkTelemetry(self._restconf_client, self._network_id, self._link_id) + return self._telemetry + + def create( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link : Dict = self._restconf_client.get(endpoint) + return link['ietf-network-topology:link'][0] + + def update( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Network: + ENDPOINT_NO_ID = '/ietf-network:networks' + ENDPOINT_ID = ENDPOINT_NO_ID + '/network={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._nodes : Dict[str, Node] = dict() + self._links : Dict[str, Link] = dict() + + def nodes(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Node.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('node', list()) + + def links(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Link.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('ietf-network-topology:link', list()) + + def node(self, node_id : str) -> Node: + _node = self._nodes.get(node_id) + if _node is not None: return _node + _node = Node(self._restconf_client, self._network_id, node_id) + return self._nodes.setdefault(node_id, _node) + + def link(self, link_id : str) -> Link: + _link = self._links.get(link_id) + if _link is not None: return _link + _link = Link(self._restconf_client, self._network_id, link_id) + return self._links.setdefault(link_id, _link) + + def create(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + networks : Dict = self._restconf_client.get(endpoint) + return networks['ietf-network:network'][0] + + def update(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + self._restconf_client.delete(endpoint) + + +class SimapClient: + def __init__(self, restconf_client : RestConfClient) -> None: + self._restconf_client = restconf_client + self._networks : Dict[str, Network] = dict() + + def networks(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Network.ENDPOINT_NO_ID) + return reply['ietf-network:networks'].get('network', list()) + + def network(self, network_id : str) -> Network: + _network = self._networks.get(network_id) + if _network is not None: return _network + _network = Network(self._restconf_client, network_id) + return self._networks.setdefault(network_id, _network) diff --git a/src/simap_connector/service/simap_updater/SimapUpdater.py b/src/simap_connector/service/simap_updater/SimapUpdater.py new file mode 100644 index 0000000000000000000000000000000000000000..343b373c719b2ec2bf09ae4dd662d6ad98d4afad --- /dev/null +++ b/src/simap_connector/service/simap_updater/SimapUpdater.py @@ -0,0 +1,683 @@ +# 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 logging, queue, threading, uuid +from typing import Any, Optional, Set +from common.Constants import DEFAULT_TOPOLOGY_NAME +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import ( + ContextEvent, DeviceEvent, Empty, LinkEvent, ServiceEvent, SliceEvent, TopologyEvent +) +from common.tools.grpc.BaseEventCollector import BaseEventCollector +from common.tools.grpc.BaseEventDispatcher import BaseEventDispatcher +from common.tools.grpc.Tools import grpc_message_to_json_string +from context.client.ContextClient import ContextClient +from simap_connector.service.telemetry.worker.data.Resources import ( + ResourceLink, Resources, SyntheticSampler +) +from simap_connector.service.telemetry.worker._Worker import WorkerTypeEnum +from simap_connector.service.telemetry.TelemetryPool import TelemetryPool +from .AllowedLinks import ALLOWED_LINKS_PER_CONTROLLER +from .MockSimaps import delete_mock_simap, set_mock_simap +from .ObjectCache import CachedEntities, ObjectCache +from .SimapClient import SimapClient +from .Tools import get_device_endpoint, get_link_endpoint #, get_service_endpoint + + +LOGGER = logging.getLogger(__name__) + + +SKIPPED_DEVICE_TYPES = { + DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER.value, + DeviceTypeEnum.IP_SDN_CONTROLLER.value, + DeviceTypeEnum.NCE.value, + DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value, +} + + +class EventDispatcher(BaseEventDispatcher): + def __init__( + self, events_queue : queue.PriorityQueue, + simap_client : SimapClient, + context_client : ContextClient, + telemetry_pool : TelemetryPool, + terminate : Optional[threading.Event] = None + ) -> None: + super().__init__(events_queue, terminate) + self._simap_client = simap_client + self._context_client = context_client + self._telemetry_pool = telemetry_pool + self._object_cache = ObjectCache(self._context_client) + self._skipped_devices : Set[str] = set() + + + def _add_skipped_device(self, device) -> None: + self._skipped_devices.add(device.device_id.device_uuid.uuid) + self._skipped_devices.add(device.name) + + + def _remove_skipped_device(self, device) -> None: + self._skipped_devices.discard(device.device_id.device_uuid.uuid) + self._skipped_devices.discard(device.name) + + + def dispatch(self, event : Any) -> None: + MSG = 'Unexpected Event: {:s}' + LOGGER.warning(MSG.format(grpc_message_to_json_string(event))) + + + def dispatch_context(self, context_event : ContextEvent) -> None: + MSG = 'Skipping Context Event: {:s}' + LOGGER.debug(MSG.format(grpc_message_to_json_string(context_event))) + + + def dispatch_slice(self, slice_event : SliceEvent) -> None: + MSG = 'Skipping Slice Event: {:s}' + LOGGER.debug(MSG.format(grpc_message_to_json_string(slice_event))) + + + def _dispatch_topology_set(self, topology_event : TopologyEvent) -> bool: + MSG = 'Processing Topology Event: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(topology_event))) + + topology_uuid = topology_event.topology_id.topology_uuid.uuid + topology = self._object_cache.get(CachedEntities.TOPOLOGY, topology_uuid) + topology_name = topology.name + + supporting_network_ids = list() + if topology_name != DEFAULT_TOPOLOGY_NAME: + supporting_network_ids.append(DEFAULT_TOPOLOGY_NAME) + + # Theoretically it should be create(), but given we have multiple clients + # updating same SIMAP server, use update to skip tricks on get-check-create-or-update. + self._simap_client.network(topology_name).update( + supporting_network_ids=supporting_network_ids + ) + return True + + + def dispatch_topology_create(self, topology_event : TopologyEvent) -> None: + if not self._dispatch_topology_set(topology_event): return + + MSG = 'Topology Create: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(topology_event))) + + + def dispatch_topology_update(self, topology_event : TopologyEvent) -> None: + if not self._dispatch_topology_set(topology_event): return + + MSG = 'Topology Updated: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(topology_event))) + + + def dispatch_topology_remove(self, topology_event : TopologyEvent) -> None: + MSG = 'Processing Topology Event: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(topology_event))) + + topology_uuid = topology_event.topology_id.topology_uuid.uuid + topology = self._object_cache.get(CachedEntities.TOPOLOGY, topology_uuid) + topology_name = topology.name + self._simap_client.network(topology_name).delete() + + self._object_cache.delete(CachedEntities.TOPOLOGY, topology_uuid) + self._object_cache.delete(CachedEntities.TOPOLOGY, topology_name) + + MSG = 'Topology Remove: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(topology_event))) + + + def _dispatch_device_set(self, device_event : DeviceEvent) -> bool: + MSG = 'Processing Device Event: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(device_event))) + + device_uuid = device_event.device_id.device_uuid.uuid + device = self._object_cache.get(CachedEntities.DEVICE, device_uuid, force_update=True) + + device_type = device.device_type + if device_type in SKIPPED_DEVICE_TYPES: + self._add_skipped_device(device) + MSG = ( + 'DeviceEvent({:s}) skipped, is of a skipped device type. ' + 'SIMAP should be updated by him: {:s}' + ) + str_device_event = grpc_message_to_json_string(device_event) + str_device = grpc_message_to_json_string(device) + LOGGER.warning(MSG.format(str_device_event, str_device)) + return False + + #device_controller_uuid = device.controller_id.device_uuid.uuid + #if len(device_controller_uuid) > 0: + # self._add_skipped_device(device) + # MSG = ( + # 'DeviceEvent({:s}) skipped, is a remotely-managed device. ' + # 'SIMAP should be populated by remote controller: {:s}' + # ) + # str_device_event = grpc_message_to_json_string(device_event) + # str_device = grpc_message_to_json_string(device) + # LOGGER.warning(MSG.format(str_device_event, str_device)) + # return + + topology_uuid, endpoint_names = get_device_endpoint(device) + if topology_uuid is None: + #self._add_skipped_device(device) + MSG = 'DeviceEvent({:s}) skipped, no endpoints to identify topology: {:s}' + str_device_event = grpc_message_to_json_string(device_event) + str_device = grpc_message_to_json_string(device) + LOGGER.warning(MSG.format(str_device_event, str_device)) + return False + + topology = self._object_cache.get(CachedEntities.TOPOLOGY, topology_uuid) + topology_name = topology.name + + te_topo = self._simap_client.network(topology_name) + te_topo.update() + + device_name = device.name + te_device = te_topo.node(device_name) + te_device.update() + + for endpoint_name in endpoint_names: + te_device.termination_point(endpoint_name).update() + + #self._remove_skipped_device(device) + return True + + + def dispatch_device_create(self, device_event : DeviceEvent) -> None: + if not self._dispatch_device_set(device_event): return + + MSG = 'Device Created: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(device_event))) + + + def dispatch_device_update(self, device_event : DeviceEvent) -> None: + if not self._dispatch_device_set(device_event): return + + MSG = 'Device Updated: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(device_event))) + + + def dispatch_device_remove(self, device_event : DeviceEvent) -> None: + MSG = 'Processing Device Event: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(device_event))) + + device_uuid = device_event.device_id.device_uuid.uuid + device = self._object_cache.get(CachedEntities.DEVICE, device_uuid) + + device_type = device.device_type + if device_type in SKIPPED_DEVICE_TYPES: + self._add_skipped_device(device) + MSG = ( + 'DeviceEvent({:s}) skipped, is of a skipped device type. ' + 'SIMAP should be updated by him: {:s}' + ) + str_device_event = grpc_message_to_json_string(device_event) + str_device = grpc_message_to_json_string(device) + LOGGER.warning(MSG.format(str_device_event, str_device)) + return + + device_controller_uuid = device.controller_id.device_uuid.uuid + if len(device_controller_uuid) > 0: + # if it is a delete of a remotely-managed device, + # should be managed by owner controller + #self._add_skipped_device(device) + MSG = ( + 'DeviceEvent({:s}) skipped, is a remotely-managed device. ' + 'SIMAP should be updated by remote controller: {:s}' + ) + str_device_event = grpc_message_to_json_string(device_event) + str_device = grpc_message_to_json_string(device) + LOGGER.warning(MSG.format(str_device_event, str_device)) + return + + topology_uuid, endpoint_names = get_device_endpoint(device) + topology = self._object_cache.get(CachedEntities.TOPOLOGY, topology_uuid) + topology_name = topology.name + + te_topo = self._simap_client.network(topology_name) + te_topo.update() + + device_name = device.name + te_device = te_topo.node(device_name) + for endpoint_name in endpoint_names: + te_device.termination_point(endpoint_name).delete() + + endpoint = self._object_cache.get(CachedEntities.ENDPOINT, device_uuid, endpoint_name) + endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid + self._object_cache.delete(CachedEntities.DEVICE, device_uuid, endpoint_uuid) + self._object_cache.delete(CachedEntities.DEVICE, device_uuid, endpoint_name) + self._object_cache.delete(CachedEntities.DEVICE, device_name, endpoint_uuid) + self._object_cache.delete(CachedEntities.DEVICE, device_name, endpoint_name) + + te_device.delete() + + self._remove_skipped_device(device) + self._object_cache.delete(CachedEntities.DEVICE, device_uuid) + self._object_cache.delete(CachedEntities.DEVICE, device_name) + + MSG = 'Device Remove: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(device_event))) + + + def _dispatch_link_set(self, link_event : LinkEvent) -> bool: + MSG = 'Processing Link Event: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(link_event))) + + link_uuid = link_event.link_id.link_uuid.uuid + link = self._object_cache.get(CachedEntities.LINK, link_uuid) + link_name = link.name + + topology_uuid, endpoint_uuids = get_link_endpoint(link) + if topology_uuid is None: + MSG = 'LinkEvent({:s}) skipped, no endpoint_ids to identify topology: {:s}' + str_link_event = grpc_message_to_json_string(link_event) + str_link = grpc_message_to_json_string(link) + LOGGER.warning(MSG.format(str_link_event, str_link)) + return False + + topology = self._object_cache.get(CachedEntities.TOPOLOGY, topology_uuid) + topology_name = topology.name + + te_topo = self._simap_client.network(topology_name) + te_topo.update() + + topologies = self._object_cache.get_all(CachedEntities.TOPOLOGY, fresh=False) + topology_names = {t.name for t in topologies} + topology_names.discard(DEFAULT_TOPOLOGY_NAME) + if len(topology_names) != 1: + MSG = 'LinkEvent({:s}) skipped, unable to identify self-controller' + str_link_event = grpc_message_to_json_string(link_event) + LOGGER.warning(MSG.format(str_link_event)) + return False + domain_name = topology_names.pop() # trans-pkt/agg/e2e + + allowed_link_names = ALLOWED_LINKS_PER_CONTROLLER.get(domain_name, set()) + if link_name not in allowed_link_names: return False + + src_device = self._object_cache.get(CachedEntities.DEVICE, endpoint_uuids[0][0], force_update =True ) + src_endpoint = self._object_cache.get(CachedEntities.ENDPOINT, *(endpoint_uuids[0]), auto_retrieve=False) + dst_device = self._object_cache.get(CachedEntities.DEVICE, endpoint_uuids[1][0], force_update =True ) + dst_endpoint = self._object_cache.get(CachedEntities.ENDPOINT, *(endpoint_uuids[1]), auto_retrieve=False) + + # Skip links that connect two management endpoints + if src_endpoint is not None and dst_endpoint is not None: + if str(src_endpoint.name).lower() == 'mgmt' and str(dst_endpoint.name).lower() == 'mgmt': + MSG = 'LinkEvent({:s}) skipped, connects two management endpoints: {:s}' + str_link_event = grpc_message_to_json_string(link_event) + str_link = grpc_message_to_json_string(link) + LOGGER.warning(MSG.format(str_link_event, str_link)) + return False + + # Skip links that connect to devices previously marked as skipped + src_uuid = src_device.device_id.device_uuid.uuid + dst_uuid = dst_device.device_id.device_uuid.uuid + src_name = src_device.name + dst_name = dst_device.name + if (src_uuid in self._skipped_devices or src_name in self._skipped_devices + or dst_uuid in self._skipped_devices or dst_name in self._skipped_devices): + MSG = 'LinkEvent({:s}) skipped, connects to skipped device(s): {:s}' + str_link_event = grpc_message_to_json_string(link_event) + str_link = grpc_message_to_json_string(link) + LOGGER.warning(MSG.format(str_link_event, str_link)) + return False + + try: + if src_device is None: + MSG = 'Device({:s}) not found in cache' + raise Exception(MSG.format(str(endpoint_uuids[0][0]))) + if src_endpoint is None: + MSG = 'Endpoint({:s}) not found in cache' + raise Exception(MSG.format(str(endpoint_uuids[0]))) + if dst_device is None: + MSG = 'Device({:s}) not found in cache' + raise Exception(MSG.format(str(endpoint_uuids[1][0]))) + if dst_endpoint is None: + MSG = 'Endpoint({:s}) not found in cache' + raise Exception(MSG.format(str(endpoint_uuids[1]))) + except Exception as e: + MSG = '{:s} in Link({:s})' + raise Exception(MSG.format(str(e), grpc_message_to_json_string(link))) from e + + te_link = te_topo.link(link_name) + te_link.update(src_device.name, src_endpoint.name, dst_device.name, dst_endpoint.name) + + worker_name = '{:s}:{:s}'.format(topology_name, link_name) + resources = Resources() + resources.links.append(ResourceLink( + domain_name=topology_name, link_name=link_name, + bandwidth_utilization_sampler=SyntheticSampler.create_random( + amplitude_scale = 25.0, + phase_scale = 1e-7, + period_scale = 86_400, + offset_scale = 25, + noise_ratio = 0.05, + min_value = 0.0, + max_value = 100.0, + ), + latency_sampler=SyntheticSampler.create_random( + amplitude_scale = 0.5, + phase_scale = 1e-7, + period_scale = 60.0, + offset_scale = 10.0, + noise_ratio = 0.05, + min_value = 0.0, + ), + related_service_ids=[], + )) + sampling_interval = 1.0 + self._telemetry_pool.start_synthesizer(worker_name, resources, sampling_interval) + + return True + + + def dispatch_link_create(self, link_event : LinkEvent) -> None: + if not self._dispatch_link_set(link_event): return + + MSG = 'Link Created: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(link_event))) + + + def dispatch_link_update(self, link_event : LinkEvent) -> None: + if not self._dispatch_link_set(link_event): return + + MSG = 'Link Updated: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(link_event))) + + + def dispatch_link_remove(self, link_event : LinkEvent) -> None: + MSG = 'Processing Link Event: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(link_event))) + + link_uuid = link_event.link_id.link_uuid.uuid + link = self._object_cache.get(CachedEntities.LINK, link_uuid) + link_name = link.name + + topology_uuid, endpoint_uuids = get_link_endpoint(link) + topology = self._object_cache.get(CachedEntities.TOPOLOGY, topology_uuid) + topology_name = topology.name + + te_topo = self._simap_client.network(topology_name) + te_topo.update() + + src_device = self._object_cache.get(CachedEntities.DEVICE, endpoint_uuids[0][0], force_update =True ) + src_endpoint = self._object_cache.get(CachedEntities.ENDPOINT, *(endpoint_uuids[0]), auto_retrieve=False) + dst_device = self._object_cache.get(CachedEntities.DEVICE, endpoint_uuids[1][0], force_update =True ) + dst_endpoint = self._object_cache.get(CachedEntities.ENDPOINT, *(endpoint_uuids[1]), auto_retrieve=False) + + # Skip links that connect two management endpoints + if src_endpoint is not None and dst_endpoint is not None: + if str(src_endpoint.name).lower() == 'mgmt' and str(dst_endpoint.name).lower() == 'mgmt': + MSG = 'LinkEvent({:s}) skipped, connects two management endpoints: {:s}' + str_link_event = grpc_message_to_json_string(link_event) + str_link = grpc_message_to_json_string(link) + LOGGER.warning(MSG.format(str_link_event, str_link)) + return + + # Skip links that connect to devices previously marked as skipped + src_uuid = src_device.device_id.device_uuid.uuid + dst_uuid = dst_device.device_id.device_uuid.uuid + src_name = src_device.name + dst_name = dst_device.name + if (src_uuid in self._skipped_devices or src_name in self._skipped_devices + or dst_uuid in self._skipped_devices or dst_name in self._skipped_devices): + MSG = 'LinkEvent({:s}) skipped, connects to skipped device(s): {:s}' + str_link_event = grpc_message_to_json_string(link_event) + str_link = grpc_message_to_json_string(link) + LOGGER.warning(MSG.format(str_link_event, str_link)) + return + + te_link = te_topo.link(link_name) + te_link.delete() + + self._object_cache.delete(CachedEntities.LINK, link_uuid) + self._object_cache.delete(CachedEntities.LINK, link_name) + + worker_name = '{:s}:{:s}'.format(topology_name, link_name) + self._telemetry_pool.stop_worker(WorkerTypeEnum.SYNTHESIZER, worker_name) + + MSG = 'Link Removed: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(link_event))) + + + def _dispatch_service_set(self, service_event : ServiceEvent) -> bool: + MSG = 'Processing Service Event: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(service_event))) + + service_uuid = service_event.service_id.service_uuid.uuid + service = self._object_cache.get(CachedEntities.SERVICE, service_uuid) + service_name = service.name + + try: + uuid.UUID(hex=service_name) + # skip it if properly parsed, means it is a service with a UUID-based name, i.e., a sub-service + MSG = 'ServiceEvent({:s}) skipped, it is a subservice: {:s}' + str_service_event = grpc_message_to_json_string(service_event) + str_service = grpc_message_to_json_string(service) + LOGGER.warning(MSG.format(str_service_event, str_service)) + return False + except: # pylint: disable=bare-except + pass + + #topology_uuid, endpoint_uuids = get_service_endpoint(service) + + #if topology_uuid is None: + # MSG = 'ServiceEvent({:s}) skipped, no endpoint_ids to identify topology: {:s}' + # str_service_event = grpc_message_to_json_string(service_event) + # str_service = grpc_message_to_json_string(service) + # LOGGER.warning(MSG.format(str_service_event, str_service)) + # return False + + #if len(endpoint_uuids) < 2: + # MSG = 'ServiceEvent({:s}) skipped, not enough endpoint_ids to compose link: {:s}' + # str_service_event = grpc_message_to_json_string(service_event) + # str_service = grpc_message_to_json_string(service) + # LOGGER.warning(MSG.format(str_service_event, str_service)) + # return False + + topologies = self._object_cache.get_all(CachedEntities.TOPOLOGY, fresh=False) + topology_names = {t.name for t in topologies} + topology_names.discard(DEFAULT_TOPOLOGY_NAME) + if len(topology_names) != 1: + MSG = 'ServiceEvent({:s}) skipped, unable to identify on which topology to insert it' + str_service_event = grpc_message_to_json_string(service_event) + LOGGER.warning(MSG.format(str_service_event)) + return False + + domain_name = topology_names.pop() # trans-pkt/agg-net/e2e-net + set_mock_simap(self._simap_client, domain_name) + + #domain_topo = self._simap_client.network(domain_name) + #domain_topo.update() + + #src_device = self._object_cache.get(CachedEntities.DEVICE, endpoint_uuids[0][0], force_update =True ) + #src_endpoint = self._object_cache.get(CachedEntities.ENDPOINT, *(endpoint_uuids[0]), auto_retrieve=False) + #dst_device = self._object_cache.get(CachedEntities.DEVICE, endpoint_uuids[1][0], force_update =True ) + #dst_endpoint = self._object_cache.get(CachedEntities.ENDPOINT, *(endpoint_uuids[1]), auto_retrieve=False) + + #try: + # if src_device is None: + # MSG = 'Device({:s}) not found in cache' + # raise Exception(MSG.format(str(endpoint_uuids[0][0]))) + # if src_endpoint is None: + # MSG = 'Endpoint({:s}) not found in cache' + # raise Exception(MSG.format(str(endpoint_uuids[0]))) + # if dst_device is None: + # MSG = 'Device({:s}) not found in cache' + # raise Exception(MSG.format(str(endpoint_uuids[1][0]))) + # if dst_endpoint is None: + # MSG = 'Endpoint({:s}) not found in cache' + # raise Exception(MSG.format(str(endpoint_uuids[1]))) + #except Exception as e: + # MSG = '{:s} in Service({:s})' + # raise Exception(MSG.format(str(e), grpc_message_to_json_string(service))) from e + + #src_dev_name = src_device.name + #src_ep_name = src_endpoint.name + #dst_dev_name = dst_device.name + #dst_ep_name = dst_endpoint.name + + #parent_domain_name = DEFAULT_TOPOLOGY_NAME # TODO: compute from service settings + + #site_1_name = 'site1' # TODO: compute from service settings + #site_1 = domain_topo.node(site_1_name) + #site_1.update(supporting_node_ids=[(parent_domain_name, src_dev_name)]) + #site_1_tp = site_1.termination_point(src_ep_name) + #site_1_tp.update(supporting_termination_point_ids=[(parent_domain_name, src_dev_name, src_ep_name)]) + + #site_2_name = 'site2' # TODO: compute from service settings + #site_2 = domain_topo.node(site_2_name) + #site_2.update(supporting_node_ids=[(parent_domain_name, dst_dev_name)]) + #site_2_tp = site_2.termination_point(dst_ep_name) + #site_2_tp.update(supporting_termination_point_ids=[(parent_domain_name, dst_dev_name, dst_ep_name)]) + + #link_name = '{:s}:{:s}-{:s}=={:s}-{:s}'.format( + # service_name, src_dev_name, src_ep_name, dst_dev_name, dst_ep_name + #) + #dom_link = domain_topo.link(link_name) + #dom_link.update(src_dev_name, src_ep_name, dst_dev_name, dst_ep_name) + + + #resources = Resources() + #sampling_interval = 1.0 + #self._telemetry_pool.start_synthesizer(domain_name, resources, sampling_interval) + return True + + + def dispatch_service_create(self, service_event : ServiceEvent) -> None: + if not self._dispatch_service_set(service_event): return + + MSG = 'Logical Link Created for Service: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(service_event))) + + + def dispatch_service_update(self, service_event : ServiceEvent) -> None: + if not self._dispatch_service_set(service_event): return + + MSG = 'Logical Link Updated for Service: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(service_event))) + + + def dispatch_service_remove(self, service_event : ServiceEvent) -> None: + MSG = 'Processing Service Event: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(service_event))) + + service_uuid = service_event.service_id.service_uuid.uuid + service = self._object_cache.get(CachedEntities.SERVICE, service_uuid) + service_name = service.name + + try: + uuid.UUID(hex=service_name) + # skip it if properly parsed, means it is a service with a UUID-based name, i.e., a sub-service + MSG = 'ServiceEvent({:s}) skipped, it is a subservice: {:s}' + str_service_event = grpc_message_to_json_string(service_event) + str_service = grpc_message_to_json_string(service) + LOGGER.warning(MSG.format(str_service_event, str_service)) + return False + except: # pylint: disable=bare-except + pass + + #topology_uuid, endpoint_uuids = get_service_endpoint(service) + #if topology_uuid is None: + # MSG = 'ServiceEvent({:s}) skipped, no endpoint_ids to identify topology: {:s}' + # str_service_event = grpc_message_to_json_string(service_event) + # str_service = grpc_message_to_json_string(service) + # LOGGER.warning(MSG.format(str_service_event, str_service)) + # return + + topologies = self._object_cache.get_all(CachedEntities.TOPOLOGY, fresh=False) + topology_names = {t.name for t in topologies} + topology_names.discard(DEFAULT_TOPOLOGY_NAME) + if len(topology_names) != 1: + MSG = 'ServiceEvent({:s}) skipped, unable to identify on which topology to insert it' + str_service_event = grpc_message_to_json_string(service_event) + LOGGER.warning(MSG.format(str_service_event)) + return + + domain_name = topology_names.pop() # trans-pkt/agg-net/e2e-net + delete_mock_simap(self._simap_client, domain_name) + + #domain_topo = self._simap_client.network(domain_name) + #domain_topo.update() + + #src_device = self._object_cache.get(CachedEntities.DEVICE, endpoint_uuids[0][0], force_update =True ) + #src_endpoint = self._object_cache.get(CachedEntities.ENDPOINT, *(endpoint_uuids[0]), auto_retrieve=False) + #dst_device = self._object_cache.get(CachedEntities.DEVICE, endpoint_uuids[1][0], force_update =True ) + #dst_endpoint = self._object_cache.get(CachedEntities.ENDPOINT, *(endpoint_uuids[1]), auto_retrieve=False) + + #try: + # if src_device is None: + # MSG = 'Device({:s}) not found in cache' + # raise Exception(MSG.format(str(endpoint_uuids[0][0]))) + # if src_endpoint is None: + # MSG = 'Endpoint({:s}) not found in cache' + # raise Exception(MSG.format(str(endpoint_uuids[0]))) + # if dst_device is None: + # MSG = 'Device({:s}) not found in cache' + # raise Exception(MSG.format(str(endpoint_uuids[1][0]))) + # if dst_endpoint is None: + # MSG = 'Endpoint({:s}) not found in cache' + # raise Exception(MSG.format(str(endpoint_uuids[1]))) + #except Exception as e: + # MSG = '{:s} in Service({:s})' + # raise Exception(MSG.format(str(e), grpc_message_to_json_string(service))) from e + + #src_dev_name = src_device.name + #src_ep_name = src_endpoint.name + #dst_dev_name = dst_device.name + #dst_ep_name = dst_endpoint.name + + #link_name = '{:s}:{:s}-{:s}=={:s}-{:s}'.format( + # service_name, src_dev_name, src_ep_name, dst_dev_name, dst_ep_name + #) + #te_link = domain_topo.link(link_name) + #te_link.delete() + + #self._object_cache.delete(CachedEntities.SERVICE, service_uuid) + #self._object_cache.delete(CachedEntities.SERVICE, service_name) + + #self._telemetry_pool.stop_worker(WorkerTypeEnum.SYNTHESIZER, domain_name) + + MSG = 'Logical Link Removed for Service: {:s}' + LOGGER.info(MSG.format(grpc_message_to_json_string(service_event))) + + +class SimapUpdater: + def __init__( + self, simap_client : SimapClient, telemetry_pool : TelemetryPool, + terminate : threading.Event + ) -> None: + self._simap_client = simap_client + self._telemetry_pool = telemetry_pool + self._context_client = ContextClient() + + self._event_collector = BaseEventCollector(terminate=terminate) + self._event_collector.install_collector( + self._context_client.GetAllEvents, Empty(), log_events_received=True + ) + + self._event_dispatcher = EventDispatcher( + self._event_collector.get_events_queue(), self._simap_client, + self._context_client, self._telemetry_pool, terminate=terminate + ) + + def start(self) -> None: + self._context_client.connect() + self._event_dispatcher.start() + self._event_collector.start() + + def stop(self): + self._event_collector.stop() + self._event_dispatcher.stop() + self._context_client.close() diff --git a/src/simap_connector/service/simap_updater/Tools.py b/src/simap_connector/service/simap_updater/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..d420f24e9b3714939b2900155c0a1bba2d350a90 --- /dev/null +++ b/src/simap_connector/service/simap_updater/Tools.py @@ -0,0 +1,162 @@ +# 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 enum +from typing import List, Optional, Set, Tuple, Union +from common.proto.context_pb2 import ( + EVENTTYPE_CREATE, EVENTTYPE_REMOVE, EVENTTYPE_UPDATE, Device, + DeviceEvent, Link, LinkEvent, Service, ServiceEvent, SliceEvent, TopologyEvent +) +from common.tools.grpc.Tools import grpc_message_to_json_string + + +class EventTypeEnum(enum.IntEnum): + CREATE = EVENTTYPE_CREATE + UPDATE = EVENTTYPE_UPDATE + REMOVE = EVENTTYPE_REMOVE + + +EVENT_TYPE = Union[DeviceEvent, LinkEvent, TopologyEvent, ServiceEvent, SliceEvent] + + +def get_event_type(event : EVENT_TYPE) -> EventTypeEnum: + int_event_type = event.event.event_type + enum_event_type = EventTypeEnum._value2member_map_.get(int_event_type) + if enum_event_type is None: + MSG = 'Unsupported EventType({:s}) in Event({:s})' + str_event = grpc_message_to_json_string(event) + raise Exception(MSG.format(str(int_event_type), str_event)) + + +def get_device_endpoint(device : Device) -> Tuple[Optional[str], List[str]]: + topology_uuids : Set[str] = set() + endpoint_device_uuids : Set[str] = set() + endpoint_names : List[str] = list() + + if len(device.device_endpoints) == 0: + return None, endpoint_names + + for endpoint in device.device_endpoints: + topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid + topology_uuids.add(topology_uuid) + + endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + endpoint_device_uuids.add(endpoint_device_uuid) + + endpoint_name = endpoint.name + endpoint_names.append(endpoint_name) + + try: + # Check topology UUIDs + if len(topology_uuids) != 1: + MSG = 'Unsupported: no/multiple Topologies({:s}) referenced' + raise Exception(MSG.format(str(topology_uuids))) + topology_uuid = list(topology_uuids)[0] + if len(topology_uuid) == 0: + MSG = 'Unsupported: empty TopologyUUID({:s}) referenced' + raise Exception(MSG.format(str(topology_uuid))) + + # Check Device UUIDs + if len(endpoint_device_uuids) != 1: + MSG = 'Unsupported: no/multiple DeviceUUID({:s}) referenced' + raise Exception(MSG.format(str(endpoint_device_uuids))) + endpoint_device_uuid = list(endpoint_device_uuids)[0] + if len(endpoint_device_uuid) == 0: + MSG = 'Unsupported: empty Endpoint.DeviceUUID({:s}) referenced' + raise Exception(MSG.format(str(endpoint_device_uuid))) + + device_uuid = device.device_id.device_uuid.uuid + if endpoint_device_uuid != device_uuid: + MSG = 'Unsupported: Endpoint.DeviceUUID({:s}) != DeviceUUID({:s})' + raise Exception(MSG.format(str(endpoint_device_uuid), str(device_uuid))) + except Exception as e: + MSG = '{:s} in Device({:s})' + raise Exception(MSG.format(str(e), grpc_message_to_json_string(device))) from e + + return topology_uuid, endpoint_names + + +def get_link_endpoint(link : Link) -> Tuple[Optional[str], List[Tuple[str, str]]]: + topology_uuids : Set[str] = set() + endpoint_uuids : List[Tuple[str, str]] = list() + + if len(link.link_endpoint_ids) == 0: + return None, endpoint_uuids + + for endpoint_id in link.link_endpoint_ids: + topology_uuid = endpoint_id.topology_id.topology_uuid.uuid + topology_uuids.add(topology_uuid) + + device_uuid = endpoint_id.device_id.device_uuid.uuid + endpoint_uuid = endpoint_id.endpoint_uuid.uuid + endpoint_uuids.append((device_uuid, endpoint_uuid)) + + try: + # Check topology UUIDs + if len(topology_uuids) != 1: + MSG = 'Unsupported: no/multiple Topologies({:s}) referenced' + raise Exception(MSG.format(str(topology_uuids))) + topology_uuid = list(topology_uuids)[0] + if len(topology_uuid) == 0: + MSG = 'Unsupported: empty TopologyUUID({:s}) referenced' + raise Exception(MSG.format(str(topology_uuid))) + + # Check Count Endpoints + if len(endpoint_uuids) != 2: + MSG = 'Unsupported: non-p2p link LinkUUIDs({:s})' + raise Exception(MSG.format(str(endpoint_uuids))) + except Exception as e: + MSG = '{:s} in Link({:s})' + raise Exception(MSG.format(str(e), grpc_message_to_json_string(link))) from e + + return topology_uuid, endpoint_uuids + + +def get_service_endpoint(service : Service) -> Tuple[Optional[str], List[Tuple[str, str]]]: + if len(service.service_endpoint_ids) == 0: + return None, list() + + topology_uuids : Set[str] = set() + endpoint_uuids : List[Tuple[str, str]] = list() + + for endpoint_id in service.service_endpoint_ids: + topology_uuid = endpoint_id.topology_id.topology_uuid.uuid + topology_uuids.add(topology_uuid) + + device_uuid = endpoint_id.device_id.device_uuid.uuid + endpoint_uuid = endpoint_id.endpoint_uuid.uuid + endpoint_uuids.append((device_uuid, endpoint_uuid)) + + try: + # Check topology UUIDs + if len(topology_uuids) != 1: + MSG = 'Unsupported: no/multiple Topologies({:s}) referenced' + raise Exception(MSG.format(str(topology_uuids))) + + topology_uuid = list(topology_uuids)[0] + if len(topology_uuid) == 0: + MSG = 'Unsupported: empty TopologyUUID({:s}) referenced' + raise Exception(MSG.format(str(topology_uuid))) + + # Check Count Endpoints + if len(endpoint_uuids) > 2: + MSG = 'Unsupported: non-p2p service ServiceUUIDs({:s})' + raise Exception(MSG.format(str(endpoint_uuids))) + + except Exception as e: + MSG = '{:s} in Service({:s})' + raise Exception(MSG.format(str(e), grpc_message_to_json_string(service))) from e + + return topology_uuid, endpoint_uuids diff --git a/src/simap_connector/service/simap_updater/__init__.py b/src/simap_connector/service/simap_updater/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/service/simap_updater/__init__.py @@ -0,0 +1,13 @@ +# 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/simap_connector/service/telemetry/TelemetryPool.py b/src/simap_connector/service/telemetry/TelemetryPool.py new file mode 100644 index 0000000000000000000000000000000000000000..8486642f37c2e01c14f7526a10554545c19d6e62 --- /dev/null +++ b/src/simap_connector/service/telemetry/TelemetryPool.py @@ -0,0 +1,153 @@ +# 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 logging, threading +from typing import Dict, Optional +from simap_connector.service.simap_updater.SimapClient import SimapClient +from .worker.data.Resources import Resources +from .worker.data.AggregationCache import AggregationCache +from .worker._Worker import _Worker, WorkerTypeEnum, get_worker_key +from .worker.AggregatorWorker import AggregatorWorker +from .worker.CollectorWorker import CollectorWorker +from .worker.SynthesizerWorker import SynthesizerWorker + + +LOGGER = logging.getLogger(__name__) + + +WORKER_CLASSES = { + WorkerTypeEnum.AGGREGATOR : AggregatorWorker, + WorkerTypeEnum.COLLECTOR : CollectorWorker, + WorkerTypeEnum.SYNTHESIZER: SynthesizerWorker, +} + + +class TelemetryPool: + def __init__( + self, simap_client : SimapClient, terminate : Optional[threading.Event] = None + ) -> None: + self._simap_client = simap_client + self._workers : Dict[str, _Worker] = dict() + self._lock = threading.Lock() + self._terminate = threading.Event() if terminate is None else terminate + + + def has_worker(self, worker_type : WorkerTypeEnum, worker_name : str) -> bool: + worker_key = get_worker_key(worker_type, worker_name) + return self.has_worker_by_key(worker_key) + + + def has_worker_by_key(self, worker_key : str) -> bool: + with self._lock: + return worker_key in self._workers + + + def get_worker(self, worker_type : WorkerTypeEnum, worker_name : str) -> Optional[_Worker]: + worker_key = get_worker_key(worker_type, worker_name) + return self.get_worker_by_key(worker_key) + + + def get_worker_by_key(self, worker_key : str) -> Optional[_Worker]: + with self._lock: + return self._workers.get(worker_key) + + + def start_aggregator( + self, worker_name : str, network_id : str, link_id : str, parent_subscription_id : int, + aggregation_cache : AggregationCache, topic : str, sampling_interval : float + ) -> None: + self._start_worker( + WorkerTypeEnum.AGGREGATOR, worker_name, self._simap_client, network_id, link_id, + parent_subscription_id, aggregation_cache, topic, sampling_interval + ) + + + def start_collector( + self, worker_name : str, controller_uuid : Optional[str], network_id : str, link_id : str, + target_uri : str, aggregation_cache : AggregationCache, sampling_interval : float + ) -> None: + self._start_worker( + WorkerTypeEnum.COLLECTOR, worker_name, controller_uuid, network_id, link_id, + target_uri, aggregation_cache, sampling_interval + ) + + + def start_synthesizer( + self, worker_name : str, resources : Resources, sampling_interval : float + ) -> None: + self._start_worker( + WorkerTypeEnum.SYNTHESIZER, worker_name, self._simap_client, resources, + sampling_interval + ) + + + def _start_worker( + self, worker_type : WorkerTypeEnum, worker_name : str, *args, **kwargs + ) -> None: + worker_key = get_worker_key(worker_type, worker_name) + with self._lock: + if worker_key in self._workers: + MSG = '[start_worker] Worker({:s}) already exists' + LOGGER.debug(MSG.format(str(worker_key))) + return + + worker_class = WORKER_CLASSES.get(worker_type) + if worker_class is None: + MSG = 'Unsupported WorkerType({:s})' + raise Exception(MSG.format(str(worker_type))) + + worker : _Worker = worker_class( + worker_name, *args, terminate=self._terminate, **kwargs + ) + worker.start() + + MSG = '[start_worker] Started Worker({:s})' + LOGGER.info(MSG.format(str(worker_key))) + + self._workers[worker_key] = worker + + + def stop_worker(self, worker_type : WorkerTypeEnum, worker_name : str) -> None: + worker_key = get_worker_key(worker_type, worker_name) + self.stop_worker_by_key(worker_key) + + + def stop_worker_by_key(self, worker_key : str) -> None: + with self._lock: + worker = self._workers.pop(worker_key, None) + + if worker is None: + MSG = '[stop_worker] Worker({:s}) not found' + LOGGER.debug(MSG.format(str(worker_key))) + return + + worker.stop() + + MSG = '[stop_worker] Stopped Worker({:s})' + LOGGER.info(MSG.format(str(worker_key))) + + + def stop_all(self) -> None: + LOGGER.info('[stop_all] Stopping all workers') + + with self._lock: + worker_keys = list(self._workers.keys()) + + for worker_key in worker_keys: + try: + self.stop_worker_by_key(worker_key) + except Exception: + MSG = '[stop_all] Unhandled Exception stopping Worker({:s})' + LOGGER.exception(MSG.format(str(worker_key))) diff --git a/src/simap_connector/service/telemetry/__init__.py b/src/simap_connector/service/telemetry/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/service/telemetry/__init__.py @@ -0,0 +1,13 @@ +# 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/simap_connector/service/telemetry/worker/AggregatorWorker.py b/src/simap_connector/service/telemetry/worker/AggregatorWorker.py new file mode 100644 index 0000000000000000000000000000000000000000..075c3b6d6e5cda25f342b2814bd66b0e23fd812f --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/AggregatorWorker.py @@ -0,0 +1,125 @@ +# 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, math, threading, time +from dataclasses import dataclass +from kafka import KafkaProducer +from typing import Dict, Optional, Union +from common.tools.kafka.Variables import KafkaConfig +from simap_connector.service.simap_updater.SimapClient import SimapClient +from .data.AggregationCache import AggregationCache +from ._Worker import _Worker, WorkerTypeEnum + + +KAFKA_BOOT_SERVERS = KafkaConfig.get_kafka_address() + +WAIT_LOOP_GRANULARITY = 0.5 + + +@dataclass +class ServerSentEvent: + event_data : Union[str, Dict] + event_type : Optional[str] = None + event_id : Optional[int] = None + + def format(self) -> str: + # SSE specs + event_data = self.event_data + if not isinstance(event_data, str): + event_data = json.dumps(event_data) + + lines = [ + 'data: {:s}'.format(line) + for line in event_data.splitlines() + ] + + if self.event_type: + lines.insert(0, 'event: {:s}'.format(str(self.event_type))) + + if self.event_id: + lines.append('id: {:d}'.format(int(self.event_id))) + + return '\n'.join(lines) + '\n\n' + + +class AggregatorWorker(_Worker): + def __init__( + self, worker_name : str, simap_client : SimapClient, network_id : str, link_id : str, + parent_subscription_id : int, aggregation_cache : AggregationCache, topic : str, + sampling_interval : float, terminate : Optional[threading.Event] = None + ) -> None: + super().__init__(WorkerTypeEnum.AGGREGATOR, worker_name, terminate=terminate) + self._simap_client = simap_client + self._network_id = network_id + self._link_id = link_id + self._parent_subscription_id = parent_subscription_id + self._aggregation_cache = aggregation_cache + self._topic = topic + self._sampling_interval = sampling_interval + + + def run(self) -> None: + self._logger.info('[run] Starting...') + + kafka_producer = KafkaProducer(bootstrap_servers=KAFKA_BOOT_SERVERS) + update_counter = 1 + + try: + while not self._stop_event.is_set() and not self._terminate.is_set(): + #self._logger.debug('[run] Aggregating...') + + link_sample = self._aggregation_cache.aggregate() + + data = {'notification': { + 'eventTime': link_sample.timestamp.isoformat() + 'Z', + 'push-update': { + 'id': update_counter, + 'datastore-contents': {'simap-telemetry:simap-telemetry': { + 'bandwidth-utilization': '{:.2f}'.format(link_sample.bandwidth_utilization), + 'latency' : '{:.3f}'.format(link_sample.latency), + 'related-service-ids' : list(link_sample.related_service_ids), + }} + } + }} + + event = ServerSentEvent( + event_data=data, event_id=update_counter, event_type='push-update' + ) + str_event = event.format() + + kafka_producer.send( + self._topic, key=self._topic.encode('utf-8'), + value=str_event.encode('utf-8') + ) + kafka_producer.flush() + + simap_link = self._simap_client.network(self._network_id).link(self._link_id) + simap_link.telemetry.update( + link_sample.bandwidth_utilization, link_sample.latency, + related_service_ids=list(link_sample.related_service_ids) + ) + + update_counter += 1 + + # Make wait responsible to terminations + iterations = int(math.ceil(self._sampling_interval / WAIT_LOOP_GRANULARITY)) + for _ in range(iterations): + if self._stop_event.is_set(): break + if self._terminate.is_set() : break + time.sleep(WAIT_LOOP_GRANULARITY) + except Exception: + self._logger.exception('[run] Unhandled Exception') + finally: + self._logger.info('[run] Terminated') diff --git a/src/simap_connector/service/telemetry/worker/CollectorWorker.py b/src/simap_connector/service/telemetry/worker/CollectorWorker.py new file mode 100644 index 0000000000000000000000000000000000000000..27b665d05d487fd165f78a00722af72222bc9ef2 --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/CollectorWorker.py @@ -0,0 +1,170 @@ +# 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, math, requests, threading, time +from requests.exceptions import ReadTimeout +from typing import Optional +from .data.AggregationCache import AggregationCache, LinkSample +from ._Worker import _Worker, WorkerTypeEnum + + +CONTROLLER_TO_ADDRESS_PORT = { + 'TFS-E2E' : ('10.254.0.10', 80), + 'TFS-AGG' : ('10.254.0.11', 80), + 'TFS-IP' : ('10.254.0.12', 80), + 'NCE-T' : ('10.254.0.9', 8082), + 'NCE-FAN' : ('10.254.0.9', 8081), + 'SIMAP' : ('10.254.0.9', 8080), +} + +WAIT_LOOP_GRANULARITY = 0.5 + + +class CollectorWorker(_Worker): + def __init__( + self, worker_name : str, controller_uuid : Optional[str], + network_id : str, link_id : str, target_uri : str, + aggregation_cache : AggregationCache, sampling_interval : float, + terminate : Optional[threading.Event] = None + ) -> None: + super().__init__(WorkerTypeEnum.COLLECTOR, worker_name, terminate=terminate) + self._controller_uuid = controller_uuid + self._network_id = network_id + self._link_id = link_id + self._target_uri = target_uri + self._aggregation_cache = aggregation_cache + self._sampling_interval = sampling_interval + + def run(self) -> None: + self._logger.info('[run] Starting...') + + try: + address_port = CONTROLLER_TO_ADDRESS_PORT.get(self._controller_uuid) + if address_port is None: + address, port = CONTROLLER_TO_ADDRESS_PORT['SIMAP'] + self.direct_simap_polling(address, port) + else: + address, port = address_port + self.underlay_subscription_stream(address, port) + except Exception: + self._logger.exception('[run] Unhandled Exception') + finally: + self._logger.info('[run] Terminated') + + def underlay_subscription_stream(self, address : str, port : int) -> None: + stream_url = 'http://{:s}:{:d}{:s}'.format(address, port, self._target_uri) + MSG = '[underlay_subscription_stream] Opening stream "{:s}"...' + self._logger.info(MSG.format(str(stream_url))) + + session = requests.Session() + try: + # NOTE: Trick: we set 1-second read_timeout to force the loop to give control + # back and be able to check termination events. + # , timeout=(10, 1) + with session.get(stream_url, stream=True) as reply: + reply.raise_for_status() + + it_lines = reply.iter_lines(decode_unicode=True, chunk_size=1024) + + while not self._stop_event.is_set() and not self._terminate.is_set(): + try: + line = next(it_lines) # may block until read_timeout + except StopIteration: + break # server closed + except ReadTimeout: + continue # no data this tick; loop to check termination conditions + + if line is None: continue + if len(line) == 0: continue + + #MSG = '[underlay_subscription_stream] ==> {:s}' + #self._logger.debug(MSG.format(str(line))) + if not line.startswith('data:'): continue + data = json.loads(line[5:]) + + if 'notification' not in data: + MSG = 'Field(notification) missing: {:s}' + raise Exception(MSG.format(str(data))) + notification = data['notification'] + + if 'push-update' not in notification: + MSG = 'Field(notification/push-update) missing: {:s}' + raise Exception(MSG.format(str(data))) + push_update = notification['push-update'] + + if 'datastore-contents' not in push_update: + MSG = 'Field(notification/push-update/datastore-contents) missing: {:s}' + raise Exception(MSG.format(str(data))) + datastore_contents = push_update['datastore-contents'] + + if 'simap-telemetry:simap-telemetry' not in datastore_contents: + MSG = ( + 'Field(notification/push-update/datastore-contents' + '/simap-telemetry:simap-telemetry) missing: {:s}' + ) + raise Exception(MSG.format(str(data))) + simap_telemetry = datastore_contents['simap-telemetry:simap-telemetry'] + + bandwidth_utilization = float(simap_telemetry['bandwidth-utilization']) + latency = float(simap_telemetry['latency']) + related_service_ids = simap_telemetry['related-service-ids'] + + link_sample = LinkSample( + network_id = self._network_id, + link_id = self._link_id, + bandwidth_utilization = bandwidth_utilization, + latency = latency, + related_service_ids = related_service_ids, + ) + self._aggregation_cache.update(link_sample) + finally: + if session is not None: + session.close() + + def direct_simap_polling(self, address : str, port : int) -> None: + simap_url = 'http://{:s}:{:d}/restconf/data{:s}'.format(address, port, self._target_uri) + + while not self._stop_event.is_set() and not self._terminate.is_set(): + MSG = '[direct_simap_polling] Requesting "{:s}"...' + self._logger.info(MSG.format(str(simap_url))) + + with requests.get(simap_url, timeout=10) as reply: + reply.raise_for_status() + data = reply.json() + + if 'simap-telemetry:simap-telemetry' not in data: + MSG = 'Field(simap-telemetry:simap-telemetry) missing: {:s}' + raise Exception(MSG.format(str(data))) + simap_telemetry = data['simap-telemetry:simap-telemetry'] + + bandwidth_utilization = float(simap_telemetry['bandwidth-utilization']) + latency = float(simap_telemetry['latency']) + related_service_ids = simap_telemetry.get('related-service-ids', list()) + + link_sample = LinkSample( + network_id = self._network_id, + link_id = self._link_id, + bandwidth_utilization = bandwidth_utilization, + latency = latency, + related_service_ids = related_service_ids, + ) + self._aggregation_cache.update(link_sample) + + # Make wait responsible to terminations + iterations = int(math.ceil(self._sampling_interval / WAIT_LOOP_GRANULARITY)) + for _ in range(iterations): + if self._stop_event.is_set(): break + if self._terminate.is_set() : break + time.sleep(WAIT_LOOP_GRANULARITY) diff --git a/src/simap_connector/service/telemetry/worker/SynthesizerWorker.py b/src/simap_connector/service/telemetry/worker/SynthesizerWorker.py new file mode 100644 index 0000000000000000000000000000000000000000..884a2cff8c794eab325a4d527460e087820420c4 --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/SynthesizerWorker.py @@ -0,0 +1,63 @@ +# 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 math, threading, time +from typing import Optional +from simap_connector.service.simap_updater.SimapClient import SimapClient +from .data.Resources import Resources +from ._Worker import _Worker, WorkerTypeEnum + + +WAIT_LOOP_GRANULARITY = 0.5 + + +class SynthesizerWorker(_Worker): + def __init__( + self, worker_name : str, simap_client : SimapClient, resources : Resources, + sampling_interval : float, terminate : Optional[threading.Event] = None + ) -> None: + super().__init__(WorkerTypeEnum.SYNTHESIZER, worker_name, terminate=terminate) + self._lock = threading.Lock() + self._simap_client = simap_client + self._resources = resources + self._sampling_interval = sampling_interval + + def change_resources(self, bandwidth_factor : float, latency_factor : float) -> None: + with self._lock: + for link in self._resources.links: + link.bandwidth_utilization_sampler.offset *= bandwidth_factor + link.latency_sampler.offset *= latency_factor + + def run(self) -> None: + self._logger.info('[run] Starting...') + + try: + while not self._stop_event.is_set() and not self._terminate.is_set(): + #self._logger.debug('[run] Sampling...') + + with self._lock: + self._resources.generate_samples(self._simap_client) + + # Make wait responsible to terminations + iterations = int(math.ceil(self._sampling_interval / WAIT_LOOP_GRANULARITY)) + for _ in range(iterations): + if self._stop_event.is_set(): break + if self._terminate.is_set() : break + time.sleep(WAIT_LOOP_GRANULARITY) + + except Exception: + self._logger.exception('[run] Unhandled Exception') + finally: + self._logger.info('[run] Terminated') diff --git a/src/simap_connector/service/telemetry/worker/_Worker.py b/src/simap_connector/service/telemetry/worker/_Worker.py new file mode 100644 index 0000000000000000000000000000000000000000..ae0da4fc78e076b9887f8b9164d5d1066dd7f7b5 --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/_Worker.py @@ -0,0 +1,57 @@ +# 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 logging, threading +from enum import Enum +from typing import Optional + + +class WorkerTypeEnum(Enum): + AGGREGATOR = 'aggregator' + COLLECTOR = 'collector' + SYNTHESIZER = 'synthesizer' + + +def get_worker_key(worker_type : WorkerTypeEnum, worker_name : str) -> str: + return '{:s}-{:s}'.format(worker_type.value, worker_name) + + +class _Worker(threading.Thread): + def __init__( + self, worker_type : WorkerTypeEnum, worker_name : str, + terminate : Optional[threading.Event] = None + ) -> None: + self._worker_type = worker_type + self._worker_name = worker_name + self._worker_key = get_worker_key(worker_type, worker_name) + name = 'TelemetryWorker({:s})'.format(self._worker_key) + super().__init__(name=name, daemon=True) + self._logger = logging.getLogger(name) + self._stop_event = threading.Event() + self._terminate = threading.Event() if terminate is None else terminate + + @property + def worker_type(self) -> WorkerTypeEnum: return self._worker_type + + @property + def worker_name(self) -> str: return self._worker_name + + @property + def worker_key(self) -> str: return self._worker_key + + def stop(self) -> None: + self._logger.info('[stop] Stopping...') + self._stop_event.set() + self.join() diff --git a/src/simap_connector/service/telemetry/worker/__init__.py b/src/simap_connector/service/telemetry/worker/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/__init__.py @@ -0,0 +1,13 @@ +# 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/simap_connector/service/telemetry/worker/data/AggregationCache.py b/src/simap_connector/service/telemetry/worker/data/AggregationCache.py new file mode 100644 index 0000000000000000000000000000000000000000..31a71d09634da3480d202cbaa4f3e20866deb154 --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/data/AggregationCache.py @@ -0,0 +1,62 @@ +# 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 threading +from dataclasses import dataclass, field +from datetime import datetime +from typing import Dict, Set, Tuple + + +@dataclass +class LinkSample: + network_id : str + link_id : str + bandwidth_utilization : float + latency : float + related_service_ids : Set[str] = field(default_factory=set) + + +@dataclass +class AggregatedLinkSample: + timestamp : datetime + bandwidth_utilization : float = field(default=0.0) + latency : float = field(default=0.0) + related_service_ids : Set[str] = field(default_factory=set) + + +class AggregationCache: + def __init__(self) -> None: + self._lock = threading.Lock() + self._samples : Dict[Tuple[str, str], LinkSample] = dict() + + + def update(self, link_sample : LinkSample) -> None: + link_key = (link_sample.network_id, link_sample.link_id) + with self._lock: + self._samples[link_key] = link_sample + + + def aggregate(self) -> AggregatedLinkSample: + with self._lock: + agg = AggregatedLinkSample(timestamp=datetime.utcnow()) + for sample in self._samples.values(): + agg.bandwidth_utilization = max( + agg.bandwidth_utilization, sample.bandwidth_utilization + ) + agg.latency = agg.latency + sample.latency + agg.related_service_ids = agg.related_service_ids.union( + sample.related_service_ids + ) + return agg diff --git a/src/simap_connector/service/telemetry/worker/data/Resources.py b/src/simap_connector/service/telemetry/worker/data/Resources.py new file mode 100644 index 0000000000000000000000000000000000000000..49c16c3404d5de650fcd13239eafcf87b4a98abc --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/data/Resources.py @@ -0,0 +1,65 @@ +# 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 dataclasses import dataclass, field +from typing import List +from simap_connector.service.simap_updater.SimapClient import SimapClient +from .SyntheticSamplers import SyntheticSampler + + +@dataclass +class ResourceNode: + domain_name : str + node_name : str + cpu_utilization_sampler : SyntheticSampler + related_service_ids : List[str] = field(default_factory=list) + + def generate_samples(self, simap_client : SimapClient) -> None: + cpu_utilization = self.cpu_utilization_sampler.get_sample() + simap_node = simap_client.network(self.domain_name).node(self.node_name) + simap_node.telemetry.update( + cpu_utilization.value, related_service_ids=self.related_service_ids + ) + + +@dataclass +class ResourceLink: + domain_name : str + link_name : str + bandwidth_utilization_sampler : SyntheticSampler + latency_sampler : SyntheticSampler + related_service_ids : List[str] = field(default_factory=list) + + def generate_samples(self, simap_client : SimapClient) -> None: + bandwidth_utilization = self.bandwidth_utilization_sampler.get_sample() + latency = self.latency_sampler.get_sample() + simap_link = simap_client.network(self.domain_name).link(self.link_name) + simap_link.telemetry.update( + bandwidth_utilization.value, latency.value, + related_service_ids=self.related_service_ids + ) + + +@dataclass +class Resources: + nodes : List[ResourceNode] = field(default_factory=list) + links : List[ResourceLink] = field(default_factory=list) + + def generate_samples(self, simap_client : SimapClient) -> None: + for resource in self.nodes: + resource.generate_samples(simap_client) + + for resource in self.links: + resource.generate_samples(simap_client) diff --git a/src/simap_connector/service/telemetry/worker/data/Sample.py b/src/simap_connector/service/telemetry/worker/data/Sample.py new file mode 100644 index 0000000000000000000000000000000000000000..f9bfb06147e4392cba0a2793d13dd357d6b05fae --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/data/Sample.py @@ -0,0 +1,23 @@ +# 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 dataclasses import dataclass + + +@dataclass +class Sample: + timestamp : float + subscription_id : int + value : float diff --git a/src/simap_connector/service/telemetry/worker/data/SyntheticSamplers.py b/src/simap_connector/service/telemetry/worker/data/SyntheticSamplers.py new file mode 100644 index 0000000000000000000000000000000000000000..b942971282afa6818bab7008bd531fd6e9739cbf --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/data/SyntheticSamplers.py @@ -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. + + +import math, random, sys, threading +from dataclasses import dataclass, field +from datetime import datetime +from typing import Dict, Optional +from .Sample import Sample + + +@dataclass +class SyntheticSampler: + amplitude : float = field(default=0.0) + phase : float = field(default=0.0) + period : float = field(default=1.0) + offset : float = field(default=0.0) + noise_ratio : float = field(default=0.0) + min_value : float = field(default=-sys.float_info.max) + max_value : float = field(default=sys.float_info.max) + + @classmethod + def create_random( + cls, amplitude_scale : float, phase_scale : float, period_scale : float, + offset_scale : float, noise_ratio : float, + min_value : Optional[float] = None, max_value : Optional[float] = None + ) -> 'SyntheticSampler': + amplitude = amplitude_scale * random.random() + phase = phase_scale * random.random() + period = period_scale * random.random() + offset = offset_scale * random.random() + amplitude + if min_value is None: min_value = -sys.float_info.max + if max_value is None: max_value = sys.float_info.max + return cls(amplitude, phase, period, offset, noise_ratio, min_value, max_value) + + def get_sample(self) -> Sample: + timestamp = datetime.timestamp(datetime.utcnow()) + + waveform = math.sin(2 * math.pi * timestamp / self.period + self.phase) + waveform *= self.amplitude + waveform += self.offset + + noise = self.amplitude * random.random() + value = abs((1.0 - self.noise_ratio) * waveform + self.noise_ratio * noise) + + value = max(value, self.min_value) + value = min(value, self.max_value) + + return Sample(timestamp, 0, value) + + +class SyntheticSamplers: + def __init__(self) -> None: + self._lock = threading.Lock() + self._samplers : Dict[str, SyntheticSampler] = dict() + + def add_sampler( + self, sampler_name : str, amplitude_scale : float, phase_scale : float, + period_scale : float, offset_scale : float, noise_ratio : float + ) -> None: + with self._lock: + if sampler_name in self._samplers: + MSG = 'SyntheticSampler({:s}) already exists' + raise Exception(MSG.format(sampler_name)) + self._samplers[sampler_name] = SyntheticSampler.create_random( + amplitude_scale, phase_scale, period_scale, offset_scale, noise_ratio + ) + + def remove_sampler(self, sampler_name : str) -> None: + with self._lock: + self._samplers.pop(sampler_name, None) + + def get_sample(self, sampler_name : str) -> Sample: + with self._lock: + sampler = self._samplers.get(sampler_name) + if sampler_name not in self._samplers: + MSG = 'SyntheticSampler({:s}) does not exist' + raise Exception(MSG.format(sampler_name)) + return sampler.get_sample() diff --git a/src/simap_connector/service/telemetry/worker/data/__init__.py b/src/simap_connector/service/telemetry/worker/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7363515f07a52d996229bcbd72932ce1423258d7 --- /dev/null +++ b/src/simap_connector/service/telemetry/worker/data/__init__.py @@ -0,0 +1,13 @@ +# 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/simap_connector/tests/__init__.py b/src/simap_connector/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/simap_connector/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/slice/service/SliceServiceServicerImpl.py b/src/slice/service/SliceServiceServicerImpl.py index e4f9ebce84109ae4cbf1fb84b57a74fb9327f148..aa87ef953dfce6741c814f92ef26df41e447731c 100644 --- a/src/slice/service/SliceServiceServicerImpl.py +++ b/src/slice/service/SliceServiceServicerImpl.py @@ -104,6 +104,7 @@ class SliceServiceServicerImpl(SliceServiceServicer): # pylint: disable=no-member service_request = Service() service_request.service_id.CopyFrom(service_id) + service_request.name = slice_with_uuids.name service_request.service_type = ServiceTypeEnum.SERVICETYPE_UNKNOWN service_request.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED service_client.CreateService(service_request) diff --git a/src/slice/service/slice_grouper/MetricsExporter.py b/src/slice/service/slice_grouper/MetricsExporter.py index 593d2edf6e50883182a087deb7fcfe1c9220bb78..d22c595d0399f0607c07f3dd6cf686462e75baa2 100644 --- a/src/slice/service/slice_grouper/MetricsExporter.py +++ b/src/slice/service/slice_grouper/MetricsExporter.py @@ -29,8 +29,8 @@ MSG_REST_FAILED = '[rest_request] Query({:s}) failed, retry={:d}/{:d}...' MSG_ERROR_MAX_RETRIES = 'Maximum number of retries achieved: {:d}' METRICSDB_HOSTNAME = os.environ.get('METRICSDB_HOSTNAME') -METRICSDB_ILP_PORT = int(os.environ.get('METRICSDB_ILP_PORT', 0)) -METRICSDB_REST_PORT = int(os.environ.get('METRICSDB_REST_PORT', 0)) +METRICSDB_ILP_PORT = int(os.environ.get('METRICSDB_ILP_PORT', 0) or 0) +METRICSDB_REST_PORT = int(os.environ.get('METRICSDB_REST_PORT', 0) or 0) METRICSDB_TABLE_SLICE_GROUPS = os.environ.get('METRICSDB_TABLE_SLICE_GROUPS') COLORS = { diff --git a/src/tests/.gitlab-ci.yml b/src/tests/.gitlab-ci.yml index 67f3b56921d8265ae4f201dbbfe111f88d52fae7..9b256f1ae6f9a3d63996266e961f9409c8453d3b 100644 --- a/src/tests/.gitlab-ci.yml +++ b/src/tests/.gitlab-ci.yml @@ -30,3 +30,4 @@ include: - local: '/src/tests/tools/mock_tfs_nbi_dependencies/.gitlab-ci.yml' - local: '/src/tests/tools/mock_qkd_node/.gitlab-ci.yml' - local: '/src/tests/tools/mock_osm_nbi/.gitlab-ci.yml' + - local: '/src/tests/tools/simap_server/.gitlab-ci.yml' diff --git a/src/tests/ecoc22/.gitlab-ci.yml b/src/tests/ecoc22/.gitlab-ci.yml index c57f7002b9a0c6efa1345acd4e93432fea245246..4f93bec064ddd273511ae62da7392d4cc32aa9d9 100644 --- a/src/tests/ecoc22/.gitlab-ci.yml +++ b/src/tests/ecoc22/.gitlab-ci.yml @@ -80,7 +80,11 @@ end2end_test ecoc22: - > for ns in ${OLD_NATS_NAMESPACES}; do if [[ "$ns" == nats* ]]; then - helm3 uninstall "$ns" -n "$ns" + if helm3 status "$ns" &>/dev/null; then + helm3 uninstall "$ns" -n "$ns" + else + echo "Release '$ns' not found, skipping..." + fi fi done - export OLD_NAMESPACES=$(echo "${K8S_NAMESPACES}" | tr ' ' '\n' | grep -E '^(tfs|crdb|qdb|kafka|nats)') diff --git a/src/tests/ecoc22/descriptors_emulated.json b/src/tests/ecoc22/descriptors_emulated.json index da36a6ae995941f8dbbf998df31f8d7258decbaf..efff904389d63bfe3be3aa002784fee1515e7fdd 100644 --- a/src/tests/ecoc22/descriptors_emulated.json +++ b/src/tests/ecoc22/descriptors_emulated.json @@ -7,83 +7,83 @@ ], "devices": [ { - "device_id": {"device_uuid": {"uuid": "DC1-GW"}}, "device_type": "emu-datacenter", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + "device_id": {"device_uuid": {"uuid": "DC1-GW"}}, "device_type": "emu-datacenter", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "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": "eth1"}, - {"sample_types": [], "type": "copper", "uuid": "eth2"}, - {"sample_types": [], "type": "copper", "uuid": "int"} + {"uuid": "eth1", "type": "copper"}, + {"uuid": "eth2", "type": "copper"}, + {"uuid": "int", "type": "copper"} ]}}} ]} }, { - "device_id": {"device_uuid": {"uuid": "DC2-GW"}}, "device_type": "emu-datacenter", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + "device_id": {"device_uuid": {"uuid": "DC2-GW"}}, "device_type": "emu-datacenter", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "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": "eth1"}, - {"sample_types": [], "type": "copper", "uuid": "eth2"}, - {"sample_types": [], "type": "copper", "uuid": "int"} + {"uuid": "eth1", "type": "copper"}, + {"uuid": "eth2", "type": "copper"}, + {"uuid": "int", "type": "copper"} ]}}} ]} }, { - "device_id": {"device_uuid": {"uuid": "CS1-GW1"}}, "device_type": "emu-packet-router", "device_drivers": [1], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + "device_id": {"device_uuid": {"uuid": "CS1-GW1"}}, "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "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": [101, 102, 201, 202], "type": "copper", "uuid": "10/1"}, - {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"} + {"uuid": "10/1", "type": "copper", "sample_types": [101, 102, 201, 202]}, + {"uuid": "1/1", "type": "copper", "sample_types": [101, 102, 201, 202]} ]}}} ]} }, { - "device_id": {"device_uuid": {"uuid": "CS1-GW2"}}, "device_type": "emu-packet-router", "device_drivers": [1], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + "device_id": {"device_uuid": {"uuid": "CS1-GW2"}}, "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "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": [101, 102, 201, 202], "type": "copper", "uuid": "10/1"}, - {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"} + {"uuid": "10/1", "type": "copper", "sample_types": [101, 102, 201, 202]}, + {"uuid": "1/1", "type": "copper", "sample_types": [101, 102, 201, 202]} ]}}} ]} }, { - "device_id": {"device_uuid": {"uuid": "CS2-GW1"}}, "device_type": "emu-packet-router", "device_drivers": [1], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + "device_id": {"device_uuid": {"uuid": "CS2-GW1"}}, "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "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": [101, 102, 201, 202], "type": "copper", "uuid": "10/1"}, - {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"} + {"uuid": "10/1", "type": "copper", "sample_types": [101, 102, 201, 202]}, + {"uuid": "1/1", "type": "copper", "sample_types": [101, 102, 201, 202]} ]}}} ]} }, { - "device_id": {"device_uuid": {"uuid": "CS2-GW2"}}, "device_type": "emu-packet-router", "device_drivers": [1], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + "device_id": {"device_uuid": {"uuid": "CS2-GW2"}}, "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "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": [101, 102, 201, 202], "type": "copper", "uuid": "10/1"}, - {"sample_types": [101, 102, 201, 202], "type": "copper", "uuid": "1/1"} + {"uuid": "10/1", "type": "copper", "sample_types": [101, 102, 201, 202]}, + {"uuid": "1/1", "type": "copper", "sample_types": [101, 102, 201, 202]} ]}}} ]} }, { - "device_id": {"device_uuid": {"uuid": "OLS"}}, "device_type": "emu-open-line-system", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + "device_id": {"device_uuid": {"uuid": "OLS"}}, "device_type": "emu-open-line-system", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "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": "aade6001-f00b-5e2f-a357-6a0a9d3de870"}, - {"sample_types": [], "type": "optical", "uuid": "eb287d83-f05e-53ec-ab5a-adf6bd2b5418"}, - {"sample_types": [], "type": "optical", "uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513"}, - {"sample_types": [], "type": "optical", "uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec"} + {"uuid": "aade6001-f00b-5e2f-a357-6a0a9d3de870", "type": "optical"}, + {"uuid": "eb287d83-f05e-53ec-ab5a-adf6bd2b5418", "type": "optical"}, + {"uuid": "0ef74f99-1acc-57bd-ab9d-4b958b06c513", "type": "optical"}, + {"uuid": "50296d99-58cc-5ce7-82f5-fc8ee4eec2ec", "type": "optical"} ]}}} ]} } diff --git a/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/network-slice1.json b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/network-slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..3bb285dfb1cec3043811c5b005c7d502452240ae --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/network-slice1.json @@ -0,0 +1,118 @@ +{ + "slice-service": [ + { + "id": "slice1", + "description": "network slice 1, PC1-VM1", + "sdps": { + "sdp": [ + { + "id": "1", + "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.204.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 ONT connected to PC1", + "ac-node-id": "172.16.61.10", + "ac-tp-id": "200" + }]} + }, + { + "id": "2", + "node-id": "172.16.204.221", + "sdp-ip-address": ["172.16.204.221"], + "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 POP2 to VM1", + "description": "AC POP2 connected to VM1", + "ac-node-id": "172.16.204.221", + "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/ecoc25-f5ga-telemetry/data/old-numbered/network-slice2.json b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/network-slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..3bb285dfb1cec3043811c5b005c7d502452240ae --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/network-slice2.json @@ -0,0 +1,118 @@ +{ + "slice-service": [ + { + "id": "slice1", + "description": "network slice 1, PC1-VM1", + "sdps": { + "sdp": [ + { + "id": "1", + "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.204.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 ONT connected to PC1", + "ac-node-id": "172.16.61.10", + "ac-tp-id": "200" + }]} + }, + { + "id": "2", + "node-id": "172.16.204.221", + "sdp-ip-address": ["172.16.204.221"], + "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 POP2 to VM1", + "description": "AC POP2 connected to VM1", + "ac-node-id": "172.16.204.221", + "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/ecoc25-f5ga-telemetry/data/old-numbered/topology-agg.json b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/topology-agg.json new file mode 100644 index 0000000000000000000000000000000000000000..5d23fdd5c0783caafb182b40cb7e3459112bbe8f --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/topology-agg.json @@ -0,0 +1,145 @@ +{ + "contexts": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}} + ], + "topologies": [ + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}} + ], + "devices": [ + {"device_id": {"device_uuid": {"uuid": "TFS-IP"}}, "name": "TFS-IP", "device_type": "teraflowsdn", + "device_drivers": ["DEVICEDRIVER_IETF_L3VPN"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "10.254.0.12"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "80" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": { + "scheme": "http", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data", + "timeout": 120, "verify": false + }}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "NCE-T"}}, "name": "NCE-T", "device_type": "ip-sdn-controller", + "device_drivers": ["DEVICEDRIVER_IETF_ACTN"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "10.254.0.9"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "8444" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": { + "scheme": "https", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data", + "timeout": 120, "verify": false + }}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "172.16.58.10"}}, "name": "172.16.58.10", "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "172.16.204.221"}}, "name": "172.16.204.221", "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "200", "name": "200", "type": "copper"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "172.1.101.22"}}, "name": "172.1.101.22", "device_type": "emu-datacenter", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500", "name": "500", "type": "copper"} + ]}}} + ]} + }, + {"device_id": {"device_uuid": {"uuid": "172.16.204.220"}}, "name": "172.16.204.220", "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "200", "name": "200", "type": "copper"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "172.1.201.22"}}, "name": "172.1.201.22", "device_type": "emu-datacenter", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500", "name": "500", "type": "copper"} + ]}}} + ]} + } + ], + "links": [ + {"link_id": {"link_uuid": {"uuid": "172.16.58.10-501"}}, "name": "172.16.58.10-501", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.58.10"}}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.25"}}, "endpoint_uuid": {"uuid": "200"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "172.16.125.25-200"}}, "name": "172.16.125.25-200", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.25"}}, "endpoint_uuid": {"uuid": "200"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.58.10"}}, "endpoint_uuid": {"uuid": "501"}} + ]}, + + {"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.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.125.32-200"}}, "name": "172.16.125.32-200", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "endpoint_uuid": {"uuid": "200"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.204.221"}}, "endpoint_uuid": {"uuid": "500"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "172.16.204.221-500"}}, "name": "172.16.204.221-500", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.204.221"}}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "endpoint_uuid": {"uuid": "200"}} + ]}, + + {"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.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.221-200"}}, "name": "172.16.204.221-200", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.204.221"}}, "endpoint_uuid": {"uuid": "200"}}, + {"device_id": {"device_uuid": {"uuid": "172.1.101.22"}}, "endpoint_uuid": {"uuid": "500"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "172.1.101.22-500"}}, "name": "172.1.101.22-500", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.1.101.22"}}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.204.221"}}, "endpoint_uuid": {"uuid": "200"}} + ]}, + + {"link_id": {"link_uuid": {"uuid": "172.16.204.220-200"}}, "name": "172.16.204.220-200", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.204.220"}}, "endpoint_uuid": {"uuid": "200"}}, + {"device_id": {"device_uuid": {"uuid": "172.1.201.22"}}, "endpoint_uuid": {"uuid": "500"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "172.1.201.22-500"}}, "name": "172.1.201.22-500", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.1.201.22"}}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.204.220"}}, "endpoint_uuid": {"uuid": "200"}} + ]} + ] +} diff --git a/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/topology-e2e.json b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/topology-e2e.json new file mode 100644 index 0000000000000000000000000000000000000000..d8634caf003dc8ed4e9c5e7e23493cd35fe734ac --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/topology-e2e.json @@ -0,0 +1,118 @@ +{ + "contexts": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}} + ], + "topologies": [ + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}} + ], + "devices": [ + {"device_id": {"device_uuid": {"uuid": "TFS-AGG"}}, "name": "TFS-AGG", "device_type": "teraflowsdn", + "device_drivers": ["DEVICEDRIVER_IETF_L3VPN"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "10.254.0.11"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "80" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": { + "scheme": "http", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data", + "timeout": 120, "verify": false + }}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "NCE-FAN"}}, "name": "NCE-FAN", "device_type": "ip-sdn-controller", + "device_drivers": ["DEVICEDRIVER_IETF_ACTN"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "10.254.0.9"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "8443" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": { + "scheme": "https", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data", + "timeout": 120, "verify": false + }}} + ]}}, + + {"device_id": {"device_uuid": {"uuid": "172.16.204.220"}}, "device_type": "emu-datacenter", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500a", "name": "500a", "type": "copper"}, + {"uuid": "500b", "name": "500b", "type": "copper"} + ]}}} + ]} + }, + {"device_id": {"device_uuid": {"uuid": "172.16.204.220"}}, "device_type": "emu-datacenter", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500a", "name": "500a", "type": "copper"}, + {"uuid": "500b", "name": "500b", "type": "copper"} + ]}}} + ]} + }, + {"device_id": {"device_uuid": {"uuid": "172.16.204.220"}}, "device_type": "emu-datacenter", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500a", "name": "500a", "type": "copper"}, + {"uuid": "500b", "name": "500b", "type": "copper"} + ]}}} + ]} + }, + {"device_id": {"device_uuid": {"uuid": "172.16.204.220"}}, "device_type": "emu-datacenter", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500a", "name": "500a", "type": "copper"}, + {"uuid": "500b", "name": "500b", "type": "copper"} + ]}}} + ]} + } + ], + "links": [ + {"link_id": {"link_uuid": {"uuid": "172.16.58.10-501"}}, "name": "172.16.58.10-501", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.58.10"}}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.25"}}, "endpoint_uuid": {"uuid": "200"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "172.16.125.25-200"}}, "name": "172.16.125.25-200", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.25"}}, "endpoint_uuid": {"uuid": "200"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.58.10"}}, "endpoint_uuid": {"uuid": "501"}} + ]}, + + {"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.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.125.32-200"}}, "name": "172.16.125.32-200", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "endpoint_uuid": {"uuid": "200"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.204.22x"}}, "endpoint_uuid": {"uuid": "500a"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "172.16.204.22x-500a"}}, "name": "172.16.204.22x-500a", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.204.22x"}}, "endpoint_uuid": {"uuid": "500a"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "endpoint_uuid": {"uuid": "200"}} + ]}, + + {"link_id": {"link_uuid": {"uuid": "172.16.204.22x-500b"}}, "name": "172.16.204.22x-500b", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.204.22x"}}, "endpoint_uuid": {"uuid": "500b"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.185.32"}}, "endpoint_uuid": {"uuid": "200"}} + ]}, + {"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.22x"}}, "endpoint_uuid": {"uuid": "500b"}} + ]} + ] +} diff --git a/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/topology-ip.json b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/topology-ip.json new file mode 100644 index 0000000000000000000000000000000000000000..f4258d52ad9a12ff628d1758706e82aec58afac5 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/old-numbered/topology-ip.json @@ -0,0 +1,129 @@ +{ + "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.125.25"}}, "name": "172.16.125.25", "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "200", "name": "200", "type": "copper"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "172.16.125.31"}}, "name": "172.16.125.31", "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "172.16.125.33"}}, "name": "172.16.125.33", "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "name": "172.16.125.32", "device_type": "emu-packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "200", "name": "200", "type": "copper"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "172.16.125.25-500"}}, "name": "172.16.125.25-500", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.25"}}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.31"}}, "endpoint_uuid": {"uuid": "500"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "172.16.125.31-500"}}, "name": "172.16.125.31-500", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.31"}}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.25"}}, "endpoint_uuid": {"uuid": "500"}} + ] + }, + + { + "link_id": {"link_uuid": {"uuid": "172.16.125.25-501"}}, "name": "172.16.125.25-501", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.25"}}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.33"}}, "endpoint_uuid": {"uuid": "500"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "172.16.125.33-500"}}, "name": "172.16.125.33-500", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.33"}}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.25"}}, "endpoint_uuid": {"uuid": "501"}} + ] + }, + + { + "link_id": {"link_uuid": {"uuid": "172.16.125.31-501"}}, "name": "172.16.125.31-501", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.31"}}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "endpoint_uuid": {"uuid": "500"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "172.16.125.32-500"}}, "name": "172.16.125.32-500", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.31"}}, "endpoint_uuid": {"uuid": "501"}} + ] + }, + + { + "link_id": {"link_uuid": {"uuid": "172.16.125.32-501"}}, "name": "172.16.125.32-501", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.33"}}, "endpoint_uuid": {"uuid": "501"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "172.16.125.33-501"}}, "name": "172.16.125.33-501", + "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "172.16.125.33"}}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "172.16.125.32"}}, "endpoint_uuid": {"uuid": "501"}} + ] + } + ] +} diff --git a/src/tests/ecoc25-f5ga-telemetry/data/slices/network-slice1.json b/src/tests/ecoc25-f5ga-telemetry/data/slices/network-slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..786a6df35d4a1623311a40c7357b77b25a07e2b7 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/slices/network-slice1.json @@ -0,0 +1,118 @@ +{ + "slice-service": [ + { + "id": "slice1", + "description": "network slice 1, PC1-VM1", + "sdps": { + "sdp": [ + { + "id": "1", + "node-id": "ONT1", + "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 ONT1", + "description": "AC ONT1 connected to PC1", + "ac-node-id": "ONT1", + "ac-tp-id": "200" + }]} + }, + { + "id": "2", + "node-id": "POP2", + "sdp-ip-address": ["172.16.204.221"], + "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.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": [{ + "id": "AC POP2 to VM1", + "description": "AC POP2 connected to VM1", + "ac-node-id": "POP2", + "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": "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.01" + } + ] + } + } + }, + { + "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": "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.01" + } + ] + } + } + } + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/tests/ecoc25-f5ga-telemetry/data/slices/network-slice2.json b/src/tests/ecoc25-f5ga-telemetry/data/slices/network-slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..f0875e25ea758d6c43866410dcbb720644da1aed --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/slices/network-slice2.json @@ -0,0 +1,118 @@ +{ + "slice-service": [ + { + "id": "slice2", + "description": "network slice 2, PC1-VM2", + "sdps": { + "sdp": [ + { + "id": "1", + "node-id": "ONT1", + "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": "line2" + }]}, + "attachment-circuits": {"attachment-circuit": [{ + "id": "AC ONT1", + "description": "AC ONT1 connected to PC1", + "ac-node-id": "ONT1", + "ac-tp-id": "200" + }]} + }, + { + "id": "2", + "node-id": "POP1", + "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: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": "line2" + }]}, + "attachment-circuits": {"attachment-circuit": [{ + "id": "AC POP1 to VM2", + "description": "AC POP1 connected to VM2", + "ac-node-id": "POP1", + "ac-tp-id": "200" + }]} + } + ] + }, + "connection-groups": { + "connection-group": [ + { + "id": "line2", + "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": "7000" + }, + { + "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": "4000" + }, + { + "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/ecoc25-f5ga-telemetry/data/slices/old/network-slice1.json b/src/tests/ecoc25-f5ga-telemetry/data/slices/old/network-slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..121e20de5fc6a4c6d16783de8d3a0d274c7a187a --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/slices/old/network-slice1.json @@ -0,0 +1,118 @@ +{ + "slice-service": [ + { + "id": "slice1", + "description": "network slice 1, PC1-VM1", + "sdps": { + "sdp": [ + { + "id": "1", + "node-id": "ONT1", + "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.204.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 ONT1", + "description": "AC ONT1 connected to PC1", + "ac-node-id": "ONT1", + "ac-tp-id": "200" + }]} + }, + { + "id": "2", + "node-id": "POP2", + "sdp-ip-address": ["172.16.204.221"], + "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 POP2 to VMa", + "description": "AC POP2 connected to VMa", + "ac-node-id": "POP2", + "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/ecoc25-f5ga-telemetry/data/slices/old/network-slice2.json b/src/tests/ecoc25-f5ga-telemetry/data/slices/old/network-slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..5afa18c7feb1dab1b4b7f76acf3ca9ef71c722c5 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/slices/old/network-slice2.json @@ -0,0 +1,118 @@ +{ + "slice-service": [ + { + "id": "slice2", + "description": "network slice 2, PC1-VM2", + "sdps": { + "sdp": [ + { + "id": "1", + "node-id": "ONT1", + "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.204.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": "line2" + }]}, + "attachment-circuits": {"attachment-circuit": [{ + "id": "AC ONT1", + "description": "AC ONT1 connected to PC1", + "ac-node-id": "ONT1", + "ac-tp-id": "200" + }]} + }, + { + "id": "2", + "node-id": "POP2", + "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.204.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": "line2" + }]}, + "attachment-circuits": {"attachment-circuit": [{ + "id": "AC POP1 to VM2", + "description": "AC POP1 connected to VM2", + "ac-node-id": "POP1", + "ac-tp-id": "200" + }]} + } + ] + }, + "connection-groups": { + "connection-group": [ + { + "id": "line2", + "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/ecoc25-f5ga-telemetry/data/telemetry/subscription-slice1.json b/src/tests/ecoc25-f5ga-telemetry/data/telemetry/subscription-slice1.json new file mode 100644 index 0000000000000000000000000000000000000000..3a2c4b96c8daaa282999ccc68916d77124f1294a --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/telemetry/subscription-slice1.json @@ -0,0 +1,9 @@ +{ + "ietf-subscribed-notifications:input": { + "datastore": "operational", + "ietf-yang-push:datastore-xpath-filter": "/ietf-network:networks/network=e2e/ietf-network-topology:link=E2E-L1/simap-telemetry", + "ietf-yang-push:periodic": { + "ietf-yang-push:period": 10 + } + } +} diff --git a/src/tests/ecoc25-f5ga-telemetry/data/telemetry/subscription-slice2.json b/src/tests/ecoc25-f5ga-telemetry/data/telemetry/subscription-slice2.json new file mode 100644 index 0000000000000000000000000000000000000000..cd0954ac1f95f99fd8a4d4174819fdb9edca99e8 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/telemetry/subscription-slice2.json @@ -0,0 +1,9 @@ +{ + "ietf-subscribed-notifications:input": { + "datastore": "operational", + "ietf-yang-push:datastore-xpath-filter": "/ietf-network:networks/network=e2e/ietf-network-topology:link=E2E-L2/simap-telemetry", + "ietf-yang-push:periodic": { + "ietf-yang-push:period": 10 + } + } +} diff --git a/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-agg.json b/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-agg.json new file mode 100644 index 0000000000000000000000000000000000000000..c761a86dd4bfc3865b03a70d5f4fb86d291a283b --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-agg.json @@ -0,0 +1,95 @@ +{ + "contexts": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}} + ], + "topologies": [ + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}, + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "agg"}}} + ], + "devices": [ + {"device_id": {"device_uuid": {"uuid": "TFS-IP"}}, "device_type": "teraflowsdn", + "device_drivers": ["DEVICEDRIVER_IETF_L3VPN"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "10.254.0.12"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "80"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": { + "scheme": "http", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data", + "timeout": 120, "verify_certs": false, "import_topology": "topology" + }}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "NCE-T"}}, "device_type": "nce", + "device_drivers": ["DEVICEDRIVER_IETF_ACTN"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "10.254.0.9"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "8082"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": { + "scheme": "http", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data", + "timeout": 120, "verify_certs": false, "import_topology": "topology" + }}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "POP1"}}, "device_type": "packet-pop", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "172.16.204.220"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "200", "name": "200", "type": "copper"}, + {"uuid": "201", "name": "201", "type": "copper"}, + {"uuid": "500", "name": "500", "type": "copper"} + ]}}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[200]", "resource_value": { + "uuid": "200", "name": "200", "type": "optical", + "address_ip": "172.1.201.1", "address_prefix": "24", + "site_location": "cloud", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[201]", "resource_value": { + "uuid": "201", "name": "201", "type": "optical", + "site_location": "transport", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[500]", "resource_value": { + "uuid": "500", "name": "500", "type": "optical", + "address_ip": "172.10.44.2", "address_prefix": "24", "vlan_tag": 101, + "site_location": "transport", "mtu": "1500" + }}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "POP2"}}, "device_type": "packet-pop", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "172.16.204.221"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "200", "name": "200", "type": "copper"}, + {"uuid": "201", "name": "201", "type": "copper"}, + {"uuid": "500", "name": "500", "type": "copper"} + ]}}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[200]", "resource_value": { + "uuid": "200", "name": "200", "type": "optical", + "address_ip": "172.1.101.1", "address_prefix": "24", + "site_location": "cloud", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[201]", "resource_value": { + "uuid": "201", "name": "201", "type": "optical", + "site_location": "transport", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[500]", "resource_value": { + "uuid": "500", "name": "500", "type": "optical", + "address_ip": "172.10.44.2", "address_prefix": "24", "vlan_tag": 201, + "site_location": "transport", "mtu": "1500" + }}} + ]}} + ], + "links": [ + {"link_id": {"link_uuid": {"uuid": "L13"}}, "link_type" : "LINKTYPE_COPPER", + "attributes": {"is_bidirectional": true, "total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "P-PE2"}}, "endpoint_uuid": {"uuid": "200"}}, + {"device_id": {"device_uuid": {"uuid": "POP2" }}, "endpoint_uuid": {"uuid": "500"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "L14"}}, "link_type" : "LINKTYPE_COPPER", + "attributes": {"is_bidirectional": true, "total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "O-PE2"}}, "endpoint_uuid": {"uuid": "200"}}, + {"device_id": {"device_uuid": {"uuid": "POP1" }}, "endpoint_uuid": {"uuid": "500"}} + ]} + ] +} diff --git a/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-e2e.json b/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-e2e.json new file mode 100644 index 0000000000000000000000000000000000000000..117e97e61881da30a29027860f3927d9f98a88ab --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-e2e.json @@ -0,0 +1,43 @@ +{ + "contexts": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}} + ], + "topologies": [ + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}, + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "e2e"}}} + ], + "devices": [ + {"device_id": {"device_uuid": {"uuid": "TFS-AGG"}}, "device_type": "teraflowsdn", + "device_drivers": ["DEVICEDRIVER_IETF_SLICE"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "10.254.0.11"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "80" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": { + "scheme": "http", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data", + "timeout": 120, "verify_certs": false, "import_topology": "topology" + }}} + ]}}, + {"device_id": {"device_uuid": {"uuid": "NCE-FAN"}}, "device_type": "nce", + "device_drivers": ["DEVICEDRIVER_NCE"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "10.254.0.9"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "8081" }}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": { + "scheme": "http", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data", + "timeout": 120, "verify_certs": false, "import_topology": "topology" + }}} + ]}} + ], + "links": [ + {"link_id": {"link_uuid": {"uuid": "L3"}}, "link_type" : "LINKTYPE_COPPER", + "attributes": {"is_bidirectional": true, "total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "OLT" }}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "P-PE1"}}, "endpoint_uuid": {"uuid": "200"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "L4"}}, "link_type" : "LINKTYPE_COPPER", + "attributes": {"is_bidirectional": true, "total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "OLT" }}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "O-PE1"}}, "endpoint_uuid": {"uuid": "200"}} + ]} + ] +} diff --git a/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-ip.json b/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-ip.json new file mode 100644 index 0000000000000000000000000000000000000000..cd772016009c21a7a36b8607d158845f9d1a5db2 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/data/topology/topology-ip.json @@ -0,0 +1,149 @@ +{ + "contexts": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}} + ], + "topologies": [ + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}, + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "trans-pkt"}}} + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "P-PE1"}}, "device_type": "packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "172.16.122.25"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "200", "name": "200", "type": "copper"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[200]", "resource_value": { + "uuid": "200", "name": "200", "type": "optical", + "address_ip": "128.32.44.254", "address_prefix": "24", "vlan_tag": 21, + "site_location": "access", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[500]", "resource_value": { + "uuid": "500", "name": "500", "type": "optical", + "address_ip": "10.44.1.1", "address_prefix": "24", + "site_location": "transport", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[501]", "resource_value": { + "uuid": "501", "name": "501", "type": "optical", + "address_ip": "10.44.2.1", "address_prefix": "24", + "site_location": "transport", "mtu": "1500" + }}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "P-P1"}}, "device_type": "packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "172.16.125.31"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[500]", "resource_value": { + "uuid": "500", "name": "500", "type": "optical", + "address_ip": "10.44.1.2", "address_prefix": "24", + "site_location": "transport", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[501]", "resource_value": { + "uuid": "501", "name": "501", "type": "optical", + "address_ip": "10.44.3.2", "address_prefix": "24", + "site_location": "transport", "mtu": "1500" + }}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "P-P2"}}, "device_type": "packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "172.16.125.33"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[500]", "resource_value": { + "uuid": "500", "name": "500", "type": "optical", + "address_ip": "10.44.2.2", "address_prefix": "24", + "site_location": "transport", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[501]", "resource_value": { + "uuid": "501", "name": "501", "type": "optical", + "address_ip": "10.44.4.2", "address_prefix": "24", + "site_location": "transport", "mtu": "1500" + }}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "P-PE2"}}, "device_type": "packet-router", + "device_drivers": ["DEVICEDRIVER_UNDEFINED"], + "device_config": {"config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "172.16.125.32"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"uuid": "lo", "name": "lo", "type": "loopback"}, + {"uuid": "200", "name": "200", "type": "copper"}, + {"uuid": "500", "name": "500", "type": "copper"}, + {"uuid": "501", "name": "501", "type": "copper"} + ]}}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[200]", "resource_value": { + "uuid": "200", "name": "200", "type": "optical", + "address_ip": "172.10.44.254", "address_prefix": "24", "vlan_tag": 201, + "site_location": "cloud", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[500]", "resource_value": { + "uuid": "500", "name": "500", "type": "optical", + "address_ip": "10.44.3.1", "address_prefix": "24", + "site_location": "transport", "mtu": "1500" + }}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[501]", "resource_value": { + "uuid": "501", "name": "501", "type": "optical", + "address_ip": "10.44.4.1", "address_prefix": "24", + "site_location": "transport", "mtu": "1500" + }}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "L5"}}, "link_type" : "LINKTYPE_COPPER", + "attributes": {"is_bidirectional": true, "total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "P-PE1"}}, "endpoint_uuid": {"uuid": "500"}}, + {"device_id": {"device_uuid": {"uuid": "P-P1" }}, "endpoint_uuid": {"uuid": "500"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "L6"}}, "link_type" : "LINKTYPE_COPPER", + "attributes": {"is_bidirectional": true, "total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "P-PE1"}}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "P-P2" }}, "endpoint_uuid": {"uuid": "500"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "L9"}}, "link_type" : "LINKTYPE_COPPER", + "attributes": {"is_bidirectional": true, "total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "P-P1" }}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "P-PE2"}}, "endpoint_uuid": {"uuid": "500"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "L10"}}, "link_type" : "LINKTYPE_COPPER", + "attributes": {"is_bidirectional": true, "total_capacity_gbps": 10, "used_capacity_gbps": 0}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "P-P2" }}, "endpoint_uuid": {"uuid": "501"}}, + {"device_id": {"device_uuid": {"uuid": "P-PE2"}}, "endpoint_uuid": {"uuid": "501"}} + ] + } + ] +} diff --git a/src/tests/ecoc25-f5ga-telemetry/deploy-specs-agg.sh b/src/tests/ecoc25-f5ga-telemetry/deploy-specs-agg.sh new file mode 100644 index 0000000000000000000000000000000000000000..c7b5e98b50ebf7e057b36a6d7b0433b0c0e85a7e --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/deploy-specs-agg.sh @@ -0,0 +1,220 @@ +#!/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" + +# 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 VNT Manager +#export TFS_COMPONENTS="${TFS_COMPONENTS} vnt_manager" + +# 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 + +# Uncomment to activate SIMAP Connector +export TFS_COMPONENTS="${TFS_COMPONENTS} simap_connector" + +# Uncomment to activate Load Generator +#export TFS_COMPONENTS="${TFS_COMPONENTS} load_generator" + + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy TFS to. +export TFS_K8S_NAMESPACE="tfs" + +# 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="" + + +# ----- 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_EXT_PORT_CLIENT="9092" + +# Set Kafka installation mode to 'single'. This option is convenient for development and testing. +# See ./deploy/all.sh or ./deploy/kafka.sh for additional details +export KFK_DEPLOY_MODE="single" + +# Disable flag for re-deploying Kafka from scratch. +export KFK_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="" + +# 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" diff --git a/src/tests/ecoc25-f5ga-telemetry/deploy-specs-e2e.sh b/src/tests/ecoc25-f5ga-telemetry/deploy-specs-e2e.sh new file mode 100644 index 0000000000000000000000000000000000000000..c7b5e98b50ebf7e057b36a6d7b0433b0c0e85a7e --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/deploy-specs-e2e.sh @@ -0,0 +1,220 @@ +#!/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" + +# 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 VNT Manager +#export TFS_COMPONENTS="${TFS_COMPONENTS} vnt_manager" + +# 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 + +# Uncomment to activate SIMAP Connector +export TFS_COMPONENTS="${TFS_COMPONENTS} simap_connector" + +# Uncomment to activate Load Generator +#export TFS_COMPONENTS="${TFS_COMPONENTS} load_generator" + + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy TFS to. +export TFS_K8S_NAMESPACE="tfs" + +# 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="" + + +# ----- 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_EXT_PORT_CLIENT="9092" + +# Set Kafka installation mode to 'single'. This option is convenient for development and testing. +# See ./deploy/all.sh or ./deploy/kafka.sh for additional details +export KFK_DEPLOY_MODE="single" + +# Disable flag for re-deploying Kafka from scratch. +export KFK_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="" + +# 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" diff --git a/src/tests/ecoc25-f5ga-telemetry/deploy-specs-ip.sh b/src/tests/ecoc25-f5ga-telemetry/deploy-specs-ip.sh new file mode 100644 index 0000000000000000000000000000000000000000..c02dac122fb3dd8cbda547be25f268920cc4e5e5 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/deploy-specs-ip.sh @@ -0,0 +1,220 @@ +#!/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 nbi webui" + +# 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 VNT Manager +#export TFS_COMPONENTS="${TFS_COMPONENTS} vnt_manager" + +# 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 + +# Uncomment to activate SIMAP Connector +export TFS_COMPONENTS="${TFS_COMPONENTS} simap_connector" + +# Uncomment to activate Load Generator +#export TFS_COMPONENTS="${TFS_COMPONENTS} load_generator" + + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy TFS to. +export TFS_K8S_NAMESPACE="tfs" + +# 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="" + + +# ----- 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_EXT_PORT_CLIENT="9092" + +# Set Kafka installation mode to 'single'. This option is convenient for development and testing. +# See ./deploy/all.sh or ./deploy/kafka.sh for additional details +export KFK_DEPLOY_MODE="single" + +# Disable flag for re-deploying Kafka from scratch. +export KFK_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="" + +# 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" diff --git a/src/tests/ecoc25-f5ga-telemetry/deploy.sh b/src/tests/ecoc25-f5ga-telemetry/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..4bdf8715d9826b9d609c2716d569fd9b47226065 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/deploy.sh @@ -0,0 +1,93 @@ +#!/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. + + +# Assuming the instances are named as: simap-server, tfs-e2e-ctrl, tfs-agg-ctrl, tfs-ip-ctrl + +# Get the current hostname +HOSTNAME=$(hostname) +echo "Deploying in ${HOSTNAME}..." + + +case "$HOSTNAME" in + simap-server) + echo "Building SIMAP Server..." + cd ~/tfs-ctrl/ + docker buildx build -t simap-server:mock -f ./src/tests/tools/simap_server/Dockerfile . + + echo "Building NCE-FAN Controller..." + cd ~/tfs-ctrl/ + docker buildx build -t nce-fan-ctrl:mock -f ./src/tests/tools/mock_nce_fan_ctrl/Dockerfile . + + echo "Building NCE-T Controller..." + cd ~/tfs-ctrl/ + docker buildx build -t nce-t-ctrl:mock -f ./src/tests/tools/mock_nce_t_ctrl/Dockerfile . + + echo "Building Traffic Changer..." + cd ~/tfs-ctrl/ + docker buildx build -t traffic-changer:mock -f ./src/tests/tools/traffic_changer/Dockerfile . + + echo "Cleaning up..." + docker rm --force simap-server + docker rm --force nce-fan-ctrl + docker rm --force nce-t-ctrl + docker rm --force traffic-changer + + echo "Deploying support services..." + docker run --detach --name simap-server --publish 8080:8080 simap-server:mock + docker run --detach --name nce-fan-ctrl --publish 8081:8080 --env SIMAP_ADDRESS=172.17.0.1 --env SIMAP_PORT=8080 nce-fan-ctrl:mock + docker run --detach --name nce-t-ctrl --publish 8082:8080 --env SIMAP_ADDRESS=172.17.0.1 --env SIMAP_PORT=8080 nce-t-ctrl:mock + docker run --detach --name traffic-changer --publish 8083:8080 traffic-changer:mock + + sleep 2 + docker ps -a + ;; + tfs-e2e-ctrl) + echo "Deploying TFS E2E Controller..." + sed -i 's|\(

ETSI TeraFlowSDN Controller\)

|\1 (End-to-End)|' src/webui/service/templates/main/home.html + source ~/tfs-ctrl/src/tests/ecoc25-f5ga-telemetry/deploy-specs-e2e.sh + ./deploy/all.sh + + echo "Waiting for NATS connection..." + while ! kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server 2>&1 | grep -q 'Subscriber is Ready? True'; do sleep 1; done + kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server + ;; + tfs-agg-ctrl) + echo "Deploying TFS Agg Controller..." + sed -i 's|\(

ETSI TeraFlowSDN Controller\)

|\1 (Aggregation)|' src/webui/service/templates/main/home.html + source ~/tfs-ctrl/src/tests/ecoc25-f5ga-telemetry/deploy-specs-agg.sh + ./deploy/all.sh + + echo "Waiting for NATS connection..." + while ! kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server 2>&1 | grep -q 'Subscriber is Ready? True'; do sleep 1; done + kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server + ;; + tfs-ip-ctrl) + echo "Deploying TFS IP Controller..." + sed -i 's|\(

ETSI TeraFlowSDN Controller\)

|\1 (IP)|' src/webui/service/templates/main/home.html + source ~/tfs-ctrl/src/tests/ecoc25-f5ga-telemetry/deploy-specs-ip.sh + ./deploy/all.sh + + echo "Waiting for NATS connection..." + while ! kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server 2>&1 | grep -q 'Subscriber is Ready? True'; do sleep 1; done + kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server + ;; + *) + echo "Unknown host: $HOSTNAME" + echo "No commands to run." + ;; +esac + +echo "Ready!" diff --git a/src/tests/ecoc25-f5ga-telemetry/destroy.sh b/src/tests/ecoc25-f5ga-telemetry/destroy.sh new file mode 100755 index 0000000000000000000000000000000000000000..47977562d859ecc85c3a56eebe483d3843769dd9 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/destroy.sh @@ -0,0 +1,56 @@ +#!/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. + + +# Assuming the instances are named as: simap-server, tfs-e2e-ctrl, tfs-agg-ctrl, tfs-ip-ctrl + +# Get the current hostname +HOSTNAME=$(hostname) +echo "Destroying in ${HOSTNAME}..." + + +case "$HOSTNAME" in + simap-server) + echo "Cleaning up..." + docker rm --force simap-server + docker rm --force nce-fan-ctrl + docker rm --force nce-t-ctrl + docker rm --force traffic-changer + + sleep 2 + docker ps -a + ;; + tfs-e2e-ctrl) + echo "Destroying TFS E2E Controller..." + source ~/tfs-ctrl/src/tests/ecoc25-f5ga-telemetry/deploy-specs-e2e.sh + kubectl delete namespace $TFS_K8S_NAMESPACE + ;; + tfs-agg-ctrl) + echo "Destroying TFS Agg Controller..." + source ~/tfs-ctrl/src/tests/ecoc25-f5ga-telemetry/deploy-specs-agg.sh + kubectl delete namespace $TFS_K8S_NAMESPACE + ;; + tfs-ip-ctrl) + echo "Destroying TFS IP Controller..." + source ~/tfs-ctrl/src/tests/ecoc25-f5ga-telemetry/deploy-specs-ip.sh + kubectl delete namespace $TFS_K8S_NAMESPACE + ;; + *) + echo "Unknown host: $HOSTNAME" + echo "No commands to run." + ;; +esac + +echo "Ready!" diff --git a/src/tests/ecoc25-f5ga-telemetry/provision-slice1.sh b/src/tests/ecoc25-f5ga-telemetry/provision-slice1.sh new file mode 100755 index 0000000000000000000000000000000000000000..fba21415c918eacc5b6ad1f351d388ae24f6b524 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/provision-slice1.sh @@ -0,0 +1,28 @@ +#!/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) + + +echo "[E2E] Provisioning slice1..." +curl --request POST --location --header 'Content-Type: application/json' \ + --data @data/slices/network-slice1.json \ + http://0.0.0.0:80/restconf/data/ietf-network-slice-service:network-slice-services +echo + + +echo "Done!" diff --git a/src/tests/ecoc25-f5ga-telemetry/provision-slice2.sh b/src/tests/ecoc25-f5ga-telemetry/provision-slice2.sh new file mode 100755 index 0000000000000000000000000000000000000000..bd1e9a73c6deef3be78d79ab2ca8bc56e6432d6e --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/provision-slice2.sh @@ -0,0 +1,28 @@ +#!/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) + + +echo "[E2E] Provisioning slice2..." +curl --request POST --location --header 'Content-Type: application/json' \ + --data @data/slices/network-slice2.json \ + http://0.0.0.0:80/restconf/data/ietf-network-slice-service:network-slice-services +echo + + +echo "Done!" diff --git a/src/tests/ecoc25-f5ga-telemetry/teardown-slice1.sh b/src/tests/ecoc25-f5ga-telemetry/teardown-slice1.sh new file mode 100755 index 0000000000000000000000000000000000000000..fe5158f58b639b06a51ecdd5135d85211c42857a --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/teardown-slice1.sh @@ -0,0 +1,23 @@ +#!/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. + + +echo "[E2E] Tear Down slice1..." +curl --request DELETE --location \ + http://0.0.0.0:80/restconf/data/ietf-network-slice-service:network-slice-services/slice-service=slice1 +echo + + +echo "Done!" diff --git a/src/tests/ecoc25-f5ga-telemetry/teardown-slice2.sh b/src/tests/ecoc25-f5ga-telemetry/teardown-slice2.sh new file mode 100755 index 0000000000000000000000000000000000000000..8bd612b3db92794eaf182ba4973eadecd7523ed1 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/teardown-slice2.sh @@ -0,0 +1,23 @@ +#!/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. + + +echo "[E2E] Tear Down slice2..." +curl --request DELETE --location \ + http://0.0.0.0:80/restconf/data/ietf-network-slice-service:network-slice-services/slice-service=slice2 +echo + + +echo "Done!" diff --git a/src/tests/ecoc25-f5ga-telemetry/telemetry-delete-slice1.py b/src/tests/ecoc25-f5ga-telemetry/telemetry-delete-slice1.py new file mode 100644 index 0000000000000000000000000000000000000000..b2924e1b201407b25e8661c946b0053dc1dac7ab --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/telemetry-delete-slice1.py @@ -0,0 +1,46 @@ +# 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 requests +from requests.auth import HTTPBasicAuth + + +RESTCONF_ADDRESS = '127.0.0.1' +RESTCONF_PORT = 80 +TELEMETRY_ID = 1109405947767160833 + +UNSUBSCRIBE_URI = '/restconf/operations/subscriptions:delete-subscription' +UNSUBSCRIBE_URL = 'http://{:s}:{:d}{:s}'.format(RESTCONF_ADDRESS, RESTCONF_PORT, UNSUBSCRIBE_URI) +REQUEST = { + 'ietf-subscribed-notifications:input': { + 'id': TELEMETRY_ID, + } +} + + +def main() -> None: + print('[E2E] Delete Telemetry slice1...') + headers = {'accept': 'application/json'} + auth = HTTPBasicAuth('admin', 'admin') + print(UNSUBSCRIBE_URL) + print(REQUEST) + reply = requests.post( + UNSUBSCRIBE_URL, headers=headers, json=REQUEST, auth=auth, + verify=False, allow_redirects=True, timeout=30 + ) + reply.raise_for_status() + +if __name__ == '__main__': + main() diff --git a/src/tests/ecoc25-f5ga-telemetry/telemetry-subscribe-slice1.py b/src/tests/ecoc25-f5ga-telemetry/telemetry-subscribe-slice1.py new file mode 100644 index 0000000000000000000000000000000000000000..86ee09dab9f2a76f41b710704a38567694a01fe7 --- /dev/null +++ b/src/tests/ecoc25-f5ga-telemetry/telemetry-subscribe-slice1.py @@ -0,0 +1,72 @@ +# 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 requests +from requests.auth import HTTPBasicAuth + + +RESTCONF_ADDRESS = '127.0.0.1' +RESTCONF_PORT = 80 +TARGET_SIMAP_NAME = 'e2e' +TARGET_LINK_NAME = 'E2E-L1' +SAMPLING_INTERVAL = 10.0 + + +SUBSCRIBE_URI = '/restconf/operations/subscriptions:establish-subscription' +SUBSCRIBE_URL = 'http://{:s}:{:d}{:s}'.format(RESTCONF_ADDRESS, RESTCONF_PORT, SUBSCRIBE_URI) +XPATH_FILTER = '/ietf-network:networks/network={:s}/ietf-network-topology:link={:s}/simap-telemetry:simap-telemetry' +REQUEST = { + 'ietf-subscribed-notifications:input': { + 'datastore': 'operational', + 'ietf-yang-push:datastore-xpath-filter': XPATH_FILTER.format(TARGET_SIMAP_NAME, TARGET_LINK_NAME), + 'ietf-yang-push:periodic': { + 'ietf-yang-push:period': SAMPLING_INTERVAL + } + } +} + + +def main() -> None: + print('[E2E] Subscribe Telemetry slice1...') + headers = {'accept': 'application/json'} + auth = HTTPBasicAuth('admin', 'admin') + print(SUBSCRIBE_URL) + print(REQUEST) + reply = requests.post( + SUBSCRIBE_URL, headers=headers, json=REQUEST, auth=auth, + verify=False, allow_redirects=True, timeout=30 + ) + content_type = reply.headers.get('Content-Type', '') + if 'application/json' not in content_type: + raise Exception('Not JSON:', reply.content.decode('UTF-8')) + try: + reply_data = reply.json() + except ValueError as e: + str_error = 'Invalid JSON: {:s}'.format(str(reply.content.decode('UTF-8'))) + raise Exception(str_error) from e + + if 'uri' not in reply_data: + raise Exception('Unexpected Reply: {:s}'.format(str(reply_data))) + subscription_uri = reply_data['uri'] + + stream_url = 'http://{:s}:{:d}{:s}'.format(RESTCONF_ADDRESS, RESTCONF_PORT, subscription_uri) + print('Opening stream "{:s}" (press Ctrl+C to stop)...'.format(stream_url)) + + with requests.get(stream_url, stream=True) as resp: + for line in resp.iter_lines(decode_unicode=True): + print(line) + +if __name__ == '__main__': + main() diff --git a/src/tests/eucnc24/.gitlab-ci.yml b/src/tests/eucnc24/.gitlab-ci.yml index f90007ceb314413efcd44e25fc7f13a01f36ca78..ee99ea2715a777c976b4102c01907a34fbe97c4f 100644 --- a/src/tests/eucnc24/.gitlab-ci.yml +++ b/src/tests/eucnc24/.gitlab-ci.yml @@ -83,7 +83,11 @@ end2end_test eucnc24: - > for ns in ${OLD_NATS_NAMESPACES}; do if [[ "$ns" == nats* ]]; then - helm3 uninstall "$ns" -n "$ns" + if helm3 status "$ns" &>/dev/null; then + helm3 uninstall "$ns" -n "$ns" + else + echo "Release '$ns' not found, skipping..." + fi fi done - export OLD_NAMESPACES=$(echo "${K8S_NAMESPACES}" | tr ' ' '\n' | grep -E '^(tfs|crdb|qdb|kafka|nats)') diff --git a/src/tests/ofc22/.gitlab-ci.yml b/src/tests/ofc22/.gitlab-ci.yml index 6b72f38dc362bb31b05384c1cb0630dab9badc73..6c3b65f4ac7d8b3432024c3b790bb11540e999e6 100644 --- a/src/tests/ofc22/.gitlab-ci.yml +++ b/src/tests/ofc22/.gitlab-ci.yml @@ -80,7 +80,11 @@ end2end_test ofc22: - > for ns in ${OLD_NATS_NAMESPACES}; do if [[ "$ns" == nats* ]]; then - helm3 uninstall "$ns" -n "$ns" + if helm3 status "$ns" &>/dev/null; then + helm3 uninstall "$ns" -n "$ns" + else + echo "Release '$ns' not found, skipping..." + fi fi done - export OLD_NAMESPACES=$(echo "${K8S_NAMESPACES}" | tr ' ' '\n' | grep -E '^(tfs|crdb|qdb|kafka|nats)') diff --git a/src/tests/ofc24/.gitlab-ci.yml b/src/tests/ofc24/.gitlab-ci.yml index eda880ae784f795617df4aa1afa220795ecc1f90..e0453200fb9600638c9cb5dc1e456ba23e9a8145 100644 --- a/src/tests/ofc24/.gitlab-ci.yml +++ b/src/tests/ofc24/.gitlab-ci.yml @@ -80,7 +80,11 @@ end2end_test ofc24: - > for ns in ${OLD_NATS_NAMESPACES}; do if [[ "$ns" == nats* ]]; then - helm3 uninstall "$ns" -n "$ns" + if helm3 status "$ns" &>/dev/null; then + helm3 uninstall "$ns" -n "$ns" + else + echo "Release '$ns' not found, skipping..." + fi fi done - export OLD_NAMESPACES=$(echo "${K8S_NAMESPACES}" | tr ' ' '\n' | grep -E '^(tfs|crdb|qdb|kafka|nats)') @@ -123,22 +127,22 @@ end2end_test ofc24: docker network create -d bridge --subnet=172.254.253.0/24 --gateway=172.254.253.254 --ip-range=172.254.253.0/24 na-br - > - docker run -dit --init --name na-t1 --network=na-br --ip 172.254.253.101 + docker run -dit --init --name na-t1 --network=na-br --ip 172.254.253.101 --publish 2022 --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-tp.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_t1.xml:/confd/examples.confd/OC23/platform.xml" asgamb1/oc23bgp.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh - > - docker run -dit --init --name na-t2 --network=na-br --ip 172.254.253.102 + docker run -dit --init --name na-t2 --network=na-br --ip 172.254.253.102 --publish 2022 --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-tp.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_t2.xml:/confd/examples.confd/OC23/platform.xml" asgamb1/oc23bgp.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh - > - docker run -dit --init --name na-r1 --network=na-br --ip 172.254.253.201 + docker run -dit --init --name na-r1 --network=na-br --ip 172.254.253.201 --publish 2022 --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-mg-on.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_r1.xml:/confd/examples.confd/OC23/platform.xml" asgamb1/flexscale-node.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh - > - docker run -dit --init --name na-r2 --network=na-br --ip 172.254.253.202 + docker run -dit --init --name na-r2 --network=na-br --ip 172.254.253.202 --publish 2022 --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-mg-on.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_r2.xml:/confd/examples.confd/OC23/platform.xml" asgamb1/flexscale-node.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh diff --git a/src/tests/ofc24/deploy-node-agents.sh b/src/tests/ofc24/deploy-node-agents.sh index 7dec352aca940d23d39936bcee74879a98db5041..18b1603a3f8432159fdd044a12daa5681e3b23d4 100755 --- a/src/tests/ofc24/deploy-node-agents.sh +++ b/src/tests/ofc24/deploy-node-agents.sh @@ -34,19 +34,19 @@ echo echo "Create Management Network and Node Agents:" echo "------------------------------------------" docker network create -d bridge --subnet=172.254.253.0/24 --gateway=172.254.253.254 --ip-range=172.254.253.0/24 na-br -docker run -dit --init --name na-t1 --network=na-br --ip 172.254.253.101 \ +docker run -dit --init --name na-t1 --network=na-br --ip 172.254.253.101 --publish 2022 \ --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-tp.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" \ --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_t1.xml:/confd/examples.confd/OC23/platform.xml" \ asgamb1/oc23bgp.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh -docker run -dit --init --name na-t2 --network=na-br --ip 172.254.253.102 \ +docker run -dit --init --name na-t2 --network=na-br --ip 172.254.253.102 --publish 2022 \ --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-tp.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" \ --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_t2.xml:/confd/examples.confd/OC23/platform.xml" \ asgamb1/oc23bgp.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh -docker run -dit --init --name na-r1 --network=na-br --ip 172.254.253.201 \ +docker run -dit --init --name na-r1 --network=na-br --ip 172.254.253.201 --publish 2022 \ --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-mg-on.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" \ --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_r1.xml:/confd/examples.confd/OC23/platform.xml" \ asgamb1/flexscale-node.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh -docker run -dit --init --name na-r2 --network=na-br --ip 172.254.253.202 \ +docker run -dit --init --name na-r2 --network=na-br --ip 172.254.253.202 --publish 2022 \ --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-mg-on.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" \ --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_r2.xml:/confd/examples.confd/OC23/platform.xml" \ asgamb1/flexscale-node.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh diff --git a/src/tests/qkd_end2end/.gitlab-ci.yml b/src/tests/qkd_end2end/.gitlab-ci.yml index 8445a8da5f5a6f4405c2ac40b33447f0a2d480d5..ca9c0018f3ba7697cc5f968ba3f10c38582ed18e 100644 --- a/src/tests/qkd_end2end/.gitlab-ci.yml +++ b/src/tests/qkd_end2end/.gitlab-ci.yml @@ -80,7 +80,11 @@ end2end_test qkd_end2end: - > for ns in ${OLD_NATS_NAMESPACES}; do if [[ "$ns" == nats* ]]; then - helm3 uninstall "$ns" -n "$ns" + if helm3 status "$ns" &>/dev/null; then + helm3 uninstall "$ns" -n "$ns" + else + echo "Release '$ns' not found, skipping..." + fi fi done - export OLD_NAMESPACES=$(echo "${K8S_NAMESPACES}" | tr ' ' '\n' | grep -E '^(tfs|crdb|qdb|kafka|nats)') @@ -104,18 +108,20 @@ end2end_test qkd_end2end: # Deploy scenario with mock QKD Nodes - docker network create --driver bridge --subnet=172.254.250.0/24 --gateway=172.254.250.254 qkd-node-br - > - docker run --detach --name qkd-node-01 --network qkd-node-br --ip 172.254.250.101 + docker run --detach --name qkd-node-01 --network qkd-node-br --ip 172.254.250.101 --publish 8080 --volume "$PWD/src/tests/${TEST_NAME}/data/qkd-node-01.json:/var/mock_qkd_node/startup.json" ${CI_REGISTRY_IMAGE}/mock-qkd-node:test - > - docker run --detach --name qkd-node-02 --network qkd-node-br --ip 172.254.250.102 + docker run --detach --name qkd-node-02 --network qkd-node-br --ip 172.254.250.102 --publish 8080 --volume "$PWD/src/tests/${TEST_NAME}/data/qkd-node-02.json:/var/mock_qkd_node/startup.json" ${CI_REGISTRY_IMAGE}/mock-qkd-node:test - > - docker run --detach --name qkd-node-03 --network qkd-node-br --ip 172.254.250.103 + docker run --detach --name qkd-node-03 --network qkd-node-br --ip 172.254.250.103 --publish 8080 --volume "$PWD/src/tests/${TEST_NAME}/data/qkd-node-03.json:/var/mock_qkd_node/startup.json" ${CI_REGISTRY_IMAGE}/mock-qkd-node:test + - docker ps -a + - echo "Waiting for QKD Nodes to initialize..." - > while ! docker logs qkd-node-01 2>&1 | grep -q "All log messages before absl::InitializeLog() is called are written to STDERR"; do @@ -134,6 +140,7 @@ end2end_test qkd_end2end: done # Dump logs of the QKD Nodes (script, before any configuration) + - docker ps -a - docker logs qkd-node-01 - docker logs qkd-node-02 - docker logs qkd-node-03 diff --git a/src/tests/qkd_end2end/redeploy-all.sh b/src/tests/qkd_end2end/redeploy-all.sh index b534e24658178d835efc9050fd59db3cf58aa7c7..95fb27c4200615ade3229229025e8c76e3721239 100755 --- a/src/tests/qkd_end2end/redeploy-all.sh +++ b/src/tests/qkd_end2end/redeploy-all.sh @@ -22,13 +22,13 @@ docker network rm --force qkd-node-br docker network create --driver bridge --subnet=172.254.250.0/24 --gateway=172.254.250.254 qkd-node-br # Create QKD Nodes -docker run --detach --name qkd-node-01 --network qkd-node-br --ip 172.254.250.101 \ +docker run --detach --name qkd-node-01 --network qkd-node-br --ip 172.254.250.101 --publish 8080 \ --volume "$PWD/src/tests/qkd_end2end/data/qkd-node-01.json:/var/mock_qkd_node/startup.json" \ mock-qkd-node:test -docker run --detach --name qkd-node-02 --network qkd-node-br --ip 172.254.250.102 \ +docker run --detach --name qkd-node-02 --network qkd-node-br --ip 172.254.250.102 --publish 8080 \ --volume "$PWD/src/tests/qkd_end2end/data/qkd-node-02.json:/var/mock_qkd_node/startup.json" \ mock-qkd-node:test -docker run --detach --name qkd-node-03 --network qkd-node-br --ip 172.254.250.103 \ +docker run --detach --name qkd-node-03 --network qkd-node-br --ip 172.254.250.103 --publish 8080 \ --volume "$PWD/src/tests/qkd_end2end/data/qkd-node-03.json:/var/mock_qkd_node/startup.json" \ mock-qkd-node:test diff --git a/src/tests/qkd_end2end/redeploy-qkd-nodes.sh b/src/tests/qkd_end2end/redeploy-qkd-nodes.sh index b01e0ecb81ba0aa2f5690810c4a31ec1841bf955..23ac3d7f13ee058f32f8f7e7d3a9a1db9050eaa6 100755 --- a/src/tests/qkd_end2end/redeploy-qkd-nodes.sh +++ b/src/tests/qkd_end2end/redeploy-qkd-nodes.sh @@ -22,13 +22,13 @@ docker network rm --force qkd-node-br docker network create --driver bridge --subnet=172.254.250.0/24 --gateway=172.254.250.254 qkd-node-br # Create QKD Nodes -docker run --detach --name qkd-node-01 --network qkd-node-br --ip 172.254.250.101 \ +docker run --detach --name qkd-node-01 --network qkd-node-br --ip 172.254.250.101 --publish 8080 \ --volume "$PWD/src/tests/qkd_end2end/data/qkd-node-01.json:/var/mock_qkd_node/startup.json" \ mock-qkd-node:test -docker run --detach --name qkd-node-02 --network qkd-node-br --ip 172.254.250.102 \ +docker run --detach --name qkd-node-02 --network qkd-node-br --ip 172.254.250.102 --publish 8080 \ --volume "$PWD/src/tests/qkd_end2end/data/qkd-node-02.json:/var/mock_qkd_node/startup.json" \ mock-qkd-node:test -docker run --detach --name qkd-node-03 --network qkd-node-br --ip 172.254.250.103 \ +docker run --detach --name qkd-node-03 --network qkd-node-br --ip 172.254.250.103 --publish 8080 \ --volume "$PWD/src/tests/qkd_end2end/data/qkd-node-03.json:/var/mock_qkd_node/startup.json" \ mock-qkd-node:test diff --git a/src/tests/tools/mock_nce_ctrl/Dockerfile b/src/tests/tools/mock_nce_ctrl/Dockerfile index ae9dde4eb469a951c1ccf3f78a79a9ab2d07c122..579bab5367e2d9320f45eb473e7d82f59b24a181 100644 --- a/src/tests/tools/mock_nce_ctrl/Dockerfile +++ b/src/tests/tools/mock_nce_ctrl/Dockerfile @@ -31,7 +31,5 @@ COPY . . 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_fan_ctrl/Dockerfile b/src/tests/tools/mock_nce_fan_ctrl/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cf4dfd9ad4cbea8fde40f147f4291b584505cbc3 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/Dockerfile @@ -0,0 +1,71 @@ +# 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 git build-essential cmake libpcre2-dev python3-dev python3-cffi && \ + rm -rf /var/lib/apt/lists/* + +# Download, build and install libyang. Note that APT package is outdated +# - Ref: https://github.com/CESNET/libyang +# - Ref: https://github.com/CESNET/libyang-python/ +RUN mkdir -p /var/libyang +RUN git clone https://github.com/CESNET/libyang.git /var/libyang +WORKDIR /var/libyang +RUN git fetch +RUN git checkout v2.1.148 +RUN mkdir -p /var/libyang/build +WORKDIR /var/libyang/build +RUN cmake -D CMAKE_BUILD_TYPE:String="Release" .. +RUN make +RUN make install +RUN ldconfig + +# 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 specific Python packages +RUN mkdir -p /var/teraflow/ +WORKDIR /var/teraflow/ +COPY src/common/tools/rest_conf/server/requirements.in ./requirements.in +RUN pip-compile --quiet --output-file=requirements.txt requirements.in +RUN python3 -m pip install -r requirements.txt + +# Get component files +RUN mkdir -p /var/teraflow/common/tools/ +WORKDIR /var/teraflow/ +COPY src/common/tools/rest_api/ ./common/tools/rest_api/ +COPY src/common/tools/rest_conf/ ./common/tools/rest_conf/ +COPY src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/*.py ./nce_fan_ctrl/ +COPY src/tests/tools/mock_nce_fan_ctrl/yang/. ./yang/ +COPY src/tests/tools/mock_nce_fan_ctrl/startup.json ./startup.json + +# Configure RESTCONF Server +ENV RESTCONF_PREFIX="/restconf" +ENV YANG_SEARCH_PATH="./yang" +ENV STARTUP_FILE="./startup.json" + +# Configure Flask for production +ENV FLASK_ENV="production" + +# Start the service +ENTRYPOINT ["gunicorn", "--workers", "1", "--worker-class", "eventlet", "--bind", "0.0.0.0:8080", "nce_fan_ctrl.app:app"] diff --git a/src/tests/tools/mock_nce_fan_ctrl/README.md b/src/tests/tools/mock_nce_fan_ctrl/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6c871662703c74a14999c85b414b497fd226d44e --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/README.md @@ -0,0 +1,24 @@ +# RESTCONF-based NCE-FAN Controller + +This server implements a basic RESTCONF Server that can load, potentially, any YANG data model. +In this case, it is prepared to load a NCE-FAN Controller based on: +- IETF Network Topology +- IETF YANG Data Model for Transport Network Client Signals +- IETF YANG Data Model for Traffic Engineering Tunnels, Label Switched Paths and Interfaces +- BBF App-Flow (preview) + + +## Build the RESTCONF-based NCE-FAN Controller Docker image +```bash +./build.sh +``` + +## Deploy the RESTCONF-based NCE-FAN Controller +```bash +./deploy.sh +``` + +## Destroy the RESTCONF-based NCE-FAN Controller +```bash +./destroy.sh +``` diff --git a/src/tests/tools/mock_nce_fan_ctrl/build.sh b/src/tests/tools/mock_nce_fan_ctrl/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..5899594354995121940e2b17c313ffc6763344ca --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/build.sh @@ -0,0 +1,22 @@ +#!/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)/../../../../ + +# Build image for NCE-FAN Controller +docker buildx build -t nce-fan-ctrl:test -f ./src/tests/tools/mock_nce_fan_ctrl/Dockerfile . +#docker tag nce-fan-ctrl:test localhost:32000/tfs/nce-fan-ctrl:test +#docker push localhost:32000/tfs/nce-fan-ctrl:test diff --git a/src/tests/tools/mock_nce_fan_ctrl/deploy.sh b/src/tests/tools/mock_nce_fan_ctrl/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..7e0d56abe468f72add462daf3cf315f178509305 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/deploy.sh @@ -0,0 +1,32 @@ +#!/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. + + +# Cleanup +docker rm --force nce-fan-ctrl + + +# Create NCE-FAN Controller +docker run --detach --name nce-fan-ctrl --publish 8080:8080 nce-fan-ctrl:test + + +sleep 2 + + +# Dump NCE-FAN Controller container +docker ps -a + + +echo "Bye!" diff --git a/src/tests/tools/mock_nce_fan_ctrl/destroy.sh b/src/tests/tools/mock_nce_fan_ctrl/destroy.sh new file mode 100755 index 0000000000000000000000000000000000000000..64148a70a5b31c83391a74708613bb7c939d1a26 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/destroy.sh @@ -0,0 +1,25 @@ +#!/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. + + +# Cleanup +docker rm --force nce-fan-ctrl + + +# Dump Docker containers +docker ps -a + + +echo "Bye!" diff --git a/src/tests/tools/mock_nce_fan_ctrl/nce_fan_client/Requests.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_client/Requests.py new file mode 100644 index 0000000000000000000000000000000000000000..042c31c8afa3144b9a94eea5a3527f27af409ecc --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_client/Requests.py @@ -0,0 +1,69 @@ +# 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. + + +QOS_PROFILE_NAME = 'AR_VR_Gaming' +URL_QOS_PROFILE_ITEM = '/huawei-nce-app-flow:qos-profiles/qos-profile={:s}'.format(QOS_PROFILE_NAME) +REQUEST_QOS_PROFILE = {"huawei-nce-app-flow:qos-profiles": {"qos-profile": [ + { + "downstream": { + "assure-bandwidth": "1000000000", + "max-bandwidth": "2000000000" + }, + "max-jitter": 10, + "max-latency": 10, + "max-loss": "0.001", + "name": QOS_PROFILE_NAME, + "upstream": { + "assure-bandwidth": "5000000000", + "max-bandwidth": "10000000000" + } + } +]}} + +APPLICATION_NAME = 'App_1_2_slice1' +URL_APPLICATION_ITEM = '/huawei-nce-app-flow:applications/application={:s}'.format(APPLICATION_NAME) +REQUEST_APPLICATION = {"huawei-nce-app-flow:applications": {"application": [ + { + "app-features": { + "app-feature": [ + { + "dest-ip": "172.1.101.22", + "dest-port": "10200", + "id": "feature_1_2_slice1", + "protocol": "tcp", + "src-ip": "172.16.204.221", + "src-port": "10500" + } + ] + }, + "app-id": ["app_1_2_slice1"], + "name": APPLICATION_NAME + } +]}} + +APP_FLOW_NAME = "App_Flow_1_2_slice1" +URL_APP_FLOW_ITEM = '/huawei-nce-app-flow:app-flows/app-flow={:s}'.format(APP_FLOW_NAME) +REQUEST_APP_FLOW = {"huawei-nce-app-flow:app-flows": {"app-flow": [ + { + "app-name": APPLICATION_NAME, + "duration": 9999, + "max-online-users": 1, + "name": APP_FLOW_NAME, + "qos-profile": QOS_PROFILE_NAME, + "service-profile": "service_1_2_slice1", + "stas": ["00:3D:E1:18:82:9E"], + "user-id": "ad2c2a94-3415-4676-867a-39eedfb9f205" + } +]}} diff --git a/src/tests/tools/mock_nce_fan_ctrl/nce_fan_client/__init__.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_client/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_client/__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_fan_ctrl/nce_fan_client/__main__.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_client/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..fd02f323d8c764cef34e23d243944998525fa2e5 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_client/__main__.py @@ -0,0 +1,45 @@ +# 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 logging +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from .Requests import ( + URL_QOS_PROFILE_ITEM, REQUEST_QOS_PROFILE, + URL_APPLICATION_ITEM, REQUEST_APPLICATION, + URL_APP_FLOW_ITEM, REQUEST_APP_FLOW, +) + +logging.basicConfig(level=logging.INFO) +logging.getLogger('RestConfClient').setLevel(logging.DEBUG) +LOGGER = logging.getLogger(__name__) + +def main() -> None: + restconf_client = RestConfClient( + '172.17.0.1', port=8081, + logger=logging.getLogger('RestConfClient') + ) + + LOGGER.info('Creating QoS Profile: {:s}'.format(str(REQUEST_QOS_PROFILE))) + restconf_client.post(URL_QOS_PROFILE_ITEM, body=REQUEST_QOS_PROFILE) + + LOGGER.info('Creating Application: {:s}'.format(str(REQUEST_APPLICATION))) + restconf_client.post(URL_APPLICATION_ITEM, body=REQUEST_APPLICATION) + + LOGGER.info('Creating App Flow: {:s}'.format(str(REQUEST_APP_FLOW))) + restconf_client.post(URL_APP_FLOW_ITEM, body=REQUEST_APP_FLOW) + + +if __name__ == '__main__': + main() diff --git a/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/Callbacks.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/Callbacks.py new file mode 100644 index 0000000000000000000000000000000000000000..1c6996581b023c7a858a2f9e3020556ec6690194 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/Callbacks.py @@ -0,0 +1,69 @@ +# 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 logging, re +from typing import Dict, Optional +from common.tools.rest_conf.server.restconf_server.Callbacks import _Callback + + +LOGGER = logging.getLogger(__name__) + + +class CallbackQosProfile(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/data' + pattern += r'/huawei-nce-app-flow:qos-profiles' + pattern += r'/qos-profile=(?P[^/]+)' + super().__init__(pattern) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + MSG = '[on_qos_profile] match={:s} path={:s} old_data={:s} new_data={:s}' + LOGGER.warning(MSG.format(str(match.groupdict()), str(path), str(old_data), str(new_data))) + return False + + +class CallbackApplication(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/data' + pattern += r'/huawei-nce-app-flow:applications' + pattern += r'/application=(?P[^/]+)' + super().__init__(pattern) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + MSG = '[on_application] match={:s} path={:s} old_data={:s} new_data={:s}' + LOGGER.warning(MSG.format(str(match.groupdict()), str(path), str(old_data), str(new_data))) + return False + + +class CallbackAppFlow(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/data' + pattern += r'/huawei-nce-app-flow:app-flows' + pattern += r'/app-flow=(?P[^/]+)' + super().__init__(pattern) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + MSG = '[on_app_flow] match={:s} path={:s} old_data={:s} new_data={:s}' + LOGGER.warning(MSG.format(str(match.groupdict()), str(path), str(old_data), str(new_data))) + return False diff --git a/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/SimapClient.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/SimapClient.py new file mode 100644 index 0000000000000000000000000000000000000000..725b08bd47e0bd127cf0f7c4131cb744313b149d --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/SimapClient.py @@ -0,0 +1,350 @@ +# 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, Optional, Tuple +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +class TerminationPoint: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}/node={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:termination-point={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str, tp_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tp_id = tp_id + + def create(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network-topology:termination-point'][0] + + def update(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + self._restconf_client.delete(endpoint) + + +class NodeTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/node={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + + def create( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class Node: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/node={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tps : Dict[str, TerminationPoint] = dict() + self._telemetry : Optional[NodeTelemetry] = None + + @property + def telemetry(self) -> NodeTelemetry: + if self._telemetry is None: + self._telemetry = NodeTelemetry(self._restconf_client, self._network_id, self._node_id) + return self._telemetry + + def termination_points(self) -> List[Dict]: + tps : Dict = self._restconf_client.get(TerminationPoint.ENDPOINT_NO_ID) + return tps['ietf-network-topology:termination-point'].get('termination-point', list()) + + def termination_point(self, tp_id : str) -> TerminationPoint: + _tp = self._tps.get(tp_id) + if _tp is not None: return _tp + _tp = TerminationPoint(self._restconf_client, self._network_id, self._node_id, tp_id) + return self._tps.setdefault(tp_id, _tp) + + def create( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network:node'][0] + + def update( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class LinkTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/ietf-network-topology:link={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + + def create( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Link: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:link={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + self._telemetry : Optional[LinkTelemetry] = None + + @property + def telemetry(self) -> LinkTelemetry: + if self._telemetry is None: + self._telemetry = LinkTelemetry(self._restconf_client, self._network_id, self._link_id) + return self._telemetry + + def create( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link : Dict = self._restconf_client.get(endpoint) + return link['ietf-network-topology:link'][0] + + def update( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Network: + ENDPOINT_NO_ID = '/ietf-network:networks' + ENDPOINT_ID = ENDPOINT_NO_ID + '/network={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._nodes : Dict[str, Node] = dict() + self._links : Dict[str, Link] = dict() + + def nodes(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Node.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('node', list()) + + def links(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Link.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('ietf-network-topology:link', list()) + + def node(self, node_id : str) -> Node: + _node = self._nodes.get(node_id) + if _node is not None: return _node + _node = Node(self._restconf_client, self._network_id, node_id) + return self._nodes.setdefault(node_id, _node) + + def link(self, link_id : str) -> Link: + _link = self._links.get(link_id) + if _link is not None: return _link + _link = Link(self._restconf_client, self._network_id, link_id) + return self._links.setdefault(link_id, _link) + + def create(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + networks : Dict = self._restconf_client.get(endpoint) + return networks['ietf-network:network'][0] + + def update(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + self._restconf_client.delete(endpoint) + + +class SimapClient: + def __init__(self, restconf_client : RestConfClient) -> None: + self._restconf_client = restconf_client + self._networks : Dict[str, Network] = dict() + + def networks(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Network.ENDPOINT_NO_ID) + return reply['ietf-network:networks'].get('network', list()) + + def network(self, network_id : str) -> Network: + _network = self._networks.get(network_id) + if _network is not None: return _network + _network = Network(self._restconf_client, network_id) + return self._networks.setdefault(network_id, _network) diff --git a/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/SimapUpdater.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/SimapUpdater.py new file mode 100644 index 0000000000000000000000000000000000000000..8cc114433a37c47ba6b8a1d9c61dbea902a503b3 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/SimapUpdater.py @@ -0,0 +1,68 @@ +# 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 logging, os +from typing import Dict +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from .SimapClient import SimapClient + + +SIMAP_ADDRESS = os.environ.get('SIMAP_ADDRESS') +SIMAP_PORT = os.environ.get('SIMAP_PORT' ) + + +class SimapUpdater: + def __init__(self): + self._simap_client = None + + if SIMAP_ADDRESS is None: return + if SIMAP_PORT is None: return + + self._restconf_client = RestConfClient( + SIMAP_ADDRESS, port=SIMAP_PORT, + logger=logging.getLogger('RestConfClient') + ) + self._simap_client = SimapClient(self._restconf_client) + + + def upload_topology(self, network_data : Dict) -> None: + if self._simap_client is None: return + + network_id = network_data['network-id'] + te_topo = self._simap_client.network(network_id) + te_topo.update() + + nodes = network_data.get('node', list()) + for node in nodes: + node_id = node['node-id'] + tp_ids = [ + tp['tp-id'] + for tp in node['ietf-network-topology:termination-point'] + ] + te_topo.node(node_id).update(termination_point_ids=tp_ids) + + links = network_data.get('ietf-network-topology:link', list()) + for link in links: + link_id = link['link-id'] + link_src = link['source'] + link_dst = link['destination'] + link_src_node_id = link_src['source-node'] + link_src_tp_id = link_src['source-tp'] + link_dst_node_id = link_dst['dest-node'] + link_dst_tp_id = link_dst['dest-tp'] + + te_topo.link(link_id).update( + link_src_node_id, link_src_tp_id, link_dst_node_id, link_dst_tp_id + ) diff --git a/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/__init__.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_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_fan_ctrl/nce_fan_ctrl/__main__.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..2c84d92efd7e33d44237e3a8791771a371e12f3f --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/__main__.py @@ -0,0 +1,26 @@ +# 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 .app import app + +BIND_ADDRESS = '0.0.0.0' +BIND_PORT = 8080 + +if __name__ == '__main__': + # Only used to run it locally during development stage; + # otherwise, app is directly launched by gunicorn. + app.run( + host=BIND_ADDRESS, port=BIND_PORT, debug=True, use_reloader=False + ) diff --git a/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/app.py b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/app.py new file mode 100644 index 0000000000000000000000000000000000000000..bf6e5c6a5c1eaf845423b9d69b90b65146330e88 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/nce_fan_ctrl/app.py @@ -0,0 +1,56 @@ +# 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. + + +# This file overwrites default RestConf Server `app.py` file. + + +import logging +from common.tools.rest_conf.server.restconf_server.RestConfServerApplication import RestConfServerApplication +from .Callbacks import CallbackApplication, CallbackAppFlow, CallbackQosProfile +from .SimapUpdater import SimapUpdater + + +logging.basicConfig( + level=logging.INFO, + format='[Worker-%(process)d][%(asctime)s] %(levelname)s:%(name)s:%(message)s', +) +LOGGER = logging.getLogger(__name__) +logging.getLogger('RestConfClient').setLevel(logging.WARN) + + +LOGGER.info('Starting...') +rcs_app = RestConfServerApplication() + +rcs_app.register_host_meta() +rcs_app.register_restconf() +LOGGER.info('All connectors registered') + +startup_data = rcs_app.get_startup_data() + +networks = startup_data.get('ietf-network:networks', dict()) +networks = networks.get('network', list()) +if len(networks) == 1 and networks[0]['network-id'] == 'admin': + simap_updater = SimapUpdater() + simap_updater.upload_topology(networks[0]) + + rcs_app.callback_dispatcher.register(CallbackApplication()) + rcs_app.callback_dispatcher.register(CallbackAppFlow()) + rcs_app.callback_dispatcher.register(CallbackQosProfile()) + LOGGER.info('All callbacks registered') + +rcs_app.dump_configuration() +app = rcs_app.get_flask_app() + +LOGGER.info('Initialization completed!') diff --git a/src/tests/tools/mock_nce_fan_ctrl/redeploy.sh b/src/tests/tools/mock_nce_fan_ctrl/redeploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..5be64707fdf16c533ec233993ed7e235d03b191c --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/redeploy.sh @@ -0,0 +1,36 @@ +#!/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. + + +echo "Building SIMAP Server..." +cd ~/tfs-ctrl/ +docker buildx build -t simap-server:mock -f ./src/tests/tools/simap_server/Dockerfile . + +echo "Building NCE-FAN Controller..." +cd ~/tfs-ctrl/ +docker buildx build -t nce-fan-ctrl:mock -f ./src/tests/tools/mock_nce_fan_ctrl/Dockerfile . + +echo "Cleaning up..." +docker rm --force simap-server +docker rm --force nce-fan-ctrl + +echo "Deploying support services..." +docker run --detach --name simap-server --publish 8080:8080 simap-server:mock +docker run --detach --name nce-fan-ctrl --publish 8081:8080 --env SIMAP_ADDRESS=172.17.0.1 --env SIMAP_PORT=8080 nce-fan-ctrl:mock + +sleep 2 +docker ps -a + +echo "Bye!" diff --git a/src/tests/tools/mock_nce_fan_ctrl/run_client.sh b/src/tests/tools/mock_nce_fan_ctrl/run_client.sh new file mode 100755 index 0000000000000000000000000000000000000000..4384f0fdbf209f11104ac39c4e0362cabb16271a --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/run_client.sh @@ -0,0 +1,19 @@ +#!/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)/../../../ + +python -m tests.tools.mock_nce_fan_ctrl.nce_fan_client diff --git a/src/tests/tools/mock_nce_fan_ctrl/startup.json b/src/tests/tools/mock_nce_fan_ctrl/startup.json new file mode 100644 index 0000000000000000000000000000000000000000..774efb417221b703cee387cfbde17cb42a218f34 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/startup.json @@ -0,0 +1,57 @@ +{ + "ietf-network:networks": { + "network": [ + { + "network-id": "admin", + "ietf-te-topology:te": { + "name": "admin" + }, + "network-types": { + "ietf-te-topology:te-topology": { + "ietf-eth-te-topology:eth-tran-topology": {} + } + }, + "node": [ + { + "node-id": "ONT1", "ietf-te-topology:te-node-id": "172.16.61.10", + "ietf-te-topology:te": {"te-node-attributes": {"name": "ONT1", "admin-status": "up"}, "oper-status": "up"}, + "ietf-network-topology:termination-point": [ + {"tp-id": "500", "ietf-te-topology:te": {"name": "500"}, "ietf-te-topology:te-tp-id": "10.1.1.2"}, + {"tp-id": "200", "ietf-te-topology:te": {"name": "200"}, "ietf-te-topology:te-tp-id": "10.0.0.1"} + ] + }, + { + "node-id": "ONT2", "ietf-te-topology:te-node-id": "172.16.61.11", + "ietf-te-topology:te": {"te-node-attributes": {"name": "ONT2", "admin-status": "up"}, "oper-status": "up"}, + "ietf-network-topology:termination-point": [ + {"tp-id": "500", "ietf-te-topology:te": {"name": "500"}, "ietf-te-topology:te-tp-id": "10.1.2.2"}, + {"tp-id": "200", "ietf-te-topology:te": {"name": "200"}, "ietf-te-topology:te-tp-id": "10.0.0.1"} + ] + }, + { + "node-id": "OLT", "ietf-te-topology:te-node-id": "172.16.58.10", + "ietf-te-topology:te": {"te-node-attributes": {"name": "OLT", "admin-status": "up"}, "oper-status": "up"}, + "ietf-network-topology:termination-point": [ + {"tp-id": "500", "ietf-te-topology:te": {"name": "500"}, "ietf-te-topology:te-tp-id": "128.32.33.2", + "ietf-eth-te-topology:eth-svc": {"supported-classification": {"port-classification": false, "vlan-classification": {"vlan-tag-classification": true, "outer-tag": { + "supported-tag-types": ["ietf-eth-tran-types:classify-c-vlan"], "vlan-bundling": false, "vlan-range": "31" + }}}} + }, + {"tp-id": "501", "ietf-te-topology:te": {"name": "501"}, "ietf-te-topology:te-tp-id": "128.32.44.2", + "ietf-eth-te-topology:eth-svc": {"supported-classification": {"port-classification": false, "vlan-classification": {"vlan-tag-classification": true, "outer-tag": { + "supported-tag-types": ["ietf-eth-tran-types:classify-c-vlan"], "vlan-bundling": false, "vlan-range": "21" + }}}} + }, + {"tp-id": "200", "ietf-te-topology:te": {"name": "200"}, "ietf-te-topology:te-tp-id": "10.1.1.1"}, + {"tp-id": "201", "ietf-te-topology:te": {"name": "201"}, "ietf-te-topology:te-tp-id": "10.1.2.1"} + ] + } + ], + "ietf-network-topology:link": [ + {"link-id": "L1", "source": {"source-node": "ONT1", "source-tp": "500"}, "destination": {"dest-node": "OLT", "dest-tp": "200"}}, + {"link-id": "L2", "source": {"source-node": "ONT2", "source-tp": "500"}, "destination": {"dest-node": "OLT", "dest-tp": "201"}} + ] + } + ] + } +} diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/.gitignore b/src/tests/tools/mock_nce_fan_ctrl/yang/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6c90250b25a5658005177d63be898480fa7d8b60 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/.gitignore @@ -0,0 +1,17 @@ +# 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. + + +# Add here folders containing non-public data models +private-*/ diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-service.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-service.yang new file mode 100644 index 0000000000000000000000000000000000000000..633d74715a8fa3dd4a4f815786218e26bdc1e987 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-service.yang @@ -0,0 +1,1010 @@ +module ietf-eth-tran-service { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-eth-tran-service"; + + prefix "ethtsvc"; + import ietf-yang-types { + prefix "yang"; + reference "RFC 6991 - Common YANG Data Types"; + } + + import ietf-network { + prefix "nw"; + reference "RFC8345 - A YANG Data Model for Network Topologies"; + } + + import ietf-network-topology { + prefix "nt"; + reference "RFC8345 - A YANG Data Model for Network Topologies"; + } + + import ietf-te-types { + prefix "te-types"; + reference "RFC 8776 - Traffic Engineering Common YANG Types"; + } + + import ietf-eth-tran-types { + prefix "etht-types"; + reference "RFC XXXX - A YANG Data Model for Transport + Network Client Signals"; + } + + import ietf-routing-types { + prefix "rt-types"; + reference "RFC 8294 - Common YANG Data Types for the + Routing Area"; + + } + + import ietf-te { + prefix "te"; + reference "RFC YYYY - A YANG Data Model for Traffic + Engineering Tunnels and Interfaces"; + } + + organization + "Internet Engineering Task Force (IETF) CCAMP WG"; + contact + " + WG List: + + ID-draft editor: + Haomian Zheng (zhenghaomian@huawei.com); + Italo Busi (italo.busi@huawei.com); + Aihua Guo (aihuaguo.ietf@gmail.com); + Anton Snitser (antons@sedonasys.com);0 + Francesco Lazzeri (francesco.lazzeri@ericsson.com); + Yunbin Xu (xuyunbin@caict.ac.cn); + Yang Zhao (zhaoyangyjy@chinamobile.com); + Xufeng Liu (xufeng.liu.ietf@gmail.com); + Giuseppe Fioccola (giuseppe.fioccola@huawei.com); + Chaode Yu (yuchaode@huawei.com) + "; + + description + "This module defines a YANG data model for describing + the Ethernet services. The model fully conforms to the + Network Management Datastore Architecture (NMDA). + + 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 + (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-10-23 { + description + "version -04 as an WG document"; + reference + "draft-ietf-ccamp-client-signal-yang"; + } + + /* + * Groupings + */ + + grouping vlan-classification { + description + "A grouping which represents classification + on an 802.1Q VLAN tag."; + + leaf tag-type { + type etht-types:eth-tag-classify; + description + "The tag type used for VLAN classification."; + } + choice individual-bundling-vlan { + description + "VLAN based classification can be individual + or bundling."; + + case individual-vlan { + leaf vlan-value { + type etht-types:vlanid; + description + "VLAN ID value."; + } + } + + case vlan-bundling { + leaf vlan-range { + type etht-types:vid-range-type; + description + "List of VLAN ID values."; + } + } + } + } + + grouping vlan-write { + description + "A grouping which represents push/pop operations + of an 802.1Q VLAN tag."; + + leaf tag-type { + type etht-types:eth-tag-type; + description + "The VLAN tag type to push/swap."; + } + leaf vlan-value { + type etht-types:vlanid; + description + "The VLAN ID value to push/swap."; + } +/* + * To be added: this attribute is used when: + * a) the ETH service has only one CoS (as in current version) + * b) as a default when a mapping between a given CoS value + * and the PCP value is not defined (in future versions) + */ + leaf default-pcp { + type uint8 { + range "0..7"; + } + description + "The default Priority Code Point (PCP) value to push/swap"; + } + } + + grouping vlan-operations { + description + "A grouping which represents VLAN operations."; + + leaf pop-tags { + type uint8 { + range "1..2"; + } + description + "The number of VLAN tags to pop (or swap if used in + conjunction with push-tags)"; + } + container push-tags { + description + "The VLAN tags to push (or swap if used in + conjunction with pop-tags)"; + + container outer-tag { + presence + "Indicates existence of the outermost VLAN tag to + push/swap"; + + description + "The outermost VLAN tag to push/swap."; + + uses vlan-write; + } + container second-tag { + must + '../outer-tag/tag-type = "etht-types:s-vlan-tag-type" and ' + + 'tag-type = "etht-types:c-vlan-tag-type"' + { + + error-message + " + When pushing/swapping two tags, the outermost tag must + be specified and of S-VLAN type and the second + outermost tag must be of C-VLAN tag type. + "; + description + " + For IEEE 802.1Q interoperability, when pushing/swapping + two tags, it is required that the outermost tag exists + and is an S-VLAN, and the second outermost tag is a + C-VLAN. + "; + } + + presence + "Indicates existence of a second outermost VLAN tag to + push/swap"; + + description + "The second outermost VLAN tag to push/swap."; + uses vlan-write; + } + } + } + + grouping named-or-value-bandwidth-profile { + description + "A grouping to configure a bandwdith profile either by + referencing a named bandwidth profile or by + configuring the values of the bandwidth profile attributes."; + choice style { + description + "Whether the bandwidth profile is named or defined by value"; + + case named { + description + "Named bandwidth profile."; + leaf bandwidth-profile-name { + type leafref { + path "/ethtsvc:etht-svc/ethtsvc:globals/" + + "ethtsvc:named-bandwidth-profiles/" + + "ethtsvc:bandwidth-profile-name"; + } + description + "Name of the bandwidth profile."; + } + } + case value { + description + "Bandwidth profile configured by value."; + uses etht-types:etht-bandwidth-profiles; + } + } + } + + grouping bandwidth-profiles { + description + "A grouping which represent bandwidth profile configuration."; + + choice direction { + description + "Whether the bandwidth profiles are symmetrical or + asymmetrical"; + case symmetrical { + description + "The same bandwidth profile is used to describe both + the ingress and the egress bandwidth profile."; + container ingress-egress-bandwidth-profile { + description + "The bandwdith profile used in both directions."; + uses named-or-value-bandwidth-profile; + } + } + case asymmetrical { + description + "Ingress and egress bandwidth profiles can be specified."; + container ingress-bandwidth-profile { + description + "The bandwdith profile used in the ingress direction."; + uses named-or-value-bandwidth-profile; + } + container egress-bandwidth-profile { + description + "The bandwdith profile used in the egress direction."; + uses named-or-value-bandwidth-profile; + } + } + } + } + + grouping etht-svc-access-parameters { + description + "ETH services access parameters"; + + leaf access-node-id { + type te-types:te-node-id; + description + "The identifier of the access node in + the ETH TE topology."; + } + + leaf access-node-uri { + type nw:node-id; + description + "The identifier of the access node in the network."; + } + + leaf access-ltp-id { + type te-types:te-tp-id; + description + "The TE link termination point identifier, used + together with access-node-id to identify the + access LTP."; + } + + leaf access-ltp-uri { + type nt:tp-id; + description + "The link termination point identifier in network topology, + used together with access-node-uri to identify the + access LTP."; + } + + leaf access-role { + type identityref { + base etht-types:access-role; + } + description + "Indicate the role of access, e.g., working or protection. "; + } + + container pm-config { + uses pm-config-grouping; + description + "This grouping is used to set the threshold value for + performance monitoring. "; + } + + container state { + config false; + description + "The state is used to monitor the status of service. "; + leaf operational-state { + type identityref { + base te-types:tunnel-state-type; + } + description + "Indicating the operational state of client signal. "; + } + leaf provisioning-state { + type identityref { + base te-types:lsp-state-type; + } + description + "Indicating the provisional state of client signal, + especially when there is a change, i.e., revise, create. "; + } + } + + leaf performance { + type identityref { + base etht-types:performance; + } + config false; + description + "Performance Monitoring for the service. "; + } + + } + + grouping etht-svc-tunnel-parameters { + description + "ETH services tunnel parameters."; + choice technology { + description + "Service multiplexing is optional and flexible."; + + case native-ethernet { + /* + placeholder to support proprietary multiplexing + (for further discussion) + */ + list eth-tunnels { + key name; + description + "ETH Tunnel list in native Ethernet scenario."; + uses tunnels-grouping; + } + } + + case frame-base { + list otn-tunnels { + key name; + description + "OTN Tunnel list in Frame-based scenario."; + uses tunnels-grouping; + } + } + + case mpls-tp { + container pw { + description + "Pseudowire information for Ethernet over MPLS-TP."; + uses pw-segment-grouping; + } + } + } + +/* + * Open issue: can we constraints it to be used only with mp services? + */ + leaf src-split-horizon-group { + type string; + description + "Identify a split horizon group at the Tunnel source TTP"; + } + leaf dst-split-horizon-group { + type string; + description + "Identify a split horizon group at the Tunnel destination TTP"; + } + } + + grouping etht-svc-pm-threshold-config { + description + "Configuraiton parameters for Ethernet service PM thresholds."; + + leaf sending-rate-high { + type uint64; + description + "High threshold of packet sending rate in kbps."; + } + leaf sending-rate-low { + type uint64; + description + "Low threshold of packet sending rate in kbps."; + } + leaf receiving-rate-high { + type uint64; + description + "High threshold of packet receiving rate in kbps."; + } + leaf receiving-rate-low { + type uint64; + description + "Low threshold of packet receiving rate in kbps."; + } + } + + grouping etht-svc-pm-stats { + description + "Ethernet service PM statistics."; + + leaf sending-rate-too-high { + type uint32; + description + "Counter that indicates the number of times the + sending rate is above the high threshold"; + } + leaf sending-rate-too-low { + type uint32; + description + "Counter that indicates the number of times the + sending rate is below the low threshold"; + } + leaf receiving-rate-too-high { + type uint32; + description + "Counter that indicates the number of times the + receiving rate is above the high threshold"; + } + leaf receiving-rate-too-low { + type uint32; + description + "Counter that indicates the number of times the + receiving rate is below the low threshold"; + } + } + + grouping etht-svc-instance-config { + description + "Configuraiton parameters for Ethernet services."; + + leaf etht-svc-name { + type string; + description + "Name of the ETH service."; + } + + leaf etht-svc-title { + type string; + description + "The Identifier of the ETH service."; + } + + leaf user-label { + type string; + description + "Alias of the ETH service."; + } + + leaf etht-svc-descr { + type string; + description + "Description of the ETH service."; + } + + leaf etht-svc-customer { + type string; + description + "Customer of the ETH service."; + } + + leaf etht-svc-type { + type etht-types:service-type; + description + "Type of ETH service (p2p, mp2mp or rmp)."; + /* Add default as p2p */ + } + + leaf etht-svc-lifecycle { + type etht-types:lifecycle-status; + description + "Lifecycle state of ETH service."; + /* Add default as installed */ + } + uses te-types:te-topology-identifier; + + uses resilience-grouping; + list etht-svc-end-points { + key etht-svc-end-point-name; + description + "The logical end point for the ETH service. "; + uses etht-svc-end-point-grouping; + } + + + container alarm-shreshold { + description "threshold configuration for the E2E client signal"; + uses alarm-shreshold-grouping; + } + + container underlay { + description + "The unterlay tunnel information that carrying the + ETH service. "; + uses etht-svc-tunnel-parameters; + } + + leaf admin-status { + type identityref { + base te-types:tunnel-admin-state-type; + } + default te-types:tunnel-admin-state-up; + description "ETH service administrative state."; + } + } + + grouping etht-svc-instance-state { + description + "State parameters for Ethernet services."; + + leaf operational-state { + type identityref { + base te-types:tunnel-state-type; + } + default te-types:tunnel-state-up; + description "ETH service operational state."; + } + leaf provisioning-state { + type identityref { + base te-types:lsp-state-type; + } + description "ETH service provisioning state."; + } + leaf creation-time { + type yang:date-and-time; + description + "Time of ETH service creation."; + } + leaf last-updated-time { + type yang:date-and-time; + description + "Time of ETH service last update."; + } + + leaf created-by { + type string; + description + "The client signal is created by whom, + can be a system or staff ID."; + } + leaf last-updated-by { + type string; + description + "The client signal is last updated by whom, + can be a system or staff ID."; + } + leaf owned-by { + type string; + description + "The client signal is last updated by whom, + can be a system ID."; + } + container pm-state { + description + "PM data of E2E Ethernet service"; + uses pm-state-grouping; + } + container error-info { + description "error messages of configuration"; + uses error-info-grouping; + } + } + + grouping pm-state-grouping { + leaf latency { + description + "latency value of the E2E Ethernet service"; + type uint32; + units microsecond; + } + } + + grouping error-info-grouping { + leaf error-code { + description "error code"; + type uint16; + } + + leaf error-description { + description "detail message of error"; + type string; + } + + leaf error-timestamp { + description "the date and time error is happened"; + type yang:date-and-time; + } + } + + grouping alarm-shreshold-grouping { + leaf latency-threshold { + description "a threshold for the E2E client signal service's + latency. Once the latency value exceed this threshold, an alarm + should be triggered."; + type uint32; + units microsecond; + } + } + + /* + * Data nodes + */ + + container etht-svc { + description + "ETH services."; + + container globals { + description + "Globals Ethernet configuration data container"; + list named-bandwidth-profiles { + key bandwidth-profile-name; + description + "List of named bandwidth profiles used by + Ethernet services."; + + leaf bandwidth-profile-name { + type string; + description + "Name of the bandwidth profile."; + } + uses etht-types:etht-bandwidth-profiles; + } + } + + list etht-svc-instances { + key etht-svc-name; + description + "The list of p2p ETH service instances"; + + uses etht-svc-instance-config; + + container state { + config false; + description + "Ethernet Service states."; + + uses etht-svc-instance-state; + } + } + } + + grouping resilience-grouping { + description + "Grouping for resilience configuration. "; + container resilience { + description + "To configure the data plane protection parameters, + currently a placeholder only, future candidate attributes + include, Revert, WTR, Hold-off Timer, ..."; + uses te:protection-restoration-properties; + } + } + + grouping etht-svc-end-point-grouping { + description + "Grouping for the end point configuration."; + leaf etht-svc-end-point-name { + type string; + description + "The name of the logical end point of ETH service. "; + } + + leaf etht-svc-end-point-id { + type string; + description + "The identifier of the logical end point of ETH service."; + } + + leaf etht-svc-end-point-descr { + type string; + description + "The description of the logical end point of ETH service. "; + } + + leaf topology-role { + type identityref { + base etht-types:topology-role; + } + description + "Indicating the underlay topology role, + e.g., hub,spoke, any-to-any "; + } + + container resilience { + description + "Placeholder for resilience configuration, for future study. "; + } + + list etht-svc-access-points { + key access-point-id; + min-elements "1"; +/* + Open Issue: + Is it possible to limit the max-elements only for p2p services? + max-elements "2"; +*/ + description + "List of the ETH trasport services access point instances."; + + leaf access-point-id { + type string; + description + "ID of the service access point instance"; + } + uses etht-svc-access-parameters; + } + + leaf service-classification-type { + type identityref { + base etht-types:service-classification-type; + } + description + "Service classification type."; + } + + choice service-classification { + description + "Access classification can be port-based or + VLAN based."; + + case port-classification { + /* no additional information */ + } + + case vlan-classification { + container outer-tag { + presence "The outermost VLAN tag exists"; + description + "Classifies traffic using the outermost VLAN tag."; + + uses vlan-classification; + } + container second-tag { + must + '../outer-tag/tag-type = "etht-types:classify-s-vlan" and ' + + 'tag-type = "etht-types:classify-c-vlan"' + { + error-message + " + When matching two tags, the outermost tag must be + specified and of S-VLAN type and the second + outermost tag must be of C-VLAN tag type. + "; + description + " + For IEEE 802.1Q interoperability, when matching two + tags, it is required that the outermost tag exists + and is an S-VLAN, and the second outermost tag is a + C-VLAN. + "; + } + presence "The second outermost VLAN tag exists"; + + description + "Classifies traffic using the second outermost VLAN tag."; + + uses vlan-classification; + } + } + } + +/* + * Open issue: can we constraints it to be used only with mp services? + */ + leaf split-horizon-group { + type string; + description "Identify a split horizon group"; + } + + uses bandwidth-profiles; + + container vlan-operations { + description + "Configuration of VLAN operations."; + choice direction { + description + "Whether the VLAN operations are symmetrical or + asymmetrical"; + case symmetrical { + container symmetrical-operation { + uses vlan-operations; + description + "Symmetrical operations. + Expressed in the ingress direction, but + the reverse operation is applied to egress traffic"; + } + } + case asymmetrical { + container asymmetrical-operation { + description "Asymmetrical operations"; + container ingress { + uses vlan-operations; + description "Ingress operations"; + } + container egress { + uses vlan-operations; + description "Egress operations"; + } + } + } + } + } + } + + grouping pm-config-grouping { + description + "Grouping used for Performance Monitoring Configuration. "; + leaf pm-enable { + type boolean; + description + "Whether to enable the performance monitoring."; + } + + leaf sending-rate-high { + type uint64; + description + "The upperbound of sending rate."; + } + + leaf sending-rate-low { + type uint64; + description + "The lowerbound of sending rate."; + } + + leaf receiving-rate-high { + type uint64; + description + "The upperbound of receiving rate."; + } + + leaf receiving-rate-low { + type uint64; + description + "The lowerbound of receiving rate."; + } + } + + grouping pw-segment-grouping { + description + "Grouping used for PW configuration. "; + leaf pw-id { + type string; + description + "The Identifier information of pseudowire. "; + } + + leaf pw-name { + type string; + description + "The name information of pseudowire."; + } + + leaf transmit-label { + type rt-types:mpls-label; + description + "Transmit label information in PW. "; + } + + leaf receive-label { + type rt-types:mpls-label; + description + "Receive label information in PW. "; + } + + leaf encapsulation-type { + type identityref { + base etht-types:encapsulation-type; + } + description + "The encapsulation type, raw or tag. "; + } + + leaf oper-status { + type identityref { + base te-types:tunnel-state-type; + } + config false; + description + "The operational state of the PW segment. "; + } + + container ingress-bandwidth-profile { + description + "Bandwidth Profile for ingress. "; + uses pw-segment-named-or-value-bandwidth-profile; + } + + list pw-paths { + key path-id; + description + "A list of pw paths. "; + + leaf path-id { + type uint8; + description + "The identifier of pw paths. "; + + } + + list tp-tunnels { + key name; + description + "Names of TP Tunnel underlay"; + leaf name { + type string; + description + "Names of TP Tunnel underlay"; + } + } + } + + } + + grouping pw-segment-named-or-value-bandwidth-profile { + description + "A grouping to configure a bandwdith profile either by + referencing a named bandwidth profile or by + configuring the values of the bandwidth profile attributes."; + choice style { + description + "Whether the bandwidth profile is named or defined by value"; + case named { + description + "Named bandwidth profile."; + leaf bandwidth-profile-name { + type leafref { + path "/ethtsvc:etht-svc/ethtsvc:globals/" + + "ethtsvc:named-bandwidth-profiles/" + + "ethtsvc:bandwidth-profile-name"; + } + description + "Name of the bandwidth profile."; + } + } + case value { + description + "Bandwidth profile configured by value."; + uses etht-types:pw-segement-bandwidth-profile-grouping; + } + } + } + + grouping tunnels-grouping { + description + "A group of tunnels. "; + leaf name { + type leafref { + path "/te:te/te:tunnels/te:tunnel/te:name"; + require-instance false; + } + description "Dependency tunnel name"; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description "LSP encoding type"; + reference "RFC3945"; + } + leaf switching-type { + type identityref { + base te-types:switching-capabilities; + } + description "LSP switching type"; + reference "RFC3945"; + } + } +} diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..3d152c058a8f623c46cccc89fc1fe8246015a04d --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-types.yang @@ -0,0 +1,460 @@ +module ietf-eth-tran-types { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-eth-tran-types"; + + prefix "etht-types"; + + organization + "Internet Engineering Task Force (IETF) CCAMP WG"; + contact + " + WG List: + + ID-draft editor: + Haomian Zheng (zhenghaomian@huawei.com); + Italo Busi (italo.busi@huawei.com); + Aihua Guo (aihuaguo.ietf@gmail.com); + Anton Snitser (antons@sedonasys.com); + Francesco Lazzeri (francesco.lazzeri@ericsson.com); + Yunbin Xu (xuyunbin@caict.ac.cn); + Yang Zhao (zhaoyangyjy@chinamobile.com); + Xufeng Liu (xufeng.liu.ietf@gmail.com); + Giuseppe Fioccola (giuseppe.fioccola@huawei.com); + Chaode Yu (yuchaode@huawei.com) + "; + + description + "This module defines the ETH types. + The model fully conforms to the Network Management + Datastore Architecture (NMDA). + + Copyright (c) 2019 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 XXXX; see + the RFC itself for full legal notices."; + + revision 2023-10-23 { + description + "version -05 as a WG draft"; + reference + "draft-ietf-ccamp-client-signal-yang"; + } + + /* + * Identities + */ + + identity eth-vlan-tag-type { + description + "ETH VLAN tag type."; + } + + identity c-vlan-tag-type { + base eth-vlan-tag-type; + description + "802.1Q Customer VLAN"; + } + + identity s-vlan-tag-type { + base eth-vlan-tag-type; + description + "802.1Q Service VLAN (QinQ)"; + } + + identity service-classification-type { + description + "Service classification."; + } + + identity port-classification { + base service-classification-type; + description + "Port classification."; + } + + identity vlan-classification { + base service-classification-type; + description + "VLAN classification."; + } + + identity eth-vlan-tag-classify { + description + "VLAN tag classification."; + } + + identity classify-c-vlan { + base eth-vlan-tag-classify; + description + "Classify 802.1Q Customer VLAN tag. + Only C-tag type is accepted"; + } + + identity classify-s-vlan { + base eth-vlan-tag-classify; + description + "Classify 802.1Q Service VLAN (QinQ) tag. + Only S-tag type is accepted"; + } + + identity classify-s-or-c-vlan { + base eth-vlan-tag-classify; + description + "Classify S-VLAN or C-VLAN tag-classify. + Either tag is accepted"; + } + + identity bandwidth-profile-type { + description + "Bandwidth Profile Types"; + } + + identity mef-10-bwp { + base bandwidth-profile-type; + description + "MEF 10 Bandwidth Profile"; + } + + identity rfc-2697-bwp { + base bandwidth-profile-type; + description + "RFC 2697 Bandwidth Profile"; + } + + identity rfc-2698-bwp { + base bandwidth-profile-type; + description + "RFC 2698 Bandwidth Profile"; + } + + identity rfc-4115-bwp { + base bandwidth-profile-type; + description + "RFC 4115 Bandwidth Profile"; + } + + identity service-type { + description + "Type of Ethernet service."; + } + + identity p2p-svc { + base service-type; + description + "Ethernet point-to-point service (EPL, EVPL)."; + } + + identity rmp-svc { + base service-type; + description + "Ethernet rooted-multitpoint service (E-TREE, EP-TREE)."; + } + + identity mp2mp-svc { + base service-type; + description + "Ethernet multipoint-to-multitpoint service (E-LAN, EP-LAN)."; + } + + identity lifecycle-status { + description + "Lifecycle Status."; + } + + identity installed { + base lifecycle-status; + description + "Installed."; + } + + identity planned { + base lifecycle-status; + description + "Planned."; + } + + identity pending-removal { + base lifecycle-status; + description + "Pending Removal."; + } + + /* + * Type Definitions + */ + + typedef eth-tag-type { + type identityref { + base eth-vlan-tag-type; + } + description + "Identifies a specific ETH VLAN tag type."; + } + + typedef eth-tag-classify { + type identityref { + base eth-vlan-tag-classify; + } + description + "Identifies a specific VLAN tag classification."; + } + + typedef vlanid { + type uint16 { + range "1..4094"; + } + description + "The 12-bit VLAN-ID used in the VLAN Tag header."; + } + + typedef vid-range-type { + type string { + pattern "([1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?" + + "(,[1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?)*)"; + } + description + "A list of VLAN Ids, or non overlapping VLAN ranges, in + ascending order, between 1 and 4094. + This type is used to match an ordered list of VLAN Ids, or + contiguous ranges of VLAN Ids. Valid VLAN Ids must be in the + range 1 to 4094, and included in the list in non overlapping + ascending order. + + For example: 1,10-100,50,500-1000"; + } + + typedef bandwidth-profile-type { + type identityref { + base bandwidth-profile-type; + } + description + "Identifies a specific Bandwidth Profile type."; + } + + typedef service-type { + type identityref { + base service-type; + } + description + "Identifies the type of Ethernet service."; + } + + typedef lifecycle-status { + type identityref { + base lifecycle-status; + } + description + "Identifies the lLifecycle Status ."; + } + + /* + * Grouping Definitions + */ + + grouping etht-bandwidth-profiles { + description + "Bandwidth profile configuration paramters."; + + leaf bandwidth-profile-type { + type etht-types:bandwidth-profile-type; + description + "The type of bandwidth profile."; + } + leaf CIR { + type uint64; + description + "Committed Information Rate in Kbps"; + } + leaf CBS { + type uint64; + description + "Committed Burst Size in in KBytes"; + } + leaf EIR { + type uint64; + /* Need to indicate that EIR is not supported by RFC 2697 + + must + '../bw-profile-type = "mef-10-bwp" or ' + + '../bw-profile-type = "rfc-2698-bwp" or ' + + '../bw-profile-type = "rfc-4115-bwp"' + + must + '../bw-profile-type != "rfc-2697-bwp"' + */ + description + "Excess Information Rate in Kbps + In case of RFC 2698, PIR = CIR + EIR"; + } + leaf EBS { + type uint64; + description + "Excess Burst Size in KBytes. + In case of RFC 2698, PBS = CBS + EBS"; + } + leaf color-aware { + type boolean; + description + "Indicates weather the color-mode is + color-aware or color-blind."; + } + leaf coupling-flag { + type boolean; + /* Need to indicate that Coupling Flag is defined only for MEF 10 + + must + '../bw-profile-type = "mef-10-bwp"' + */ + description + "Coupling Flag."; + } + } + + identity topology-role { + description + "The role of underlay topology: e.g., hub, spoke, + any-to-any."; + } + + identity resilience { + description + "Placeholder for resilience information in data plane, + for future study. "; + } + + identity access-role { + description + "Indicating whether the access is a working or protection access."; + } + + identity root-primary { + base access-role; + description + "Designates the primary root UNI of an E-Tree service, and may also + designates the UNI access role of E-LINE and E-LAN service."; + } + + identity root-backup { + base access-role; + description + "Designates the backup root UNI of an E-Tree service."; + } + + identity leaf-access { + base access-role; + description + "Designates the leaf UNI of an E-Tree service."; + } + + identity leaf-edge { + base access-role; + description ""; + } + + identity performance { + description + "Placeholder for performance information, for future study."; + } + + identity encapsulation-type { + description + "Indicating how the service is encapsulated (to PW), e.g, raw or tag. "; + } + grouping pw-segement-bandwidth-profile-grouping { + description + "bandwidth profile grouping for PW segment. "; + leaf bandwidth-profile-type { + type etht-types:bandwidth-profile-type; + description + "The type of bandwidth profile."; + } + leaf CIR { + type uint64; + description + "Committed Information Rate in Kbps"; + } + leaf CBS { + type uint64; + description + "Committed Burst Size in in KBytes"; + } + leaf EIR { + type uint64; + /* Need to indicate that EIR is not supported by RFC 2697 + + must + '../bw-profile-type = "mef-10-bwp" or ' + + '../bw-profile-type = "rfc-2698-bwp" or ' + + '../bw-profile-type = "rfc-4115-bwp"' + + must + '../bw-profile-type != "rfc-2697-bwp"' + */ + description + "Excess Information Rate in Kbps + In case of RFC 2698, PIR = CIR + EIR"; + } + leaf EBS { + type uint64; + description + "Excess Burst Size in KBytes. + In case of RFC 2698, PBS = CBS + EBS"; + } + } + grouping eth-bandwidth { + description + "Available bandwith for ethernet."; + leaf eth-bandwidth { + type uint64{ + range "0..10000000000"; + } + units "Kbps"; + description + "Available bandwith value expressed in kilobits per second"; + } + } + + grouping eth-label-restriction { + description + "Label Restriction for ethernet."; + leaf tag-type { + type etht-types:eth-tag-type; + description "VLAN tag type."; + } + leaf priority { + type uint8; + description "priority."; + } + } + grouping eth-label { + description + "Label for ethernet."; + leaf vlanid { + type etht-types:vlanid; + description + "VLAN tag id."; + } + } + + grouping eth-label-step { + description "Label step for Ethernet VLAN"; + leaf eth-step { + type uint16 { + range "1..4095"; + } + default 1; + description + "Label step which represent possible increments for + an Ethernet VLAN tag."; + reference + "IEEE 802.1ad: Provider Bridges."; + } + } +} diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-service.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-service.yang new file mode 100644 index 0000000000000000000000000000000000000000..f84cae94c73a214834745ba3c594a707d9de0332 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-service.yang @@ -0,0 +1,325 @@ +module ietf-trans-client-service { + /* TODO: FIXME */ + yang-version 1.1; + + namespace "urn:ietf:params:xml:ns:yang:ietf-trans-client-service"; + prefix "clntsvc"; + + import ietf-network { + prefix "nw"; + reference "RFC8345 - A YANG Data Model for Network Topologies"; + } + + import ietf-network-topology { + prefix "nt"; + reference "RFC8345 - A YANG Data Model for Network Topologies"; + } + + import ietf-te-types { + prefix "te-types"; + reference "RFC 8776 - Traffic Engineering Common YANG Types"; + } + + import ietf-layer1-types { + prefix "layer1-types"; + reference "RFC ZZZZ - A YANG Data Model for Layer 1 Types"; + } + + import ietf-yang-types { + prefix "yang"; + reference "RFC 6991 - Common YANG Data Types"; + } + + import ietf-trans-client-svc-types { + prefix "clntsvc-types"; + reference "RFC XXXX - A YANG Data Model for + Transport Network Client Signals"; + } + + organization + "Internet Engineering Task Force (IETF) CCAMP WG"; + contact + " + ID-draft editor: + Haomian Zheng (zhenghaomian@huawei.com); + Aihua Guo (aihuaguo.ietf@gmail.com); + Italo Busi (italo.busi@huawei.com); + Anton Snitser (antons@sedonasys.com); + Francesco Lazzeri (francesco.lazzeri@ericsson.com); + Yunbin Xu (xuyunbin@caict.ac.cn); + Yang Zhao (zhaoyangyjy@chinamobile.com); + Xufeng Liu (Xufeng_Liu@jabil.com); + Giuseppe Fioccola (giuseppe.fioccola@huawei.com); + Chaode Yu (yuchaode@huawei.com); + "; + + description + "This module defines a YANG data model for describing + transport network client services. The model fully conforms + to the Network Management Datastore Architecture (NMDA). + + 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 + (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-10-23 { + description + "version -04 as a WG document"; + reference + "draft-ietf-ccamp-client-signal-yang"; + } + + /* + * Groupings + */ + grouping client-svc-access-parameters { + description + "Transport network client signals access parameters"; + + leaf access-node-id { + type te-types:te-node-id; + description + "The identifier of the access node in the TE topology."; + } + + leaf access-node-uri { + type nw:node-id; + description + "The identifier of the access node in the network."; + } + + leaf access-ltp-id { + type te-types:te-tp-id; + description + "The TE link termination point identifier in TE topology, used + together with access-node-id to identify the access LTP."; + } + + leaf access-ltp-uri { + type nt:tp-id; + description + "The link termination point identifier in network topology, + used together with access-node-uri to identify the access LTP"; + } + + leaf client-signal { + type identityref { + base layer1-types:client-signal; + } + description + "Identify the client signal type associated with this port"; + } + + } + + grouping pm-state-grouping { + leaf latency { + description "latency value of the E2E client signal service"; + type uint32; + units microsecond; + } + } + + grouping error-info-grouping { + leaf error-code { + description "error code"; + type uint16; + } + + leaf error-description { + description "detail message of error"; + type string; + } + + leaf error-timestamp { + description "the date and time error is happened"; + type yang:date-and-time; + } + } + + grouping alarm-shreshold-grouping { + leaf latency-threshold { + description "a threshold for the E2E client signal service's + latency. Once the latency value exceed this threshold, an alarm + should be triggered."; + type uint32; + units microsecond; + } + } + + grouping client-svc-tunnel-parameters { + description + "Transport network client signals tunnel parameters"; + + leaf tunnel-name { + type string; + description + "TE tunnel instance name."; + } + } + + grouping client-svc-instance-config { + description + "Configuration parameters for client services."; + leaf client-svc-name { + type string; + description + "Identifier of the p2p transport network client signals."; + } + + leaf client-svc-title { + type string; + description + "Name of the p2p transport network client signals."; + } + + leaf user-label { + type string; + description + "Alias of the p2p transport network client signals."; + } + + leaf client-svc-descr { + type string; + description + "Description of the transport network client signals."; + } + + leaf client-svc-customer { + type string; + description + "Customer of the transport network client signals."; + } + + container resilience { + description "Place holder for resilience functionalities"; + } + + uses te-types:te-topology-identifier; + + leaf admin-status { + type identityref { + base te-types:tunnel-admin-state-type; + } + default te-types:tunnel-admin-state-up; + description "Client signals administrative state."; + } + + container src-access-ports { + description + "Source access port of a client signal."; + uses client-svc-access-parameters; + } + container dst-access-ports { + description + "Destination access port of a client signal."; + uses client-svc-access-parameters; + } + + container pm-state { + config false; + description "PM data of E2E client signal"; + uses pm-state-grouping; + } + + container error-info { + config false; + description "error messages of configuration"; + uses error-info-grouping; + } + + container alarm-shreshold { + description "threshold configuration for the E2E client signal"; + uses alarm-shreshold-grouping; + } + + leaf direction { + type identityref { + base clntsvc-types:direction; + } + description "Uni-dir or Bi-dir for the client signal."; + } + + list svc-tunnels { + key tunnel-name; + description + "List of the TE Tunnels supporting the client signal."; + uses client-svc-tunnel-parameters; + } + } + + grouping client-svc-instance-state { + description + "State parameters for client services."; + leaf operational-state { + type identityref { + base te-types:tunnel-state-type; + } + config false; + description "Client signal operational state."; + } + leaf provisioning-state { + type identityref { + base te-types:lsp-state-type; + } + config false; + description "Client signal provisioning state."; + } + leaf creation-time { + type yang:date-and-time; + config false; + description "The time of the client signal be created."; + } + leaf last-updated-time { + type yang:date-and-time; + config false; + description "The time of the client signal's latest update."; + } + leaf created-by { + type string; + config false; + description + "The client signal is created by whom, + can be a system or staff ID."; + } + leaf last-updated-by { + type string; + config false; + description + "The client signal is last updated by whom, + can be a system or staff ID."; + } + leaf owned-by { + type string; + config false; + description + "The client signal is owned by whom, + can be a system ID."; + } + } + + /* + * Data nodes + */ + + container client-svc { + description + "Transport client services."; + + list client-svc-instances { + key client-svc-name; + description + "The list of p2p transport client service instances"; + + uses client-svc-instance-config; + uses client-svc-instance-state; + } + } +} diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-svc-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-svc-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..925511735e724b5fd2c2c18624f3e0a8fd13702b --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-svc-types.yang @@ -0,0 +1,63 @@ +module ietf-trans-client-svc-types { + namespace "urn:ietf:params:xml:ns:yang:ietf-trans-client-svc-types"; + prefix "clntsvc-types"; + + organization + "Internet Engineering Task Force (IETF) CCAMP WG"; + contact + " + ID-draft editor: + Haomian Zheng (zhenghaomian@huawei.com); + Aihua Guo (aihuaguo.ietf@gmail.com); + Italo Busi (italo.busi@huawei.com); + Anton Snitser (antons@sedonasys.com); + Francesco Lazzeri (francesco.lazzeri@ericsson.com); + Yunbin Xu (xuyunbin@caict.ac.cn); + Yang Zhao (zhaoyangyjy@chinamobile.com); + Xufeng Liu (Xufeng_Liu@jabil.com); + Giuseppe Fioccola (giuseppe.fioccola@huawei.com); + Chaode Yu (yuchaode@huawei.com); + "; + + description + "This module defines a YANG data model for describing + transport network client types. The model fully conforms + to the Network Management Datastore Architecture (NMDA). + + Copyright (c) 2019 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 XXXX; see + the RFC itself for full legal notices."; + + revision 2023-10-23 { + description + "version -01 as a WG document"; + reference + "draft-ietf-ccamp-client-signal-yang"; + } + + identity direction { + description + "Direction information of Client Signal."; + } + + identity bidirectional { + base direction; + description + "Client Signal is bi-directional."; + } + + identity unidirectional { + base direction; + description + "Client Signal is uni-directional."; + } + +} diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-eth-client-te-topo-yang-09/ietf-eth-te-topology.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-eth-client-te-topo-yang-09/ietf-eth-te-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..a04eb213daf0b8a60d99caec8d84b5470264a9dd --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-eth-client-te-topo-yang-09/ietf-eth-te-topology.yang @@ -0,0 +1,2278 @@ +module ietf-eth-te-topology { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-eth-te-topology"; + prefix "etht"; + + 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"; + } + + import ietf-te-topology { + prefix "tet"; + reference + "RFC 8795: YANG Data Model for Traffic Engineering + (TE) Topologies"; + } + + import ietf-yang-types { + prefix "yang"; + reference + "RFC 6991: Common YANG Data Types"; + } + + import ietf-eth-tran-types { + prefix "etht-types"; + reference + "RFC YYYY: A YANG Data Model for Transport Network Client + Signals"; + } + // RFC Ed.: replace YYYY with actual RFC number, update date + // information and remove this note + + organization + "IETF CCAMP Working Group"; + contact + "WG Web: + WG List: + + Editor: Haomian Zheng + + + Editor: Italo Busi + + + Editor: Aihua Guo + + + Editor: Yunbin Xu + + + Editor: Yang Zhao + + + Editor: Xufeng Liu + "; + + description + "This module defines a YANG data model for describing + layer-2 Ethernet transport topologies. The model fully + conforms to the Network Management Datastore + Architecture (NMDA). + + Copyright (c) 2023 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. + + 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."; + + revision 2023-09-28 { + description + "Initial Revision"; + reference + "RFC XXXX: A YANG Data Model for Ethernet TE Topology"; + // RFC Ed.: replace XXXX with actual RFC number, update date + // information and remove this note + } + + /* + * Groupings + */ + + grouping label-range-info { + description + "Ethernet technology-specific label range related + information with a presence container indicating that the + label range is an Ethernet technology-specific label range. + + This grouping SHOULD be used together with the + eth-label and eth-label-step groupings to provide Ethernet + technology-specific label information to the models which + use the label-restriction-info grouping defined in the module + ietf-te-types."; + + container ethernet-label-range { + presence + "Indicates the label range is an Ethernet label range. + + This container must not be present if there are other + presence containers or attributes indicating another type + of label range."; + description + "Ethernet-specific label range related information."; + + uses etht-types:eth-label-restriction; + } + } + + grouping eth-tran-topology-type { + description + "Identifies the Ethernet Transport topology type"; + + container eth-tran-topology { + presence "indicates a topology type of + Ethernet Transport Network."; + description "Eth transport topology type"; + } + } + + grouping ltp-bandwidth-profiles { + description + "A grouping which represents the bandwidth profile(s) + for the ETH LTP."; + + choice direction { + description + "Whether the bandwidth profiles are symmetrical or + asymmetrical"; + case symmetrical { + description + "The same bandwidth profile is used to describe the ingress + and the egress bandwidth profile."; + + container ingress-egress-bandwidth-profile { + description + "The bandwith profile used in the ingress and egress + direction."; + uses etht-types:etht-bandwidth-profiles; + } + } + case asymmetrical { + description + "Different ingress and egress bandwidth profiles + can be specified."; + container ingress-bandwidth-profile { + description + "The bandwidth profile used in the ingress direction."; + uses etht-types:etht-bandwidth-profiles; + } + container egress-bandwidth-profile { + description + "The bandwidth profile used in the egress direction."; + uses etht-types:etht-bandwidth-profiles; + } + } + } + } + + grouping eth-ltp-attributes { + description + "Ethernet transport Link Termination Point (LTP) attributes"; + + leaf ltp-mac-address { + type yang:mac-address; + description + "The MAC address of the Ethernet LTP."; + } + leaf port-vlan-id { + type etht-types:vlanid; + description + "The Port VLAN ID of the Ethernet LTP."; + reference + "IEEE 802.1Q: Virtual Bridged Local Area Networks"; + } + leaf maximum-frame-size { + type uint16 { + range "64 .. 65535"; + } + description + "Maximum frame size"; + reference + "IEEE 802.1Q: Virtual Bridged Local Area Networks"; + } + uses ltp-bandwidth-profiles; + } + + grouping svc-vlan-classification { + description + "Grouping defining the capabilities for VLAN classification."; + + leaf-list supported-tag-types { + type etht-types:eth-tag-classify; + description + "List of VLAN tag types that can be used for the VLAN + classification. In case VLAN classification is not + supported, the list is empty."; + } + leaf vlan-bundling { + type boolean; + description + "In case VLAN classification is supported, indicates whether + VLAN bundling classification is also supported."; + reference + "MEF 10.3: Ethernet Services Attributes Phase 3"; + } + leaf vlan-range { + type etht-types:vid-range-type; + description + "In case VLAN classification is supported, indicates the + of available VLAN ID values."; + } + } + + grouping svc-vlan-push { + description + "Grouping defining the capabilities for VLAN push or swap + operations."; + + leaf-list supported-tag-types { + type etht-types:eth-tag-type; + description + "List of VLAN tag types that can be used to push or swap a + VLAN tag. In case VLAN push/swap is not supported, the list + is empty."; + reference + "IEEE 802.1Q: Virtual Bridged Local Area Networks"; + } + leaf vlan-range { + type etht-types:vid-range-type; + description + "In case VLAN push/swap operation is supported, the range + of available VLAN ID values."; + } + } + + grouping eth-svc-attributes { + description + "Ethernet Link Termination Point (LTP) service attributes."; + + container supported-classification { + description + "Service classification capability supported by the + Ethernet Link Termination Point (LTP)."; + + leaf port-classification { + type boolean; + description + "Indicates that the ETH LTP support port-based service + classification."; + } + container vlan-classification { + description + "Service classification capabilities based on the VLAN + tag(s) supported by the ETH LTP."; + + leaf vlan-tag-classification { + type boolean; + description + "Indicates that the ETH LTP supports VLAN service + classification."; + } + container outer-tag { + description + "Service classification capabilities based on the outer + VLAN tag, supported by the ETH LTP."; + uses svc-vlan-classification; + } + container second-tag { + description + "Service classification capabilities based on the second + VLAN tag, supported by the ETH LTP."; + leaf second-tag-classification { + type boolean; + must ". = 'false' or " + + "../../vlan-tag-classification = 'true'" { + description + "VLAN service classification based on the second + VLAN tag can be supported only when VLAN service + classification"; + } + description + "Indicates that the ETH LTP support VLAN service + classification based on the second VLAN tag."; + } + uses svc-vlan-classification; + } + } + } + + container supported-vlan-operations { + description + "Reports the VLAN operations supported by the ETH LTP."; + + leaf asymmetrical-operations { + type boolean; + description + "Indicates whether the ETH LTP supports also asymmetrical + VLAN operations.It is assumed that symmetrical VLAN + operations are alwyas supported."; + } + leaf transparent-vlan-operations { + type boolean; + description + "Indicates that the ETH LTP supports transparent + operations."; + } + container vlan-pop { + description + "Indicates VLAN pop or swap operations capabilities."; + + leaf vlan-pop-operations { + type boolean; + description + "Indicates that the ETH LTP supports VLAN pop or + swap operations."; + } + leaf max-pop-tags { + type uint8 { + range "1..2"; + } + description + "Indicates the maximum number of tags that can be + popped/swapped."; + } + } + container vlan-push { + description + "Indicates VLAN push or swap operations capabilities."; + + leaf vlan-push-operation { + type boolean; + description + "Indicates that the ETH LTP supports VLAN push or + swap operations."; + } + container outer-tag { + description + "Indicates the supported VLAN operation capabilities + on the outer VLAN tag."; + uses svc-vlan-push; + } + container second-tag { + description + "Indicates the supported VLAN operation capabilities + on the second VLAN tag."; + leaf push-second-tag { + type boolean; + description + "Indicates that the ETH LTP supports VLAN push or swap + operations for the second VLAN tag."; + } + uses svc-vlan-push; + } + } + } + } + + /* + * Data nodes + */ + + augment "/nw:networks/nw:network/nw:network-types/" + + "tet:te-topology" { + description + "Augment network types to include ETH transport newtork"; + + uses eth-tran-topology-type; + } + + augment "/nw:networks/nw:network/nw:node/tet:te" + + "/tet:te-node-attributes" { + when "../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description "Augment only for Ethernet transport network."; + } + description "Augment TE node attributes."; + container eth-node { + presence "The TE node is an Ethernet node."; + description + "Presence container used only to indicate that the TE node + is an Ethernet node."; + } + } + + augment "/nw:networks/nw:network/nt:link" { + when "../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description "Augment only for Ethernet transport network."; + } + description "Augment link configuration"; + + container eth-svc { + presence + "When present, indicates that the Link supports Ethernet + client signals."; + description + "Presence container used only to indicate that the link + supports Ethernet client signals."; + } + } + + augment "/nw:networks/nw:network/nw:node/nt:termination-point" { + when "../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description "Augment only for Ethernet transport network."; + } + description + "Augment ETH LTP attributes"; + + container eth-svc { + presence + "When present, indicates that the Link Termination Point + (LTP) supports Ethernet client signals."; + description + "ETH LTP Service attributes."; + + uses eth-svc-attributes; + } + container eth-link-tp { + description + "Attributes of the Ethernet Link Termination Point (LTP)."; + uses eth-ltp-attributes; + } + } + + /* + * Augment TE bandwidth + */ + + augment "/nw:networks/nw:network/nw:node/nt:termination-point/" + + "tet:te/" + + "tet:interface-switching-capability/tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum LSP TE bandwidth for the link termination + point (LTP)."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints of the TE node + connectivity matrices."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints of the + connectivity matrix entry."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints of the TE node + connectivity matrices information source."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints of the + connectivity matrix entry information source"; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:client-layer-adaptation/tet:switching-capability/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment client TE bandwidth of the tunnel termination point + (TTP)"; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/tet:path-constraints/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints for the TTP + Local Link Connectivities."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/tet:path-constraints/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints for the TTP + Local Link Connectivity entry."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:interface-switching-capability/tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum LSP TE bandwidth for the TE link."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum TE bandwidth for the TE link"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum reservable TE bandwidth for the TE link"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment unreserved TE bandwidth for the TE Link"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:interface-switching-capability/" + + "tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum LSP TE bandwidth for the TE link + information source"; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum TE bandwidth for the TE link + information source"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum reservable TE bandwidth for the TE link + information-source"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment unreserved TE bandwidth of the TE link + information source"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:interface-switching-capability/" + + "tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + description + "Augment maximum LSP TE bandwidth of the TE link + template"; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment maximum TE bandwidth the TE link template"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment maximum reservable TE bandwidth for the TE link + template."; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment unreserved TE bandwidth the TE link template"; + uses etht-types:eth-bandwidth; + } + + /* + * Augment TE label range information + */ + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TE node + connectivity matrices."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the source LTP + of the connectivity matrix entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the destination LTP + of the connectivity matrix entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TE node + connectivity matrices information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the source LTP + of the connectivity matrix entry information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the destination LTP + of the connectivity matrix entry information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TTP + Local Link Connectivities."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TTP + Local Link Connectivity entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TE link."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TE link + information source."; + uses label-range-info; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction" { + description + "Augment TE label range information for the TE link template."; + uses label-range-info; + } + + /* + * Augment TE label. + */ + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TE node + connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/" + + "tet:label-restriction/tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TE node + connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/" + + "tet:label-restriction/tet:label-step/" + + "tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TE node + connectivity matrices"; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:underlay/tet:primary-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path of the + TE node connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:underlay/tet:backup-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path of the + TE node connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TE node connectivity + matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TE node connectivity + matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TE node connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the source LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the source LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the source LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the destination LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the destination LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the destination LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:primary-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:backup-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:optimizations/" + + "tet:algorithm/tet:metric/tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:optimizations/" + + "tet:algorithm/tet:metric/tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TE node connectivity + matrices information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TE node connectivity + matrices information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TE node connectivity + matrices information source."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TE node connectivity matrices of the information + source entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TE node connectivity matrices of the information + source entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TE node connectivity matrices + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TE node connectivity matrices + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TE node connectivity matrices information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the source LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the source LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the source LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the destination LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the destination LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the destination LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the connectivity matrix entry + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the connectivity matrix entry + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TTP + Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology"{ + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TTP + Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology"{ + when "../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TTP + Local Link Connectivities."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TTP Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TTP Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TTP Local Link + Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TTP Local Link + Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TTP Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TTP + Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TTP + Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TTP + Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TTP Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TTP Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TTP Local Link + Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TTP Local Link + Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TTP Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TE link."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TE link."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TE link."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TE link."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TE link."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TE link + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TE link + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TE link + information source."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + description + "Augment TE label hop for the underlay primary path + of the TE link template."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + description + "Augment TE label hop for the underlay backup path + of the TE link template."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + description + "Augment TE label range start for the TE link template."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + description + "Augment TE label range end for the TE link template."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + description + "Augment TE label range step for the TE link template."; + case eth { + uses etht-types:eth-label-step; + } + } + +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-otn-topo-yang-20/ietf-otn-topology.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-otn-topo-yang-20/ietf-otn-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..15e7ac508d893a3dff2d64be67a89f8c8a2feae8 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-ccamp-otn-topo-yang-20/ietf-otn-topology.yang @@ -0,0 +1,2230 @@ +module ietf-otn-topology { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-otn-topology"; + prefix "otnt"; + + 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"; + } + + import ietf-te-topology { + prefix "tet"; + reference + "RFC 8795: YANG Data Model for Traffic Engineering + (TE) Topologies"; + } + + import ietf-layer1-types { + prefix "l1-types"; + reference + "RFC YYYY: A YANG Data Model for Layer 1 Types"; + } + // RFC Editor: replace YYYY with actual RFC number assigned to + // [I-D.ietf-ccamp-layer1-types] and remove this note + + organization + "IETF CCAMP Working Group"; + contact + "WG Web: + WG List: + + Editor: Haomian Zheng + + + Editor: Italo Busi + + + Editor: Xufeng Liu + + + Editor: Sergio Belotti + + + Editor: Oscar Gonzalez de Dios + "; + + description + "This module defines a protocol independent Layer 1/ODU topology + data model. The model fully conforms + to the Network Management Datastore Architecture (NMDA). + + 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. + + 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."; + + revision 2024-06-21 { + description + "Initial Revision"; + reference + "RFC XXXX: A YANG Data Model for Optical Transport Network + Topology"; + } + // RFC Editor: replace XXXX with actual RFC number, update date + // information and remove this note + + /* + * Groupings + */ + + grouping label-range-info { + description + "OTN technology-specific label range related information with + a presence container indicating that the label range is an + OTN technology-specific label range. + + This grouping SHOULD be used together with the + otn-label-start-end and otn-label-step groupings to provide + OTN technology-specific label information to the models which + use the label-restriction-info grouping defined in the module + ietf-te-types."; + uses l1-types:otn-label-range-info { + refine otn-label-range { + presence + "Indicates the label range is an OTN label range. + + This container MUST NOT be present if there are other + presence containers or attributes indicating another type + of label range."; + } + } + } + + /* + * Data nodes + */ + + augment "/nw:networks/nw:network/nw:network-types/" + + "tet:te-topology" { + container otn-topology { + presence "indicates a topology type of Optical Transport + Network (OTN)-electrical layer."; + description "OTN topology type"; + } + description "augment network types to include OTN."; + } + + augment "/nw:networks/nw:network/nw:node/tet:te" + + "/tet:te-node-attributes" { + when "../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description "Augment only for OTN."; + } + description "Augment TE node attributes."; + container otn-node { + presence "The TE node is an OTN node."; + description + "Introduce new TE node type for OTN node."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes" { + when "../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description "Augment only for OTN."; + } + description "Augment link configuration"; + + container otn-link { + description + "Attributes of the OTN Link."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs set up + on this OTN Link."; + } + leaf tsg { + type identityref { + base l1-types:tributary-slot-granularity; + } + description "Tributary slot granularity."; + reference + "ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + leaf distance { + type uint32; + description "distance in the unit of kilometers"; + } + } + container client-svc { + presence + "When present, indicates that the Link supports Constant + Bit Rate (CBR) client signals."; + description + "Attributes of the Link supporting CBR client signals."; + leaf-list supported-client-signal { + type identityref { + base l1-types:client-signal; + } + min-elements 1; + description + "List of client signal types supported by the Link."; + } + } + } + + augment "/nw:networks/nw:network/nw:node/nt:termination-point/" + + "tet:te" { + when "../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description "Augment only for OTN."; + } + description + "Augment link termination point (LTP) configuration."; + + container otn-link-tp { + description + "Attributes of the OTN Link Termination Point (LTP)."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs set up + on this OTN Link Termination Point (LTP)."; + } + } + container client-svc { + presence + "When present, indicates that the Link Termination Point + (LTP) supports Constant Bit Rate (CBR) client signals."; + description + "OTN LTP Service attributes."; + leaf-list supported-client-signal { + type identityref { + base l1-types:client-signal; + } + description + "List of client signal types supported by the LTP."; + } + } + } + + /* + * Augment TE bandwidth + */ + + augment "/nw:networks/nw:network/nw:node/nt:termination-point/" + + "tet:te/" + + "tet:interface-switching-capability/tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum LSP TE bandwidth for the link termination + point (LTP)."; + case otn { + uses l1-types:otn-max-path-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link Termination + Point (LTP) is used to compute the number of Tributary + Slots (TS) required by the ODUflex LSPs set up on this + OTN LTP."; + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints of the TE node + connectivity matrices."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay paths of these OTN + connectivity matrices."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints of the + connectivity matrix entry."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay path of this OTN + connectivity matrix entry."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints of the TE node + connectivity matrices information source."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay paths of these OTN + connectivity matrices."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints of the + connectivity matrix entry information source"; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay path of this OTN + connectivity matrix entry."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:client-layer-adaptation/tet:switching-capability/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment client TE bandwidth of the tunnel termination point + (TTP)"; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + terminated on this OTN Tunnel Termination Point + (TTP)."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/tet:path-constraints/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints for the TTP + Local Link Connectivities."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay paths of these OTN Local + Link Connectivities."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/tet:path-constraints/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints for the TTP + Local Link Connectivity entry."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay path of this OTN Local + Link Connectivity entry."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:interface-switching-capability/tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum LSP TE bandwidth for the TE link."; + case otn { + uses l1-types:otn-max-path-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum TE bandwidth for the TE link"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum reservable TE bandwidth for the TE link"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment unreserved TE bandwidth for the TE Link"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:interface-switching-capability/" + + "tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum LSP TE bandwidth for the TE link + information source"; + case otn { + uses l1-types:otn-max-path-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum TE bandwidth for the TE link + information source"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum reservable TE bandwidth for the TE link + information-source"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment unreserved TE bandwidth of the TE link + information source"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:interface-switching-capability/" + + "tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + description + "Augment maximum LSP TE bandwidth of the TE link + template"; + case otn { + uses l1-types:otn-max-path-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on the OTN Link that uses this + Link Template."; + } + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment maximum TE bandwidth the TE link template"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on the OTN Link that uses this + Link Template."; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment maximum reservable TE bandwidth for the TE link + template."; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on the OTN Link that uses this + Link Template."; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment unreserved TE bandwidth the TE link template"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on the OTN Link that uses this + Link Template."; + } + } + + /* + * Augment TE label range information + */ + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TE node + connectivity matrices."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the source LTP + of the connectivity matrix entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the destination LTP + of the connectivity matrix entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TE node + connectivity matrices information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the source LTP + of the connectivity matrix entry information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the destination LTP + of the connectivity matrix entry information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TTP + Local Link Connectivities."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TTP + Local Link Connectivity entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TE link."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TE link + information source."; + uses label-range-info; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction" { + description + "Augment TE label range information for the TE link template."; + uses label-range-info; + } + + /* + * Augment TE label + */ + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TE node + connectivity matrices"; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/" + + "tet:label-restriction/tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TE node + connectivity matrices"; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/" + + "tet:label-restriction/tet:label-step/" + + "tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TE node + connectivity matrices"; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:underlay/tet:primary-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path of the + TE node connectivity matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:underlay/tet:backup-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path of the + TE node connectivity matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TE node connectivity + matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TE node connectivity + matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TE node connectivity matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the source LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the source LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the source LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the destination LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the destination LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the destination LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:primary-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:backup-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:optimizations/" + + "tet:algorithm/tet:metric/tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:optimizations/" + + "tet:algorithm/tet:metric/tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TE node connectivity + matrices information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TE node connectivity + matrices information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TE node connectivity + matrices information source."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TE node connectivity matrices of the information + source entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TE node connectivity matrices of the information + source entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TE node connectivity matrices + information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TE node connectivity matrices + information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TE node connectivity matrices information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the source LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the source LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the source LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the destination LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the destination LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the destination LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the connectivity matrix entry + information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the connectivity matrix entry + information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TTP + Local Link Connectivities."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology"{ + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TTP + Local Link Connectivities."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology"{ + when "../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TTP + Local Link Connectivities."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TTP Local Link Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TTP Local Link Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TTP Local Link + Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TTP Local Link + Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TTP Local Link Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TTP + Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TTP + Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TTP + Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TTP Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TTP Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TTP Local Link + Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TTP Local Link + Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TTP Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TE link."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TE link."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TE link."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TE link."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TE link."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TE link + information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TE link + information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TE link + information source."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + description + "Augment TE label hop for the underlay primary path + of the TE link template."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + description + "Augment TE label hop for the underlay backup path + of the TE link template."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + description + "Augment TE label range start for the TE link template."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + description + "Augment TE label range end for the TE link template."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + description + "Augment TE label range step for the TE link template."; + case otn { + uses l1-types:otn-label-step; + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-packet-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-packet-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..834e78bcdd0109409aef6a4529341f99decd846e --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-packet-types.yang @@ -0,0 +1,835 @@ +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: + WG List: + + Editor: Tarek Saad + + + Editor: Rakesh Gandhi + + + Editor: Vishnu Pavan Beeram + + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + "; + description + "This YANG module contains a collection of generally useful YANG + data type definitions specific to Packet Traffic Engineering + (TE). + + The model 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) 2025 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 2025-01-24 { + 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"; + } + + 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"; + } + + /* + * 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 "kilobits per second"; + description + "Bandwidth values, expressed in kilobits per second."; + } + + typedef bandwidth-mbps { + type uint64; + units "megabits per second"; + description + "Bandwidth values, expressed in megabits per second."; + } + + typedef bandwidth-gbps { + type uint64; + units "gigabits per second"; + description + "Bandwidth values, expressed in gigabits per second."; + } + + /* + * Groupings + */ + + grouping performance-metrics-attributes-packet { + description + "Contains Performance Metrics (PM) information."; + uses te-types:performance-metrics-attributes { + augment "performance-metrics-one-way" { + description + "Performance Metrics (PM) one-way packet-specific + augmentation for a generic PM grouping."; + leaf one-way-min-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + description + "One-way minimum delay or latency."; + } + 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"; + } + units "microseconds"; + description + "One-way maximum delay or latency."; + } + 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"; + } + units "microseconds"; + description + "One-way delay variation."; + 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"; + } + units "%"; + description + "One-way packet loss as a percentage of the total traffic + sent over a configurable interval. + + The finest precision is 0.000003%."; + 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"; + } + } + augment "performance-metrics-two-way" { + description + "Performance Metrics (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"; + leaf two-way-min-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way minimum delay or latency."; + } + 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"; + } + units "microseconds"; + default "0"; + description + "Two-way maximum delay or latency."; + } + 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"; + } + units "microseconds"; + default "0"; + description + "Two-way delay variation."; + 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"; + } + units "%"; + 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."; + } + } + } + } + + grouping one-way-performance-metrics-packet { + description + "One-way packet Performance Metrics (PM) throttle grouping."; + leaf one-way-min-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "One-way minimum delay or latency."; + } + leaf one-way-max-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "One-way maximum delay or latency."; + } + leaf one-way-delay-variation { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "One-way delay variation."; + } + leaf one-way-packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + units "%"; + 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 Performance Metrics (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; + units "microseconds"; + description + "One-way minimum delay or latency."; + } + leaf one-way-max-delay { + type yang:gauge64; + units "microseconds"; + description + "One-way maximum delay or latency."; + reference + "RFC 7679: A One-Way Delay Metric for IP Performance + Metrics (IPPM)"; + } + leaf one-way-delay-variation { + type yang:gauge64; + units "microseconds"; + description + "One-way delay variation."; + 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 Performance Metrics (PM) throttle grouping."; + leaf two-way-min-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way minimum delay or latency."; + } + leaf two-way-max-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way maximum delay or latency."; + } + leaf two-way-delay-variation { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way delay variation."; + } + leaf two-way-packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + units "%"; + 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 Performance Metrics (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; + units "microseconds"; + description + "Two-way minimum delay or latency."; + reference + "RFC 2681: A Round-trip Delay Metric for IPPM"; + } + leaf two-way-max-delay { + type yang:gauge64; + units "microseconds"; + description + "Two-way maximum delay or latency."; + reference + "RFC 2681: A Round-trip Delay Metric for IPPM"; + } + leaf two-way-delay-variation { + type yang:gauge64; + units "microseconds"; + description + "Two-way delay variation."; + 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 Performance Metrics (PM) threshold grouping."; + uses te-types:performance-metrics-throttle-container { + augment "throttle/threshold-out" { + description + "Performance Metrics (PM) threshold-out packet + augmentation for a generic grouping."; + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + } + augment "throttle/threshold-in" { + description + "Performance Metrics (PM) threshold-in packet augmentation + for a generic grouping."; + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + } + augment "throttle/threshold-accelerated-advertisement" { + description + "Performance Metrics (PM) accelerated advertisement packet + augmentation for a generic grouping."; + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + } + } + } + + grouping bandwidth-profile-parameters { + description + "Common parameters to define bandwidth profiles in packet + networks."; + leaf cir { + type uint64; + units "bits per second"; + description + "Committed Information Rate (CIR)."; + } + leaf cbs { + type uint64; + units "bytes"; + description + "Committed Burst Size (CBS)."; + } + leaf eir { + type uint64; + units "bits per second"; + description + "Excess Information Rate (EIR)."; + } + leaf ebs { + type uint64; + units "bytes"; + description + "Excess Burst Size (EBS)."; + } + leaf pir { + type uint64; + units "bits per 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 per second"; + description + "Bandwidth value for Packet TE links."; + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..aef9434ed2eddc05b401519d557c137167d7e188 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-types.yang @@ -0,0 +1,4473 @@ +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: + WG List: + + Editor: Tarek Saad + + + Editor: Rakesh Gandhi + + + Editor: Vishnu Pavan Beeram + + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + "; + description + "This YANG module contains a collection of generally useful + YANG data type definitions specific to TE. + + The model 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) 2025 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 2025-01-24 { + 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: + - explicit-route-hop-with-srlg; + - 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"; + } + + /* + * 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 Shared Risk Link Group (SRLG)."; + } + + 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 + "Shared Risk Link Group (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, signalled and configured after + the failure detection. + + This restoration scheme is also known as + 'Full LSP Re-routing', with the alternate route being + computed after the failure occurs."; + 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 precomputed, presignalled and + preconfigured prior to the failure."; + } + + identity restoration-scheme-precomputed { + base restoration-scheme-type; + description + "Restoration LSP is precomputed, but not presignalled nor + preconfigured, prior to the failure. + + This restoration scheme is also known as + 'Full LSP Re-routing', with the alternate route being + pre-computed and stored for use when the failure occurs."; + 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, but not preconfigured, + prior to the failure. + + This restoration scheme is also known as + 'Pre-planned LSP Re-routing'."; + 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 Shared Risk Link Groups (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 + "RFC 8233: 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 Shared Risk Link Group (SRLG) path + computation."; + } + + identity srlg-ignore { + base path-computation-srlg-type; + description + "Ignores Shared Risk Link Groups (SRLGs) in the path + computation."; + } + + identity srlg-strict { + base path-computation-srlg-type; + description + "Includes a strict Shared Risk Link Group (SRLG) check in + the path computation."; + } + + identity srlg-preferred { + base path-computation-srlg-type; + description + "Includes a preferred Shared Risk Link Group (SRLG) check in + the path computation."; + } + + identity srlg-weighted { + base path-computation-srlg-type; + description + "Includes a weighted Shared Risk Link Group (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 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)"; + } + + /* + * 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 + "Shared Risk Link Group (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 may follow + the type 'bandwidth-ieee-float32' as defined in RFC 8294 + (e.g., 0x1p10), where the units are in bytes per second. + + 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 + "Traffic Engineering (TE) 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"; + } + + 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 + "Shared Risk Link Group (SRLG) 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 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"; + } + units "microseconds"; + description + "One-way delay or latency."; + } + leaf one-way-delay-normality { + type te-types:performance-metrics-normality; + description + "One-way delay normality."; + } + } + + grouping performance-metrics-two-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 two-way-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + description + "Two-way delay or latency."; + } + leaf two-way-delay-normality { + type te-types:performance-metrics-normality; + description + "Two-way delay normality."; + } + } + + grouping performance-metrics-one-way-bandwidth { + description + "Performance Metrics (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 Performance Metrics (PM) throttle grouping."; + leaf one-way-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "One-way delay or latency."; + } + 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 Performance Metrics (PM) throttle grouping."; + leaf two-way-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way delay or latency."; + } + } + + 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 Performance Metrics (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 Performance Metrics (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"; + } + units "microseconds"; + default "0"; + description + "Offset value to be added to the measured delay value."; + } + leaf measure-interval { + type uint32; + units "seconds"; + default "30"; + description + "Interval to measure the extended metric values."; + } + leaf advertisement-interval { + type uint32; + units "seconds"; + default "0"; + description + "Interval to advertise the extended metric values."; + } + leaf suppression-interval { + type uint32 { + range "1..max"; + } + units "seconds"; + default "120"; + description + "Interval to suppress advertisement of the extended metric + values."; + reference + "RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions, Section 6"; + } + container threshold-out { + 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."; + uses performance-metrics-thresholds; + } + container threshold-in { + 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."; + uses performance-metrics-thresholds; + } + 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 needs to be present."; + } + 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)"; + 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."; + } + } + } + case numbered-link-hop { + container numbered-link-hop { + 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)"; + 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."; + } + } + } + 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 need to be + present."; + } + 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)"; + 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."; + } + } + } + case as-number { + container as-number-hop { + description + "AS explicit route 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."; + } + } + } + case label { + description + "The label explicit route hop type."; + container label-hop { + description + "Label hop type."; + uses te-label; + } + } + } + } + + grouping explicit-route-hop-with-srlg { + description + "Augments the explicit route entry grouping with Shared Risk + Link Group (SRLG) hop type."; + uses explicit-route-hop { + augment "type" { + description + "Augmentation for a generic explicit route for Shared + Risk Link Group (SRLG) inclusion or exclusion."; + case srlg { + description + "An Shared Risk Link Group (SRLG) value to be + included or excluded."; + container srlg { + description + "Shared Risk Link Group (SRLG) container."; + leaf srlg { + type uint32; + description + "Shared Risk Link Group (SRLG) value."; + } + } + } + } + } + } + + 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 { + description + "Numbered node route hop."; + container numbered-node-hop { + must 'node-id-uri or node-id' { + description + "At least one node identifier need to 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"; + } + } + } + case numbered-link-hop { + description + "Numbered link route 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"; + } + } + } + case unnumbered-link-hop { + description + "Unnumbered link route 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 need to be + present."; + } + description + "Unnumbered link Record Route hop."; + reference + "RFC 3477: Signalling Unnumbered Links in Resource + ReSerVation Protocol - Traffic Engineering + (RSVP-TE)"; + 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"; + } + } + } + case label { + description + "The label Record Route entry types."; + 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"; + } + } + } + } + } + + 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 MUST 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-with-srlg; + } + } + } + + 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-with-srlg; + } + } + + 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 + specified '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"; + description + "List of named affinities."; + leaf name { + type string; + description + "Identifies a named affinity entry."; + } + } + } + } + } + + grouping generic-path-srlgs { + description + "Path Shared Risk Link Group (SRLG) grouping."; + container path-srlgs-lists { + description + "Path Shared Risk Link Group (SRLG) properties container."; + list path-srlgs-list { + key "usage"; + description + "List of Shared Risk Link Group (SRLG) values to be + included or excluded."; + leaf usage { + type identityref { + base route-usage-type; + } + description + "Identifies an entry in a list of Shared Risk Link Groups + (SRLGs) to either include or exclude."; + } + leaf-list values { + type srlg; + description + "List of Shared Risk Link Group (SRLG) values."; + } + } + } + container path-srlgs-names { + description + "Container for the list of named Shared Risk Link Groups + (SRLGs)."; + list path-srlgs-name { + key "usage"; + description + "List of named Shared Risk Link Groups (SRLGs) to be + included or excluded."; + leaf usage { + type identityref { + base route-usage-type; + } + description + "Identifies an entry in a list of named Shared Risk Link + Groups (SRLGs) to either include or exclude."; + } + leaf-list names { + type string; + description + "List of named Shared Risk Link Groups (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."; + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te-device.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te-device.yang new file mode 100644 index 0000000000000000000000000000000000000000..f788fa2ea4ab9bb983b5771215f21df00b8ff5df --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te-device.yang @@ -0,0 +1,595 @@ +module ietf-te-device { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-te-device"; + + /* Replace with IANA when assigned */ + + prefix te-dev; + + /* Import TE module */ + + import ietf-te { + prefix te; + reference + "RFCXXXX: A YANG Data Model for Traffic Engineering + Tunnels and Interfaces"; + } + + /* Import TE types */ + + import ietf-te-types { + prefix te-types; + reference + "draft-ietf-teas-rfc8776-update: Common YANG Data Types + for Traffic Engineering."; + } + import ietf-interfaces { + prefix if; + reference + "RFC8343: A YANG Data Model for Interface Management"; + } + import ietf-routing-types { + prefix rt-types; + reference + "RFC8294: Common YANG Data Types for the Routing Area"; + } + + organization + "IETF Traffic Engineering Architecture and Signaling (TEAS) + Working Group"; + contact + "WG Web: + WG List: + + Editor: Tarek Saad + + + Editor: Rakesh Gandhi + + + Editor: Vishnu Pavan Beeram + + + Editor: Himanshu Shah + + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + + + Editor: Oscar Gonzalez de Dios + "; + + description + "This module defines a data model for TE device configurations, + state, and RPCs. The model fully conforms to the + Network Management Datastore Architecture (NMDA). + + Copyright (c) 2023 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."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + + revision 2024-02-02 { + description + "Initial revision for the TE device YANG module."; + reference + "RFCXXXX: A YANG Data Model for Traffic Engineering Tunnels + and Interfaces"; + } + + grouping lsp-device-timers { + description + "Device TE LSP timers configs."; + leaf lsp-install-interval { + type uint32; + units "seconds"; + description + "TE LSP installation delay time."; + } + leaf lsp-cleanup-interval { + type uint32; + units "seconds"; + description + "TE LSP cleanup delay time."; + } + leaf lsp-invalidation-interval { + type uint32; + units "seconds"; + description + "TE LSP path invalidation before taking action delay time."; + } + } + + grouping te-igp-flooding-bandwidth-config { + description + "Configurable items for igp flooding bandwidth + threshold configuration."; + leaf threshold-type { + type enumeration { + enum delta { + description + "'delta' indicates that the local + system should flood IGP updates when a + change in reserved bandwidth >= the specified + delta occurs on the interface."; + } + enum threshold-crossed { + description + "THRESHOLD-CROSSED indicates that + the local system should trigger an update (and + hence flood) the reserved bandwidth when the + reserved bandwidth changes such that it crosses, + or becomes equal to one of the threshold values."; + } + } + description + "The type of threshold that should be used to specify the + values at which bandwidth is flooded. 'delta' indicates that + the local system should flood IGP updates when a change in + reserved bandwidth >= the specified delta occurs on the + interface. Where 'threshold-crossed' is specified, the local + system should trigger an update (and hence flood) the + reserved bandwidth when the reserved bandwidth changes such + that it crosses, or becomes equal to one of the threshold + values."; + } + leaf delta-percentage { + when "../threshold-type = 'delta'" { + description + "The percentage delta can only be specified when the + threshold type is specified to be a percentage delta of + the reserved bandwidth."; + } + type rt-types:percentage; + description + "The percentage of the maximum-reservable-bandwidth + considered as the delta that results in an IGP update + being flooded."; + } + leaf threshold-specification { + when "../threshold-type = 'threshold-crossed'" { + description + "The selection of whether mirrored or separate threshold + values are to be used requires user specified thresholds + to be set."; + } + type enumeration { + enum mirrored-up-down { + description + "mirrored-up-down indicates that a single set of + threshold values should be used for both increasing + and decreasing bandwidth when determining whether + to trigger updated bandwidth values to be flooded + in the IGP TE extensions."; + } + enum separate-up-down { + description + "separate-up-down indicates that a separate + threshold values should be used for the increasing + and decreasing bandwidth when determining whether + to trigger updated bandwidth values to be flooded + in the IGP TE extensions."; + } + } + description + "This value specifies whether a single set of threshold + values should be used for both increasing and decreasing + bandwidth when determining whether to trigger updated + bandwidth values to be flooded in the IGP TE extensions. + 'mirrored-up-down' indicates that a single value (or set of + values) should be used for both increasing and decreasing + values, where 'separate-up-down' specifies that the + increasing and decreasing values will be separately + specified."; + } + leaf-list up-thresholds { + when "../threshold-type = 'threshold-crossed'" + + "and ../threshold-specification = 'separate-up-down'" { + description + "A list of up-thresholds can only be specified when the + bandwidth update is triggered based on crossing a + threshold and separate up and down thresholds are + required."; + } + type rt-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth) at which bandwidth updates are to be + triggered when the bandwidth is increasing."; + } + leaf-list down-thresholds { + when "../threshold-type = 'threshold-crossed'" + + "and ../threshold-specification = 'separate-up-down'" { + description + "A list of down-thresholds can only be specified when the + bandwidth update is triggered based on crossing a + threshold and separate up and down thresholds are + required."; + } + type rt-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth) at which bandwidth updates are to be + triggered when the bandwidth is decreasing."; + } + leaf-list up-down-thresholds { + when "../threshold-type = 'threshold-crossed'" + + "and ../threshold-specification = 'mirrored-up-down'" { + description + "A list of thresholds corresponding to both increasing + and decreasing bandwidths can be specified only when an + update is triggered based on crossing a threshold, and + the same up and down thresholds are required."; + } + type rt-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth of the interface) at which bandwidth + updates are flooded - used both when the bandwidth is + increasing and decreasing."; + } + } + + /** + * TE device augmentations + */ + augment "/te:te" { + description + "TE global container."; + /* TE Interface Configuration Data */ + container interfaces { + description + "Configuration data model for TE interfaces."; + uses te-igp-flooding-bandwidth-config; + list interface { + key "name"; + description + "The list of interfaces enabled for TE."; + leaf name { + type if:interface-ref; + description + "The reference to interface enabled for TE."; + } + /* TE interface parameters */ + leaf te-metric { + type te-types:te-metric; + description + "TE interface metric."; + } + choice admin-group-type { + description + "TE interface administrative groups + representation type."; + case value-admin-groups { + choice value-admin-group-type { + description + "choice of admin-groups."; + case admin-groups { + description + "Administrative group/Resource + class/Color."; + leaf admin-group { + type te-types:admin-group; + description + "TE interface administrative group."; + } + } + case extended-admin-groups { + if-feature "te-types:extended-admin-groups"; + description + "Extended administrative group/Resource + class/Color."; + leaf extended-admin-group { + type te-types:extended-admin-group; + description + "TE interface extended administrative group."; + } + } + } + } + case named-admin-groups { + list named-admin-groups { + if-feature "te-types:extended-admin-groups"; + if-feature "te-types:named-extended-admin-groups"; + key "named-admin-group"; + description + "A list of named admin-group entries."; + leaf named-admin-group { + type leafref { + path "../../../../te:globals/" + + "te:named-admin-groups/te:named-admin-group/" + + "te:name"; + } + description + "A named admin-group entry."; + } + } + } + } + choice srlg-type { + description + "Choice of SRLG configuration."; + case value-srlgs { + list values { + key "value"; + description + "List of SRLG values that + this link is part of."; + leaf value { + type uint32 { + range "0..4294967295"; + } + description + "Value of the SRLG"; + } + } + } + case named-srlgs { + list named-srlgs { + if-feature "te-types:named-srlg-groups"; + key "named-srlg"; + description + "A list of named SRLG entries."; + leaf named-srlg { + type leafref { + path "../../../../te:globals/" + + "te:named-srlgs/te:named-srlg/te:name"; + } + description + "A named SRLG entry."; + } + } + } + } + uses te-igp-flooding-bandwidth-config; + list switching-capabilities { + key "switching-capability"; + description + "List of interface capabilities for this interface."; + leaf switching-capability { + type identityref { + base te-types:switching-capabilities; + } + description + "Switching Capability for this interface."; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "Encoding supported by this interface."; + } + } + container te-advertisements-state { + config false; + description + "TE interface advertisements state container."; + leaf flood-interval { + type uint32; + description + "The periodic flooding interval."; + } + leaf last-flooded-time { + type uint32; + units "seconds"; + description + "Time elapsed since last flooding in seconds."; + } + leaf next-flooded-time { + type uint32; + units "seconds"; + description + "Time remained for next flooding in seconds."; + } + leaf last-flooded-trigger { + type enumeration { + enum link-up { + description + "Link-up flooding trigger."; + } + enum link-down { + description + "Link-down flooding trigger."; + } + enum threshold-up { + description + "Bandwidth reservation up threshold."; + } + enum threshold-down { + description + "Bandwidth reservation down threshold."; + } + enum bandwidth-change { + description + "Bandwidth capacity change."; + } + enum user-initiated { + description + "Initiated by user."; + } + enum srlg-change { + description + "SRLG property change."; + } + enum periodic-timer { + description + "Periodic timer expired."; + } + } + default "periodic-timer"; + description + "Trigger for the last flood."; + } + list advertised-level-areas { + key "level-area"; + description + "List of level-areas that the TE interface is + advertised in."; + leaf level-area { + type uint32; + description + "The IGP area or level where the TE interface link + state is advertised in."; + } + } + } + } + } + } + + /* TE globals device augmentation */ + + augment "/te:te/te:globals" { + description + "Global TE device specific configuration parameters."; + uses lsp-device-timers; + } + + /* TE tunnels device configuration augmentation */ + + augment "/te:te/te:tunnels/te:tunnel" { + description + "Tunnel device dependent augmentation."; + leaf path-invalidation-action { + type identityref { + base te-types:path-invalidation-action-type; + } + description + "Tunnel path invalidation action."; + } + uses lsp-device-timers; + } + + /* TE LSPs device state augmentation */ + + augment "/te:te/te:lsps/te:lsp" { + description + "TE LSP device dependent augmentation."; + container lsp-timers { + when "../te:origin-type = 'ingress'" { + description + "Applicable to ingress LSPs only."; + } + description + "Ingress LSP timers."; + leaf uptime { + type uint32; + units "seconds"; + description + "The LSP uptime."; + } + leaf time-to-install { + type uint32; + units "seconds"; + description + "The time remaining for a new LSP to be instantiated + in forwarding to carry traffic."; + } + leaf time-to-destroy { + type uint32; + units "seconds"; + description + "The time remaining for a existing LSP to be deleted + from forwarding."; + } + } + container downstream-info { + when "../te:origin-type != 'egress'" { + description + "Downstream information of the LSP."; + } + description + "downstream information."; + leaf nhop { + type te-types:te-tp-id; + description + "downstream next-hop address."; + } + leaf outgoing-interface { + type if:interface-ref; + description + "downstream interface."; + } + container neighbor { + uses te-types:te-generic-node-id; + description + "downstream neighbor address."; + } + leaf label { + type rt-types:generalized-label; + description + "downstream label."; + } + } + container upstream-info { + when "../te:origin-type != 'ingress'" { + description + "Upstream information of the LSP."; + } + description + "upstream information."; + leaf phop { + type te-types:te-tp-id; + description + "upstream next-hop or previous-hop address."; + } + container neighbor { + uses te-types:te-generic-node-id; + description + "upstream neighbor address."; + } + leaf label { + type rt-types:generalized-label; + description + "upstream label."; + } + } + } + + /* TE interfaces RPCs/execution Data */ + + rpc link-state-update { + description + "Triggers a link state update for the specific interface."; + input { + choice filter-type { + mandatory true; + description + "Filter choice."; + case match-all { + leaf all { + type empty; + mandatory true; + description + "Match all TE interfaces."; + } + } + case match-one-interface { + leaf interface { + type if:interface-ref; + description + "Match a specific TE interface."; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te.yang new file mode 100644 index 0000000000000000000000000000000000000000..48b160305e76f7b192257f9996de4479a221e367 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te.yang @@ -0,0 +1,1516 @@ +module ietf-te { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-te"; + + /* Replace with IANA when assigned */ + + prefix te; + + /* Import TE generic types */ + import ietf-te-types { + prefix te-types; + reference + "draft-ietf-teas-rfc8776-update: Common YANG Data Types + for Traffic Engineering."; + } + import ietf-yang-types { + prefix yang; + reference + "RFC6991: Common YANG Data Types."; + } + + 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: + WG List: + + Editor: Tarek Saad + + + Editor: Rakesh Gandhi + + + Editor: Vishnu Pavan Beeram + + + Editor: Himanshu Shah + + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + + + Editor: Oscar Gonzalez de Dios + "; + + description + "YANG data module for TE configuration, state, and RPCs. + The model fully conforms to the Network Management + Datastore Architecture (NMDA). + + Copyright (c) 2023 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."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + + revision 2024-02-02 { + description + "Initial revision for the TE generic YANG module."; + reference + "RFCXXXX: A YANG Data Model for Traffic Engineering Tunnels + and Interfaces."; + } + + typedef tunnel-ref { + type leafref { + path "/te:te/te:tunnels/te:tunnel/te:name"; + require-instance false; + } + description + "This type is used by data models that need to reference + configured TE tunnel."; + } + + /** + * TE tunnel generic groupings + */ + + grouping path-common-properties { + description + "Common path attributes."; + leaf name { + type string; + description + "TE path name."; + } + leaf path-computation-method { + type identityref { + base te-types:path-computation-method; + } + default "te-types:path-locally-computed"; + description + "The method used for computing the path, either + locally computed, queried from a server or not + computed at all (explicitly configured)."; + } + container path-computation-server { + when "derived-from-or-self(../path-computation-method, " + + "'te-types:path-externally-queried')" { + description + "The path-computation server when the path is + externally queried."; + } + uses te-types:te-generic-node-id; + description + "Address of the external path computation + server."; + } + leaf compute-only { + type empty; + description + "When present, the path is computed and updated whenever + the topology is updated. No resources are committed + or reserved in the network."; + } + leaf use-path-computation { + when "derived-from-or-self(../path-computation-method, " + + "'te-types:path-locally-computed')"; + type boolean; + default "true"; + description + "When 'true' indicates the path is dynamically computed + and/or validated against the Traffic-Engineering Database + (TED), and when 'false' indicates no path expansion or + validation against the TED is required."; + } + leaf lockdown { + type empty; + description + "When present, indicates no reoptimization to be attempted + for this path."; + } + leaf path-scope { + type identityref { + base te-types:path-scope-type; + } + default "te-types:path-scope-end-to-end"; + config false; + description + "Indicates whether the path is a segment or portion of + of the full path., or is the an end-to-end path for + the TE Tunnel."; + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping path-compute-info { + description + "Attributes used for path computation request."; + uses tunnel-associations-properties; + uses te-types:generic-path-optimization; + leaf named-path-constraint { + if-feature "te-types:named-path-constraints"; + type leafref { + path "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:name"; + } + description + "Reference to a globally defined named path constraint set."; + } + uses path-constraints-common; + } + + /* This grouping is re-used in path-computation rpc */ + grouping path-forward-properties { + description + "The path preference."; + leaf preference { + type uint8 { + range "1..255"; + } + default "1"; + description + "Specifies a preference for this path. The lower the number + higher the preference."; + } + leaf co-routed { + when "/te:te/te:tunnels/te:tunnel/te:bidirectional = 'true'" { + description + "Applicable to bidirectional tunnels only."; + } + type boolean; + default "false"; + description + "Indicates whether the reverse path must to be co-routed + with the primary."; + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping k-requested-paths { + description + "The k-shortest paths requests."; + leaf k-requested-paths { + type uint8; + default "1"; + description + "The number of k-shortest-paths requested from the path + computation server and returned sorted by its optimization + objective."; + } + } + + grouping path-state { + description + "TE per path state parameters."; + uses path-computation-response; + container lsp-provisioning-error-infos { + config false; + description + "LSP provisioning error information."; + list lsp-provisioning-error-info { + description + "List of LSP provisioning error info entries."; + leaf error-reason { + type identityref { + base te-types:lsp-provisioning-error-reason; + } + description + "LSP provision error type."; + } + leaf error-description { + type string; + description + "The textual representation of the error occurred during + path computation."; + } + leaf error-timestamp { + type yang:date-and-time; + description + "Timestamp of when the reported error occurred."; + } + leaf error-node-id { + type te-types:te-node-id; + description + "Node identifier of node where error occurred."; + } + leaf error-link-id { + type te-types:te-tp-id; + description + "Link ID where the error occurred."; + } + leaf lsp-id { + type uint16; + description + "The LSP-ID for which path computation was performed."; + } + } + } + container lsps { + config false; + description + "The TE LSPs container."; + list lsp { + key "node lsp-id"; + description + "List of LSPs associated with the tunnel."; + leaf tunnel-name { + type leafref { + path "/te:te/te:lsps/te:lsp/te:tunnel-name"; + } + description "TE tunnel name."; + } + leaf node { + type leafref { + path "/te:te/te:lsps/te:lsp[tunnel-name=" + + "current()/../te:tunnel-name][lsp-id=" + + "current()/../te:lsp-id]/te:node"; + } + description "The node where the LSP state resides on."; + } + leaf lsp-id { + type leafref { + path "/te:te/te:lsps/te:lsp[tunnel-name=" + + "current()/../tunnel-name]/te:lsp-id"; + } + description "The TE LSP identifier."; + } + } + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping path-computation-response { + description + "Attributes reported by path computation response."; + container computed-paths-properties { + config false; + description + "Computed path properties container."; + list computed-path-properties { + key "k-index"; + description + "List of computed paths."; + leaf k-index { + type uint8; + description + "The k-th path returned from the computation server. + A lower k value path is more optimal than higher k + value path(s)"; + } + uses te-types:generic-path-properties { + augment "path-properties" { + description + "additional path properties returned by path + computation."; + uses te-types:te-bandwidth; + leaf disjointness-type { + type te-types:te-path-disjointness; + config false; + description + "The type of resource disjointness. + When reported for a primary path, it represents the + minimum level of disjointness of all the secondary + paths. When reported for a secondary path, it + represents the disjointness of the secondary path."; + } + } + } + } + } + container computed-path-error-infos { + config false; + description + "Path computation information container."; + list computed-path-error-info { + description + "List of path computation info entries."; + leaf error-description { + type string; + description + "Textual representation of the error that occurred + during path computation."; + } + leaf error-timestamp { + type yang:date-and-time; + description + "Timestamp of last path computation attempt."; + } + leaf error-reason { + type identityref { + base te-types:path-computation-error-reason; + } + description + "Reason for the path computation error."; + } + } + } + } + + grouping protection-restoration-properties { + description + "Protection and restoration parameters."; + container protection { + description + "Protection parameters."; + leaf protection-type { + type identityref { + base te-types:lsp-protection-type; + } + default "te-types:lsp-protection-unprotected"; + description + "LSP protection type."; + } + leaf protection-reversion-disable { + type boolean; + default "false"; + description + "Disable protection reversion to working path."; + } + leaf hold-off-time { + type uint32; + units "milli-seconds"; + description + "The time between the declaration of an SF or SD condition + and the initialization of the protection switching + algorithm."; + reference + "RFC4427"; + } + leaf wait-to-revert { + type uint16; + units "seconds"; + description + "Time to wait before attempting LSP reversion."; + reference + "RFC4427"; + } + leaf aps-signal-id { + type uint8 { + range "1..255"; + } + default "1"; + description + "The APS signal number used to reference the traffic of + this tunnel. The default value for normal traffic is 1. + The default value for extra-traffic is 255. If not + specified, non-default values can be assigned by the + server, if and only if, the server controls both + endpoints."; + reference + "ITU_G.808.1"; + } + } + container restoration { + description + "Restoration parameters."; + leaf restoration-type { + type identityref { + base te-types:lsp-restoration-type; + } + description + "LSP restoration type."; + } + leaf restoration-scheme { + type identityref { + base te-types:restoration-scheme-type; + } + description + "LSP restoration scheme."; + } + leaf restoration-reversion-disable { + type boolean; + default "false"; + description + "Disable restoration reversion to working path."; + } + leaf hold-off-time { + type uint32; + units "milli-seconds"; + description + "The time between the declaration of an SF or SD condition + and the initialization of the protection switching + algorithm."; + reference + "RFC4427"; + } + leaf wait-to-restore { + type uint16; + units "seconds"; + description + "Time to wait before attempting LSP restoration."; + reference + "RFC4427"; + } + leaf wait-to-revert { + type uint16; + units "seconds"; + description + "Time to wait before attempting LSP reversion."; + reference + "RFC4427"; + } + } + } + + grouping tunnel-associations-properties { + description + "TE tunnel association grouping."; + container association-objects { + description + "TE tunnel associations."; + list association-object { + key "association-key"; + unique "type id source/id source/type"; + description + "List of association base objects."; + reference + "RFC4872"; + leaf association-key { + type string; + description + "Association key used to identify a specific + association in the list"; + } + leaf type { + type identityref { + base te-types:association-type; + } + description + "Association type."; + reference + "RFC4872"; + } + leaf id { + type uint16; + description + "Association identifier."; + reference + "RFC4872"; + } + container source { + uses te-types:te-generic-node-id; + description + "Association source."; + reference + "RFC4872"; + } + } + list association-object-extended { + key "association-key"; + unique + "type id source/id source/type global-source extended-id"; + description + "List of extended association objects."; + reference + "RFC6780"; + leaf association-key { + type string; + description + "Association key used to identify a specific + association in the list"; + } + leaf type { + type identityref { + base te-types:association-type; + } + description + "Association type."; + reference + "RFC4872, RFC6780"; + } + leaf id { + type uint16; + description + "Association identifier."; + reference + "RFC4872, RFC6780"; + } + container source { + uses te-types:te-generic-node-id; + description + "Association source."; + reference + "RFC4872, RFC6780"; + } + leaf global-source { + type uint32; + description + "Association global source."; + reference + "RFC6780"; + } + leaf extended-id { + type yang:hex-string; + description + "Association extended identifier."; + reference + "RFC6780"; + } + } + } + } + + grouping tunnel-end-point { + description + "Common grouping used to specify the tunnel source and + destination end-points."; + leaf node-id { + type nw:node-id; + description + "The TE tunnel end-point node identifier"; + } + leaf te-node-id { + type te-types:te-node-id; + description + "The TE tunnel end-point TE node identifier"; + } + leaf tunnel-tp-id { + when "../node-id or ../te-node-id" { + description + "The TE tunnel termination point identifier is local to + a node"; + } + type binary; + description + "The TE tunnel end-point TE tunnel termination point + identifier"; + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping tunnel-common-attributes { + description + "Common grouping to define the TE tunnel parameters"; + container source { + description + "TE tunnel source end-point."; + uses tunnel-end-point; + } + container destination { + description + "TE tunnel destination end-point."; + uses tunnel-end-point; + } + leaf bidirectional { + type boolean; + default "false"; + description + "Indicates a bidirectional tunnel"; + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping tunnel-hierarchy-properties { + description + "A grouping for TE tunnel hierarchy information."; + container hierarchy { + description + "Container for TE hierarchy related information."; + container dependency-tunnels { + description + "List of tunnels that this tunnel can be potentially + dependent on."; + list dependency-tunnel { + key "name"; + description + "A tunnel entry that this tunnel can potentially depend + on."; + leaf name { + type tunnel-ref; + description + "Dependency tunnel name. The tunnel may not have been + instantiated yet."; + } + uses te-types:encoding-and-switching-type; + } + } + container hierarchical-link { + description + "Identifies a hierarchical link (in client layer) + that this tunnel is associated with. By default, the + topology of the hierarchical link is the same topology of + the tunnel;"; + reference + "RFC4206"; + leaf enable { + type boolean; + default "false"; + description + "Enables the hierarchical link properties supported by + this tunnel"; + } + leaf local-node-id { + type nw:node-id; + description + "The local node identifier."; + } + leaf local-te-node-id { + type te-types:te-node-id; + description + "The local TE node identifier."; + } + leaf local-link-tp-id { + type nt:tp-id; + description + "The local link termination point identifier."; + reference + "RFC8345"; + } + leaf local-te-link-tp-id { + type te-types:te-tp-id; + description + "The local TE link termination point identifier."; + } + leaf remote-node-id { + type nw:node-id; + description + "The remote node identifier."; + } + leaf remote-link-tp-id { + type nt:tp-id; + description + "The remote link termination point identifier."; + reference + "RFC8345"; + } + leaf remote-te-link-tp-id { + type te-types:te-tp-id; + description + "The remote TE link termination point identifier."; + } + leaf remote-te-node-id { + type te-types:te-node-id; + description + "Remote TE node identifier."; + } + leaf link-id { + type nt:link-id; + config false; + description + "A network topology assigned identifier to the link"; + reference + "RFC8345"; + } + leaf network-id { + type nw:network-id; + description + "The network topology identifier where the hierarchical + link supported by this TE tunnel is instantiated."; + } + uses te-types:te-topology-identifier { + description + "The TE topology identifier where the hierarchical link + supported by this TE tunnel is instantiated."; + } + } + } + } + + grouping path-constraints-common { + description + "Global named path constraints configuration + grouping."; + uses te-types:common-path-constraints-attributes; + uses te-types:generic-path-disjointness; + uses te-types:path-constraints-route-objects; + container path-in-segment { + presence "The end-to-end tunnel starts in a previous domain; + this tunnel is a segment in the current domain."; + description + "If an end-to-end tunnel crosses multiple domains using + the same technology, some additional constraints have to be + taken in consideration in each domain. + This TE tunnel segment is stitched to the upstream TE tunnel + segment."; + uses te-types:label-set-info; + } + container path-out-segment { + presence + "The end-to-end tunnel is not terminated in this domain; + this tunnel is a segment in the current domain."; + description + "If an end-to-end tunnel crosses multiple domains using + the same technology, some additional constraints have to be + taken in consideration in each domain. + This TE tunnel segment is stitched to the downstream TE + tunnel segment."; + uses te-types:label-set-info; + } + } + + /** + * TE container + */ + + container te { + description + "TE global container."; + leaf enable { + type boolean; + description + "Enables the TE component features."; + } + + /* TE Global Data */ + container globals { + description + "Globals TE system-wide configuration data container."; + container named-admin-groups { + description + "TE named admin groups container."; + list named-admin-group { + if-feature "te-types:extended-admin-groups"; + if-feature "te-types:named-extended-admin-groups"; + key "name"; + description + "List of named TE admin-groups."; + leaf name { + type string; + description + "A string name that uniquely identifies a TE + interface named admin-group."; + } + leaf bit-position { + type uint32; + description + "Bit position representing the administrative group."; + reference + "RFC3209 and RFC7308"; + } + + } + } + container named-srlgs { + description + "TE named SRLGs container."; + list named-srlg { + if-feature "te-types:named-srlg-groups"; + key "name"; + description + "A list of named SRLG groups."; + leaf name { + type string; + description + "A string name that uniquely identifies a TE + interface named SRLG."; + } + leaf value { + type te-types:srlg; + description + "An SRLG value."; + } + leaf cost { + type uint32; + description + "SRLG associated cost. Used during path to append + the path cost when traversing a link with this SRLG."; + } + } + } + container named-path-constraints { + description + "TE named path constraints container."; + list named-path-constraint { + if-feature "te-types:named-path-constraints"; + key "name"; + leaf name { + type string; + description + "A string name that uniquely identifies a + path constraint set."; + } + uses path-constraints-common; + description + "A list of named path constraints."; + } + } + } + + /* TE Tunnel Data */ + container tunnels { + description + "Tunnels TE configuration data container."; + list tunnel { + key "name"; + description + "The list of TE tunnels."; + leaf name { + type string; + description + "TE tunnel name."; + } + leaf alias { + type string; + description + "An alternate name of the TE tunnel that can be modified + anytime during its lifetime."; + } + leaf identifier { + type uint32; + description + "TE tunnel Identifier."; + reference + "RFC3209"; + } + leaf color { + type uint32; + description "The color associated with the TE tunnel."; + reference "RFC9012"; + } + leaf description { + type string; + default "None"; + description + "Textual description for this TE tunnel."; + } + leaf admin-state { + type identityref { + base te-types:tunnel-admin-state-type; + } + default "te-types:tunnel-admin-state-up"; + description + "TE tunnel administrative state."; + } + leaf operational-state { + type identityref { + base te-types:tunnel-state-type; + } + config false; + description + "TE tunnel operational state."; + } + uses te-types:encoding-and-switching-type; + uses tunnel-common-attributes; + container controller { + description + "Contains tunnel data relevant to external controller(s). + This target node may be augmented by external module(s), + for example, to add data for PCEP initiated and/or + delegated tunnels."; + leaf protocol-origin { + type identityref { + base te-types:protocol-origin-type; + } + description + "The protocol origin for instantiating the tunnel."; + } + leaf controller-entity-id { + type string; + description + "An identifier unique within the scope of visibility + that associated with the entity that controls the + tunnel."; + reference "RFC8232"; + } + } + leaf reoptimize-timer { + type uint16; + units "seconds"; + description + "Frequency of reoptimization of a traffic engineered + LSP."; + } + uses tunnel-associations-properties; + uses protection-restoration-properties; + uses te-types:tunnel-constraints; + uses tunnel-hierarchy-properties; + container primary-paths { + description + "The set of primary paths."; + reference "RFC4872"; + list primary-path { + key "name"; + description + "List of primary paths for this tunnel."; + leaf active { + type boolean; + config false; + description + "Indicates an active path that + has been selected from the primary paths list."; + } + uses path-common-properties; + uses path-forward-properties; + uses k-requested-paths; + uses path-compute-info; + uses path-state; + container primary-reverse-path { + when "../../../te:bidirectional = 'true'"; + description + "The reverse primary path properties."; + uses path-common-properties; + uses path-compute-info; + uses path-state; + container candidate-secondary-reverse-paths { + description + "The set of referenced candidate reverse secondary + paths from the full set of secondary reverse paths + which may be used for this primary path."; + list candidate-secondary-reverse-path { + key "secondary-reverse-path"; + ordered-by user; + description + "List of candidate secondary reverse path(s)"; + leaf secondary-reverse-path { + type leafref { + path "../../../../../../" + + "te:secondary-reverse-paths/" + + "te:secondary-reverse-path/te:name"; + } + description + "A reference to the secondary reverse path that + may be utilized when the containing primary + reverse path is in use."; + } + leaf active { + type boolean; + config false; + description + "Indicates an active path that has been + selected from the secondary reverse paths + list."; + } + } + } + } + container candidate-secondary-paths { + description + "The set of candidate secondary paths which may be + used for this primary path. When secondary paths are + specified in the list the path of the secondary LSP + in use must be restricted to those paths + referenced. + The priority of the secondary paths is specified + within the list. Higher priority values are less + preferred - that is to say that a path with priority + 0 is the most preferred path. In the case that the + list is empty, any secondary path may be + utilised when the current primary path is in use."; + list candidate-secondary-path { + key "secondary-path"; + ordered-by user; + description + "List of candidate secondary paths for this + tunnel."; + leaf secondary-path { + type leafref { + path "../../../../../te:secondary-paths/" + + "te:secondary-path/te:name"; + } + description + "A reference to the secondary path that may be + utilised when the containing primary path is + in use."; + } + leaf active { + type boolean; + config false; + description + "Indicates an active path that has been selected + from the candidate secondary paths."; + } + } + } + } + } + container secondary-paths { + description + "The set of secondary paths."; + reference "RFC4872"; + list secondary-path { + key "name"; + description + "List of secondary paths for this tunnel."; + uses path-common-properties; + leaf preference { + type uint8 { + range "1..255"; + } + default "1"; + description + "Specifies a preference for this path. The lower the + number higher the preference."; + } + leaf secondary-reverse-path { + type leafref { + path "../../../" + + "te:secondary-reverse-paths/" + + "te:secondary-reverse-path/te:name"; + } + description + "A reference to the reverse secondary path when + co-routed with the secondary path."; + } + uses path-compute-info; + uses protection-restoration-properties; + uses path-state; + } + } + container secondary-reverse-paths { + description + "The set of secondary reverse paths."; + list secondary-reverse-path { + key "name"; + description + "List of secondary paths for this tunnel."; + uses path-common-properties; + leaf preference { + type uint8 { + range "1..255"; + } + default "1"; + description + "Specifies a preference for this path. The lower the + number higher the preference. Paths that have the + same preference will be activated together."; + } + uses path-compute-info; + uses protection-restoration-properties; + uses path-state; + } + } + action tunnel-action { + description + "Action commands to manipulate the TE tunnel state."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 2.5"; + input { + leaf action-type { + type identityref { + base te-types:tunnel-action-type; + } + description + "The action to be invoked on the TE tunnel."; + } + } + output { + leaf action-result { + type identityref { + base te-types:te-action-result; + } + description + "The result of the tunnel action operation."; + } + } + } + action protection-external-commands { + description + "Actions to manipulate the protection external + commands of the TE tunnel."; + reference + "RFC 4427: Recovery (Protection and Restoration) + Terminology for Generalized Multi-Protocol Label + Switching (GMPLS)"; + input { + leaf protection-external-command { + type identityref { + base te-types:protection-external-commands; + } + description + "Protection external command."; + } + leaf protection-group-ingress-node { + type boolean; + default "true"; + description + "When 'true', indicates that the action is + applied on ingress node. + By default, the action applies to the ingress node + only."; + } + leaf protection-group-egress-node { + type boolean; + default "false"; + description + "When set to 'true', indicates that the action is + applied on egress node. + By default, the action applies to the ingress node + only."; + } + leaf path-name { + type string; + description + "The name of the path that the external command + applies to."; + } + leaf path-type { + type te-types:path-type; + description + "The type of the path that the external command + applies to."; + } + leaf traffic-type { + type enumeration { + enum normal-traffic { + description + "The manual-switch or forced-switch command + applies to the normal traffic (this Tunnel)."; + } + enum null-traffic { + description + "The manual-switch or forced-switch command + applies to the null traffic."; + } + enum extra-traffic { + description + "The manual-switch or forced-switch command + applies to the extra traffic (the extra-traffic + Tunnel sharing protection bandwidth with this + Tunnel)."; + } + } + description + "Indicates whether the manual-switch or forced-switch + commands applies to the normal traffic, the null + traffic or the extra-traffic."; + reference + "RFC4427"; + } + leaf extra-traffic-tunnel-ref { + type tunnel-ref; + description + "In case there are multiple extra-traffic tunnels + sharing protection bandwidth with this Tunnel + (m:n protection), represents which extra-traffic + Tunnel the manual-switch or forced-switch to + extra-traffic command applies to."; + } + } + } + } + } + + /* TE LSPs Data */ + container lsps { + config false; + description + "TE LSPs state container."; + list lsp { + key "tunnel-name lsp-id node"; + unique "source destination tunnel-id lsp-id " + + "extended-tunnel-id"; + description + "List of LSPs associated with the tunnel."; + leaf tunnel-name { + type string; + description "The TE tunnel name."; + } + leaf lsp-id { + type uint16; + description + "Identifier used in the SENDER_TEMPLATE and the + FILTER_SPEC that can be changed to allow a sender to + share resources with itself."; + reference + "RFC3209"; + } + leaf node { + type te-types:te-node-id; + description + "The node where the TE LSP state resides on."; + } + leaf source { + type te-types:te-node-id; + description + "Tunnel sender address extracted from + SENDER_TEMPLATE object."; + reference + "RFC3209"; + } + leaf destination { + type te-types:te-node-id; + description + "The tunnel endpoint address."; + reference + "RFC3209"; + } + leaf tunnel-id { + type uint16; + description + "The tunnel identifier that remains + constant over the life of the tunnel."; + reference + "RFC3209"; + } + leaf extended-tunnel-id { + type yang:dotted-quad; + description + "The LSP Extended Tunnel ID."; + reference + "RFC3209"; + } + leaf operational-state { + type identityref { + base te-types:lsp-state-type; + } + description + "The LSP operational state."; + } + leaf signaling-type { + type identityref { + base te-types:path-signaling-type; + } + description + "The signaling protocol used to set up this LSP."; + } + leaf origin-type { + type enumeration { + enum ingress { + description + "Origin ingress."; + } + enum egress { + description + "Origin egress."; + } + enum transit { + description + "Origin transit."; + } + } + description + "The origin of the LSP relative to the location of the + local switch in the path."; + } + leaf lsp-resource-status { + type enumeration { + enum primary { + description + "A primary LSP is a fully established LSP for which + the resource allocation has been committed at the + data plane."; + } + enum secondary { + description + "A secondary LSP is an LSP that has been provisioned + in the control plane only; e.g. resource allocation + has not been committed at the data plane."; + } + } + description + "LSP resource allocation state."; + reference + "RFC4872, section 4.2.1"; + } + leaf lockout-of-normal { + type boolean; + description + "When set to 'true', it represents a lockout of normal + traffic external command. When set to 'false', it + represents a clear lockout of normal traffic external + command. The lockout of normal traffic command applies + to this Tunnel."; + reference + "RFC4427"; + } + leaf freeze { + type boolean; + description + "When set to 'true', it represents a freeze external + command. When set to 'false', it represents a clear + freeze external command. The freeze command applies to + all the Tunnels which are sharing the protection + resources with this Tunnel."; + reference + "RFC4427"; + } + leaf lsp-protection-role { + type enumeration { + enum working { + description + "A working LSP must be a primary LSP whilst a + protecting LSP can be either a primary or a + secondary LSP. Also, known as protected LSPs when + working LSPs are associated with protecting LSPs."; + } + enum protecting { + description + "A secondary LSP is an LSP that has been provisioned + in the control plane only; e.g. resource allocation + has not been committed at the data plane."; + } + } + description + "LSP role type."; + reference + "RFC4872, section 4.2.1"; + } + leaf lsp-protection-state { + type identityref { + base te-types:lsp-protection-state; + } + config false; + description + "The reported protection state controlling which + tunnel is using the resources of the protecting LSP."; + } + leaf protection-group-ingress-node-id { + type te-types:te-node-id; + description + "Indicates the te-node-id of the protection group + ingress node when the APS state represents an external + command (LoP, SF, MS) applied to it or a WTR timer + running on it. If the external command is not applied to + the ingress node or the WTR timer is not running on it, + this attribute is not specified. A value 0.0.0.0 is used + when the te-node-id of the protection group ingress node + is unknown (e.g., because the ingress node is outside + the scope of control of the server)"; + } + leaf protection-group-egress-node-id { + type te-types:te-node-id; + description + "Indicates the te-node-id of the protection group egress + node when the APS state represents an external command + (LoP, SF, MS) applied to it or a WTR timer running on + it. If the external command is not applied to the + ingress node or the WTR timer is not running on it, this + attribute is not specified. A value 0.0.0.0 is used when + the te-node-id of the protection group ingress node is + unknown (e.g., because the ingress node is outside the + scope of control of the server)"; + } + container lsp-actual-route-information { + description + "RSVP recorded route object information."; + list lsp-actual-route-information { + when "../../origin-type = 'ingress'" { + description + "Applicable on ingress LSPs only."; + } + key "index"; + description + "Record route list entry."; + uses te-types:record-route-state; + } + } + } + } + } + + /* TE Tunnel RPCs/execution Data */ + + rpc tunnels-path-compute { + description + "This RPC is a generic API whose + input and output parameters are expected to be provided by + augments to this module."; + reference + "RFC 4655: A Path Computation Element (PCE)-Based + Architecture."; + input { + container path-compute-info { + /* + * An external path compute module may augment this + * target. + */ + description + "RPC input information."; + } + } + output { + container path-compute-result { + /* + * An external path compute module may augment this + * target. + */ + description + "RPC output information."; + } + } + } + + rpc tunnels-actions { + description + "RPC that manipulates the state of a TE tunnel."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 2.5"; + input { + container tunnel-info { + description + "TE tunnel information."; + choice filter-type { + mandatory true; + description + "Filter choice."; + case all-tunnels { + leaf all { + type empty; + mandatory true; + description + "When present, applies the action on all TE + tunnels."; + } + } + case one-tunnel { + leaf tunnel { + type tunnel-ref; + description + "Apply action on the specific TE tunnel."; + } + } + } + } + container action-info { + description + "TE tunnel action information."; + leaf action { + type identityref { + base te-types:tunnel-action-type; + } + description + "The action type."; + } + leaf disruptive { + when "derived-from-or-self(../action, " + + "'te-types:tunnel-action-reoptimize')"; + type empty; + description + "When present, specifies whether or not the + reoptimization + action is allowed to be disruptive."; + } + } + } + output { + leaf action-result { + type identityref { + base te-types:te-action-result; + } + description + "The result of the tunnel action operation."; + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/draft-layer1-types/ietf-layer1-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-layer1-types/ietf-layer1-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..ba3820b72746cd5027c457529aafe04a9dc84e7b --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/draft-layer1-types/ietf-layer1-types.yang @@ -0,0 +1,1361 @@ +module ietf-layer1-types { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-layer1-types"; + prefix "l1-types"; + + import ietf-routing-types { + prefix rt-types; + reference + "RFC 8294: Common YANG Data Types for the Routing Area"; + } + + organization + "IETF CCAMP Working Group"; + contact + "WG Web: + WG List: + + Editor: Haomian Zheng + + + Editor: Italo Busi + "; + + description + "This module defines Layer 1 YANG types. The model fully conforms + to the Network Management Datastore Architecture (NMDA). + + 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. + + 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."; + + revision "2024-02-22" { + description + "Initial Version"; + reference + "RFC XXXX: A YANG Data Model for Layer 1 Types"; + // RFC Editor: replace RFC XXXX with actual RFC number, + // update date information and remove this note. + } + + /* + * Identities + */ + + identity tributary-slot-granularity { + description + "Tributary Slot Granularity (TSG)."; + reference + "ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity tsg-1.25G { + base tributary-slot-granularity; + description + "1.25G tributary slot granularity."; + } + + identity tsg-2.5G { + base tributary-slot-granularity; + description + "2.5G tributary slot granularity."; + } + + identity tsg-5G { + base tributary-slot-granularity; + description + "5G tributary slot granularity."; + } + + identity odu-type { + description + "Base identity from which specific Optical Data Unit (ODU) + type is derived."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU0 { + base odu-type; + description + "ODU0 type (1.24Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU1 { + base odu-type; + description + "ODU1 type (2.49Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU2 { + base odu-type; + description + "ODU2 type (10.03Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU2e { + base odu-type; + description + "ODU2e type (10.39Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU3 { + base odu-type; + description + "ODU3 type (40.31Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU4 { + base odu-type; + description + "ODU4 type (104.79Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODUflex { + base odu-type; + description + "ODUflex type (flexible bit rate, not resizable). + + It could be used for any type of ODUflex, including + ODUflex(CBR), ODUflex(GFP), ODUflex(GFP,n,k), ODUflex(IMP,s), + ODUflex(IMP) and ODUflex(FlexE-aware)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + identity ODUflex-resizable { + base odu-type; + description + "ODUflex protocol (flexible bit rate, resizable). + + It could be used only for ODUflex(GFP,n,k)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity protocol { + description + "Base identity from which specific protocol is derived."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity Ethernet { + base protocol; + description + "Ethernet protocol."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity Fibre-Channel { + base protocol; + description + "Fibre-Channel (FC) protocol."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity SDH { + base protocol; + description + "SDH protocol."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity SONET { + base protocol; + description + "SONET protocol."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity client-signal { + description + "Base identity from which specific Constant Bit Rate (CBR) + client signal is derived"; + } + + identity coding-func { + description + "Base identity from which specific coding function + is derived."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-1Gb { + base client-signal; + description + "Client signal type of 1GbE."; + reference + "IEEE 802.3-2018, Clause 36: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ETH-10Gb-LAN { + base client-signal; + description + "Client signal type of ETH-10Gb-LAN (10.3 Gb/s)."; + reference + "IEEE 802.3-2018, Clause 49: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ETH-10Gb-WAN { + base client-signal; + description + "Client signal type of ETH-10Gb-WAN (9.95 Gb/s)."; + reference + "IEEE 802.3-2018, Clause 50: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ETH-40Gb { + base client-signal; + description + "Client signal type of 40GbE."; + reference + "IEEE 802.3-2018, Clause 82: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ETH-100Gb { + base client-signal; + description + "Client signal type of 100GbE."; + reference + "IEEE 802.3-2018, Clause 82: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity STM-1 { + base client-signal; + base coding-func; + description + "Client signal type of STM-1; + STM-1 G.707 (N=1) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity STM-4 { + base client-signal; + base coding-func; + description + "Client signal type of STM-4; + STM-4 G.707 (N=4) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity STM-16 { + base client-signal; + base coding-func; + description + "Client signal type of STM-16; + STM-16 G.707 (N=16) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity STM-64 { + base client-signal; + base coding-func; + description + "Client signal type of STM-64; + STM-64 G.707 (N=64) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity STM-256 { + base client-signal; + base coding-func; + description + "Client signal type of STM-256; + STM-256 G.707 (N=256) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-3 { + base client-signal; + base coding-func; + description + "Client signal type of OC3; + OC-3 GR-253-CORE (N=3) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-12 { + base client-signal; + base coding-func; + description + "Client signal type of OC12; + OC-12 GR-253-CORE (N=12) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-48 { + base client-signal; + base coding-func; + description + "Client signal type of OC48; + OC-48 GR-253-CORE (N=48) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-192 { + base client-signal; + base coding-func; + description + "Client signal type of OC192; + OC-192 GR-253-CORE (N=192) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-768 { + base client-signal; + base coding-func; + description + "Client signal type of OC768; + OC-768 GR-253-CORE (N=768) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-100 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-100; + FC-100 FC-FS-2 (1.0625 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-200 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-200; + FC-200 FC-FS-2 (2.125 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-400 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-400; + FC-400 FC-FS-2 (4.250 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-800 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-800; + FC-800 FC-FS-2 (8.500 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-1200 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-1200; + FC-1200 FC-10GFC (10.51875 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-1600 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-1600; + FC-1600 FC-FS-3 (14.025 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-3200 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-3200; + FC-3200 FC-FS-4 (28.05 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-1000X { + base coding-func; + description + "1000BASE-X PCS clause 36 coding function."; + reference + "IEEE 802.3-2018, Clause 36: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-10GW { + base coding-func; + description + "IEEE 802.3-2018, Clause 50: IEEE Standard for Ethernet + + 10GBASE-W (WAN PHY) PCS clause 49 and WIS clause 50 + coding function."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-10GR { + base coding-func; + description + "10GBASE-R (LAN PHY) PCS clause 49 coding function."; + reference + "IEEE 802.3-2018, Clause 49: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-40GR { + base coding-func; + description + "40GBASE-R PCS clause 82 coding function."; + reference + "IEEE 802.3-2018, Clause 82: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-100GR { + base coding-func; + description + "100GBASE-R PCS clause 82 coding function."; + reference + "IEEE 802.3-2018, Clause 82: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity optical-interface-func { + description + "Base identity from which optical-interface-function + is derived."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity SX-PMD-1000 { + base optical-interface-func; + description + "SX-PMD-clause-38 Optical Interface function for + 1000BASE-X PCS-36."; + reference + "IEEE 802.3-2018, Clause 38: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LX-PMD-1000 { + base optical-interface-func; + description + "LX-PMD-clause-38 Optical Interface function for + 1000BASE-X PCS-36."; + reference + "IEEE 802.3-2018, Clause 38: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LX10-PMD-1000 { + base optical-interface-func; + description + "LX10-PMD-clause-59 Optical Interface function for + 1000BASE-X PCS-36."; + reference + "IEEE 802.3-2018, Clause 59: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity BX10-PMD-1000 { + base optical-interface-func; + description + "BX10-PMD-clause-59 Optical Interface function for + 1000BASE-X PCS-36."; + reference + "IEEE 802.3-2018, Clause 59: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LW-PMD-10G { + base optical-interface-func; + description + "LW-PMD-clause-52 Optical Interface function for + 10GBASE-W PCS-49-WIS-50."; + reference + "IEEE 802.3-2018, Clause 52: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity EW-PMD-10G { + base optical-interface-func; + description + "EW-PMD-clause-52 Optical Interface function for + 10GBASE-W PCS-49-WIS-50."; + reference + "IEEE 802.3-2018, Clause 52: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LR-PMD-10G { + base optical-interface-func; + description + "LR-PMD-clause-52 Optical Interface function for + 10GBASE-R PCS-49."; + reference + "IEEE 802.3-2018, Clause 52: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ER-PMD-10G { + base optical-interface-func; + description + "ER-PMD-clause-52 Optical Interface function for + 10GBASE-R PCS-49."; + reference + "IEEE 802.3-2018, Clause 52: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LR4-PMD-40G { + base optical-interface-func; + description + "LR4-PMD-clause-87 Optical Interface function for + 40GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 87: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ER4-PMD-40G { + base optical-interface-func; + description + "ER4-PMD-clause-87 Optical Interface function for + 40GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 87: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FR-PMD-40G { + base optical-interface-func; + description + "FR-PMD-clause-89 Optical Interface function for + 40GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 89: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LR4-PMD-100G { + base optical-interface-func; + description + "LR4-PMD-clause-88 Optical Interface function for + 100GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 88: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + identity ER4-PMD-100G { + base optical-interface-func; + description + "ER4-PMD-clause-88 Optical Interface function for + 100GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 88: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + /* + * Typedefs + */ + + typedef otn-tpn { + type uint16 { + range "1..4095"; + } + description + "Tributary Port Number (TPN) for OTN."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks."; + } + + typedef otn-ts { + type uint16 { + range "1..4095"; + } + description + "Tributary Slot (TS) for OTN."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks."; + } + + typedef otn-label-range-type { + type enumeration { + enum trib-slot { + description + "Defines a range of OTN tributary slots (TS)."; + } + enum trib-port { + description + "Defines a range of OTN tributary ports (TPN)."; + } + } + description + "Defines the type of OTN label range: TS or TPN."; + } + + typedef gfp-k { + type enumeration { + enum 2 { + description + "The ODU2.ts rate (1,249,177.230 kbit/s) is used + to compute the rate of an ODUflex(GFP,n,2)."; + } + enum 3 { + description + "The ODU3.ts rate (1,254,470.354 kbit/s) is used + to compute the rate of an ODUflex(GFP,n,3)."; + } + enum 4 { + description + "The ODU4.ts rate (1,301,467.133 kbit/s) is used + to compute the rate of an ODUflex(GFP,n,4)."; + } + } + description + "The ODUk.ts used to compute the rate of an ODUflex(GFP,n,k)."; + reference + "ITU-T G.709 v6.0 (06/2020), Table 7-8 and L.7: Interfaces for + the Optical Transport Network (OTN)"; + } + + typedef flexe-client-rate { + type union { + type uint16; + type enumeration { + enum "10G" { + description + "Represents a 10G FlexE Client signal (s=2)."; + } + enum "40G" { + description + "Represents a 40G FlexE Client signal (s=8)."; + } + } + } + description + "The FlexE Client signal rate (s x 5,156,250.000 kbit/s) + used to compute the rate of an ODUflex(IMP, s). + + Valid values for s are s=2 (10G), s=4 (40G) and + s=5 x n (n x 25G). + + In the first two cases an enumeration value + (either 10G or 40G) is used, while in the latter case + the value of n is used."; + reference + "ITU-T G.709 v6.0 (06/2020), Table 7-2: Interfaces for the + Optical Transport Network (OTN)"; + } + + typedef odtu-flex-type { + type enumeration { + enum "2" { + description + "The ODTU2.ts ODTU type."; + } + enum "3" { + description + "The ODTU3.ts ODTU type."; + } + enum "4" { + description + "The ODTU4.ts ODTU type."; + } + enum "Cn" { + description + "The ODTUCn.ts ODTU type."; + } + } + description + "The type of Optical Data Tributary Unit (ODTU), + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by an ODUflex LSP, according to + the (19-1a) and (20-1a) formulas defined in G.709."; + reference + "ITU-T G.709 v6.0 (06/2020), Table 7-7, clause 19.6 and + clause 20.5: Interfaces for the Optical Transport + Network (OTN)"; + } + + typedef bandwidth-scientific-notation { + type string { + pattern + '0(\.0?)?([eE](\+)?0?)?|' + + '[1-9](\.[0-9]{0,6})?[eE](\+)?(9[0-6]|[1-8][0-9]|0?[0-9])?'; + } + units "bps"; + description + "Bandwidth values, expressed using the scientific notation + in bits per second. + + The encoding format is the external decimal-significant + character sequences specified in IEEE 754 and ISO/IEC 9899:1999 + for 32-bit decimal floating-point numbers: + (-1)**(S) * 10**(Exponent) * (Significant), + where Significant uses 7 digits. + + An implementation for this representation MAY use decimal32 + or binary32. The range of the Exponent is from -95 to +96 + for decimal32, and from -38 to +38 for binary32. + As a bandwidth value, the format is restricted to be + normalized, non-negative, and non-fraction: + n.dddddde{+}dd, N.DDDDDDE{+}DD, 0e0 or 0E0, + where 'd' and 'D' are decimal digits; 'n' and 'N' are + non-zero decimal digits; 'e' and 'E' indicate a power of ten. + Some examples are 0e0, 1e10, and 9.953e9."; + reference + "IEEE Std 754-2001: IEEE Standard for Floating-Point + Arithmetic + + ISO/IEC 9899:1999: Information technology - Programming + Languages - C"; + } + + /* + * Groupings + */ + + grouping otn-link-bandwidth { + description + "Bandwidth attributes for OTN links."; + container otn-bandwidth { + description + "Bandwidth attributes for OTN links."; + list odulist { + key "odu-type"; + description + "OTN bandwidth definition"; + leaf odu-type { + type identityref { + base odu-type; + } + description "ODU type"; + } + leaf number { + type uint16; + description "Number of ODUs."; + } + leaf ts-number { + when 'derived-from-or-self(../odu-type,"ODUflex") or + derived-from-or-self(../odu-type, + "ODUflex-resizable")' { + description + "Applicable when odu-type is ODUflex or + ODUflex-resizable."; + } + type uint16 { + range "1..4095"; + } + description + "The number of Tributary Slots (TS) that + could be used by all the ODUflex LSPs."; + } + } + } + } + + grouping otn-path-bandwidth { + description + "Bandwidth attributes for OTN paths."; + container otn-bandwidth { + description + "Bandwidth attributes for OTN paths."; + leaf odu-type { + type identityref { + base odu-type; + } + description "ODU type"; + } + choice oduflex-type { + when 'derived-from-or-self(./odu-type,"ODUflex") or + derived-from-or-self(./odu-type, + "ODUflex-resizable")' { + description + "Applicable when odu-type is ODUflex or + ODUflex-resizable."; + } + description + "Types of ODUflex used to compute the ODUflex + nominal bit rate."; + reference + "ITU-T G.709 v6.0 (06/2020), Table 7-2: Interfaces for the + Optical Transport Network (OTN)"; + case generic { + leaf nominal-bit-rate { + type union { + type l1-types:bandwidth-scientific-notation; + type rt-types:bandwidth-ieee-float32; + } + mandatory true; + description + "Nominal ODUflex bit rate."; + } + } + case cbr { + leaf client-type { + type identityref { + base client-signal; + } + mandatory true; + description + "The type of Constant Bit Rate (CBR) client signal + of an ODUflex(CBR)."; + } + } + case gfp-n-k { + leaf gfp-n { + type uint8 { + range "1..80"; + } + mandatory true; + description + "The value of n for an ODUflex(GFP,n,k)."; + reference + "ITU-T G.709 v6.0 (06/2020), Tables 7-8 and L.7: + Interfaces for the Optical Transport Network (OTN)"; + } + leaf gfp-k { + type gfp-k; + description + "The value of k for an ODUflex(GFP,n,k). + + If omitted, it is calculated from the value of gfp-n + as described in Table 7-8 of G.709."; + reference + "ITU-T G.709 v6.0 (06/2020), Tables 7-8 and L.7: + Interfaces for the Optical Transport Network (OTN)"; + } + } + case flexe-client { + leaf flexe-client { + type flexe-client-rate; + mandatory true; + description + "The rate of the FlexE-client for an ODUflex(IMP,s)."; + } + } + case flexe-aware { + leaf flexe-aware-n { + type uint16; + mandatory true; + description + "The rate of FlexE-aware client signal + for ODUflex(FlexE-aware)"; + } + } + case packet { + leaf opuflex-payload-rate { + type union { + type l1-types:bandwidth-scientific-notation; + type rt-types:bandwidth-ieee-float32; + } + mandatory true; + description + "Either the GFP-F encapsulated packet client nominal + bit rate for an ODUflex(GFP) or the 64b/66b encoded + packet client nominal bit rate for an ODUflex(IMP)."; + } + } + } + } + } + + grouping otn-max-path-bandwidth { + description + "Maximum bandwidth attributes for OTN paths."; + container otn-bandwidth { + description + "Maximum bandwidth attributes for OTN paths."; + leaf odu-type { + type identityref { + base odu-type; + } + description "ODU type."; + } + leaf max-ts-number { + when 'derived-from-or-self(../odu-type,"ODUflex") or + derived-from-or-self(../odu-type, + "ODUflex-resizable")' { + description + "Applicable when odu-type is ODUflex or + ODUflex-resizable."; + } + type uint16 { + range "1..4095"; + } + description + "The maximum number of Tributary Slots (TS) that could be + used by an ODUflex LSP."; + } + } + } + + grouping otn-label-range-info { + description + "Label range information for OTN. + + This grouping SHOULD be used together with the + otn-label-start-end and otn-label-step groupings to provide + OTN technology-specific label information to the models which + use the label-restriction-info grouping defined in the module + ietf-te-types."; + container otn-label-range { + description + "Label range information for OTN."; + leaf range-type { + type otn-label-range-type; + description "The type of range (e.g., TPN or TS) + to which the label range applies"; + } + leaf tsg { + type identityref { + base tributary-slot-granularity; + } + description + "Tributary slot granularity (TSG) to which the label range + applies. + + This leaf MUST be present when the range-type is TS. + + This leaf MAY be omitted when mapping an ODUk over an OTUk + Link. In this case the range-type is tpn, with only one + entry (ODUk), and the tpn range has only one value (1)."; + reference + "ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + leaf-list odu-type-list { + type identityref { + base odu-type; + } + description + "List of ODU types to which the label range applies. + + An Empty odu-type-list means that the label range + applies to all the supported ODU types."; + } + leaf priority { + type uint8 { + range 0..7; + } + description + "Priority in Interface Switching Capability + Descriptor (ISCD)."; + reference + "RFC4203: OSPF Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS)"; + } + } + } + + grouping otn-label-start-end { + description + "The OTN label-start or label-end used to specify an OTN label + range. + + This grouping is dependent on the range-type defined in the + otn-label-range-info grouping. + + This grouping SHOULD be used together with the + otn-label-range-info and otn-label-step groupings to provide + OTN technology-specific label information to the models which + use the label-restriction-info grouping defined in the module + ietf-te-types."; + container otn-label { + description + "Label start or label end for OTN. + + It is either a TPN or a TS depending on the OTN label range + type specified in the 'range-type' leaf defined in the + otn-label-range-info grouping."; + leaf tpn { + when "../../../../otn-label-range/range-type = + 'trib-port'" { + description + "Valid only when range-type represented by + trib-port."; + } + type otn-tpn; + description + "Tributary Port Number (TPN)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + leaf ts { + when "../../../../otn-label-range/range-type = + 'trib-slot'" { + description + "Valid only when range-type represented by + trib-slot."; + } + type otn-ts; + description + "Tributary Slot (TS) number."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + } + } + + grouping otn-label-hop { + description "OTN Label"; + reference + "RFC7139, section 6: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + container otn-label { + description + "Label hop for OTN."; + leaf tpn { + type otn-tpn; + description + "Tributary Port Number (TPN)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + leaf tsg { + type identityref { + base tributary-slot-granularity; + } + description "Tributary Slot Granularity (TSG)."; + reference + "ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + leaf ts-list { + type string { + pattern "([1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?" + + "(,[1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?)*)"; + } + description + "A list of available Tributary Slots (TS) ranging + between 1 and 4095. If multiple values or + ranges are given, they all MUST be disjoint + and MUST be in ascending order. + For example 1-20,25,50-1000."; + reference + "RFC 7139: GMPLS Signaling Extensions for Control + of Evolving G.709 Optical Transport Networks"; + } + } + } + + grouping otn-label-step { + description + "Label step for OTN. + + This grouping is dependent on the range-type defined in the + otn-label-range-info grouping. + + This grouping SHOULD be used together with the + otn-label-range-info and otn-label-start-end groupings to + provide OTN technology-specific label information to the + models which use the label-restriction-info grouping defined + in the module ietf-te-types."; + container otn-label-step { + description + "Label step for OTN. + + It is either a TPN or a TS depending on the OTN label range + type specified in the 'range-type' leaf defined in the + otn-label-range-info grouping."; + leaf tpn { + when "../../../otn-label-range/range-type = + 'trib-port'" { + description + "Valid only when range-type represented by + trib-port."; + } + type otn-tpn; + description + "Label step which represents possible increments for + Tributary Port Number (TPN)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + leaf ts { + when "../../../otn-label-range/range-type = + 'trib-slot'" { + description + "Valid only when range-type represented by + trib-slot"; + } + type otn-ts; + description + "Label step which represents possible increments for + Tributary Slot (TS) number."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc6991/ietf-inet-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc6991/ietf-inet-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..a1ef0dfaa71bb591bd84ff397565eb5e6c693310 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc6991/ietf-inet-types.yang @@ -0,0 +1,458 @@ +module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2013 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 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - ip-address-no-zone + - ipv4-address-no-zone + - 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 2460."; + } + } + 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 2460: 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 2460: 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. A current list of + all assignments is available from . + + 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 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** 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 inet:ipv4-address; + type inet: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])' + + '(%[\p{N}\p{L}]+)?'; + } + 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. + + 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])))' + + '(%[\p{N}\p{L}]+)?'; + 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. + + 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 inet:ipv4-address-no-zone; + type inet: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 inet:ipv4-address { + pattern '[0-9\.]*'; + } + description + "An IPv4 address without a zone index. This type, derived from + ipv4-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + } + + typedef ipv6-address-no-zone { + type inet:ipv6-address { + pattern '[0-9a-fA-F:\.]*'; + } + description + "An IPv6 address without a zone index. This type, derived from + ipv6-address, may be used in situations where the zone is + known from the context and hence 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-prefix { + type union { + type inet:ipv4-prefix; + type inet: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 address 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."; + } + + 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 address 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 IPv6 address should have all bits that do not belong + to the prefix set to zero. + + 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."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + 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]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + 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. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + 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 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + 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, which are normalized to uppercase as described in + Section 6.2.2.1. + + 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)"; + } + +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc6991/ietf-yang-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc6991/ietf-yang-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..f6624fed83e6e59d67c277df15f6e0b82ee666a4 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc6991/ietf-yang-types.yang @@ -0,0 +1,474 @@ +module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2013 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 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - yang-identifier + - hex-string + - uuid + - dotted-quad"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier-related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifiers. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type; the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of types related to date and time***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z + all represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using + the time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually + referring to the notion of local time) uses the time-offset + -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence + happened. The specific occurrence must be defined in the + description of any schema node defined using this type. When + the specific occurrence occurred prior to the last time the + associated timeticks attribute was zero, then the timestamp + value is zero. Note that this requires all timestamp values + to be reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML-specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + /*** collection of string types ***/ + + typedef hex-string { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "A hexadecimal string with octets represented as hex digits + separated by colons. The canonical representation uses + lowercase characters."; + } + + typedef uuid { + type string { + pattern '[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}'; + } + description + "A Universally Unique IDentifier in the string representation + defined in RFC 4122. The canonical representation uses + lowercase characters. + + The following is an example of a UUID in string representation: + f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + "; + reference + "RFC 4122: A Universally Unique IDentifier (UUID) URN + Namespace"; + } + + typedef dotted-quad { + 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 + "An unsigned 32-bit number expressed in the dotted-quad + notation, i.e., four octets written as decimal numbers + and separated with the '.' (full stop) character."; + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8294/iana-routing-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8294/iana-routing-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..e57ebd2392e6b3672dab81928d8add0372811a6e --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8294/iana-routing-types.yang @@ -0,0 +1,471 @@ +module iana-routing-types { + namespace "urn:ietf:params:xml:ns:yang:iana-routing-types"; + prefix iana-rt-types; + + organization + "IANA"; + contact + "Internet Assigned Numbers Authority + + Postal: ICANN + 12025 Waterfront Drive, Suite 300 + Los Angeles, CA 90094-2536 + United States of America + Tel: +1 310 301 5800 + "; + + description + "This module contains a collection of YANG data types + considered defined by IANA and used 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 4."; + } + + /*** Collection of IANA types related to routing ***/ + /*** IANA Address Family enumeration ***/ + + typedef address-family { + type enumeration { + enum ipv4 { + value 1; + description + "IPv4 Address Family."; + } + + enum ipv6 { + value 2; + description + "IPv6 Address Family."; + } + + enum nsap { + value 3; + description + "OSI Network Service Access Point (NSAP) Address Family."; + } + + enum hdlc { + value 4; + description + "High-Level Data Link Control (HDLC) Address Family."; + } + + enum bbn1822 { + value 5; + description + "Bolt, Beranek, and Newman Report 1822 (BBN 1822) + Address Family."; + } + + enum ieee802 { + value 6; + description + "IEEE 802 Committee Address Family + (aka Media Access Control (MAC) address)."; + } + + enum e163 { + value 7; + description + "ITU-T E.163 Address Family."; + } + enum e164 { + value 8; + description + "ITU-T E.164 (Switched Multimegabit Data Service (SMDS), + Frame Relay, ATM) Address Family."; + } + + enum f69 { + value 9; + description + "ITU-T F.69 (Telex) Address Family."; + } + + enum x121 { + value 10; + description + "ITU-T X.121 (X.25, Frame Relay) Address Family."; + } + + enum ipx { + value 11; + description + "Novell Internetwork Packet Exchange (IPX) + Address Family."; + } + + enum appletalk { + value 12; + description + "Apple AppleTalk Address Family."; + } + + enum decnet-iv { + value 13; + description + "Digital Equipment DECnet Phase IV Address Family."; + } + + enum vines { + value 14; + description + "Banyan Vines Address Family."; + } + + enum e164-nsap { + value 15; + description + "ITU-T E.164 with NSAP sub-address Address Family."; + } + + enum dns { + value 16; + description + "Domain Name System (DNS) Address Family."; + } + + enum distinguished-name { + value 17; + description + "Distinguished Name Address Family."; + } + + enum as-num { + value 18; + description + "Autonomous System (AS) Number Address Family."; + } + + enum xtp-v4 { + value 19; + description + "Xpress Transport Protocol (XTP) over IPv4 + Address Family."; + } + + enum xtp-v6 { + value 20; + description + "XTP over IPv6 Address Family."; + } + + enum xtp-native { + value 21; + description + "XTP native mode Address Family."; + } + + enum fc-port { + value 22; + description + "Fibre Channel (FC) World-Wide Port Name Address Family."; + } + enum fc-node { + value 23; + description + "FC World-Wide Node Name Address Family."; + } + + enum gwid { + value 24; + description + "ATM Gateway Identifier (GWID) Number Address Family."; + } + + enum l2vpn { + value 25; + description + "Layer 2 VPN (L2VPN) Address Family."; + } + + enum mpls-tp-section-eid { + value 26; + description + "MPLS Transport Profile (MPLS-TP) Section Endpoint + Identifier Address Family."; + } + + enum mpls-tp-lsp-eid { + value 27; + description + "MPLS-TP Label Switched Path (LSP) Endpoint Identifier + Address Family."; + } + + enum mpls-tp-pwe-eid { + value 28; + description + "MPLS-TP Pseudowire Endpoint Identifier Address Family."; + } + + enum mt-v4 { + value 29; + description + "Multi-Topology IPv4 Address Family."; + } + + enum mt-v6 { + value 30; + description + "Multi-Topology IPv6 Address Family."; + } + + enum eigrp-common-sf { + value 16384; + description + "Enhanced Interior Gateway Routing Protocol (EIGRP) + Common Service Family Address Family."; + } + + enum eigrp-v4-sf { + value 16385; + description + "EIGRP IPv4 Service Family Address Family."; + } + + enum eigrp-v6-sf { + value 16386; + description + "EIGRP IPv6 Service Family Address Family."; + } + + enum lcaf { + value 16387; + description + "Locator/ID Separation Protocol (LISP) + Canonical Address Format (LCAF) Address Family."; + } + + enum bgp-ls { + value 16388; + description + "Border Gateway Protocol - Link State (BGP-LS) + Address Family."; + } + + enum mac-48 { + value 16389; + description + "IEEE 48-bit MAC Address Family."; + } + + enum mac-64 { + value 16390; + description + "IEEE 64-bit MAC Address Family."; + } + + enum trill-oui { + value 16391; + description + "Transparent Interconnection of Lots of Links (TRILL) + IEEE Organizationally Unique Identifier (OUI) + Address Family."; + } + + enum trill-mac-24 { + value 16392; + description + "TRILL final 3 octets of 48-bit MAC Address Family."; + } + + enum trill-mac-40 { + value 16393; + description + "TRILL final 5 octets of 64-bit MAC Address Family."; + } + + enum ipv6-64 { + value 16394; + description + "First 8 octets (64 bits) of IPv6 address + Address Family."; + } + + enum trill-rbridge-port-id { + value 16395; + description + "TRILL Routing Bridge (RBridge) Port ID Address Family."; + } + + enum trill-nickname { + value 16396; + description + "TRILL Nickname Address Family."; + } + } + + description + "Enumeration containing all the IANA-defined + Address Families."; + + } + + /*** Subsequent Address Family Identifiers (SAFIs) ***/ + /*** for multiprotocol BGP enumeration ***/ + + typedef bgp-safi { + type enumeration { + enum unicast-safi { + value 1; + description + "Unicast SAFI."; + } + + enum multicast-safi { + value 2; + description + "Multicast SAFI."; + } + + enum labeled-unicast-safi { + value 4; + description + "Labeled Unicast SAFI."; + } + + enum multicast-vpn-safi { + value 5; + description + "Multicast VPN SAFI."; + } + + enum pseudowire-safi { + value 6; + description + "Multi-segment Pseudowire VPN SAFI."; + } + + enum tunnel-encap-safi { + value 7; + description + "Tunnel Encap SAFI."; + } + + enum mcast-vpls-safi { + value 8; + description + "Multicast Virtual Private LAN Service (VPLS) SAFI."; + } + + enum tunnel-safi { + value 64; + description + "Tunnel SAFI."; + } + + enum vpls-safi { + value 65; + description + "VPLS SAFI."; + } + + enum mdt-safi { + value 66; + description + "Multicast Distribution Tree (MDT) SAFI."; + } + + enum v4-over-v6-safi { + value 67; + description + "IPv4 over IPv6 SAFI."; + } + + enum v6-over-v4-safi { + value 68; + description + "IPv6 over IPv4 SAFI."; + } + + enum l1-vpn-auto-discovery-safi { + value 69; + description + "Layer 1 VPN Auto-Discovery SAFI."; + } + + enum evpn-safi { + value 70; + description + "Ethernet VPN (EVPN) SAFI."; + } + + enum bgp-ls-safi { + value 71; + description + "BGP-LS SAFI."; + } + + enum bgp-ls-vpn-safi { + value 72; + description + "BGP-LS VPN SAFI."; + } + + enum sr-te-safi { + value 73; + description + "Segment Routing - Traffic Engineering (SR-TE) SAFI."; + } + + enum labeled-vpn-safi { + value 128; + description + "MPLS Labeled VPN SAFI."; + } + + enum multicast-mpls-vpn-safi { + value 129; + description + "Multicast for BGP/MPLS IP VPN SAFI."; + } + + enum route-target-safi { + value 132; + description + "Route Target SAFI."; + } + + enum ipv4-flow-spec-safi { + value 133; + description + "IPv4 Flow Specification SAFI."; + } + + enum vpnv4-flow-spec-safi { + value 134; + description + "IPv4 VPN Flow Specification SAFI."; + } + + enum vpn-auto-discovery-safi { + value 140; + description + "VPN Auto-Discovery SAFI."; + } + } + description + "Enumeration for BGP SAFI."; + reference + "RFC 4760: Multiprotocol Extensions for BGP-4."; + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8294/ietf-routing-types.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8294/ietf-routing-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..65c83bc848a9edd1bf6b0b4156d09ed14aa3cd28 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8294/ietf-routing-types.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: + WG List: + + Editors: Xufeng Liu + + Yingzhen Qu + + Acee Lindem + + Christian Hopps + + Lou Berger + "; + + 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 . + + 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 . + + 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."; + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8343/ietf-interfaces.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8343/ietf-interfaces.yang new file mode 100644 index 0000000000000000000000000000000000000000..96d416753364e1f12651190655833be8da0283aa --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8343/ietf-interfaces.yang @@ -0,0 +1,1123 @@ +module ietf-interfaces { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (Network Modeling) Working Group"; + + contact + "WG Web: + WG List: + + Editor: Martin Bjorklund + "; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + 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 8343; see + the RFC itself for full legal notices."; + + revision 2018-02-20 { + description + "Updated to support NMDA."; + reference + "RFC 8343: A YANG Data Model for Interface Management"; + } + + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7223: A YANG Data Model for Interface Management"; + } + + /* + * Typedefs + */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + interfaces."; + } + + /* + * Identities + */ + + identity interface-type { + description + "Base identity from which specific interface types are + derived."; + } + + /* + * Features + */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + feature if-mib { + description + "This feature indicates that the device implements + the IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* + * Data nodes + */ + + container interfaces { + description + "Interface parameters."; + + list interface { + key "name"; + + description + "The list of interfaces on the device. + + The status of an interface is available in this list in the + operational state. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the operational + state. If the configuration of a user-controlled interface + cannot be used by the system, the configured interface is + not instantiated in the operational state. + + System-controlled interfaces created by the system are + always present in this list in the operational state, + whether or not they are configured."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + For system-controlled interfaces, this leaf is the + device-specific name of the interface. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + operational state, the server MAY reject the request if + the implementation does not support pre-provisioning of + interfaces or if the name refers to an interface that can + never exist in the system. A Network Configuration + Protocol (NETCONF) server MUST reply with an rpc-error + with the error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the 'pre-provisioning' feature is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the 'arbitrary-names' feature is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + operational state. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf description { + type string; + description + "A textual description of the interface. + + A server implementation MAY map this leaf to the ifAlias + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifAlias. The definition of + such a mechanism is outside the scope of this document. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + configuration."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the intended configuration to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the intended configuration are + reflected in ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + description + "The device will generate linkUp/linkDown SNMP + notifications for this interface."; + } + enum disabled { + value 2; + description + "The device will not generate linkUp/linkDown SNMP + notifications for this interface."; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces that do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + config false; + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + config false; + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + config false; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + config false; + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + config false; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-ref; + config false; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-ref; + config false; + + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + config false; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + config false; + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + + } + } + + /* + * Legacy typedefs + */ + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + status deprecated; + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* + * Legacy operational state data nodes + */ + + container interfaces-state { + config false; + status deprecated; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + status deprecated; + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether or not they are + configured."; + + leaf name { + type string; + status deprecated; + description + "The name of the interface. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + status deprecated; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + status deprecated; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + status deprecated; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + status deprecated; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + status deprecated; + description + "The ifIndex value for the ifEntry represented by this + interface."; + + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + status deprecated; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + status deprecated; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + status deprecated; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + status deprecated; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + status deprecated; + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + status deprecated; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + status deprecated; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + status deprecated; + + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + status deprecated; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + status deprecated; + + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + leaf out-multicast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + status deprecated; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8345/ietf-network-topology.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8345/ietf-network-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..df3685827d5f72c2eb0671de82594ead28553468 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8345/ietf-network-topology.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: + WG List: + + Editor: Alexander Clemm + + + Editor: Jan Medved + + + Editor: Robert Varga + + + Editor: Nitin Bahadur + + + Editor: Hariharan Ananthakrishnan + + + Editor: Xufeng Liu + "; + + 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)."; + } + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8345/ietf-network.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8345/ietf-network.yang new file mode 100644 index 0000000000000000000000000000000000000000..c67a3fa40f7b719cda6199f7a1352c8bd9e7bec8 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8345/ietf-network.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: + WG List: + + Editor: Alexander Clemm + + + Editor: Jan Medved + + + Editor: Robert Varga + + + Editor: Nitin Bahadur + + + Editor: Hariharan Ananthakrishnan + + + Editor: Xufeng Liu + "; + 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."; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8346/ietf-l3-unicast-topology.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8346/ietf-l3-unicast-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..56941fdca9637af7766ecc1d57cf3564ddedb183 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8346/ietf-l3-unicast-topology.yang @@ -0,0 +1,359 @@ +module ietf-l3-unicast-topology { + yang-version 1.1; + namespace + "urn:ietf:params:xml:ns:yang:ietf-l3-unicast-topology"; + prefix "l3t"; + import ietf-network { + prefix "nw"; + } + import ietf-network-topology { + prefix "nt"; + } + import ietf-inet-types { + prefix "inet"; + } + import ietf-routing-types { + prefix "rt-types"; + } + organization + "IETF I2RS (Interface to the Routing System) Working Group"; + contact + "WG Web: + WG List: + Editor: Alexander Clemm + + Editor: Jan Medved + + Editor: Robert Varga + + Editor: Xufeng Liu + + Editor: Nitin Bahadur + + Editor: Hariharan Ananthakrishnan + "; + description + "This module defines a model for Layer 3 Unicast + topologies. + + 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 8346; see the RFC itself for full legal notices."; + revision "2018-02-26" { + description + "Initial revision."; + reference + "RFC 8346: A YANG Data Model for Layer 3 Topologies"; + } + + identity flag-identity { + description "Base type for flags"; + } + + typedef l3-event-type { + type enumeration { + enum "add" { + description + "A Layer 3 node, link, prefix, or termination point has + been added"; + } + enum "remove" { + description + "A Layer 3 node, link, prefix, or termination point has + been removed"; + } + enum "update" { + description + "A Layer 3 node, link, prefix, or termination point has + been updated"; + } + } + description "Layer 3 event type for notifications"; + } + + typedef prefix-flag-type { + type identityref { + base "flag-identity"; + } + description "Prefix flag attributes"; + } + + typedef node-flag-type { + type identityref { + base "flag-identity"; + } + description "Node flag attributes"; + } + + typedef link-flag-type { + type identityref { + base "flag-identity"; + } + description "Link flag attributes"; + } + + typedef l3-flag-type { + type identityref { + base "flag-identity"; + } + description "L3 flag attributes"; + } + + grouping l3-prefix-attributes { + description + "L3 prefix attributes"; + leaf prefix { + type inet:ip-prefix; + description + "IP prefix value"; + } + leaf metric { + type uint32; + description + "Prefix metric"; + } + leaf-list flag { + type prefix-flag-type; + description + "Prefix flags"; + } + } + grouping l3-unicast-topology-type { + description "Identifies the topology type to be L3 Unicast."; + container l3-unicast-topology { + presence "indicates L3 Unicast topology"; + description + "The presence of the container node indicates L3 Unicast + topology"; + } + } + grouping l3-topology-attributes { + description "Topology scope attributes"; + container l3-topology-attributes { + description "Contains topology attributes"; + leaf name { + type string; + description + "Name of the topology"; + } + leaf-list flag { + type l3-flag-type; + description + "Topology flags"; + } + } + } + grouping l3-node-attributes { + description "L3 node scope attributes"; + container l3-node-attributes { + description + "Contains node attributes"; + leaf name { + type inet:domain-name; + description + "Node name"; + } + leaf-list flag { + type node-flag-type; + description + "Node flags"; + } + leaf-list router-id { + type rt-types:router-id; + description + "Router-id for the node"; + } + list prefix { + key "prefix"; + description + "A list of prefixes along with their attributes"; + uses l3-prefix-attributes; + } + } + } + grouping l3-link-attributes { + description + "L3 link scope attributes"; + container l3-link-attributes { + description + "Contains link attributes"; + leaf name { + type string; + description + "Link Name"; + } + leaf-list flag { + type link-flag-type; + description + "Link flags"; + } + leaf metric1 { + type uint64; + description + "Link Metric 1"; + } + leaf metric2 { + type uint64; + description + "Link Metric 2"; + } + } + } + grouping l3-termination-point-attributes { + description "L3 termination point scope attributes"; + container l3-termination-point-attributes { + description + "Contains termination point attributes"; + choice termination-point-type { + description + "Indicates the termination point type"; + case ip { + leaf-list ip-address { + type inet:ip-address; + description + "IPv4 or IPv6 address."; + } + } + case unnumbered { + leaf unnumbered-id { + type uint32; + description + "Unnumbered interface identifier. + The identifier will correspond to the ifIndex value + of the interface, i.e., the ifIndex value of the + ifEntry that represents the interface in + implementations where the Interfaces Group MIB + (RFC 2863) is supported."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + } + case interface-name { + leaf interface-name { + type string; + description + "Name of the interface. The name can (but does not + have to) correspond to an interface reference of a + containing node's interface, i.e., the path name of a + corresponding interface data node on the containing + node reminiscent of data type interface-ref defined + in RFC 8343. It should be noted that data type + interface-ref of RFC 8343 cannot be used directly, + + as this data type is used to reference an interface + in a datastore of a single node in the network, not + to uniquely reference interfaces across a network."; + reference + "RFC 8343: A YANG Data Model for Interface Management"; + } + } + } + } + } + augment "/nw:networks/nw:network/nw:network-types" { + description + "Introduces new network type for L3 Unicast topology"; + uses l3-unicast-topology-type; + } + augment "/nw:networks/nw:network" { + when "nw:network-types/l3t:l3-unicast-topology" { + description + "Augmentation parameters apply only for networks with + L3 Unicast topology"; + } + description + "L3 Unicast for the network as a whole"; + uses l3-topology-attributes; + } + augment "/nw:networks/nw:network/nw:node" { + when "../nw:network-types/l3t:l3-unicast-topology" { + description + "Augmentation parameters apply only for networks with + L3 Unicast topology"; + } + description + "L3 Unicast node-level attributes "; + uses l3-node-attributes; + } + augment "/nw:networks/nw:network/nt:link" { + when "../nw:network-types/l3t:l3-unicast-topology" { + description + "Augmentation parameters apply only for networks with + L3 Unicast topology"; + } + description + "Augments topology link attributes"; + uses l3-link-attributes; + } + augment "/nw:networks/nw:network/nw:node/" + +"nt:termination-point" { + when "../../nw:network-types/l3t:l3-unicast-topology" { + description + "Augmentation parameters apply only for networks with + L3 Unicast topology"; + } + description "Augments topology termination point configuration"; + uses l3-termination-point-attributes; + } + notification l3-node-event { + description + "Notification event for L3 node"; + leaf l3-event-type { + type l3-event-type; + description + "Event type"; + } + uses nw:node-ref; + uses l3-unicast-topology-type; + uses l3-node-attributes; + } + notification l3-link-event { + description + "Notification event for L3 link"; + leaf l3-event-type { + type l3-event-type; + description + "Event type"; + } + uses nt:link-ref; + uses l3-unicast-topology-type; + uses l3-link-attributes; + } + notification l3-prefix-event { + description + "Notification event for L3 prefix"; + leaf l3-event-type { + type l3-event-type; + description + "Event type"; + } + uses nw:node-ref; + uses l3-unicast-topology-type; + container prefix { + description + "Contains L3 prefix attributes"; + uses l3-prefix-attributes; + } + } + notification termination-point-event { + description + "Notification event for L3 termination point"; + leaf l3-event-type { + type l3-event-type; + description + "Event type"; + } + uses nt:tp-ref; + uses l3-unicast-topology-type; + uses l3-termination-point-attributes; + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8795/ietf-te-topology.yang b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8795/ietf-te-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..41edbcd1f419980b0f22c507d1f5c8e3e7838d48 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/rfc8795/ietf-te-topology.yang @@ -0,0 +1,1952 @@ +module ietf-te-topology { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-te-topology"; + prefix tet; + + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-te-types { + prefix te-types; + reference + "RFC 8776: Common YANG Data Types for Traffic Engineering"; + } + 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: + WG List: + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + + + Editor: Vishnu Pavan Beeram + + + Editor: Tarek Saad + + + Editor: Himanshu Shah + + + Editor: Oscar Gonzalez de Dios + "; + description + "This YANG module defines a TE topology model for representing, + retrieving, and manipulating technology-agnostic TE topologies. + + Copyright (c) 2020 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 8795; see the + RFC itself for full legal notices."; + + revision 2020-08-06 { + description + "Initial revision."; + reference + "RFC 8795: YANG Data Model for Traffic Engineering (TE) + Topologies"; + } + + /* + * Features + */ + + feature nsrlg { + description + "This feature indicates that the system supports NSRLGs + (Non-Shared Risk Link Groups)."; + } + + feature te-topology-hierarchy { + description + "This feature indicates that the system allows an underlay + and/or overlay TE topology hierarchy."; + } + + feature template { + description + "This feature indicates that the system supports + template configuration."; + } + + /* + * Typedefs + */ + + typedef geographic-coordinate-degree { + type decimal64 { + fraction-digits 8; + } + description + "Decimal degree (DD) used to express latitude and longitude + geographic coordinates."; + } + // geographic-coordinate-degree + + typedef te-info-source { + type enumeration { + enum unknown { + description + "The source is unknown."; + } + enum locally-configured { + description + "Configured entity."; + } + enum ospfv2 { + description + "OSPFv2."; + } + enum ospfv3 { + description + "OSPFv3."; + } + enum isis { + description + "IS-IS."; + } + enum bgp-ls { + description + "BGP-LS."; + reference + "RFC 7752: North-Bound Distribution of Link-State and + Traffic Engineering (TE) Information Using BGP"; + } + enum system-processed { + description + "System-processed entity."; + } + enum other { + description + "Other source."; + } + } + description + "Describes the type of source that has provided the + related information, and the source's credibility."; + } + // te-info-source + + /* + * Groupings + */ + + grouping connectivity-matrix-entry-path-attributes { + description + "Attributes of a connectivity matrix entry."; + leaf is-allowed { + type boolean; + description + "'true' - switching is allowed; + 'false' - switching is disallowed."; + } + container underlay { + if-feature "te-topology-hierarchy"; + description + "Attributes of the TE link underlay."; + reference + "RFC 4206: Label Switched Paths (LSP) Hierarchy with + Generalized Multi-Protocol Label Switching (GMPLS) + Traffic Engineering (TE)"; + uses te-link-underlay-attributes; + } + uses te-types:generic-path-constraints; + uses te-types:generic-path-optimization; + uses te-types:generic-path-properties; + } + // connectivity-matrix-entry-path-attributes + + grouping geolocation-container { + description + "Contains a GPS location."; + container geolocation { + config false; + description + "Contains a GPS location."; + leaf altitude { + type int64; + units "millimeters"; + description + "Distance above sea level."; + } + leaf latitude { + type geographic-coordinate-degree { + range "-90..90"; + } + description + "Relative position north or south on the Earth's surface."; + } + leaf longitude { + type geographic-coordinate-degree { + range "-180..180"; + } + description + "Angular distance east or west on the Earth's surface."; + } + } + // geolocation + } + // geolocation-container + + grouping information-source-state-attributes { + description + "The attributes identifying the source that has provided the + related information, and the source's credibility."; + leaf credibility-preference { + type uint16; + description + "The preference value for calculating the Traffic + Engineering database credibility value used for + tie-break selection between different information-source + values. A higher value is preferable."; + } + leaf logical-network-element { + type string; + description + "When applicable, this is the name of a logical network + element from which the information is learned."; + } + leaf network-instance { + type string; + description + "When applicable, this is the name of a network instance + from which the information is learned."; + } + } + // information-source-state-attributes + + grouping information-source-per-link-attributes { + description + "Per-node container of the attributes identifying the source + that has provided the related information, and the source's + credibility."; + leaf information-source { + type te-info-source; + config false; + description + "Indicates the type of information source."; + } + leaf information-source-instance { + type string; + config false; + description + "The name indicating the instance of the information + source."; + } + container information-source-state { + config false; + description + "Contains state attributes related to the information + source."; + uses information-source-state-attributes; + container topology { + description + "When the information is processed by the system, + the attributes in this container indicate which topology + is used to generate the result information."; + uses nt:link-ref; + } + } + } + // information-source-per-link-attributes + + grouping information-source-per-node-attributes { + description + "Per-node container of the attributes identifying the source + that has provided the related information, and the source's + credibility."; + leaf information-source { + type te-info-source; + config false; + description + "Indicates the type of information source."; + } + leaf information-source-instance { + type string; + config false; + description + "The name indicating the instance of the information + source."; + } + container information-source-state { + config false; + description + "Contains state attributes related to the information + source."; + uses information-source-state-attributes; + container topology { + description + "When the information is processed by the system, + the attributes in this container indicate which topology + is used to generate the result information."; + uses nw:node-ref; + } + } + } + // information-source-per-node-attributes + + grouping interface-switching-capability-list { + description + "List of Interface Switching Capability Descriptors (ISCDs)."; + list interface-switching-capability { + key "switching-capability encoding"; + description + "List of ISCDs for this link."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description + RFC 4203: OSPF Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS)"; + leaf switching-capability { + type identityref { + base te-types:switching-capabilities; + } + description + "Switching capability for this interface."; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "Encoding supported by this interface."; + } + uses te-link-iscd-attributes; + } + // interface-switching-capability + } + // interface-switching-capability-list + + grouping statistics-per-link { + description + "Statistics attributes per TE link."; + leaf discontinuity-time { + type yang:date-and-time; + description + "The time of the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + /* Administrative attributes */ + leaf disables { + type yang:counter32; + description + "Number of times that a link was disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that a link was enabled."; + } + leaf maintenance-clears { + type yang:counter32; + description + "Number of times that a link was taken out of maintenance."; + } + leaf maintenance-sets { + type yang:counter32; + description + "Number of times that a link was put in maintenance."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that a link was modified."; + } + /* Operational attributes */ + leaf downs { + type yang:counter32; + description + "Number of times that a link was set to an operational state + of 'down'."; + } + leaf ups { + type yang:counter32; + description + "Number of times that a link was set to an operational state + of 'up'."; + } + /* Recovery attributes */ + leaf fault-clears { + type yang:counter32; + description + "Number of times that a link experienced a fault-clear + event."; + } + leaf fault-detects { + type yang:counter32; + description + "Number of times that a link experienced fault detection."; + } + leaf protection-switches { + type yang:counter32; + description + "Number of times that a link experienced protection + switchover."; + } + leaf protection-reverts { + type yang:counter32; + description + "Number of times that a link experienced protection + reversion."; + } + leaf restoration-failures { + type yang:counter32; + description + "Number of times that a link experienced restoration + failure."; + } + leaf restoration-starts { + type yang:counter32; + description + "Number of times that a link experienced restoration + start."; + } + leaf restoration-successes { + type yang:counter32; + description + "Number of times that a link experienced restoration + success."; + } + leaf restoration-reversion-failures { + type yang:counter32; + description + "Number of times that a link experienced restoration + reversion failure."; + } + leaf restoration-reversion-starts { + type yang:counter32; + description + "Number of times that a link experienced restoration + reversion start."; + } + leaf restoration-reversion-successes { + type yang:counter32; + description + "Number of times that a link experienced restoration + reversion success."; + } + } + // statistics-per-link + + grouping statistics-per-node { + description + "Statistics attributes per TE node."; + leaf discontinuity-time { + type yang:date-and-time; + description + "The time of the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + container node { + description + "Contains statistics attributes at the TE node level."; + leaf disables { + type yang:counter32; + description + "Number of times that a node was disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that a node was enabled."; + } + leaf maintenance-sets { + type yang:counter32; + description + "Number of times that a node was put in maintenance."; + } + leaf maintenance-clears { + type yang:counter32; + description + "Number of times that a node was taken out of + maintenance."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that a node was modified."; + } + } + // node + container connectivity-matrix-entry { + description + "Contains statistics attributes at the level of a + connectivity matrix entry."; + leaf creates { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + created."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 7.2, 'create' operation"; + } + leaf deletes { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + deleted."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 7.2, 'delete' operation"; + } + leaf disables { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + enabled."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + modified."; + } + } + // connectivity-matrix-entry + } + // statistics-per-node + + grouping statistics-per-ttp { + description + "Statistics attributes per TE TTP (Tunnel Termination Point)."; + leaf discontinuity-time { + type yang:date-and-time; + description + "The time of the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + container tunnel-termination-point { + description + "Contains statistics attributes at the TE TTP level."; + /* Administrative attributes */ + leaf disables { + type yang:counter32; + description + "Number of times that a TTP was disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that a TTP was enabled."; + } + leaf maintenance-clears { + type yang:counter32; + description + "Number of times that a TTP was taken out of maintenance."; + } + leaf maintenance-sets { + type yang:counter32; + description + "Number of times that a TTP was put in maintenance."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that a TTP was modified."; + } + /* Operational attributes */ + leaf downs { + type yang:counter32; + description + "Number of times that a TTP was set to an operational state + of 'down'."; + } + leaf ups { + type yang:counter32; + description + "Number of times that a TTP was set to an operational state + of 'up'."; + } + leaf in-service-clears { + type yang:counter32; + description + "Number of times that a TTP was taken out of service + (TE tunnel was released)."; + } + leaf in-service-sets { + type yang:counter32; + description + "Number of times that a TTP was put in service by a TE + tunnel (TE tunnel was set up)."; + } + } + // tunnel-termination-point + container local-link-connectivity { + description + "Contains statistics attributes at the TE LLCL (Local Link + Connectivity List) level."; + leaf creates { + type yang:counter32; + description + "Number of times that an LLCL entry was created."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 7.2, 'create' operation"; + } + leaf deletes { + type yang:counter32; + description + "Number of times that an LLCL entry was deleted."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 7.2, 'delete' operation"; + } + leaf disables { + type yang:counter32; + description + "Number of times that an LLCL entry was disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that an LLCL entry was enabled."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that an LLCL entry was modified."; + } + } + // local-link-connectivity + } + // statistics-per-ttp + + grouping te-link-augment { + description + "Augmentation for a TE link."; + uses te-link-config; + uses te-link-state-derived; + container statistics { + config false; + description + "Statistics data."; + uses statistics-per-link; + } + } + // te-link-augment + + grouping te-link-config { + description + "TE link configuration grouping."; + choice bundle-stack-level { + description + "The TE link can be partitioned into bundled links or + component links."; + case bundle { + container bundled-links { + description + "A set of bundled links."; + reference + "RFC 4201: Link Bundling in MPLS Traffic + Engineering (TE)"; + list bundled-link { + key "sequence"; + description + "Specifies a bundled interface that is + further partitioned."; + leaf sequence { + type uint32; + description + "Identifies the sequence in the bundle."; + } + } + } + } + case component { + container component-links { + description + "A set of component links."; + list component-link { + key "sequence"; + description + "Specifies a component interface that is + sufficient to unambiguously identify the + appropriate resources."; + leaf sequence { + type uint32; + description + "Identifies the sequence in the bundle."; + } + leaf src-interface-ref { + type string; + description + "Reference to a component link interface on the + source node."; + } + leaf des-interface-ref { + type string; + description + "Reference to a component link interface on the + destination node."; + } + } + } + } + } + // bundle-stack-level + leaf-list te-link-template { + if-feature "template"; + type leafref { + path "../../../../te/templates/link-template/name"; + } + description + "The reference to a TE link template."; + } + uses te-link-config-attributes; + } + // te-link-config + + grouping te-link-config-attributes { + description + "Link configuration attributes in a TE topology."; + container te-link-attributes { + description + "Link attributes in a TE topology."; + leaf access-type { + type te-types:te-link-access-type; + description + "Link access type, which can be point-to-point or + multi-access."; + } + container external-domain { + description + "For an inter-domain link, specifies the attributes of + the remote end of the link, to facilitate the signaling at + the local end."; + uses nw:network-ref; + leaf remote-te-node-id { + type te-types:te-node-id; + description + "Remote TE node identifier, used together with + 'remote-te-link-tp-id' to identify the remote Link + Termination Point (LTP) in a different domain."; + } + leaf remote-te-link-tp-id { + type te-types:te-tp-id; + description + "Remote TE LTP identifier, used together with + 'remote-te-node-id' to identify the remote LTP in a + different domain."; + } + } + leaf is-abstract { + type empty; + description + "Present if the link is abstract."; + } + leaf name { + type string; + description + "Link name."; + } + container underlay { + if-feature "te-topology-hierarchy"; + description + "Attributes of the TE link underlay."; + reference + "RFC 4206: Label Switched Paths (LSP) Hierarchy with + Generalized Multi-Protocol Label Switching (GMPLS) + Traffic Engineering (TE)"; + uses te-link-underlay-attributes; + } + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the link."; + } + uses te-link-info-attributes; + } + // te-link-attributes + } + // te-link-config-attributes + + grouping te-link-info-attributes { + description + "Advertised TE information attributes."; + leaf link-index { + type uint64; + description + "The link identifier. If OSPF is used, this object + represents an ospfLsdbID. If IS-IS is used, this object + represents an isisLSPID. If a locally configured link is + used, this object represents a unique value, which is + locally defined in a router."; + } + leaf administrative-group { + type te-types:admin-groups; + description + "Administrative group or color of the link. + This attribute covers both administrative groups (defined + in RFCs 3630 and 5305) and Extended Administrative Groups + (defined in RFC 7308)."; + 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)"; + } + uses interface-switching-capability-list; + uses te-types:label-set-info; + leaf link-protection-type { + type identityref { + base te-types:link-protection-type; + } + description + "Link Protection Type desired for this link."; + reference + "RFC 4202: Routing Extensions in Support of + Generalized Multi-Protocol Label Switching (GMPLS)"; + } + container max-link-bandwidth { + uses te-types:te-bandwidth; + description + "Maximum bandwidth that can be seen on this link in this + direction. Units are in bytes per second."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + } + container max-resv-link-bandwidth { + uses te-types:te-bandwidth; + description + "Maximum amount of bandwidth that can be reserved in this + direction in this link. Units are in bytes per second."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + } + list unreserved-bandwidth { + key "priority"; + max-elements 8; + description + "Unreserved bandwidth for priority levels 0-7. Units are in + bytes per second."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + leaf priority { + type uint8 { + range "0..7"; + } + description + "Priority."; + } + uses te-types:te-bandwidth; + } + leaf te-default-metric { + type uint32; + description + "Traffic Engineering metric."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + } + leaf te-delay-metric { + type uint32; + description + "Traffic Engineering delay metric."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions"; + } + leaf te-igp-metric { + type uint32; + description + "IGP metric used for Traffic Engineering."; + reference + "RFC 3785: Use of Interior Gateway Protocol (IGP) Metric as a + second MPLS Traffic Engineering (TE) Metric"; + } + container te-srlgs { + description + "Contains a list of SRLGs."; + leaf-list value { + type te-types:srlg; + description + "SRLG value."; + reference + "RFC 4202: Routing Extensions in Support of + Generalized Multi-Protocol Label Switching (GMPLS)"; + } + } + container te-nsrlgs { + if-feature "nsrlg"; + description + "Contains a list of NSRLGs (Non-Shared Risk Link Groups). + When an abstract TE link is configured, this list specifies + the request that underlay TE paths need to be mutually + disjoint with other TE links in the same groups."; + leaf-list id { + type uint32; + description + "NSRLG ID, uniquely configured within a topology."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + } + } + // te-link-info-attributes + + grouping te-link-iscd-attributes { + description + "TE link ISCD attributes."; + reference + "RFC 4203: OSPF Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS), Section 1.4"; + list max-lsp-bandwidth { + key "priority"; + max-elements 8; + description + "Maximum Label Switched Path (LSP) bandwidth at + priorities 0-7."; + leaf priority { + type uint8 { + range "0..7"; + } + description + "Priority."; + } + uses te-types:te-bandwidth; + } + } + // te-link-iscd-attributes + + grouping te-link-state-derived { + description + "Link state attributes in a TE topology."; + leaf oper-status { + type te-types:te-oper-status; + config false; + description + "The current operational state of the link."; + } + leaf is-transitional { + type empty; + config false; + description + "Present if the link is transitional; used as an + alternative approach in lieu of 'inter-layer-lock-id' + for path computation in a TE topology covering multiple + layers or multiple regions."; + reference + "RFC 5212: Requirements for GMPLS-Based Multi-Region and + Multi-Layer Networks (MRN/MLN) + RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + } + uses information-source-per-link-attributes; + list information-source-entry { + key "information-source information-source-instance"; + config false; + description + "A list of information sources learned, including the source + that is used."; + uses information-source-per-link-attributes; + uses te-link-info-attributes; + } + container recovery { + config false; + description + "Status of the recovery process."; + leaf restoration-status { + type te-types:te-recovery-status; + description + "Restoration status."; + } + leaf protection-status { + type te-types:te-recovery-status; + description + "Protection status."; + } + } + container underlay { + if-feature "te-topology-hierarchy"; + config false; + description + "State attributes for the TE link underlay."; + leaf dynamic { + type boolean; + description + "'true' if the underlay is dynamically created."; + } + leaf committed { + type boolean; + description + "'true' if the underlay is committed."; + } + } + } + // te-link-state-derived + + grouping te-link-underlay-attributes { + description + "Attributes for the TE link underlay."; + reference + "RFC 4206: Label Switched Paths (LSP) Hierarchy with + Generalized Multi-Protocol Label Switching (GMPLS) + Traffic Engineering (TE)"; + leaf enabled { + type boolean; + description + "'true' if the underlay is enabled. + 'false' if the underlay is disabled."; + } + container primary-path { + description + "The service path on the underlay topology that + supports this link."; + uses nw:network-ref; + list path-element { + key "path-element-id"; + description + "A list of path elements describing the service path."; + leaf path-element-id { + type uint32; + description + "To identify the element in a path."; + } + uses te-path-element; + } + } + // primary-path + list backup-path { + key "index"; + description + "A list of backup service paths on the underlay topology that + protect the underlay primary path. If the primary path is + not protected, the list contains zero elements. If the + primary path is protected, the list contains one or more + elements."; + leaf index { + type uint32; + description + "A sequence number to identify a backup path."; + } + uses nw:network-ref; + list path-element { + key "path-element-id"; + description + "A list of path elements describing the backup service + path."; + leaf path-element-id { + type uint32; + description + "To identify the element in a path."; + } + uses te-path-element; + } + } + // backup-path + leaf protection-type { + type identityref { + base te-types:lsp-protection-type; + } + description + "Underlay protection type desired for this link."; + } + container tunnel-termination-points { + description + "Underlay TTPs desired for this link."; + leaf source { + type binary; + description + "Source TTP identifier."; + } + leaf destination { + type binary; + description + "Destination TTP identifier."; + } + } + container tunnels { + description + "Underlay TE tunnels supporting this TE link."; + leaf sharing { + type boolean; + default "true"; + description + "'true' if the underlay tunnel can be shared with other + TE links; + 'false' if the underlay tunnel is dedicated to this + TE link. + This leaf is the default option for all TE tunnels + and may be overridden by the per-TE-tunnel value."; + } + list tunnel { + key "tunnel-name"; + description + "Zero, one, or more underlay TE tunnels that support this + TE link."; + leaf tunnel-name { + type string; + description + "A tunnel name uniquely identifies an underlay TE tunnel, + used together with the 'source-node' value for this + link."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels"; + } + leaf sharing { + type boolean; + description + "'true' if the underlay tunnel can be shared with other + TE links; + 'false' if the underlay tunnel is dedicated to this + TE link."; + } + } + // tunnel + } + // tunnels + } + // te-link-underlay-attributes + + grouping te-node-augment { + description + "Augmentation for a TE node."; + uses te-node-config; + uses te-node-state-derived; + container statistics { + config false; + description + "Statistics data."; + uses statistics-per-node; + } + list tunnel-termination-point { + key "tunnel-tp-id"; + description + "A termination point can terminate a tunnel."; + leaf tunnel-tp-id { + type binary; + description + "TTP identifier."; + } + uses te-node-tunnel-termination-point-config; + leaf oper-status { + type te-types:te-oper-status; + config false; + description + "The current operational state of the TTP."; + } + uses geolocation-container; + container statistics { + config false; + description + "Statistics data."; + uses statistics-per-ttp; + } + // Relationship to other TTPs + list supporting-tunnel-termination-point { + key "node-ref tunnel-tp-ref"; + description + "Identifies the TTPs on which this TTP depends."; + leaf node-ref { + type inet:uri; + description + "This leaf identifies the node in which the supporting + TTP is present. + This node is either the supporting node or a node in + an underlay topology."; + } + leaf tunnel-tp-ref { + type binary; + description + "Reference to a TTP that is in either the supporting node + or a node in an underlay topology."; + } + } + // supporting-tunnel-termination-point + } + // tunnel-termination-point + } + // te-node-augment + + grouping te-node-config { + description + "TE node configuration grouping."; + leaf-list te-node-template { + if-feature "template"; + type leafref { + path "../../../../te/templates/node-template/name"; + } + description + "The reference to a TE node template."; + } + uses te-node-config-attributes; + } + // te-node-config + + grouping te-node-config-attributes { + description + "Configuration node attributes in a TE topology."; + container te-node-attributes { + description + "Contains node attributes in a TE topology."; + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the link."; + } + uses te-node-connectivity-matrices; + uses te-node-info-attributes; + } + } + // te-node-config-attributes + + grouping te-node-config-attributes-template { + description + "Configuration node attributes for a template in a TE + topology."; + container te-node-attributes { + description + "Contains node attributes in a TE topology."; + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the link."; + } + uses te-node-info-attributes; + } + } + // te-node-config-attributes-template + + grouping te-node-connectivity-matrices { + description + "Connectivity matrix on a TE node."; + container connectivity-matrices { + description + "Contains a connectivity matrix on a TE node."; + leaf number-of-entries { + type uint16; + description + "The number of connectivity matrix entries. + If this number is specified in the configuration request, + the number is the requested number of entries, which may + not all be listed in the list; + if this number is reported in the state data, + the number is the current number of operational entries."; + } + uses te-types:label-set-info; + uses connectivity-matrix-entry-path-attributes; + list connectivity-matrix { + key "id"; + description + "Represents a node's switching limitations, i.e., + limitations in the interconnecting network TE links + across the node."; + reference + "RFC 7579: General Network Element Constraint Encoding + for GMPLS-Controlled Networks"; + leaf id { + type uint32; + description + "Identifies the connectivity matrix entry."; + } + } + // connectivity-matrix + } + // connectivity-matrices + } + // te-node-connectivity-matrices + + grouping te-node-connectivity-matrix-attributes { + description + "Termination point references of a connectivity matrix entry."; + container from { + description + "Reference to a source LTP."; + leaf tp-ref { + type leafref { + path "../../../../../../nt:termination-point/nt:tp-id"; + } + description + "Relative reference to a termination point."; + } + uses te-types:label-set-info; + } + container to { + description + "Reference to a destination LTP."; + leaf tp-ref { + type leafref { + path "../../../../../../nt:termination-point/nt:tp-id"; + } + description + "Relative reference to a termination point."; + } + uses te-types:label-set-info; + } + uses connectivity-matrix-entry-path-attributes; + } + // te-node-connectivity-matrix-attributes + + grouping te-node-info-attributes { + description + "Advertised TE information attributes."; + leaf domain-id { + type uint32; + description + "Identifies the domain to which this node belongs. + This attribute is used to support inter-domain links."; + reference + "RFC 5152: A Per-Domain Path Computation Method for + Establishing Inter-Domain Traffic Engineering (TE) + Label Switched Paths (LSPs) + RFC 5316: ISIS Extensions in Support of Inter-Autonomous + System (AS) MPLS and GMPLS Traffic Engineering + RFC 5392: OSPF Extensions in Support of Inter-Autonomous + System (AS) MPLS and GMPLS Traffic Engineering"; + } + leaf is-abstract { + type empty; + description + "Present if the node is abstract; not present if the node + is actual."; + } + leaf name { + type string; + description + "Node name."; + } + leaf-list signaling-address { + type inet:ip-address; + description + "The node's signaling address."; + } + container underlay-topology { + if-feature "te-topology-hierarchy"; + description + "When an abstract node encapsulates a topology, the + attributes in this container point to said topology."; + uses nw:network-ref; + } + } + // te-node-info-attributes + + grouping te-node-state-derived { + description + "Node state attributes in a TE topology."; + leaf oper-status { + type te-types:te-oper-status; + config false; + description + "The current operational state of the node."; + } + uses geolocation-container; + leaf is-multi-access-dr { + type empty; + config false; + description + "The presence of this attribute indicates that this TE node + is a pseudonode elected as a designated router."; + reference + "RFC 1195: Use of OSI IS-IS for Routing in TCP/IP and Dual + Environments + RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2"; + } + uses information-source-per-node-attributes; + list information-source-entry { + key "information-source information-source-instance"; + config false; + description + "A list of information sources learned, including the source + that is used."; + uses information-source-per-node-attributes; + uses te-node-connectivity-matrices; + uses te-node-info-attributes; + } + } + // te-node-state-derived + + grouping te-node-tunnel-termination-point-config { + description + "Termination capability of a TTP on a TE node."; + uses te-node-tunnel-termination-point-config-attributes; + container local-link-connectivities { + description + "Contains an LLCL for a TTP on a TE node."; + leaf number-of-entries { + type uint16; + description + "The number of LLCL entries. + If this number is specified in the configuration request, + the number is the requested number of entries, which may + not all be listed in the list; + if this number is reported in the state data, + the number is the current number of operational entries."; + } + uses te-types:label-set-info; + uses connectivity-matrix-entry-path-attributes; + } + } + // te-node-tunnel-termination-point-config + + grouping te-node-tunnel-termination-point-config-attributes { + description + "Configuration attributes of a TTP on a TE node."; + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the TTP."; + } + leaf name { + type string; + description + "A descriptive name for the TTP."; + } + leaf switching-capability { + type identityref { + base te-types:switching-capabilities; + } + description + "Switching capability for this interface."; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "Encoding supported by this interface."; + } + leaf-list inter-layer-lock-id { + type uint32; + description + "Inter-layer lock ID, used for path computation in a TE + topology covering multiple layers or multiple regions."; + reference + "RFC 5212: Requirements for GMPLS-Based Multi-Region and + Multi-Layer Networks (MRN/MLN) + RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + } + leaf protection-type { + type identityref { + base te-types:lsp-protection-type; + } + description + "The protection type that this TTP is capable of."; + } + container client-layer-adaptation { + description + "Contains capability information to support a client-layer + adaptation in a multi-layer topology."; + list switching-capability { + key "switching-capability encoding"; + description + "List of supported switching capabilities."; + reference + "RFC 4202: Routing Extensions in Support of + Generalized Multi-Protocol Label Switching (GMPLS) + RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + leaf switching-capability { + type identityref { + base te-types:switching-capabilities; + } + description + "Switching capability for the client-layer adaptation."; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "Encoding supported by the client-layer adaptation."; + } + uses te-types:te-bandwidth; + } + } + } + // te-node-tunnel-termination-point-config-attributes + + grouping te-node-tunnel-termination-point-llc-list { + description + "LLCL of a TTP on a TE node."; + list local-link-connectivity { + key "link-tp-ref"; + description + "The termination capabilities between the TTP and the LTP. + This capability information can be used to compute + the tunnel path. + The Interface Adjustment Capability Descriptors (IACDs) + (defined in RFC 6001) on each LTP can be derived from + this list."; + reference + "RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + leaf link-tp-ref { + type leafref { + path "../../../../../nt:termination-point/nt:tp-id"; + } + description + "LTP."; + } + uses te-types:label-set-info; + uses connectivity-matrix-entry-path-attributes; + } + } + // te-node-tunnel-termination-point-llc-list + + grouping te-path-element { + description + "A group of attributes defining an element in a TE path, + such as a TE node, TE link, TE atomic resource, or label."; + uses te-types:explicit-route-hop; + } + // te-path-element + + grouping te-termination-point-augment { + description + "Augmentation for a TE termination point."; + leaf te-tp-id { + type te-types:te-tp-id; + description + "An identifier that uniquely identifies a TE termination + point."; + } + container te { + must '../te-tp-id'; + presence "TE support"; + description + "Indicates TE support."; + uses te-termination-point-config; + leaf oper-status { + type te-types:te-oper-status; + config false; + description + "The current operational state of the LTP."; + } + uses geolocation-container; + } + } + // te-termination-point-augment + + grouping te-termination-point-config { + description + "TE termination point configuration grouping."; + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the LTP."; + } + leaf name { + type string; + description + "A descriptive name for the LTP."; + } + uses interface-switching-capability-list; + leaf inter-domain-plug-id { + type binary; + description + "A network-wide unique number that identifies on the + network a connection that supports a given inter-domain + TE link. This is a more flexible alternative to specifying + 'remote-te-node-id' and 'remote-te-link-tp-id' on a TE link + when the provider either does not know 'remote-te-node-id' + and 'remote-te-link-tp-id' or needs to give the client the + flexibility to mix and match multiple topologies."; + } + leaf-list inter-layer-lock-id { + type uint32; + description + "Inter-layer lock ID, used for path computation in a TE + topology covering multiple layers or multiple regions."; + reference + "RFC 5212: Requirements for GMPLS-Based Multi-Region and + Multi-Layer Networks (MRN/MLN) + RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + } + } + // te-termination-point-config + + grouping te-topologies-augment { + description + "Augmentation for TE topologies."; + container te { + presence "TE support"; + description + "Indicates TE support."; + container templates { + description + "Configuration parameters for templates used for a TE + topology."; + list node-template { + if-feature "template"; + key "name"; + leaf name { + type te-types:te-template-name; + description + "The name to identify a TE node template."; + } + description + "The list of TE node templates used to define sharable + and reusable TE node attributes."; + uses template-attributes; + uses te-node-config-attributes-template; + } + // node-template + list link-template { + if-feature "template"; + key "name"; + leaf name { + type te-types:te-template-name; + description + "The name to identify a TE link template."; + } + description + "The list of TE link templates used to define sharable + and reusable TE link attributes."; + uses template-attributes; + uses te-link-config-attributes; + } + // link-template + } + // templates + } + // te + } + // te-topologies-augment + + grouping te-topology-augment { + description + "Augmentation for a TE topology."; + uses te-types:te-topology-identifier; + container te { + must '../te-topology-identifier/provider-id' + + ' and ../te-topology-identifier/client-id' + + ' and ../te-topology-identifier/topology-id'; + presence "TE support"; + description + "Indicates TE support."; + uses te-topology-config; + uses geolocation-container; + } + } + // te-topology-augment + + grouping te-topology-config { + description + "TE topology configuration grouping."; + leaf name { + type string; + description + "Name of the TE topology. This attribute is optional and can + be specified by the operator to describe the TE topology, + which can be useful when 'network-id' (RFC 8345) is not + descriptive and not modifiable because of being generated + by the system."; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + leaf preference { + type uint8 { + range "1..255"; + } + description + "Specifies a preference for this topology. A lower number + indicates a higher preference."; + } + leaf optimization-criterion { + type identityref { + base te-types:objective-function-type; + } + description + "Optimization criterion applied to this topology."; + reference + "RFC 3272: Overview and Principles of Internet Traffic + Engineering"; + } + list nsrlg { + if-feature "nsrlg"; + key "id"; + description + "List of NSRLGs (Non-Shared Risk Link Groups)."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + leaf id { + type uint32; + description + "Identifies the NSRLG entry."; + } + leaf disjointness { + type te-types:te-path-disjointness; + description + "The type of resource disjointness."; + } + } + // nsrlg + } + // te-topology-config + + grouping template-attributes { + description + "Common attributes for all templates."; + leaf priority { + type uint16; + description + "The preference value for resolving conflicts between + different templates. When two or more templates specify + values for one configuration attribute, the value from the + template with the highest priority is used. + A lower number indicates a higher priority. The highest + priority is 0."; + } + leaf reference-change-policy { + type enumeration { + enum no-action { + description + "When an attribute changes in this template, the + configuration node referring to this template does + not take any action."; + } + enum not-allowed { + description + "When any configuration object has a reference to this + template, changing this template is not allowed."; + } + enum cascade { + description + "When an attribute changes in this template, the + configuration object referring to this template applies + the new attribute value to the corresponding + configuration."; + } + } + description + "This attribute specifies the action taken for a + configuration node that has a reference to this template."; + } + } + // template-attributes + + /* + * Data nodes + */ + + augment "/nw:networks/nw:network/nw:network-types" { + description + "Introduces a new network type for a TE topology."; + container te-topology { + presence "Indicates a TE topology"; + description + "Its presence identifies the TE topology type."; + } + } + + augment "/nw:networks" { + description + "Augmentation parameters for TE topologies."; + uses te-topologies-augment; + } + + augment "/nw:networks/nw:network" { + when 'nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Configuration parameters for a TE topology."; + uses te-topology-augment; + } + + augment "/nw:networks/nw:network/nw:node" { + when '../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Configuration parameters for TE at the node level."; + leaf te-node-id { + type te-types:te-node-id; + description + "The identifier of a node in the TE topology. + A node is specific to a topology to which it belongs."; + } + container te { + must '../te-node-id' { + description + "'te-node-id' is mandatory."; + } + must 'count(../nw:supporting-node)<=1' { + description + "For a node in a TE topology, there cannot be more + than one supporting node. If multiple nodes are + abstracted, the 'underlay-topology' field is used."; + } + presence "TE support"; + description + "Indicates TE support."; + uses te-node-augment; + } + } + + augment "/nw:networks/nw:network/nt:link" { + when '../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Configuration parameters for TE at the link level."; + container te { + must 'count(../nt:supporting-link)<=1' { + description + "For a link in a TE topology, there cannot be more + than one supporting link. If one or more link paths are + abstracted, the underlay is used."; + } + presence "TE support"; + description + "Indicates TE support."; + uses te-link-augment; + } + } + + augment "/nw:networks/nw:network/nw:node/" + + "nt:termination-point" { + when '../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Configuration parameters for TE at the termination point + level."; + uses te-termination-point-augment; + } + + augment "/nw:networks/nw:network/nt:link/te/bundle-stack-level/" + + "bundle/bundled-links/bundled-link" { + when '../../../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Augmentation for a TE bundled link."; + leaf src-tp-ref { + type leafref { + path "../../../../../nw:node[nw:node-id = " + + "current()/../../../../nt:source/" + + "nt:source-node]/" + + "nt:termination-point/nt:tp-id"; + require-instance true; + } + description + "Reference to another TE termination point on the + same source node."; + } + leaf des-tp-ref { + type leafref { + path "../../../../../nw:node[nw:node-id = " + + "current()/../../../../nt:destination/" + + "nt:dest-node]/" + + "nt:termination-point/nt:tp-id"; + require-instance true; + } + description + "Reference to another TE termination point on the + same destination node."; + } + } + + augment "/nw:networks/nw:network/nw:node/te/" + + "information-source-entry/connectivity-matrices/" + + "connectivity-matrix" { + when '../../../../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Augmentation for the TE node connectivity matrix."; + uses te-node-connectivity-matrix-attributes; + } + + augment "/nw:networks/nw:network/nw:node/te/te-node-attributes/" + + "connectivity-matrices/connectivity-matrix" { + when '../../../../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Augmentation for the TE node connectivity matrix."; + uses te-node-connectivity-matrix-attributes; + } + + augment "/nw:networks/nw:network/nw:node/te/" + + "tunnel-termination-point/local-link-connectivities" { + when '../../../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Augmentation for TE node TTP LLCs (Local Link + Connectivities)."; + uses te-node-tunnel-termination-point-llc-list; + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_fan_ctrl/yang/yang-repo-url.txt b/src/tests/tools/mock_nce_fan_ctrl/yang/yang-repo-url.txt new file mode 100644 index 0000000000000000000000000000000000000000..df60dab3b781fe879d9a451582bec0cf7534bb59 --- /dev/null +++ b/src/tests/tools/mock_nce_fan_ctrl/yang/yang-repo-url.txt @@ -0,0 +1 @@ +https://github.com/YangModels/yang diff --git a/src/tests/tools/mock_nce_t_ctrl/Dockerfile b/src/tests/tools/mock_nce_t_ctrl/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..60f7a7b06fddbc4db31180481a69b55d30b2fdc0 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/Dockerfile @@ -0,0 +1,70 @@ +# 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 git build-essential cmake libpcre2-dev python3-dev python3-cffi && \ + rm -rf /var/lib/apt/lists/* + +# Download, build and install libyang. Note that APT package is outdated +# - Ref: https://github.com/CESNET/libyang +# - Ref: https://github.com/CESNET/libyang-python/ +RUN mkdir -p /var/libyang +RUN git clone https://github.com/CESNET/libyang.git /var/libyang +WORKDIR /var/libyang +RUN git fetch +RUN git checkout v2.1.148 +RUN mkdir -p /var/libyang/build +WORKDIR /var/libyang/build +RUN cmake -D CMAKE_BUILD_TYPE:String="Release" .. +RUN make +RUN make install +RUN ldconfig + +# 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 specific Python packages +RUN mkdir -p /var/teraflow/ +WORKDIR /var/teraflow/ +COPY src/common/tools/rest_conf/server/requirements.in ./requirements.in +RUN pip-compile --quiet --output-file=requirements.txt requirements.in +RUN python3 -m pip install -r requirements.txt + +# Get component files +RUN mkdir -p /var/teraflow/common/tools/ +WORKDIR /var/teraflow/ +COPY src/common/tools/rest_api/ ./common/tools/rest_api/ +COPY src/common/tools/rest_conf/ ./common/tools/rest_conf/ +COPY src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/*.py ./nce_t_ctrl/ +COPY src/tests/tools/mock_nce_t_ctrl/yang/. ./yang/ +COPY src/tests/tools/mock_nce_t_ctrl/startup.json ./startup.json + +# Configure RESTCONF Server +ENV RESTCONF_PREFIX="/restconf" +ENV YANG_SEARCH_PATH="./yang" +ENV STARTUP_FILE="./startup.json" + +# Configure Flask for production +ENV FLASK_ENV="production" + +# Start the service +ENTRYPOINT ["gunicorn", "--workers", "1", "--worker-class", "eventlet", "--bind", "0.0.0.0:8080", "nce_t_ctrl.app:app"] diff --git a/src/tests/tools/mock_nce_t_ctrl/README.md b/src/tests/tools/mock_nce_t_ctrl/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e6877a94bd3a36ef237d480ddb1848af9a6d31a8 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/README.md @@ -0,0 +1,23 @@ +# RESTCONF-based NCE-T Controller + +This server implements a basic RESTCONF Server that can load, potentially, any YANG data model. +In this case, it is prepared to load a NCE-T Controller based on: +- IETF Network Topology +- IETF YANG Data Model for Transport Network Client Signals +- IETF YANG Data Model for Traffic Engineering Tunnels, Label Switched Paths and Interfaces + + +## Build the RESTCONF-based NCE-T Controller Docker image +```bash +./build.sh +``` + +## Deploy the RESTCONF-based NCE-T Controller +```bash +./deploy.sh +``` + +## Destroy the RESTCONF-based NCE-T Controller +```bash +./destroy.sh +``` diff --git a/src/tests/tools/mock_nce_t_ctrl/build.sh b/src/tests/tools/mock_nce_t_ctrl/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..d02c0fc435bf0d108a42fa639a1a4c48067a09e3 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/build.sh @@ -0,0 +1,22 @@ +#!/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)/../../../../ + +# Build image for NCE-T Controller +docker buildx build -t nce-t-ctrl:test -f ./src/tests/tools/mock_nce_t_ctrl/Dockerfile . +#docker tag nce-t-ctrl:test localhost:32000/tfs/nce-t-ctrl:test +#docker push localhost:32000/tfs/nce-t-ctrl:test diff --git a/src/tests/tools/mock_nce_t_ctrl/deploy.sh b/src/tests/tools/mock_nce_t_ctrl/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..e1d36506eab531cd5c5480e9afa6db6a51eee952 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/deploy.sh @@ -0,0 +1,32 @@ +#!/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. + + +# Cleanup +docker rm --force nce-t-ctrl + + +# Create NCE-T Controller +docker run --detach --name nce-t-ctrl --publish 8080:8080 nce-t-ctrl:test + + +sleep 2 + + +# Dump NCE-T Controller container +docker ps -a + + +echo "Bye!" diff --git a/src/tests/tools/mock_nce_t_ctrl/destroy.sh b/src/tests/tools/mock_nce_t_ctrl/destroy.sh new file mode 100755 index 0000000000000000000000000000000000000000..44ee8703c25ed283dd3db32a542ec29e15e45a22 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/destroy.sh @@ -0,0 +1,25 @@ +#!/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. + + +# Cleanup +docker rm --force nce-t-ctrl + + +# Dump Docker containers +docker ps -a + + +echo "Bye!" diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_client/Requests.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_client/Requests.py new file mode 100644 index 0000000000000000000000000000000000000000..fb3e06ecb7eba54d0ffe4876de1003e0a4b115fe --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_client/Requests.py @@ -0,0 +1,149 @@ +# 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. + + +OSU_TUNNEL_NAME = 'osu_tunnel_1' +URL_OSU_TUNNEL_ITEM = '/ietf-te:te/tunnels/tunnel={:s}'.format(OSU_TUNNEL_NAME) +REQUEST_OSU_TUNNEL = {"ietf-te:te": {"tunnels": {"tunnel": [ + { + "name": OSU_TUNNEL_NAME, + "title": "OSU_TUNNEL_1", + "admin-state": "ietf-te-types:tunnel-admin-state-up", + "delay": 20, + "te-bandwidth": { + "layer": "odu", + "odu-type": "osuflex", + "number": 40 + }, + "bidirectional": True, + "destination-endpoints": { + "destination-endpoint": [ + { + "node-id": "10.0.30.1", + "tp-id": "200", + "ttp-channel-name": "och:1-odu2:1-oduflex:3-osuflex:1", + "protection-role": "work" + } + ] + }, + "source-endpoints": { + "source-endpoint": [ + { + "node-id": "10.0.10.1", + "tp-id": "200", + "ttp-channel-name": "och:1-odu2:1-oduflex:1-osuflex:2", + "protection-role": "work" + } + ] + }, + "restoration": { + "restoration-type": "ietf-te-types:lsp-restoration-not-applicable", + "restoration-lock": False + }, + "protection": { + "protection-type": "ietf-te-types:lsp-protection-unprotected", + "protection-reversion-disable": True + } + } +]}}} + +ETHT_SERVICE_NAME = 'etht_service_1' +URL_ETHT_SERVICE_ITEM = '/ietf-eth-tran-service:etht-svc/etht-svc-instances={:s}'.format(ETHT_SERVICE_NAME) +REQUEST_ETHT_SERVICE = {"ietf-eth-tran-service:etht-svc": {"etht-svc-instances": [ + { + "etht-svc-name": ETHT_SERVICE_NAME, + "etht-svc-title": "ETHT_SERVICE_1", + "etht-svc-type": "op-mp2mp-svc", + "source-endpoints": { + "source-endpoint": [ + { + "node-id": "10.0.10.1", + "tp-id": "200", + "protection-role": "work", + "layer-specific": { + "access-type": "port" + }, + "is-extendable": False, + "is-terminal": True, + "static-route-list": [ + { + "destination": "128.32.10.5", + "destination-mask": 24, + "next-hop": "128.32.33.5" + }, + { + "destination": "128.32.20.5", + "destination-mask": 24, + "next-hop": "128.32.33.5" + } + ], + "outer-tag": { + "tag-type": "ietf-eth-tran-types:classify-c-vlan", + "vlan-value": 21 + }, + "service-classification-type": "ietf-eth-tran-type:vlan-classification", + "ingress-egress-bandwidth-profile" : { + "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp", + "CIR": 10000000, + "EIR": 10000000 + } + } + ] + }, + "destination-endpoints": { + "destination-endpoint": [ + { + "node-id": "10.0.30.1", + "tp-id": "200", + "protection-role": "work", + "layer-specific": { + "access-type": "port" + }, + "is-extendable": False, + "is-terminal": True, + "static-route-list": [ + { + "destination": "172.1.101.22", + "destination-mask": 24, + "next-hop": "172.10.33.5" + } + ], + "outer-tag": { + "tag-type": "ietf-eth-tran-types:classify-c-vlan", + "vlan-value": 101 + }, + "service-classification-type": "ietf-eth-tran-type:vlan-classification", + "ingress-egress-bandwidth-profile" : { + "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp", + "CIR": 10000000, + "EIR": 10000000 + } + } + ] + }, + "svc-tunnel": [ + { + "tunnel-name": "OSU_TUNNEL_NAME" + } + ], + "optimizations": { + "optimization-metric": [ + { + "metric-role": "work", + "metric-type": "ietf-te-types:path-metric-te" + } + ] + } + } +]}} diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_client/__init__.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_client/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_client/__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_t_ctrl/nce_t_client/__main__.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_client/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..c4dc92bc55114da7fe2b85aba939a52dd3e3b48f --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_client/__main__.py @@ -0,0 +1,41 @@ +# 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 logging +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from .Requests import ( + URL_OSU_TUNNEL_ITEM, REQUEST_OSU_TUNNEL, + URL_ETHT_SERVICE_ITEM, REQUEST_ETHT_SERVICE, +) + +logging.basicConfig(level=logging.INFO) +logging.getLogger('RestConfClient').setLevel(logging.DEBUG) +LOGGER = logging.getLogger(__name__) + +def main() -> None: + restconf_client = RestConfClient( + '172.17.0.1', port=8081, restconf_version='v2', + logger=logging.getLogger('RestConfClient') + ) + + LOGGER.info('Creating OSU Tunnel: {:s}'.format(str(REQUEST_OSU_TUNNEL))) + restconf_client.post(URL_OSU_TUNNEL_ITEM, body=REQUEST_OSU_TUNNEL) + + LOGGER.info('Creating ETH-T Service: {:s}'.format(str(REQUEST_ETHT_SERVICE))) + restconf_client.post(URL_ETHT_SERVICE_ITEM, body=REQUEST_ETHT_SERVICE) + + +if __name__ == '__main__': + main() diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/Callbacks.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/Callbacks.py new file mode 100644 index 0000000000000000000000000000000000000000..ea2e7f748ee154e6cee02a7fee6b127e1333d2ce --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/Callbacks.py @@ -0,0 +1,53 @@ +# 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 logging, re +from typing import Dict, Optional +from common.tools.rest_conf.server.restconf_server.Callbacks import _Callback + + +LOGGER = logging.getLogger(__name__) + + +class CallbackOsuTunnel(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/data' + pattern += r'/ietf-te:te/tunnels' + pattern += r'/tunnel=(?P[^/]+)' + super().__init__(pattern) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + MSG = '[on_osu_tunnel] match={:s} path={:s} old_data={:s} new_data={:s}' + LOGGER.warning(MSG.format(str(match.groupdict()), str(path), str(old_data), str(new_data))) + return False + + +class CallbackEthTService(_Callback): + def __init__(self) -> None: + pattern = r'/restconf/data' + pattern += r'/ietf-eth-tran-service:etht-svc' + pattern += r'/etht-svc-instances=(?P[^/]+)' + super().__init__(pattern) + + def execute_data( + self, match : re.Match, path : str, old_data : Optional[Dict], + new_data : Optional[Dict] + ) -> bool: + MSG = '[on_etht_service] match={:s} path={:s} old_data={:s} new_data={:s}' + LOGGER.warning(MSG.format(str(match.groupdict()), str(path), str(old_data), str(new_data))) + return False diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/ResourceEthServices.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/ResourceEthServices.py new file mode 100644 index 0000000000000000000000000000000000000000..b7d41f41adcebe285a69ea824c2e75fcad064d72 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/ResourceEthServices.py @@ -0,0 +1,80 @@ +# 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 abort, jsonify, make_response, request +from flask_restful import Resource +from .SimapUpdater import SimapUpdater + +ETHT_SERVICES = {} + +class EthServices(Resource): + def __init__(self, simap_updater : SimapUpdater): + super().__init__() + self._simap_updater = simap_updater + + def get(self): + etht_services = [etht_service for etht_service in ETHT_SERVICES.values()] + data = {'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': etht_services}} + return make_response(jsonify(data), 200) + + def post(self): + json_request = request.get_json() + if not json_request: abort(400) + if not isinstance(json_request, dict): abort(400) + if 'ietf-eth-tran-service:etht-svc' not in json_request: abort(400) + json_request = json_request['ietf-eth-tran-service:etht-svc'] + if 'etht-svc-instances' not in json_request: abort(400) + etht_services = json_request['etht-svc-instances'] + if not isinstance(etht_services, list): abort(400) + if len(etht_services) != 1: abort(400) + etht_service = etht_services[0] + etht_service_name = etht_service['etht-svc-name'] + ETHT_SERVICES[etht_service_name] = etht_service + self._simap_updater.create_simap_trans_otn(etht_service) + return make_response(jsonify({}), 201) + +class EthService(Resource): + def __init__(self, simap_updater : SimapUpdater): + super().__init__() + self._simap_updater = simap_updater + + def get(self, etht_service_name : str): + etht_service = ETHT_SERVICES.get(etht_service_name, None) + data,status = ({}, 404) if etht_service is None else (etht_service, 200) + return make_response(jsonify(data), status) + + def post(self, etht_service_name : str): + json_request = request.get_json() + if not json_request: abort(400) + if not isinstance(json_request, dict): abort(400) + if 'ietf-eth-tran-service:etht-svc' not in json_request: abort(400) + json_request = json_request['ietf-eth-tran-service:etht-svc'] + if 'etht-svc-instances' not in json_request: abort(400) + etht_services = json_request['etht-svc-instances'] + if not isinstance(etht_services, list): abort(400) + if len(etht_services) != 1: abort(400) + etht_service = etht_services[0] + assert etht_service_name == etht_service['etht-svc-name'] + ETHT_SERVICES[etht_service_name] = etht_service + self._simap_updater.create_simap_trans_otn(etht_service) + return make_response(jsonify({}), 201) + + def delete(self, etht_service_name : str): + etht_service = ETHT_SERVICES.pop(etht_service_name, None) + data,status = ({}, 404) if etht_service is None else (etht_service, 204) + self._simap_updater.delete_simap_trans_otn(etht_service_name) + return make_response(jsonify(data), status) diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/ResourceOsuTunnels.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/ResourceOsuTunnels.py new file mode 100644 index 0000000000000000000000000000000000000000..af5a00b6e636ca738fdbb84133e23adac7f3cb16 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/ResourceOsuTunnels.py @@ -0,0 +1,89 @@ +# 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 Traffic Engineering Tunnels, +# Label Switched Paths and Interfaces". +# Ref: https://www.ietf.org/archive/id/draft-ietf-teas-yang-te-34.html + + +from flask import abort, jsonify, make_response, request +from flask_restful import Resource +from .SimapUpdater import SimapUpdater + +OSU_TUNNELS = {} + +class OsuTunnels(Resource): + def __init__(self, simap_updater : SimapUpdater): + super().__init__() + self._simap_updater = simap_updater + + def get(self): + osu_tunnels = [osu_tunnel for osu_tunnel in OSU_TUNNELS.values()] + data = {'ietf-te:te': {'tunnels': {'tunnel': osu_tunnels}}} + return make_response(jsonify(data), 200) + + def post(self): + json_request = request.get_json() + if not json_request: abort(400) + if not isinstance(json_request, dict): abort(400) + #if 'ietf-te:te' not in json_request: abort(400) + #te_data = json_request['ietf-te:te'] + #if not isinstance(te_data, dict): abort(400) + #if 'tunnels' not in te_data: abort(400) + #te_tunnels = te_data['tunnels'] + #if 'tunnel' not in te_tunnels: abort(400) + #osu_tunnels = te_tunnels['tunnel'] + if 'ietf-te:tunnel' not in json_request: abort(400) + osu_tunnels = json_request['ietf-te:tunnel'] + if not isinstance(osu_tunnels, list): abort(400) + if len(osu_tunnels) != 1: abort(400) + osu_tunnel = osu_tunnels[0] + osu_tunnel_name = osu_tunnel['name'] + OSU_TUNNELS[osu_tunnel_name] = osu_tunnel + return make_response(jsonify({}), 201) + +class OsuTunnel(Resource): + def __init__(self, simap_updater : SimapUpdater): + super().__init__() + self._simap_updater = simap_updater + + def get(self, osu_tunnel_name : str): + osu_tunnel = OSU_TUNNELS.get(osu_tunnel_name, None) + data,status = ({}, 404) if osu_tunnel is None else (osu_tunnel, 200) + return make_response(jsonify(data), status) + + def post(self, osu_tunnel_name : str): + json_request = request.get_json() + if not json_request: abort(400) + if not isinstance(json_request, dict): abort(400) + #if 'ietf-te:te' not in json_request: abort(400) + #te_data = json_request['ietf-te:te'] + #if not isinstance(te_data, dict): abort(400) + #if 'tunnels' not in te_data: abort(400) + #te_tunnels = te_data['tunnels'] + #if 'tunnel' not in te_tunnels: abort(400) + #osu_tunnels = te_tunnels['tunnel'] + if 'ietf-te:tunnel' not in json_request: abort(400) + osu_tunnels = json_request['ietf-te:tunnel'] + if not isinstance(osu_tunnels, list): abort(400) + if len(osu_tunnels) != 1: abort(400) + osu_tunnel = osu_tunnels[0] + assert osu_tunnel_name == osu_tunnel['name'] + OSU_TUNNELS[osu_tunnel_name] = osu_tunnel + return make_response(jsonify({}), 201) + + def delete(self, osu_tunnel_name : str): + osu_tunnel = OSU_TUNNELS.pop(osu_tunnel_name, None) + data,status = ({}, 404) if osu_tunnel is None else (osu_tunnel, 204) + return make_response(jsonify(data), status) diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/SimapClient.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/SimapClient.py new file mode 100644 index 0000000000000000000000000000000000000000..725b08bd47e0bd127cf0f7c4131cb744313b149d --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/SimapClient.py @@ -0,0 +1,350 @@ +# 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, Optional, Tuple +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +class TerminationPoint: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}/node={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:termination-point={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str, tp_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tp_id = tp_id + + def create(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network-topology:termination-point'][0] + + def update(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + self._restconf_client.delete(endpoint) + + +class NodeTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/node={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + + def create( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class Node: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/node={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tps : Dict[str, TerminationPoint] = dict() + self._telemetry : Optional[NodeTelemetry] = None + + @property + def telemetry(self) -> NodeTelemetry: + if self._telemetry is None: + self._telemetry = NodeTelemetry(self._restconf_client, self._network_id, self._node_id) + return self._telemetry + + def termination_points(self) -> List[Dict]: + tps : Dict = self._restconf_client.get(TerminationPoint.ENDPOINT_NO_ID) + return tps['ietf-network-topology:termination-point'].get('termination-point', list()) + + def termination_point(self, tp_id : str) -> TerminationPoint: + _tp = self._tps.get(tp_id) + if _tp is not None: return _tp + _tp = TerminationPoint(self._restconf_client, self._network_id, self._node_id, tp_id) + return self._tps.setdefault(tp_id, _tp) + + def create( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network:node'][0] + + def update( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class LinkTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/ietf-network-topology:link={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + + def create( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Link: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:link={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + self._telemetry : Optional[LinkTelemetry] = None + + @property + def telemetry(self) -> LinkTelemetry: + if self._telemetry is None: + self._telemetry = LinkTelemetry(self._restconf_client, self._network_id, self._link_id) + return self._telemetry + + def create( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link : Dict = self._restconf_client.get(endpoint) + return link['ietf-network-topology:link'][0] + + def update( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Network: + ENDPOINT_NO_ID = '/ietf-network:networks' + ENDPOINT_ID = ENDPOINT_NO_ID + '/network={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._nodes : Dict[str, Node] = dict() + self._links : Dict[str, Link] = dict() + + def nodes(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Node.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('node', list()) + + def links(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Link.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('ietf-network-topology:link', list()) + + def node(self, node_id : str) -> Node: + _node = self._nodes.get(node_id) + if _node is not None: return _node + _node = Node(self._restconf_client, self._network_id, node_id) + return self._nodes.setdefault(node_id, _node) + + def link(self, link_id : str) -> Link: + _link = self._links.get(link_id) + if _link is not None: return _link + _link = Link(self._restconf_client, self._network_id, link_id) + return self._links.setdefault(link_id, _link) + + def create(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + networks : Dict = self._restconf_client.get(endpoint) + return networks['ietf-network:network'][0] + + def update(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + self._restconf_client.delete(endpoint) + + +class SimapClient: + def __init__(self, restconf_client : RestConfClient) -> None: + self._restconf_client = restconf_client + self._networks : Dict[str, Network] = dict() + + def networks(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Network.ENDPOINT_NO_ID) + return reply['ietf-network:networks'].get('network', list()) + + def network(self, network_id : str) -> Network: + _network = self._networks.get(network_id) + if _network is not None: return _network + _network = Network(self._restconf_client, network_id) + return self._networks.setdefault(network_id, _network) diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/SimapUpdater.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/SimapUpdater.py new file mode 100644 index 0000000000000000000000000000000000000000..cc792313b4b93ff8ff521ca7c80e38579dc21346 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/SimapUpdater.py @@ -0,0 +1,122 @@ +# 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 logging, os +from typing import Dict +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from .SimapClient import SimapClient + + +SIMAP_ADDRESS = os.environ.get('SIMAP_ADDRESS') +SIMAP_PORT = os.environ.get('SIMAP_PORT' ) + + +class SimapUpdater: + def __init__(self): + self._simap_client = None + + if SIMAP_ADDRESS is None: return + if SIMAP_PORT is None: return + + self._restconf_client = RestConfClient( + SIMAP_ADDRESS, port=SIMAP_PORT, + logger=logging.getLogger('RestConfClient') + ) + self._simap_client = SimapClient(self._restconf_client) + + + def upload_topology(self, network_data : Dict) -> None: + if self._simap_client is None: return + + network_id = network_data['network-id'] + te_topo = self._simap_client.network(network_id) + te_topo.update() + + nodes = network_data.get('node', list()) + for node in nodes: + node_id = node['node-id'] + tp_ids = [ + tp['tp-id'] + for tp in node['ietf-network-topology:termination-point'] + ] + te_topo.node(node_id).update(termination_point_ids=tp_ids) + + links = network_data.get('ietf-network-topology:link', list()) + for link in links: + link_id = link['link-id'] + link_src = link['source'] + link_dst = link['destination'] + link_src_node_id = link_src['source-node'] + link_src_tp_id = link_src['source-tp'] + link_dst_node_id = link_dst['dest-node'] + link_dst_tp_id = link_dst['dest-tp'] + + te_topo.link(link_id).update( + link_src_node_id, link_src_tp_id, link_dst_node_id, link_dst_tp_id + ) + + + def create_simap_trans_otn(self, etht_service : Dict) -> None: + #etht_svc_name = etht_service['etht-svc-name'] + + #src_node_ep = etht_service['source-endpoints']['source-endpoint'][0] + #src_node_id = src_node_ep['node-id'] + #src_tp_id = src_node_ep['tp-id'] + + #dst_node_ep = etht_service['destination-endpoints']['destination-endpoint'][0] + #dst_node_id = dst_node_ep['node-id'] + #dst_tp_id = dst_node_ep['tp-id'] + + simap = self._simap_client.network('trans-otn') + simap.update(supporting_network_ids=['admin']) + + node_a = simap.node('site1') + node_a.update(supporting_node_ids=[('admin', 'O-PE1')]) + node_a.termination_point('200').update(supporting_termination_point_ids=[('admin', 'O-PE1', '200')]) + node_a.termination_point('500').update(supporting_termination_point_ids=[('admin', 'O-PE1', '500')]) + node_a.termination_point('501').update(supporting_termination_point_ids=[('admin', 'O-PE1', '501')]) + + node_b = simap.node('site2') + node_b.update(supporting_node_ids=[('admin', 'O-PE2')]) + node_b.termination_point('200').update(supporting_termination_point_ids=[('admin', 'O-PE2', '200')]) + node_b.termination_point('500').update(supporting_termination_point_ids=[('admin', 'O-PE2', '500')]) + node_b.termination_point('501').update(supporting_termination_point_ids=[('admin', 'O-PE2', '501')]) + + link_ab = simap.link('Trans-L1ab') + link_ab.update( + 'site1', '500', 'site2', '500', + supporting_link_ids=[ + ('admin', 'L7ab'), ('admin', 'L11ab'), + ] + ) + + link_ba = simap.link('Trans-L1ba') + link_ba.update( + 'site2', '500', 'site1', '500', + supporting_link_ids=[ + ('admin', 'L11ba'), ('admin', 'L7ba'), + ] + ) + + + def delete_simap_trans_otn(self, etht_svc_name : str) -> None: + simap = self._simap_client.network('trans-otn') + simap.update(supporting_network_ids=['admin']) + + link_ab = simap.link('Trans-L1ab') + link_ab.delete() + + link_ba = simap.link('Trans-L1ba') + link_ba.delete() diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/__init__.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_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_t_ctrl/nce_t_ctrl/__main__.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..2c84d92efd7e33d44237e3a8791771a371e12f3f --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/__main__.py @@ -0,0 +1,26 @@ +# 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 .app import app + +BIND_ADDRESS = '0.0.0.0' +BIND_PORT = 8080 + +if __name__ == '__main__': + # Only used to run it locally during development stage; + # otherwise, app is directly launched by gunicorn. + app.run( + host=BIND_ADDRESS, port=BIND_PORT, debug=True, use_reloader=False + ) diff --git a/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/app.py b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/app.py new file mode 100644 index 0000000000000000000000000000000000000000..601350454487e2339ee59f61cbc964784423d9aa --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/nce_t_ctrl/app.py @@ -0,0 +1,93 @@ +# 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. + + +# This file overwrites default RestConf Server `app.py` file. + +# 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 + +# NOTE: we need here OSUflex tunnels that are still not standardized; hardcoded. + +import logging +from common.tools.rest_conf.server.restconf_server.RestConfServerApplication import RestConfServerApplication +from .Callbacks import CallbackEthTService, CallbackOsuTunnel +from .ResourceEthServices import EthService, EthServices +from .ResourceOsuTunnels import OsuTunnel, OsuTunnels +from .SimapUpdater import SimapUpdater + + +logging.basicConfig( + level=logging.INFO, + format='[Worker-%(process)d][%(asctime)s] %(levelname)s:%(name)s:%(message)s', +) +LOGGER = logging.getLogger(__name__) +logging.getLogger('RestConfClient').setLevel(logging.WARN) + + +LOGGER.info('Starting...') + +simap_updater = SimapUpdater() + +rcs_app = RestConfServerApplication() +rcs_app.register_host_meta() +rcs_app.register_restconf() + +rcs_app.register_custom( + OsuTunnels, + '/restconf/v2/data/ietf-te:te/tunnels', + add_prefix_to_urls=False, + resource_class_args=(simap_updater,) +) +rcs_app.register_custom( + OsuTunnel, + '/restconf/v2/data/ietf-te:te/tunnels/tunnel=', + add_prefix_to_urls=False, + resource_class_args=(simap_updater,) +) +rcs_app.register_custom( + EthServices, + '/restconf/v2/data/ietf-eth-tran-service:etht-svc', + add_prefix_to_urls=False, + resource_class_args=(simap_updater,) +) +rcs_app.register_custom( + EthService, + '/restconf/v2/data/ietf-eth-tran-service:etht-svc/etht-svc-instances=', + add_prefix_to_urls=False, + resource_class_args=(simap_updater,) +) + +LOGGER.info('All connectors registered') + +startup_data = rcs_app.get_startup_data() + +networks = startup_data.get('ietf-network:networks', dict()) +networks = networks.get('network', list()) +if len(networks) == 1 and networks[0]['network-id'] == 'admin': + simap_updater.upload_topology(networks[0]) + + rcs_app.callback_dispatcher.register(CallbackOsuTunnel()) + rcs_app.callback_dispatcher.register(CallbackEthTService()) + LOGGER.info('All callbacks registered') + +rcs_app.dump_configuration() +app = rcs_app.get_flask_app() + +LOGGER.info('Initialization completed!') diff --git a/src/tests/tools/mock_nce_t_ctrl/redeploy.sh b/src/tests/tools/mock_nce_t_ctrl/redeploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..ed5c254576ba94955b8d090c7b623f27881ecc0c --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/redeploy.sh @@ -0,0 +1,36 @@ +#!/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. + + +echo "Building SIMAP Server..." +cd ~/tfs-ctrl/ +docker buildx build -t simap-server:mock -f ./src/tests/tools/simap_server/Dockerfile . + +echo "Building NCE-T Controller..." +cd ~/tfs-ctrl/ +docker buildx build -t nce-t-ctrl:mock -f ./src/tests/tools/mock_nce_t_ctrl/Dockerfile . + +echo "Cleaning up..." +docker rm --force simap-server +docker rm --force nce-t-ctrl + +echo "Deploying support services..." +docker run --detach --name simap-server --publish 8080:8080 simap-server:mock +docker run --detach --name nce-t-ctrl --publish 8081:8080 --env SIMAP_ADDRESS=172.17.0.1 --env SIMAP_PORT=8080 nce-t-ctrl:mock + +sleep 2 +docker ps -a + +echo "Bye!" diff --git a/src/tests/tools/mock_nce_t_ctrl/run_client.sh b/src/tests/tools/mock_nce_t_ctrl/run_client.sh new file mode 100755 index 0000000000000000000000000000000000000000..fe5155b3912e9fe52736db4484e27f64f3720b46 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/run_client.sh @@ -0,0 +1,19 @@ +#!/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)/../../../ + +python -m tests.tools.mock_nce_t_ctrl.nce_t_client diff --git a/src/tests/tools/mock_nce_t_ctrl/startup.json b/src/tests/tools/mock_nce_t_ctrl/startup.json new file mode 100644 index 0000000000000000000000000000000000000000..8b8b3cbc55394b963a163cfcca3563c14dcd9e4a --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/startup.json @@ -0,0 +1,72 @@ +{ + "ietf-network:networks": { + "network": [ + { + "network-id": "admin", + "ietf-te-topology:te": { + "name": "admin" + }, + "network-types": { + "ietf-te-topology:te-topology": { + "ietf-otn-topology:otn-topology": {}, + "ietf-eth-te-topology:eth-tran-topology": {} + } + }, + "node": [ + { + "node-id": "O-PE1", "ietf-te-topology:te-node-id": "172.16.182.25", + "ietf-te-topology:te": {"te-node-attributes": {"otn-node": {}, "name": "O-PE1", "admin-status": "up"}, "oper-status": "up"}, + "ietf-network-topology:termination-point": [ + {"tp-id": "500", "ietf-te-topology:te": {"name": "500"}, "ietf-te-topology:te-tp-id": "10.33.1.1"}, + {"tp-id": "501", "ietf-te-topology:te": {"name": "501"}, "ietf-te-topology:te-tp-id": "10.33.2.1"}, + {"tp-id": "200", "ietf-te-topology:te": {"name": "200"}, "ietf-te-topology:te-tp-id": "128.32.33.254", + "ietf-eth-te-topology:eth-svc": {"supported-classification": {"port-classification": false, "vlan-classification": {"vlan-tag-classification": true, "outer-tag": { + "supported-tag-types": ["ietf-eth-tran-types:classify-c-vlan"], "vlan-bundling": false, "vlan-range": "31" + }}}} + } + ] + }, + { + "node-id": "O-P1", "ietf-te-topology:te-node-id": "172.16.185.31", + "ietf-te-topology:te": {"te-node-attributes": {"otn-node": {}, "name": "O-P1", "admin-status": "up"}, "oper-status": "up"}, + "ietf-network-topology:termination-point": [ + {"tp-id": "500", "ietf-te-topology:te": {"name": "500"}, "ietf-te-topology:te-tp-id": "10.33.4.2"}, + {"tp-id": "501", "ietf-te-topology:te": {"name": "501"}, "ietf-te-topology:te-tp-id": "10.33.2.2"} + ] + }, + { + "node-id": "O-P2", "ietf-te-topology:te-node-id": "172.16.185.33", + "ietf-te-topology:te": {"te-node-attributes": {"otn-node": {}, "name": "O-P2", "admin-status": "up"}, "oper-status": "up"}, + "ietf-network-topology:termination-point": [ + {"tp-id": "500", "ietf-te-topology:te": {"name": "500"}, "ietf-te-topology:te-tp-id": "10.33.1.2"}, + {"tp-id": "501", "ietf-te-topology:te": {"name": "501"}, "ietf-te-topology:te-tp-id": "10.33.3.2"} + ] + }, + { + "node-id": "O-PE2", "ietf-te-topology:te-node-id": "172.16.185.32", + "ietf-te-topology:te": {"te-node-attributes": {"otn-node": {}, "name": "O-PE2", "admin-status": "up"}, "oper-status": "up"}, + "ietf-network-topology:termination-point": [ + {"tp-id": "500", "ietf-te-topology:te": {"name": "500"}, "ietf-te-topology:te-tp-id": "10.33.4.1"}, + {"tp-id": "501", "ietf-te-topology:te": {"name": "501"}, "ietf-te-topology:te-tp-id": "10.33.3.1"}, + {"tp-id": "200", "ietf-te-topology:te": {"name": "200"}, "ietf-te-topology:te-tp-id": "128.32.33.254", + "ietf-eth-te-topology:eth-svc": {"supported-classification": {"port-classification": false, "vlan-classification": {"vlan-tag-classification": true, "outer-tag": { + "supported-tag-types": ["ietf-eth-tran-types:classify-c-vlan"], "vlan-bundling": false, "vlan-range": "101" + }}}} + } + ] + } + ], + "ietf-network-topology:link": [ + {"link-id": "L7ab", "source": {"source-node": "O-PE1", "source-tp": "501"}, "destination": {"dest-node": "O-P1", "dest-tp": "501"}}, + {"link-id": "L7ba", "source": {"source-node": "O-P1", "source-tp": "501"}, "destination": {"dest-node": "O-PE1", "dest-tp": "501"}}, + {"link-id": "L8ab", "source": {"source-node": "O-PE1", "source-tp": "500"}, "destination": {"dest-node": "O-P2", "dest-tp": "500"}}, + {"link-id": "L8ba", "source": {"source-node": "O-P2", "source-tp": "500"}, "destination": {"dest-node": "O-PE1", "dest-tp": "500"}}, + {"link-id": "L11ab", "source": {"source-node": "O-PE2", "source-tp": "500"}, "destination": {"dest-node": "O-P1", "dest-tp": "500"}}, + {"link-id": "L11ba", "source": {"source-node": "O-P1", "source-tp": "500"}, "destination": {"dest-node": "O-PE2", "dest-tp": "500"}}, + {"link-id": "L12ab", "source": {"source-node": "O-PE2", "source-tp": "501"}, "destination": {"dest-node": "O-P2", "dest-tp": "501"}}, + {"link-id": "L12ba", "source": {"source-node": "O-P2", "source-tp": "501"}, "destination": {"dest-node": "O-PE2", "dest-tp": "501"}} + ] + } + ] + } +} diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-service.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-service.yang new file mode 100644 index 0000000000000000000000000000000000000000..633d74715a8fa3dd4a4f815786218e26bdc1e987 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-service.yang @@ -0,0 +1,1010 @@ +module ietf-eth-tran-service { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-eth-tran-service"; + + prefix "ethtsvc"; + import ietf-yang-types { + prefix "yang"; + reference "RFC 6991 - Common YANG Data Types"; + } + + import ietf-network { + prefix "nw"; + reference "RFC8345 - A YANG Data Model for Network Topologies"; + } + + import ietf-network-topology { + prefix "nt"; + reference "RFC8345 - A YANG Data Model for Network Topologies"; + } + + import ietf-te-types { + prefix "te-types"; + reference "RFC 8776 - Traffic Engineering Common YANG Types"; + } + + import ietf-eth-tran-types { + prefix "etht-types"; + reference "RFC XXXX - A YANG Data Model for Transport + Network Client Signals"; + } + + import ietf-routing-types { + prefix "rt-types"; + reference "RFC 8294 - Common YANG Data Types for the + Routing Area"; + + } + + import ietf-te { + prefix "te"; + reference "RFC YYYY - A YANG Data Model for Traffic + Engineering Tunnels and Interfaces"; + } + + organization + "Internet Engineering Task Force (IETF) CCAMP WG"; + contact + " + WG List: + + ID-draft editor: + Haomian Zheng (zhenghaomian@huawei.com); + Italo Busi (italo.busi@huawei.com); + Aihua Guo (aihuaguo.ietf@gmail.com); + Anton Snitser (antons@sedonasys.com);0 + Francesco Lazzeri (francesco.lazzeri@ericsson.com); + Yunbin Xu (xuyunbin@caict.ac.cn); + Yang Zhao (zhaoyangyjy@chinamobile.com); + Xufeng Liu (xufeng.liu.ietf@gmail.com); + Giuseppe Fioccola (giuseppe.fioccola@huawei.com); + Chaode Yu (yuchaode@huawei.com) + "; + + description + "This module defines a YANG data model for describing + the Ethernet services. The model fully conforms to the + Network Management Datastore Architecture (NMDA). + + 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 + (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-10-23 { + description + "version -04 as an WG document"; + reference + "draft-ietf-ccamp-client-signal-yang"; + } + + /* + * Groupings + */ + + grouping vlan-classification { + description + "A grouping which represents classification + on an 802.1Q VLAN tag."; + + leaf tag-type { + type etht-types:eth-tag-classify; + description + "The tag type used for VLAN classification."; + } + choice individual-bundling-vlan { + description + "VLAN based classification can be individual + or bundling."; + + case individual-vlan { + leaf vlan-value { + type etht-types:vlanid; + description + "VLAN ID value."; + } + } + + case vlan-bundling { + leaf vlan-range { + type etht-types:vid-range-type; + description + "List of VLAN ID values."; + } + } + } + } + + grouping vlan-write { + description + "A grouping which represents push/pop operations + of an 802.1Q VLAN tag."; + + leaf tag-type { + type etht-types:eth-tag-type; + description + "The VLAN tag type to push/swap."; + } + leaf vlan-value { + type etht-types:vlanid; + description + "The VLAN ID value to push/swap."; + } +/* + * To be added: this attribute is used when: + * a) the ETH service has only one CoS (as in current version) + * b) as a default when a mapping between a given CoS value + * and the PCP value is not defined (in future versions) + */ + leaf default-pcp { + type uint8 { + range "0..7"; + } + description + "The default Priority Code Point (PCP) value to push/swap"; + } + } + + grouping vlan-operations { + description + "A grouping which represents VLAN operations."; + + leaf pop-tags { + type uint8 { + range "1..2"; + } + description + "The number of VLAN tags to pop (or swap if used in + conjunction with push-tags)"; + } + container push-tags { + description + "The VLAN tags to push (or swap if used in + conjunction with pop-tags)"; + + container outer-tag { + presence + "Indicates existence of the outermost VLAN tag to + push/swap"; + + description + "The outermost VLAN tag to push/swap."; + + uses vlan-write; + } + container second-tag { + must + '../outer-tag/tag-type = "etht-types:s-vlan-tag-type" and ' + + 'tag-type = "etht-types:c-vlan-tag-type"' + { + + error-message + " + When pushing/swapping two tags, the outermost tag must + be specified and of S-VLAN type and the second + outermost tag must be of C-VLAN tag type. + "; + description + " + For IEEE 802.1Q interoperability, when pushing/swapping + two tags, it is required that the outermost tag exists + and is an S-VLAN, and the second outermost tag is a + C-VLAN. + "; + } + + presence + "Indicates existence of a second outermost VLAN tag to + push/swap"; + + description + "The second outermost VLAN tag to push/swap."; + uses vlan-write; + } + } + } + + grouping named-or-value-bandwidth-profile { + description + "A grouping to configure a bandwdith profile either by + referencing a named bandwidth profile or by + configuring the values of the bandwidth profile attributes."; + choice style { + description + "Whether the bandwidth profile is named or defined by value"; + + case named { + description + "Named bandwidth profile."; + leaf bandwidth-profile-name { + type leafref { + path "/ethtsvc:etht-svc/ethtsvc:globals/" + + "ethtsvc:named-bandwidth-profiles/" + + "ethtsvc:bandwidth-profile-name"; + } + description + "Name of the bandwidth profile."; + } + } + case value { + description + "Bandwidth profile configured by value."; + uses etht-types:etht-bandwidth-profiles; + } + } + } + + grouping bandwidth-profiles { + description + "A grouping which represent bandwidth profile configuration."; + + choice direction { + description + "Whether the bandwidth profiles are symmetrical or + asymmetrical"; + case symmetrical { + description + "The same bandwidth profile is used to describe both + the ingress and the egress bandwidth profile."; + container ingress-egress-bandwidth-profile { + description + "The bandwdith profile used in both directions."; + uses named-or-value-bandwidth-profile; + } + } + case asymmetrical { + description + "Ingress and egress bandwidth profiles can be specified."; + container ingress-bandwidth-profile { + description + "The bandwdith profile used in the ingress direction."; + uses named-or-value-bandwidth-profile; + } + container egress-bandwidth-profile { + description + "The bandwdith profile used in the egress direction."; + uses named-or-value-bandwidth-profile; + } + } + } + } + + grouping etht-svc-access-parameters { + description + "ETH services access parameters"; + + leaf access-node-id { + type te-types:te-node-id; + description + "The identifier of the access node in + the ETH TE topology."; + } + + leaf access-node-uri { + type nw:node-id; + description + "The identifier of the access node in the network."; + } + + leaf access-ltp-id { + type te-types:te-tp-id; + description + "The TE link termination point identifier, used + together with access-node-id to identify the + access LTP."; + } + + leaf access-ltp-uri { + type nt:tp-id; + description + "The link termination point identifier in network topology, + used together with access-node-uri to identify the + access LTP."; + } + + leaf access-role { + type identityref { + base etht-types:access-role; + } + description + "Indicate the role of access, e.g., working or protection. "; + } + + container pm-config { + uses pm-config-grouping; + description + "This grouping is used to set the threshold value for + performance monitoring. "; + } + + container state { + config false; + description + "The state is used to monitor the status of service. "; + leaf operational-state { + type identityref { + base te-types:tunnel-state-type; + } + description + "Indicating the operational state of client signal. "; + } + leaf provisioning-state { + type identityref { + base te-types:lsp-state-type; + } + description + "Indicating the provisional state of client signal, + especially when there is a change, i.e., revise, create. "; + } + } + + leaf performance { + type identityref { + base etht-types:performance; + } + config false; + description + "Performance Monitoring for the service. "; + } + + } + + grouping etht-svc-tunnel-parameters { + description + "ETH services tunnel parameters."; + choice technology { + description + "Service multiplexing is optional and flexible."; + + case native-ethernet { + /* + placeholder to support proprietary multiplexing + (for further discussion) + */ + list eth-tunnels { + key name; + description + "ETH Tunnel list in native Ethernet scenario."; + uses tunnels-grouping; + } + } + + case frame-base { + list otn-tunnels { + key name; + description + "OTN Tunnel list in Frame-based scenario."; + uses tunnels-grouping; + } + } + + case mpls-tp { + container pw { + description + "Pseudowire information for Ethernet over MPLS-TP."; + uses pw-segment-grouping; + } + } + } + +/* + * Open issue: can we constraints it to be used only with mp services? + */ + leaf src-split-horizon-group { + type string; + description + "Identify a split horizon group at the Tunnel source TTP"; + } + leaf dst-split-horizon-group { + type string; + description + "Identify a split horizon group at the Tunnel destination TTP"; + } + } + + grouping etht-svc-pm-threshold-config { + description + "Configuraiton parameters for Ethernet service PM thresholds."; + + leaf sending-rate-high { + type uint64; + description + "High threshold of packet sending rate in kbps."; + } + leaf sending-rate-low { + type uint64; + description + "Low threshold of packet sending rate in kbps."; + } + leaf receiving-rate-high { + type uint64; + description + "High threshold of packet receiving rate in kbps."; + } + leaf receiving-rate-low { + type uint64; + description + "Low threshold of packet receiving rate in kbps."; + } + } + + grouping etht-svc-pm-stats { + description + "Ethernet service PM statistics."; + + leaf sending-rate-too-high { + type uint32; + description + "Counter that indicates the number of times the + sending rate is above the high threshold"; + } + leaf sending-rate-too-low { + type uint32; + description + "Counter that indicates the number of times the + sending rate is below the low threshold"; + } + leaf receiving-rate-too-high { + type uint32; + description + "Counter that indicates the number of times the + receiving rate is above the high threshold"; + } + leaf receiving-rate-too-low { + type uint32; + description + "Counter that indicates the number of times the + receiving rate is below the low threshold"; + } + } + + grouping etht-svc-instance-config { + description + "Configuraiton parameters for Ethernet services."; + + leaf etht-svc-name { + type string; + description + "Name of the ETH service."; + } + + leaf etht-svc-title { + type string; + description + "The Identifier of the ETH service."; + } + + leaf user-label { + type string; + description + "Alias of the ETH service."; + } + + leaf etht-svc-descr { + type string; + description + "Description of the ETH service."; + } + + leaf etht-svc-customer { + type string; + description + "Customer of the ETH service."; + } + + leaf etht-svc-type { + type etht-types:service-type; + description + "Type of ETH service (p2p, mp2mp or rmp)."; + /* Add default as p2p */ + } + + leaf etht-svc-lifecycle { + type etht-types:lifecycle-status; + description + "Lifecycle state of ETH service."; + /* Add default as installed */ + } + uses te-types:te-topology-identifier; + + uses resilience-grouping; + list etht-svc-end-points { + key etht-svc-end-point-name; + description + "The logical end point for the ETH service. "; + uses etht-svc-end-point-grouping; + } + + + container alarm-shreshold { + description "threshold configuration for the E2E client signal"; + uses alarm-shreshold-grouping; + } + + container underlay { + description + "The unterlay tunnel information that carrying the + ETH service. "; + uses etht-svc-tunnel-parameters; + } + + leaf admin-status { + type identityref { + base te-types:tunnel-admin-state-type; + } + default te-types:tunnel-admin-state-up; + description "ETH service administrative state."; + } + } + + grouping etht-svc-instance-state { + description + "State parameters for Ethernet services."; + + leaf operational-state { + type identityref { + base te-types:tunnel-state-type; + } + default te-types:tunnel-state-up; + description "ETH service operational state."; + } + leaf provisioning-state { + type identityref { + base te-types:lsp-state-type; + } + description "ETH service provisioning state."; + } + leaf creation-time { + type yang:date-and-time; + description + "Time of ETH service creation."; + } + leaf last-updated-time { + type yang:date-and-time; + description + "Time of ETH service last update."; + } + + leaf created-by { + type string; + description + "The client signal is created by whom, + can be a system or staff ID."; + } + leaf last-updated-by { + type string; + description + "The client signal is last updated by whom, + can be a system or staff ID."; + } + leaf owned-by { + type string; + description + "The client signal is last updated by whom, + can be a system ID."; + } + container pm-state { + description + "PM data of E2E Ethernet service"; + uses pm-state-grouping; + } + container error-info { + description "error messages of configuration"; + uses error-info-grouping; + } + } + + grouping pm-state-grouping { + leaf latency { + description + "latency value of the E2E Ethernet service"; + type uint32; + units microsecond; + } + } + + grouping error-info-grouping { + leaf error-code { + description "error code"; + type uint16; + } + + leaf error-description { + description "detail message of error"; + type string; + } + + leaf error-timestamp { + description "the date and time error is happened"; + type yang:date-and-time; + } + } + + grouping alarm-shreshold-grouping { + leaf latency-threshold { + description "a threshold for the E2E client signal service's + latency. Once the latency value exceed this threshold, an alarm + should be triggered."; + type uint32; + units microsecond; + } + } + + /* + * Data nodes + */ + + container etht-svc { + description + "ETH services."; + + container globals { + description + "Globals Ethernet configuration data container"; + list named-bandwidth-profiles { + key bandwidth-profile-name; + description + "List of named bandwidth profiles used by + Ethernet services."; + + leaf bandwidth-profile-name { + type string; + description + "Name of the bandwidth profile."; + } + uses etht-types:etht-bandwidth-profiles; + } + } + + list etht-svc-instances { + key etht-svc-name; + description + "The list of p2p ETH service instances"; + + uses etht-svc-instance-config; + + container state { + config false; + description + "Ethernet Service states."; + + uses etht-svc-instance-state; + } + } + } + + grouping resilience-grouping { + description + "Grouping for resilience configuration. "; + container resilience { + description + "To configure the data plane protection parameters, + currently a placeholder only, future candidate attributes + include, Revert, WTR, Hold-off Timer, ..."; + uses te:protection-restoration-properties; + } + } + + grouping etht-svc-end-point-grouping { + description + "Grouping for the end point configuration."; + leaf etht-svc-end-point-name { + type string; + description + "The name of the logical end point of ETH service. "; + } + + leaf etht-svc-end-point-id { + type string; + description + "The identifier of the logical end point of ETH service."; + } + + leaf etht-svc-end-point-descr { + type string; + description + "The description of the logical end point of ETH service. "; + } + + leaf topology-role { + type identityref { + base etht-types:topology-role; + } + description + "Indicating the underlay topology role, + e.g., hub,spoke, any-to-any "; + } + + container resilience { + description + "Placeholder for resilience configuration, for future study. "; + } + + list etht-svc-access-points { + key access-point-id; + min-elements "1"; +/* + Open Issue: + Is it possible to limit the max-elements only for p2p services? + max-elements "2"; +*/ + description + "List of the ETH trasport services access point instances."; + + leaf access-point-id { + type string; + description + "ID of the service access point instance"; + } + uses etht-svc-access-parameters; + } + + leaf service-classification-type { + type identityref { + base etht-types:service-classification-type; + } + description + "Service classification type."; + } + + choice service-classification { + description + "Access classification can be port-based or + VLAN based."; + + case port-classification { + /* no additional information */ + } + + case vlan-classification { + container outer-tag { + presence "The outermost VLAN tag exists"; + description + "Classifies traffic using the outermost VLAN tag."; + + uses vlan-classification; + } + container second-tag { + must + '../outer-tag/tag-type = "etht-types:classify-s-vlan" and ' + + 'tag-type = "etht-types:classify-c-vlan"' + { + error-message + " + When matching two tags, the outermost tag must be + specified and of S-VLAN type and the second + outermost tag must be of C-VLAN tag type. + "; + description + " + For IEEE 802.1Q interoperability, when matching two + tags, it is required that the outermost tag exists + and is an S-VLAN, and the second outermost tag is a + C-VLAN. + "; + } + presence "The second outermost VLAN tag exists"; + + description + "Classifies traffic using the second outermost VLAN tag."; + + uses vlan-classification; + } + } + } + +/* + * Open issue: can we constraints it to be used only with mp services? + */ + leaf split-horizon-group { + type string; + description "Identify a split horizon group"; + } + + uses bandwidth-profiles; + + container vlan-operations { + description + "Configuration of VLAN operations."; + choice direction { + description + "Whether the VLAN operations are symmetrical or + asymmetrical"; + case symmetrical { + container symmetrical-operation { + uses vlan-operations; + description + "Symmetrical operations. + Expressed in the ingress direction, but + the reverse operation is applied to egress traffic"; + } + } + case asymmetrical { + container asymmetrical-operation { + description "Asymmetrical operations"; + container ingress { + uses vlan-operations; + description "Ingress operations"; + } + container egress { + uses vlan-operations; + description "Egress operations"; + } + } + } + } + } + } + + grouping pm-config-grouping { + description + "Grouping used for Performance Monitoring Configuration. "; + leaf pm-enable { + type boolean; + description + "Whether to enable the performance monitoring."; + } + + leaf sending-rate-high { + type uint64; + description + "The upperbound of sending rate."; + } + + leaf sending-rate-low { + type uint64; + description + "The lowerbound of sending rate."; + } + + leaf receiving-rate-high { + type uint64; + description + "The upperbound of receiving rate."; + } + + leaf receiving-rate-low { + type uint64; + description + "The lowerbound of receiving rate."; + } + } + + grouping pw-segment-grouping { + description + "Grouping used for PW configuration. "; + leaf pw-id { + type string; + description + "The Identifier information of pseudowire. "; + } + + leaf pw-name { + type string; + description + "The name information of pseudowire."; + } + + leaf transmit-label { + type rt-types:mpls-label; + description + "Transmit label information in PW. "; + } + + leaf receive-label { + type rt-types:mpls-label; + description + "Receive label information in PW. "; + } + + leaf encapsulation-type { + type identityref { + base etht-types:encapsulation-type; + } + description + "The encapsulation type, raw or tag. "; + } + + leaf oper-status { + type identityref { + base te-types:tunnel-state-type; + } + config false; + description + "The operational state of the PW segment. "; + } + + container ingress-bandwidth-profile { + description + "Bandwidth Profile for ingress. "; + uses pw-segment-named-or-value-bandwidth-profile; + } + + list pw-paths { + key path-id; + description + "A list of pw paths. "; + + leaf path-id { + type uint8; + description + "The identifier of pw paths. "; + + } + + list tp-tunnels { + key name; + description + "Names of TP Tunnel underlay"; + leaf name { + type string; + description + "Names of TP Tunnel underlay"; + } + } + } + + } + + grouping pw-segment-named-or-value-bandwidth-profile { + description + "A grouping to configure a bandwdith profile either by + referencing a named bandwidth profile or by + configuring the values of the bandwidth profile attributes."; + choice style { + description + "Whether the bandwidth profile is named or defined by value"; + case named { + description + "Named bandwidth profile."; + leaf bandwidth-profile-name { + type leafref { + path "/ethtsvc:etht-svc/ethtsvc:globals/" + + "ethtsvc:named-bandwidth-profiles/" + + "ethtsvc:bandwidth-profile-name"; + } + description + "Name of the bandwidth profile."; + } + } + case value { + description + "Bandwidth profile configured by value."; + uses etht-types:pw-segement-bandwidth-profile-grouping; + } + } + } + + grouping tunnels-grouping { + description + "A group of tunnels. "; + leaf name { + type leafref { + path "/te:te/te:tunnels/te:tunnel/te:name"; + require-instance false; + } + description "Dependency tunnel name"; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description "LSP encoding type"; + reference "RFC3945"; + } + leaf switching-type { + type identityref { + base te-types:switching-capabilities; + } + description "LSP switching type"; + reference "RFC3945"; + } + } +} diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..3d152c058a8f623c46cccc89fc1fe8246015a04d --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-eth-tran-types.yang @@ -0,0 +1,460 @@ +module ietf-eth-tran-types { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-eth-tran-types"; + + prefix "etht-types"; + + organization + "Internet Engineering Task Force (IETF) CCAMP WG"; + contact + " + WG List: + + ID-draft editor: + Haomian Zheng (zhenghaomian@huawei.com); + Italo Busi (italo.busi@huawei.com); + Aihua Guo (aihuaguo.ietf@gmail.com); + Anton Snitser (antons@sedonasys.com); + Francesco Lazzeri (francesco.lazzeri@ericsson.com); + Yunbin Xu (xuyunbin@caict.ac.cn); + Yang Zhao (zhaoyangyjy@chinamobile.com); + Xufeng Liu (xufeng.liu.ietf@gmail.com); + Giuseppe Fioccola (giuseppe.fioccola@huawei.com); + Chaode Yu (yuchaode@huawei.com) + "; + + description + "This module defines the ETH types. + The model fully conforms to the Network Management + Datastore Architecture (NMDA). + + Copyright (c) 2019 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 XXXX; see + the RFC itself for full legal notices."; + + revision 2023-10-23 { + description + "version -05 as a WG draft"; + reference + "draft-ietf-ccamp-client-signal-yang"; + } + + /* + * Identities + */ + + identity eth-vlan-tag-type { + description + "ETH VLAN tag type."; + } + + identity c-vlan-tag-type { + base eth-vlan-tag-type; + description + "802.1Q Customer VLAN"; + } + + identity s-vlan-tag-type { + base eth-vlan-tag-type; + description + "802.1Q Service VLAN (QinQ)"; + } + + identity service-classification-type { + description + "Service classification."; + } + + identity port-classification { + base service-classification-type; + description + "Port classification."; + } + + identity vlan-classification { + base service-classification-type; + description + "VLAN classification."; + } + + identity eth-vlan-tag-classify { + description + "VLAN tag classification."; + } + + identity classify-c-vlan { + base eth-vlan-tag-classify; + description + "Classify 802.1Q Customer VLAN tag. + Only C-tag type is accepted"; + } + + identity classify-s-vlan { + base eth-vlan-tag-classify; + description + "Classify 802.1Q Service VLAN (QinQ) tag. + Only S-tag type is accepted"; + } + + identity classify-s-or-c-vlan { + base eth-vlan-tag-classify; + description + "Classify S-VLAN or C-VLAN tag-classify. + Either tag is accepted"; + } + + identity bandwidth-profile-type { + description + "Bandwidth Profile Types"; + } + + identity mef-10-bwp { + base bandwidth-profile-type; + description + "MEF 10 Bandwidth Profile"; + } + + identity rfc-2697-bwp { + base bandwidth-profile-type; + description + "RFC 2697 Bandwidth Profile"; + } + + identity rfc-2698-bwp { + base bandwidth-profile-type; + description + "RFC 2698 Bandwidth Profile"; + } + + identity rfc-4115-bwp { + base bandwidth-profile-type; + description + "RFC 4115 Bandwidth Profile"; + } + + identity service-type { + description + "Type of Ethernet service."; + } + + identity p2p-svc { + base service-type; + description + "Ethernet point-to-point service (EPL, EVPL)."; + } + + identity rmp-svc { + base service-type; + description + "Ethernet rooted-multitpoint service (E-TREE, EP-TREE)."; + } + + identity mp2mp-svc { + base service-type; + description + "Ethernet multipoint-to-multitpoint service (E-LAN, EP-LAN)."; + } + + identity lifecycle-status { + description + "Lifecycle Status."; + } + + identity installed { + base lifecycle-status; + description + "Installed."; + } + + identity planned { + base lifecycle-status; + description + "Planned."; + } + + identity pending-removal { + base lifecycle-status; + description + "Pending Removal."; + } + + /* + * Type Definitions + */ + + typedef eth-tag-type { + type identityref { + base eth-vlan-tag-type; + } + description + "Identifies a specific ETH VLAN tag type."; + } + + typedef eth-tag-classify { + type identityref { + base eth-vlan-tag-classify; + } + description + "Identifies a specific VLAN tag classification."; + } + + typedef vlanid { + type uint16 { + range "1..4094"; + } + description + "The 12-bit VLAN-ID used in the VLAN Tag header."; + } + + typedef vid-range-type { + type string { + pattern "([1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?" + + "(,[1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?)*)"; + } + description + "A list of VLAN Ids, or non overlapping VLAN ranges, in + ascending order, between 1 and 4094. + This type is used to match an ordered list of VLAN Ids, or + contiguous ranges of VLAN Ids. Valid VLAN Ids must be in the + range 1 to 4094, and included in the list in non overlapping + ascending order. + + For example: 1,10-100,50,500-1000"; + } + + typedef bandwidth-profile-type { + type identityref { + base bandwidth-profile-type; + } + description + "Identifies a specific Bandwidth Profile type."; + } + + typedef service-type { + type identityref { + base service-type; + } + description + "Identifies the type of Ethernet service."; + } + + typedef lifecycle-status { + type identityref { + base lifecycle-status; + } + description + "Identifies the lLifecycle Status ."; + } + + /* + * Grouping Definitions + */ + + grouping etht-bandwidth-profiles { + description + "Bandwidth profile configuration paramters."; + + leaf bandwidth-profile-type { + type etht-types:bandwidth-profile-type; + description + "The type of bandwidth profile."; + } + leaf CIR { + type uint64; + description + "Committed Information Rate in Kbps"; + } + leaf CBS { + type uint64; + description + "Committed Burst Size in in KBytes"; + } + leaf EIR { + type uint64; + /* Need to indicate that EIR is not supported by RFC 2697 + + must + '../bw-profile-type = "mef-10-bwp" or ' + + '../bw-profile-type = "rfc-2698-bwp" or ' + + '../bw-profile-type = "rfc-4115-bwp"' + + must + '../bw-profile-type != "rfc-2697-bwp"' + */ + description + "Excess Information Rate in Kbps + In case of RFC 2698, PIR = CIR + EIR"; + } + leaf EBS { + type uint64; + description + "Excess Burst Size in KBytes. + In case of RFC 2698, PBS = CBS + EBS"; + } + leaf color-aware { + type boolean; + description + "Indicates weather the color-mode is + color-aware or color-blind."; + } + leaf coupling-flag { + type boolean; + /* Need to indicate that Coupling Flag is defined only for MEF 10 + + must + '../bw-profile-type = "mef-10-bwp"' + */ + description + "Coupling Flag."; + } + } + + identity topology-role { + description + "The role of underlay topology: e.g., hub, spoke, + any-to-any."; + } + + identity resilience { + description + "Placeholder for resilience information in data plane, + for future study. "; + } + + identity access-role { + description + "Indicating whether the access is a working or protection access."; + } + + identity root-primary { + base access-role; + description + "Designates the primary root UNI of an E-Tree service, and may also + designates the UNI access role of E-LINE and E-LAN service."; + } + + identity root-backup { + base access-role; + description + "Designates the backup root UNI of an E-Tree service."; + } + + identity leaf-access { + base access-role; + description + "Designates the leaf UNI of an E-Tree service."; + } + + identity leaf-edge { + base access-role; + description ""; + } + + identity performance { + description + "Placeholder for performance information, for future study."; + } + + identity encapsulation-type { + description + "Indicating how the service is encapsulated (to PW), e.g, raw or tag. "; + } + grouping pw-segement-bandwidth-profile-grouping { + description + "bandwidth profile grouping for PW segment. "; + leaf bandwidth-profile-type { + type etht-types:bandwidth-profile-type; + description + "The type of bandwidth profile."; + } + leaf CIR { + type uint64; + description + "Committed Information Rate in Kbps"; + } + leaf CBS { + type uint64; + description + "Committed Burst Size in in KBytes"; + } + leaf EIR { + type uint64; + /* Need to indicate that EIR is not supported by RFC 2697 + + must + '../bw-profile-type = "mef-10-bwp" or ' + + '../bw-profile-type = "rfc-2698-bwp" or ' + + '../bw-profile-type = "rfc-4115-bwp"' + + must + '../bw-profile-type != "rfc-2697-bwp"' + */ + description + "Excess Information Rate in Kbps + In case of RFC 2698, PIR = CIR + EIR"; + } + leaf EBS { + type uint64; + description + "Excess Burst Size in KBytes. + In case of RFC 2698, PBS = CBS + EBS"; + } + } + grouping eth-bandwidth { + description + "Available bandwith for ethernet."; + leaf eth-bandwidth { + type uint64{ + range "0..10000000000"; + } + units "Kbps"; + description + "Available bandwith value expressed in kilobits per second"; + } + } + + grouping eth-label-restriction { + description + "Label Restriction for ethernet."; + leaf tag-type { + type etht-types:eth-tag-type; + description "VLAN tag type."; + } + leaf priority { + type uint8; + description "priority."; + } + } + grouping eth-label { + description + "Label for ethernet."; + leaf vlanid { + type etht-types:vlanid; + description + "VLAN tag id."; + } + } + + grouping eth-label-step { + description "Label step for Ethernet VLAN"; + leaf eth-step { + type uint16 { + range "1..4095"; + } + default 1; + description + "Label step which represent possible increments for + an Ethernet VLAN tag."; + reference + "IEEE 802.1ad: Provider Bridges."; + } + } +} diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-service.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-service.yang new file mode 100644 index 0000000000000000000000000000000000000000..f84cae94c73a214834745ba3c594a707d9de0332 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-service.yang @@ -0,0 +1,325 @@ +module ietf-trans-client-service { + /* TODO: FIXME */ + yang-version 1.1; + + namespace "urn:ietf:params:xml:ns:yang:ietf-trans-client-service"; + prefix "clntsvc"; + + import ietf-network { + prefix "nw"; + reference "RFC8345 - A YANG Data Model for Network Topologies"; + } + + import ietf-network-topology { + prefix "nt"; + reference "RFC8345 - A YANG Data Model for Network Topologies"; + } + + import ietf-te-types { + prefix "te-types"; + reference "RFC 8776 - Traffic Engineering Common YANG Types"; + } + + import ietf-layer1-types { + prefix "layer1-types"; + reference "RFC ZZZZ - A YANG Data Model for Layer 1 Types"; + } + + import ietf-yang-types { + prefix "yang"; + reference "RFC 6991 - Common YANG Data Types"; + } + + import ietf-trans-client-svc-types { + prefix "clntsvc-types"; + reference "RFC XXXX - A YANG Data Model for + Transport Network Client Signals"; + } + + organization + "Internet Engineering Task Force (IETF) CCAMP WG"; + contact + " + ID-draft editor: + Haomian Zheng (zhenghaomian@huawei.com); + Aihua Guo (aihuaguo.ietf@gmail.com); + Italo Busi (italo.busi@huawei.com); + Anton Snitser (antons@sedonasys.com); + Francesco Lazzeri (francesco.lazzeri@ericsson.com); + Yunbin Xu (xuyunbin@caict.ac.cn); + Yang Zhao (zhaoyangyjy@chinamobile.com); + Xufeng Liu (Xufeng_Liu@jabil.com); + Giuseppe Fioccola (giuseppe.fioccola@huawei.com); + Chaode Yu (yuchaode@huawei.com); + "; + + description + "This module defines a YANG data model for describing + transport network client services. The model fully conforms + to the Network Management Datastore Architecture (NMDA). + + 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 + (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-10-23 { + description + "version -04 as a WG document"; + reference + "draft-ietf-ccamp-client-signal-yang"; + } + + /* + * Groupings + */ + grouping client-svc-access-parameters { + description + "Transport network client signals access parameters"; + + leaf access-node-id { + type te-types:te-node-id; + description + "The identifier of the access node in the TE topology."; + } + + leaf access-node-uri { + type nw:node-id; + description + "The identifier of the access node in the network."; + } + + leaf access-ltp-id { + type te-types:te-tp-id; + description + "The TE link termination point identifier in TE topology, used + together with access-node-id to identify the access LTP."; + } + + leaf access-ltp-uri { + type nt:tp-id; + description + "The link termination point identifier in network topology, + used together with access-node-uri to identify the access LTP"; + } + + leaf client-signal { + type identityref { + base layer1-types:client-signal; + } + description + "Identify the client signal type associated with this port"; + } + + } + + grouping pm-state-grouping { + leaf latency { + description "latency value of the E2E client signal service"; + type uint32; + units microsecond; + } + } + + grouping error-info-grouping { + leaf error-code { + description "error code"; + type uint16; + } + + leaf error-description { + description "detail message of error"; + type string; + } + + leaf error-timestamp { + description "the date and time error is happened"; + type yang:date-and-time; + } + } + + grouping alarm-shreshold-grouping { + leaf latency-threshold { + description "a threshold for the E2E client signal service's + latency. Once the latency value exceed this threshold, an alarm + should be triggered."; + type uint32; + units microsecond; + } + } + + grouping client-svc-tunnel-parameters { + description + "Transport network client signals tunnel parameters"; + + leaf tunnel-name { + type string; + description + "TE tunnel instance name."; + } + } + + grouping client-svc-instance-config { + description + "Configuration parameters for client services."; + leaf client-svc-name { + type string; + description + "Identifier of the p2p transport network client signals."; + } + + leaf client-svc-title { + type string; + description + "Name of the p2p transport network client signals."; + } + + leaf user-label { + type string; + description + "Alias of the p2p transport network client signals."; + } + + leaf client-svc-descr { + type string; + description + "Description of the transport network client signals."; + } + + leaf client-svc-customer { + type string; + description + "Customer of the transport network client signals."; + } + + container resilience { + description "Place holder for resilience functionalities"; + } + + uses te-types:te-topology-identifier; + + leaf admin-status { + type identityref { + base te-types:tunnel-admin-state-type; + } + default te-types:tunnel-admin-state-up; + description "Client signals administrative state."; + } + + container src-access-ports { + description + "Source access port of a client signal."; + uses client-svc-access-parameters; + } + container dst-access-ports { + description + "Destination access port of a client signal."; + uses client-svc-access-parameters; + } + + container pm-state { + config false; + description "PM data of E2E client signal"; + uses pm-state-grouping; + } + + container error-info { + config false; + description "error messages of configuration"; + uses error-info-grouping; + } + + container alarm-shreshold { + description "threshold configuration for the E2E client signal"; + uses alarm-shreshold-grouping; + } + + leaf direction { + type identityref { + base clntsvc-types:direction; + } + description "Uni-dir or Bi-dir for the client signal."; + } + + list svc-tunnels { + key tunnel-name; + description + "List of the TE Tunnels supporting the client signal."; + uses client-svc-tunnel-parameters; + } + } + + grouping client-svc-instance-state { + description + "State parameters for client services."; + leaf operational-state { + type identityref { + base te-types:tunnel-state-type; + } + config false; + description "Client signal operational state."; + } + leaf provisioning-state { + type identityref { + base te-types:lsp-state-type; + } + config false; + description "Client signal provisioning state."; + } + leaf creation-time { + type yang:date-and-time; + config false; + description "The time of the client signal be created."; + } + leaf last-updated-time { + type yang:date-and-time; + config false; + description "The time of the client signal's latest update."; + } + leaf created-by { + type string; + config false; + description + "The client signal is created by whom, + can be a system or staff ID."; + } + leaf last-updated-by { + type string; + config false; + description + "The client signal is last updated by whom, + can be a system or staff ID."; + } + leaf owned-by { + type string; + config false; + description + "The client signal is owned by whom, + can be a system ID."; + } + } + + /* + * Data nodes + */ + + container client-svc { + description + "Transport client services."; + + list client-svc-instances { + key client-svc-name; + description + "The list of p2p transport client service instances"; + + uses client-svc-instance-config; + uses client-svc-instance-state; + } + } +} diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-svc-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-svc-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..925511735e724b5fd2c2c18624f3e0a8fd13702b --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-client-signal-yang-10/ietf-trans-client-svc-types.yang @@ -0,0 +1,63 @@ +module ietf-trans-client-svc-types { + namespace "urn:ietf:params:xml:ns:yang:ietf-trans-client-svc-types"; + prefix "clntsvc-types"; + + organization + "Internet Engineering Task Force (IETF) CCAMP WG"; + contact + " + ID-draft editor: + Haomian Zheng (zhenghaomian@huawei.com); + Aihua Guo (aihuaguo.ietf@gmail.com); + Italo Busi (italo.busi@huawei.com); + Anton Snitser (antons@sedonasys.com); + Francesco Lazzeri (francesco.lazzeri@ericsson.com); + Yunbin Xu (xuyunbin@caict.ac.cn); + Yang Zhao (zhaoyangyjy@chinamobile.com); + Xufeng Liu (Xufeng_Liu@jabil.com); + Giuseppe Fioccola (giuseppe.fioccola@huawei.com); + Chaode Yu (yuchaode@huawei.com); + "; + + description + "This module defines a YANG data model for describing + transport network client types. The model fully conforms + to the Network Management Datastore Architecture (NMDA). + + Copyright (c) 2019 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 XXXX; see + the RFC itself for full legal notices."; + + revision 2023-10-23 { + description + "version -01 as a WG document"; + reference + "draft-ietf-ccamp-client-signal-yang"; + } + + identity direction { + description + "Direction information of Client Signal."; + } + + identity bidirectional { + base direction; + description + "Client Signal is bi-directional."; + } + + identity unidirectional { + base direction; + description + "Client Signal is uni-directional."; + } + +} diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-eth-client-te-topo-yang-09/ietf-eth-te-topology.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-eth-client-te-topo-yang-09/ietf-eth-te-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..a04eb213daf0b8a60d99caec8d84b5470264a9dd --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-eth-client-te-topo-yang-09/ietf-eth-te-topology.yang @@ -0,0 +1,2278 @@ +module ietf-eth-te-topology { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-eth-te-topology"; + prefix "etht"; + + 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"; + } + + import ietf-te-topology { + prefix "tet"; + reference + "RFC 8795: YANG Data Model for Traffic Engineering + (TE) Topologies"; + } + + import ietf-yang-types { + prefix "yang"; + reference + "RFC 6991: Common YANG Data Types"; + } + + import ietf-eth-tran-types { + prefix "etht-types"; + reference + "RFC YYYY: A YANG Data Model for Transport Network Client + Signals"; + } + // RFC Ed.: replace YYYY with actual RFC number, update date + // information and remove this note + + organization + "IETF CCAMP Working Group"; + contact + "WG Web: + WG List: + + Editor: Haomian Zheng + + + Editor: Italo Busi + + + Editor: Aihua Guo + + + Editor: Yunbin Xu + + + Editor: Yang Zhao + + + Editor: Xufeng Liu + "; + + description + "This module defines a YANG data model for describing + layer-2 Ethernet transport topologies. The model fully + conforms to the Network Management Datastore + Architecture (NMDA). + + Copyright (c) 2023 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. + + 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."; + + revision 2023-09-28 { + description + "Initial Revision"; + reference + "RFC XXXX: A YANG Data Model for Ethernet TE Topology"; + // RFC Ed.: replace XXXX with actual RFC number, update date + // information and remove this note + } + + /* + * Groupings + */ + + grouping label-range-info { + description + "Ethernet technology-specific label range related + information with a presence container indicating that the + label range is an Ethernet technology-specific label range. + + This grouping SHOULD be used together with the + eth-label and eth-label-step groupings to provide Ethernet + technology-specific label information to the models which + use the label-restriction-info grouping defined in the module + ietf-te-types."; + + container ethernet-label-range { + presence + "Indicates the label range is an Ethernet label range. + + This container must not be present if there are other + presence containers or attributes indicating another type + of label range."; + description + "Ethernet-specific label range related information."; + + uses etht-types:eth-label-restriction; + } + } + + grouping eth-tran-topology-type { + description + "Identifies the Ethernet Transport topology type"; + + container eth-tran-topology { + presence "indicates a topology type of + Ethernet Transport Network."; + description "Eth transport topology type"; + } + } + + grouping ltp-bandwidth-profiles { + description + "A grouping which represents the bandwidth profile(s) + for the ETH LTP."; + + choice direction { + description + "Whether the bandwidth profiles are symmetrical or + asymmetrical"; + case symmetrical { + description + "The same bandwidth profile is used to describe the ingress + and the egress bandwidth profile."; + + container ingress-egress-bandwidth-profile { + description + "The bandwith profile used in the ingress and egress + direction."; + uses etht-types:etht-bandwidth-profiles; + } + } + case asymmetrical { + description + "Different ingress and egress bandwidth profiles + can be specified."; + container ingress-bandwidth-profile { + description + "The bandwidth profile used in the ingress direction."; + uses etht-types:etht-bandwidth-profiles; + } + container egress-bandwidth-profile { + description + "The bandwidth profile used in the egress direction."; + uses etht-types:etht-bandwidth-profiles; + } + } + } + } + + grouping eth-ltp-attributes { + description + "Ethernet transport Link Termination Point (LTP) attributes"; + + leaf ltp-mac-address { + type yang:mac-address; + description + "The MAC address of the Ethernet LTP."; + } + leaf port-vlan-id { + type etht-types:vlanid; + description + "The Port VLAN ID of the Ethernet LTP."; + reference + "IEEE 802.1Q: Virtual Bridged Local Area Networks"; + } + leaf maximum-frame-size { + type uint16 { + range "64 .. 65535"; + } + description + "Maximum frame size"; + reference + "IEEE 802.1Q: Virtual Bridged Local Area Networks"; + } + uses ltp-bandwidth-profiles; + } + + grouping svc-vlan-classification { + description + "Grouping defining the capabilities for VLAN classification."; + + leaf-list supported-tag-types { + type etht-types:eth-tag-classify; + description + "List of VLAN tag types that can be used for the VLAN + classification. In case VLAN classification is not + supported, the list is empty."; + } + leaf vlan-bundling { + type boolean; + description + "In case VLAN classification is supported, indicates whether + VLAN bundling classification is also supported."; + reference + "MEF 10.3: Ethernet Services Attributes Phase 3"; + } + leaf vlan-range { + type etht-types:vid-range-type; + description + "In case VLAN classification is supported, indicates the + of available VLAN ID values."; + } + } + + grouping svc-vlan-push { + description + "Grouping defining the capabilities for VLAN push or swap + operations."; + + leaf-list supported-tag-types { + type etht-types:eth-tag-type; + description + "List of VLAN tag types that can be used to push or swap a + VLAN tag. In case VLAN push/swap is not supported, the list + is empty."; + reference + "IEEE 802.1Q: Virtual Bridged Local Area Networks"; + } + leaf vlan-range { + type etht-types:vid-range-type; + description + "In case VLAN push/swap operation is supported, the range + of available VLAN ID values."; + } + } + + grouping eth-svc-attributes { + description + "Ethernet Link Termination Point (LTP) service attributes."; + + container supported-classification { + description + "Service classification capability supported by the + Ethernet Link Termination Point (LTP)."; + + leaf port-classification { + type boolean; + description + "Indicates that the ETH LTP support port-based service + classification."; + } + container vlan-classification { + description + "Service classification capabilities based on the VLAN + tag(s) supported by the ETH LTP."; + + leaf vlan-tag-classification { + type boolean; + description + "Indicates that the ETH LTP supports VLAN service + classification."; + } + container outer-tag { + description + "Service classification capabilities based on the outer + VLAN tag, supported by the ETH LTP."; + uses svc-vlan-classification; + } + container second-tag { + description + "Service classification capabilities based on the second + VLAN tag, supported by the ETH LTP."; + leaf second-tag-classification { + type boolean; + must ". = 'false' or " + + "../../vlan-tag-classification = 'true'" { + description + "VLAN service classification based on the second + VLAN tag can be supported only when VLAN service + classification"; + } + description + "Indicates that the ETH LTP support VLAN service + classification based on the second VLAN tag."; + } + uses svc-vlan-classification; + } + } + } + + container supported-vlan-operations { + description + "Reports the VLAN operations supported by the ETH LTP."; + + leaf asymmetrical-operations { + type boolean; + description + "Indicates whether the ETH LTP supports also asymmetrical + VLAN operations.It is assumed that symmetrical VLAN + operations are alwyas supported."; + } + leaf transparent-vlan-operations { + type boolean; + description + "Indicates that the ETH LTP supports transparent + operations."; + } + container vlan-pop { + description + "Indicates VLAN pop or swap operations capabilities."; + + leaf vlan-pop-operations { + type boolean; + description + "Indicates that the ETH LTP supports VLAN pop or + swap operations."; + } + leaf max-pop-tags { + type uint8 { + range "1..2"; + } + description + "Indicates the maximum number of tags that can be + popped/swapped."; + } + } + container vlan-push { + description + "Indicates VLAN push or swap operations capabilities."; + + leaf vlan-push-operation { + type boolean; + description + "Indicates that the ETH LTP supports VLAN push or + swap operations."; + } + container outer-tag { + description + "Indicates the supported VLAN operation capabilities + on the outer VLAN tag."; + uses svc-vlan-push; + } + container second-tag { + description + "Indicates the supported VLAN operation capabilities + on the second VLAN tag."; + leaf push-second-tag { + type boolean; + description + "Indicates that the ETH LTP supports VLAN push or swap + operations for the second VLAN tag."; + } + uses svc-vlan-push; + } + } + } + } + + /* + * Data nodes + */ + + augment "/nw:networks/nw:network/nw:network-types/" + + "tet:te-topology" { + description + "Augment network types to include ETH transport newtork"; + + uses eth-tran-topology-type; + } + + augment "/nw:networks/nw:network/nw:node/tet:te" + + "/tet:te-node-attributes" { + when "../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description "Augment only for Ethernet transport network."; + } + description "Augment TE node attributes."; + container eth-node { + presence "The TE node is an Ethernet node."; + description + "Presence container used only to indicate that the TE node + is an Ethernet node."; + } + } + + augment "/nw:networks/nw:network/nt:link" { + when "../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description "Augment only for Ethernet transport network."; + } + description "Augment link configuration"; + + container eth-svc { + presence + "When present, indicates that the Link supports Ethernet + client signals."; + description + "Presence container used only to indicate that the link + supports Ethernet client signals."; + } + } + + augment "/nw:networks/nw:network/nw:node/nt:termination-point" { + when "../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description "Augment only for Ethernet transport network."; + } + description + "Augment ETH LTP attributes"; + + container eth-svc { + presence + "When present, indicates that the Link Termination Point + (LTP) supports Ethernet client signals."; + description + "ETH LTP Service attributes."; + + uses eth-svc-attributes; + } + container eth-link-tp { + description + "Attributes of the Ethernet Link Termination Point (LTP)."; + uses eth-ltp-attributes; + } + } + + /* + * Augment TE bandwidth + */ + + augment "/nw:networks/nw:network/nw:node/nt:termination-point/" + + "tet:te/" + + "tet:interface-switching-capability/tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum LSP TE bandwidth for the link termination + point (LTP)."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints of the TE node + connectivity matrices."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints of the + connectivity matrix entry."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints of the TE node + connectivity matrices information source."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints of the + connectivity matrix entry information source"; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:client-layer-adaptation/tet:switching-capability/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment client TE bandwidth of the tunnel termination point + (TTP)"; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/tet:path-constraints/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints for the TTP + Local Link Connectivities."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/tet:path-constraints/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE bandwidth path constraints for the TTP + Local Link Connectivity entry."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:interface-switching-capability/tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum LSP TE bandwidth for the TE link."; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum TE bandwidth for the TE link"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum reservable TE bandwidth for the TE link"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment unreserved TE bandwidth for the TE Link"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:interface-switching-capability/" + + "tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum LSP TE bandwidth for the TE link + information source"; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum TE bandwidth for the TE link + information source"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment maximum reservable TE bandwidth for the TE link + information-source"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment unreserved TE bandwidth of the TE link + information source"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:interface-switching-capability/" + + "tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + description + "Augment maximum LSP TE bandwidth of the TE link + template"; + case eth { + uses etht-types:eth-bandwidth; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment maximum TE bandwidth the TE link template"; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment maximum reservable TE bandwidth for the TE link + template."; + uses etht-types:eth-bandwidth; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment unreserved TE bandwidth the TE link template"; + uses etht-types:eth-bandwidth; + } + + /* + * Augment TE label range information + */ + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TE node + connectivity matrices."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the source LTP + of the connectivity matrix entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the destination LTP + of the connectivity matrix entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TE node + connectivity matrices information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the source LTP + of the connectivity matrix entry information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the destination LTP + of the connectivity matrix entry information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TTP + Local Link Connectivities."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TTP + Local Link Connectivity entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TE link."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range information for the TE link + information source."; + uses label-range-info; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction" { + description + "Augment TE label range information for the TE link template."; + uses label-range-info; + } + + /* + * Augment TE label. + */ + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TE node + connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/" + + "tet:label-restriction/tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TE node + connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/" + + "tet:label-restriction/tet:label-step/" + + "tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TE node + connectivity matrices"; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:underlay/tet:primary-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path of the + TE node connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:underlay/tet:backup-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path of the + TE node connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TE node connectivity + matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TE node connectivity + matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TE node connectivity matrices"; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the source LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the source LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the source LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the destination LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the destination LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the destination LTP + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:primary-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:backup-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:optimizations/" + + "tet:algorithm/tet:metric/tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:optimizations/" + + "tet:algorithm/tet:metric/tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the connectivity matrix entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TE node connectivity + matrices information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TE node connectivity + matrices information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TE node connectivity + matrices information source."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TE node connectivity matrices of the information + source entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TE node connectivity matrices of the information + source entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TE node connectivity matrices + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TE node connectivity matrices + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TE node connectivity matrices information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the source LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the source LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the source LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the destination LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the destination LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the destination LTP + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the connectivity matrix entry + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the connectivity matrix entry + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the connectivity matrix entry information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TTP + Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology"{ + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TTP + Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology"{ + when "../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TTP + Local Link Connectivities."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TTP Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TTP Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TTP Local Link + Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TTP Local Link + Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TTP Local Link Connectivities."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TTP + Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TTP + Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TTP + Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TTP Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TTP Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TTP Local Link + Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TTP Local Link + Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TTP Local Link Connectivity entry."; + case eth { + uses etht-types:eth-label; + } + } + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TE link."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TE link."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TE link."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TE link."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TE link."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range start for the TE link + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range end for the TE link + information source."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "etht:eth-tran-topology" { + description + "Augmentation parameters apply only for networks with + Ethernet topology type."; + } + description + "Augment TE label range step for the TE link + information source."; + case eth { + uses etht-types:eth-label-step; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + description + "Augment TE label hop for the underlay primary path + of the TE link template."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + description + "Augment TE label hop for the underlay backup path + of the TE link template."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + description + "Augment TE label range start for the TE link template."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + description + "Augment TE label range end for the TE link template."; + case eth { + uses etht-types:eth-label; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + description + "Augment TE label range step for the TE link template."; + case eth { + uses etht-types:eth-label-step; + } + } + +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-otn-topo-yang-20/ietf-otn-topology.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-otn-topo-yang-20/ietf-otn-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..15e7ac508d893a3dff2d64be67a89f8c8a2feae8 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-otn-topo-yang-20/ietf-otn-topology.yang @@ -0,0 +1,2230 @@ +module ietf-otn-topology { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-otn-topology"; + prefix "otnt"; + + 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"; + } + + import ietf-te-topology { + prefix "tet"; + reference + "RFC 8795: YANG Data Model for Traffic Engineering + (TE) Topologies"; + } + + import ietf-layer1-types { + prefix "l1-types"; + reference + "RFC YYYY: A YANG Data Model for Layer 1 Types"; + } + // RFC Editor: replace YYYY with actual RFC number assigned to + // [I-D.ietf-ccamp-layer1-types] and remove this note + + organization + "IETF CCAMP Working Group"; + contact + "WG Web: + WG List: + + Editor: Haomian Zheng + + + Editor: Italo Busi + + + Editor: Xufeng Liu + + + Editor: Sergio Belotti + + + Editor: Oscar Gonzalez de Dios + "; + + description + "This module defines a protocol independent Layer 1/ODU topology + data model. The model fully conforms + to the Network Management Datastore Architecture (NMDA). + + 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. + + 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."; + + revision 2024-06-21 { + description + "Initial Revision"; + reference + "RFC XXXX: A YANG Data Model for Optical Transport Network + Topology"; + } + // RFC Editor: replace XXXX with actual RFC number, update date + // information and remove this note + + /* + * Groupings + */ + + grouping label-range-info { + description + "OTN technology-specific label range related information with + a presence container indicating that the label range is an + OTN technology-specific label range. + + This grouping SHOULD be used together with the + otn-label-start-end and otn-label-step groupings to provide + OTN technology-specific label information to the models which + use the label-restriction-info grouping defined in the module + ietf-te-types."; + uses l1-types:otn-label-range-info { + refine otn-label-range { + presence + "Indicates the label range is an OTN label range. + + This container MUST NOT be present if there are other + presence containers or attributes indicating another type + of label range."; + } + } + } + + /* + * Data nodes + */ + + augment "/nw:networks/nw:network/nw:network-types/" + + "tet:te-topology" { + container otn-topology { + presence "indicates a topology type of Optical Transport + Network (OTN)-electrical layer."; + description "OTN topology type"; + } + description "augment network types to include OTN."; + } + + augment "/nw:networks/nw:network/nw:node/tet:te" + + "/tet:te-node-attributes" { + when "../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description "Augment only for OTN."; + } + description "Augment TE node attributes."; + container otn-node { + presence "The TE node is an OTN node."; + description + "Introduce new TE node type for OTN node."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes" { + when "../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description "Augment only for OTN."; + } + description "Augment link configuration"; + + container otn-link { + description + "Attributes of the OTN Link."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs set up + on this OTN Link."; + } + leaf tsg { + type identityref { + base l1-types:tributary-slot-granularity; + } + description "Tributary slot granularity."; + reference + "ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + leaf distance { + type uint32; + description "distance in the unit of kilometers"; + } + } + container client-svc { + presence + "When present, indicates that the Link supports Constant + Bit Rate (CBR) client signals."; + description + "Attributes of the Link supporting CBR client signals."; + leaf-list supported-client-signal { + type identityref { + base l1-types:client-signal; + } + min-elements 1; + description + "List of client signal types supported by the Link."; + } + } + } + + augment "/nw:networks/nw:network/nw:node/nt:termination-point/" + + "tet:te" { + when "../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description "Augment only for OTN."; + } + description + "Augment link termination point (LTP) configuration."; + + container otn-link-tp { + description + "Attributes of the OTN Link Termination Point (LTP)."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs set up + on this OTN Link Termination Point (LTP)."; + } + } + container client-svc { + presence + "When present, indicates that the Link Termination Point + (LTP) supports Constant Bit Rate (CBR) client signals."; + description + "OTN LTP Service attributes."; + leaf-list supported-client-signal { + type identityref { + base l1-types:client-signal; + } + description + "List of client signal types supported by the LTP."; + } + } + } + + /* + * Augment TE bandwidth + */ + + augment "/nw:networks/nw:network/nw:node/nt:termination-point/" + + "tet:te/" + + "tet:interface-switching-capability/tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum LSP TE bandwidth for the link termination + point (LTP)."; + case otn { + uses l1-types:otn-max-path-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link Termination + Point (LTP) is used to compute the number of Tributary + Slots (TS) required by the ODUflex LSPs set up on this + OTN LTP."; + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints of the TE node + connectivity matrices."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay paths of these OTN + connectivity matrices."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints of the + connectivity matrix entry."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay path of this OTN + connectivity matrix entry."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints of the TE node + connectivity matrices information source."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay paths of these OTN + connectivity matrices."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-constraints/tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints of the + connectivity matrix entry information source"; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay path of this OTN + connectivity matrix entry."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:client-layer-adaptation/tet:switching-capability/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment client TE bandwidth of the tunnel termination point + (TTP)"; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + terminated on this OTN Tunnel Termination Point + (TTP)."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/tet:path-constraints/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints for the TTP + Local Link Connectivities."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay paths of these OTN Local + Link Connectivities."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/tet:path-constraints/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE bandwidth path constraints for the TTP + Local Link Connectivity entry."; + case otn { + uses l1-types:otn-link-bandwidth { + augment otn-bandwidth { + description + "Augment OTN link bandwidth information."; + leaf odtu-flex-type { + type l1-types:odtu-flex-type; + description + "The type of Optical Data Tributary Unit (ODTU) + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by the ODUflex LSPs + set up along the underlay path of this OTN Local + Link Connectivity entry."; + } + } + } + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:interface-switching-capability/tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum LSP TE bandwidth for the TE link."; + case otn { + uses l1-types:otn-max-path-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum TE bandwidth for the TE link"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum reservable TE bandwidth for the TE link"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment unreserved TE bandwidth for the TE Link"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:interface-switching-capability/" + + "tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum LSP TE bandwidth for the TE link + information source"; + case otn { + uses l1-types:otn-max-path-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum TE bandwidth for the TE link + information source"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment maximum reservable TE bandwidth for the TE link + information-source"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment unreserved TE bandwidth of the TE link + information source"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on this OTN Link."; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:interface-switching-capability/" + + "tet:max-lsp-bandwidth/" + + "tet:te-bandwidth/tet:technology" { + description + "Augment maximum LSP TE bandwidth of the TE link + template"; + case otn { + uses l1-types:otn-max-path-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on the OTN Link that uses this + Link Template."; + } + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:max-link-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment maximum TE bandwidth the TE link template"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on the OTN Link that uses this + Link Template."; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:max-resv-link-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment maximum reservable TE bandwidth for the TE link + template."; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on the OTN Link that uses this + Link Template."; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:unreserved-bandwidth/" + + "tet:te-bandwidth" { + description + "Augment unreserved TE bandwidth the TE link template"; + uses l1-types:otn-link-bandwidth { + description + "The odtu-flex-type attribute of the OTN Link is used + to compute the number of Tributary Slots (TS) required + by the ODUflex LSPs set up on the OTN Link that uses this + Link Template."; + } + } + + /* + * Augment TE label range information + */ + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TE node + connectivity matrices."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the source LTP + of the connectivity matrix entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the destination LTP + of the connectivity matrix entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TE node + connectivity matrices information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the source LTP + of the connectivity matrix entry information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the destination LTP + of the connectivity matrix entry information source."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TTP + Local Link Connectivities."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TTP + Local Link Connectivity entry."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TE link."; + uses label-range-info; + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction" { + when "../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range information for the TE link + information source."; + uses label-range-info; + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction" { + description + "Augment TE label range information for the TE link template."; + uses label-range-info; + } + + /* + * Augment TE label + */ + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TE node + connectivity matrices"; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/" + + "tet:label-restriction/tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TE node + connectivity matrices"; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:label-restrictions/" + + "tet:label-restriction/tet:label-step/" + + "tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TE node + connectivity matrices"; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:underlay/tet:primary-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path of the + TE node connectivity matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:underlay/tet:backup-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path of the + TE node connectivity matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TE node connectivity + matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TE node connectivity + matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TE node connectivity matrices"; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the source LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the source LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:from/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the source LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the destination LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the destination LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:to/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the destination LTP + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:primary-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:backup-path/tet:path-element/" + + "tet:type/tet:label/tet:label-hop/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:optimizations/" + + "tet:algorithm/tet:metric/tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/tet:optimizations/" + + "tet:algorithm/tet:metric/tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:te-node-attributes/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the connectivity matrix entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TE node connectivity + matrices information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TE node connectivity + matrices information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/" + + "tet:connectivity-matrices/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TE node connectivity + matrices information source."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TE node connectivity matrices of the information + source entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TE node connectivity matrices of the information + source entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TE node connectivity matrices + information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TE node connectivity matrices + information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TE node connectivity matrices information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the source LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the source LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:from/tet:label-restrictions/" + + "tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the source LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the destination LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the destination LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:to/tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the destination LTP + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the connectivity matrix entry + information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the connectivity matrix entry + information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:information-source-entry/tet:connectivity-matrices/" + + "tet:connectivity-matrix/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the connectivity matrix entry information source."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/" + + "tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TTP + Local Link Connectivities."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/" + + "tet:te-label/tet:technology"{ + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TTP + Local Link Connectivities."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/" + + "tet:technology"{ + when "../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TTP + Local Link Connectivities."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TTP Local Link Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TTP Local Link Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TTP Local Link + Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TTP Local Link + Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TTP Local Link Connectivities."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TTP + Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TTP + Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TTP + Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TTP Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TTP Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-exclude-objects/" + + "tet:route-object-exclude-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects excluded + by the path computation of the TTP Local Link + Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:optimizations/tet:algorithm/tet:metric/" + + "tet:optimization-metric/" + + "tet:explicit-route-include-objects/" + + "tet:route-object-include-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the explicit route objects included + by the path computation of the TTP Local Link + Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nw:node/tet:te/" + + "tet:tunnel-termination-point/" + + "tet:local-link-connectivities/" + + "tet:local-link-connectivity/" + + "tet:path-properties/tet:path-route-objects/" + + "tet:path-route-object/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the computed path route objects + of the TTP Local Link Connectivity entry."; + case otn { + uses l1-types:otn-label-hop; + } + } + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay primary path + of the TE link."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + when "../../../../../../../../" + + "nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label hop for the underlay backup path + of the TE link."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TE link."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TE link."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TE link."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range start for the TE link + information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + when "../../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range end for the TE link + information source."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/nw:network/nt:link/tet:te/" + + "tet:information-source-entry/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + when "../../../../../../nw:network-types/tet:te-topology/" + + "otnt:otn-topology" { + description + "Augmentation parameters apply only for networks with + OTN topology type."; + } + description + "Augment TE label range step for the TE link + information source."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:underlay/tet:primary-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + description + "Augment TE label hop for the underlay primary path + of the TE link template."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:underlay/tet:backup-path/tet:path-element/tet:type/" + + "tet:label/tet:label-hop/tet:te-label/tet:technology" { + description + "Augment TE label hop for the underlay backup path + of the TE link template."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-start/tet:te-label/tet:technology" { + description + "Augment TE label range start for the TE link template."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-end/tet:te-label/tet:technology" { + description + "Augment TE label range end for the TE link template."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/nw:networks/tet:te/tet:templates/" + + "tet:link-template/tet:te-link-attributes/" + + "tet:label-restrictions/tet:label-restriction/" + + "tet:label-step/tet:technology" { + description + "Augment TE label range step for the TE link template."; + case otn { + uses l1-types:otn-label-step; + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-otn-tunnel-model-23/ietf-otn-tunnel.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-otn-tunnel-model-23/ietf-otn-tunnel.yang new file mode 100644 index 0000000000000000000000000000000000000000..aa9e1d5e545313f7f525b560681dd586d4fdef5d --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-ccamp-otn-tunnel-model-23/ietf-otn-tunnel.yang @@ -0,0 +1,1022 @@ +module ietf-otn-tunnel { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-otn-tunnel"; + prefix "otn-tnl"; + + import ietf-te { + prefix "te"; + reference + "RFC ZZZZ: A YANG Data Model for Traffic Engineering Tunnels + and Interfaces."; + } + /* Note: The RFC Editor will replace ZZZZ with the number assigned + to the RFC once draft-ietf-teas-yang-te becomes an RFC.*/ + + import ietf-layer1-types { + prefix "l1-types"; + reference + "RFC YYYY: Common YANG Data Types for Layer 1 Networks."; + } + /* Note: The RFC Editor will replace YYYY with the number assigned + to the RFC once draft-ietf-ccamp-layer1-types becomes an RFC.*/ + + organization + "IETF CCAMP Working Group"; + contact + "WG Web: + WG List: + + Editor: Haomian Zheng + + + Editor: Italo Busi + + + Editor: Sergio Belotti + + + Editor: Victor Lopez + + + Editor: Yunbin Xu + "; + + description + "This module defines a model for OTN Tunnel Services. + + The model fully conforms to the Network Management + Datastore Architecture (NMDA). + + 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-03-21" { + description + "Initial version"; + reference + "RFC XXXX: A YANG Data Model for Optical Transport Network + (OTN) Tunnels and Label Switched Paths"; + // RFC Ed.: replace XXXX with actual RFC number, update date + // information and remove this note + } + + /* + * Data nodes + */ + + /* + * Augment TE bandwidth + */ + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the named path constraint."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the tunnel."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the primary path."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:computed-paths-properties/" + + "te:computed-path-properties/te:path-properties/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of primary path's computed path + properties."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the primary reverse path."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:computed-paths-properties/" + + "te:computed-path-properties/te:path-properties/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the primary reverse path's computed + path properties."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the secondary path."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:computed-paths-properties/" + + "te:computed-path-properties/te:path-properties/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the secondary path's computed path + properties."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/" + + "te:secondary-reverse-path/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the secondary reverse path."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/" + + "te:secondary-reverse-path/" + + "te:computed-paths-properties/" + + "te:computed-path-properties/te:path-properties/" + + "te:te-bandwidth/te:technology" { + description + "Augment TE bandwidth of the secondary reverse path's computed + path properties."; + case otn { + uses l1-types:otn-path-bandwidth; + } + } + + /* + * Augment TE label range information + */ + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:path-in-segment/" + + "te:label-restrictions/te:label-restriction" { + description + "Augment TE label range information for the ingress segment + of the named path constraint."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:path-out-segment/" + + "te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the egress segment + of the named path constraint."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the ingress segment + of the primay path."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the egress segment + of the primay path."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the ingress segment + of the primay reverse path."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the egress segment + of the primay reverse path."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the ingress segment + of the secondary path."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the egress segment + of the secondary path."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the ingress segment + of the secondary reverse path."; + uses l1-types:otn-label-range-info; + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction" { + description + "Augment TE label range information for the egress segment + of the secondary reverse path."; + uses l1-types:otn-label-range-info; + } + + /* + * Augment TE label. + */ + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/" + + "te:explicit-route-objects/" + + "te:route-object-exclude-always/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects always + excluded by the path computation with the named path + constraint."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/" + + "te:explicit-route-objects/" + + "te:route-object-include-exclude/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects included + or excluded by the path computation with the named path + constraint."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:path-in-segment/" + + "te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the ingress segment + of the named path constraint."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:path-in-segment/" + + "te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the ingress segment + of the named path constraint."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:path-in-segment/" + + "te:label-restrictions/te:label-restriction/" + + "te:label-step/te:technology" { + description + "Augment TE label range step for the ingress segment + of the named path constraint."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:path-out-segment/" + + "te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the egress segment + of the named path constraint."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:path-out-segment/" + + "te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the egress segment + of the named path constraint."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:path-out-segment/" + + "te:label-restrictions/te:label-restriction/" + + "te:label-step/te:technology" { + description + "Augment TE label range step for the egress segment + of the named path constraint."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:optimizations/te:algorithm/te:metric/" + + "te:optimization-metric/te:explicit-route-exclude-objects/" + + "te:route-object-exclude-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the optimization of the explicit + route objects excluded by the path computation of the primary + path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:optimizations/te:algorithm/te:metric/" + + "te:optimization-metric/te:explicit-route-include-objects/" + + "te:route-object-include-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the optimization of the explicit + route objects included by the path computation of the primary + path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:explicit-route-objects/" + + "te:route-object-exclude-always/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects always + excluded by the path computation of the primary path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:explicit-route-objects/" + + "te:route-object-include-exclude/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects included + or excluded by the path computation of the primary path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the ingress segment + of the primay path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the ingress segment + of the primay path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-step/te:technology" { + description + "Augment TE label range step for the ingress segment + of the primay path."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the egress segment + of the primay path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the egress segment + of the primay path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-step/te:technology" { + description + "Augment TE label range end for the egress segment + of the primay path."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:computed-paths-properties/" + + "te:computed-path-properties/te:path-properties/" + + "te:path-route-objects/te:path-route-object/" + + "te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the route object of the computed + primary path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:optimizations/te:algorithm/te:metric/" + + "te:optimization-metric/te:explicit-route-exclude-objects/" + + "te:route-object-exclude-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the optimization of the explicit + route objects excluded by the path computation of the primary + reverse path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:optimizations/te:algorithm/te:metric/" + + "te:optimization-metric/te:explicit-route-include-objects/" + + "te:route-object-include-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the optimization of the explicit + route objects included by the path computation of the primary + reverse path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:explicit-route-objects/" + + "te:route-object-exclude-always/" + + "te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects always + excluded by the path computation of the primary reverse + path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:explicit-route-objects/" + + "te:route-object-include-exclude/" + + "te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects included + or excluded by the path computation of the primary reverse + path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the ingress segment + of the primay reverse path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the ingress segment + of the primay reverse path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-step/te:technology" { + description + "Augment TE label range step for the ingress segment + of the primay reverse path."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the egress segment + of the primay reverse path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the egress segment + of the primay reverse path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-step/te:technology" { + description + "Augment TE label range step for the egress segment + of the primay reverse path."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:primary-paths/te:primary-path/" + + "te:primary-reverse-path/" + + "te:computed-paths-properties/te:computed-path-properties/" + + "te:path-properties/te:path-route-objects/" + + "te:path-route-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the route object of the computed + primary reverse path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:optimizations/te:algorithm/te:metric/" + + "te:optimization-metric/te:explicit-route-exclude-objects/" + + "te:route-object-exclude-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the optimization of the explicit + route objects excluded by the path computation of the + secondary path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:optimizations/te:algorithm/te:metric/" + + "te:optimization-metric/te:explicit-route-include-objects/" + + "te:route-object-include-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the optimization of the explicit + route objects included by the path computation of the + secondary path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:explicit-route-objects/" + + "te:route-object-exclude-always/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects always + excluded by the path computation of the secondary path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:explicit-route-objects/" + + "te:route-object-include-exclude/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects included + or excluded by the path computation of the secondary path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the ingress segment + of the secondary path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the ingress segment + of the secondary path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-step/te:technology" { + description + "Augment TE label range step for the ingress segment + of the secondary path."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the egress segment + of the secondary path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the egress segment + of the secondary path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-step/te:technology" { + description + "Augment TE label range step for the egress segment + of the secondary path."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-paths/te:secondary-path/" + + "te:computed-paths-properties/" + + "te:computed-path-properties/" + + "te:path-properties/te:path-route-objects/" + + "te:path-route-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the route object of the computed + secondary path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:optimizations/te:algorithm/te:metric/" + + "te:optimization-metric/te:explicit-route-exclude-objects/" + + "te:route-object-exclude-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the optimization of the explicit + route objects excluded by the path computation of the + secondary reverse path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:optimizations/te:algorithm/te:metric/" + + "te:optimization-metric/te:explicit-route-include-objects/" + + "te:route-object-include-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the optimization of the explicit + route objects included by the path computation of the + secondary reverse path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:explicit-route-objects/" + + "te:route-object-exclude-always/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects always + excluded by the path computation of the secondary reverse + path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:explicit-route-objects/" + + "te:route-object-include-exclude/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the explicit route objects included + or excluded by the path computation of the secondary reverse + path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the ingress segment + of the secondary reverse path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the ingress segment + of the secondary reverse path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:path-in-segment/te:label-restrictions/" + + "te:label-restriction/te:label-step/te:technology" { + description + "Augment TE label range step for the ingress segment + of the secondary reverse path."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-start/" + + "te:te-label/te:technology" { + description + "Augment TE label range start for the egress segment + of the secondary reverse path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-end/" + + "te:te-label/te:technology" { + description + "Augment TE label range end for the egress segment + of the secondary reverse path."; + case otn { + uses l1-types:otn-label-start-end; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:path-out-segment/te:label-restrictions/" + + "te:label-restriction/te:label-step/te:technology" { + description + "Augment TE label range step for the egress segment + of the secondary reverse path."; + case otn { + uses l1-types:otn-label-step; + } + } + + augment "/te:te/te:tunnels/te:tunnel/" + + "te:secondary-reverse-paths/te:secondary-reverse-path/" + + "te:computed-paths-properties/" + + "te:computed-path-properties/" + + "te:path-properties/te:path-route-objects/" + + "te:path-route-object/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the route object of the computed + secondary reverse path."; + case otn { + uses l1-types:otn-label-hop; + } + } + + augment "/te:te/te:lsps/" + + "te:lsp/te:lsp-actual-route-information/" + + "te:lsp-actual-route-information/te:type/te:label/" + + "te:label-hop/te:te-label/te:technology" { + description + "Augment TE label hop for the record route of the LSP."; + case otn { + uses l1-types:otn-label-hop; + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-packet-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-packet-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..834e78bcdd0109409aef6a4529341f99decd846e --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-packet-types.yang @@ -0,0 +1,835 @@ +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: + WG List: + + Editor: Tarek Saad + + + Editor: Rakesh Gandhi + + + Editor: Vishnu Pavan Beeram + + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + "; + description + "This YANG module contains a collection of generally useful YANG + data type definitions specific to Packet Traffic Engineering + (TE). + + The model 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) 2025 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 2025-01-24 { + 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"; + } + + 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"; + } + + /* + * 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 "kilobits per second"; + description + "Bandwidth values, expressed in kilobits per second."; + } + + typedef bandwidth-mbps { + type uint64; + units "megabits per second"; + description + "Bandwidth values, expressed in megabits per second."; + } + + typedef bandwidth-gbps { + type uint64; + units "gigabits per second"; + description + "Bandwidth values, expressed in gigabits per second."; + } + + /* + * Groupings + */ + + grouping performance-metrics-attributes-packet { + description + "Contains Performance Metrics (PM) information."; + uses te-types:performance-metrics-attributes { + augment "performance-metrics-one-way" { + description + "Performance Metrics (PM) one-way packet-specific + augmentation for a generic PM grouping."; + leaf one-way-min-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + description + "One-way minimum delay or latency."; + } + 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"; + } + units "microseconds"; + description + "One-way maximum delay or latency."; + } + 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"; + } + units "microseconds"; + description + "One-way delay variation."; + 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"; + } + units "%"; + description + "One-way packet loss as a percentage of the total traffic + sent over a configurable interval. + + The finest precision is 0.000003%."; + 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"; + } + } + augment "performance-metrics-two-way" { + description + "Performance Metrics (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"; + leaf two-way-min-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way minimum delay or latency."; + } + 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"; + } + units "microseconds"; + default "0"; + description + "Two-way maximum delay or latency."; + } + 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"; + } + units "microseconds"; + default "0"; + description + "Two-way delay variation."; + 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"; + } + units "%"; + 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."; + } + } + } + } + + grouping one-way-performance-metrics-packet { + description + "One-way packet Performance Metrics (PM) throttle grouping."; + leaf one-way-min-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "One-way minimum delay or latency."; + } + leaf one-way-max-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "One-way maximum delay or latency."; + } + leaf one-way-delay-variation { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "One-way delay variation."; + } + leaf one-way-packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + units "%"; + 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 Performance Metrics (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; + units "microseconds"; + description + "One-way minimum delay or latency."; + } + leaf one-way-max-delay { + type yang:gauge64; + units "microseconds"; + description + "One-way maximum delay or latency."; + reference + "RFC 7679: A One-Way Delay Metric for IP Performance + Metrics (IPPM)"; + } + leaf one-way-delay-variation { + type yang:gauge64; + units "microseconds"; + description + "One-way delay variation."; + 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 Performance Metrics (PM) throttle grouping."; + leaf two-way-min-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way minimum delay or latency."; + } + leaf two-way-max-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way maximum delay or latency."; + } + leaf two-way-delay-variation { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way delay variation."; + } + leaf two-way-packet-loss { + type decimal64 { + fraction-digits 6; + range "0..50.331642"; + } + units "%"; + 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 Performance Metrics (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; + units "microseconds"; + description + "Two-way minimum delay or latency."; + reference + "RFC 2681: A Round-trip Delay Metric for IPPM"; + } + leaf two-way-max-delay { + type yang:gauge64; + units "microseconds"; + description + "Two-way maximum delay or latency."; + reference + "RFC 2681: A Round-trip Delay Metric for IPPM"; + } + leaf two-way-delay-variation { + type yang:gauge64; + units "microseconds"; + description + "Two-way delay variation."; + 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 Performance Metrics (PM) threshold grouping."; + uses te-types:performance-metrics-throttle-container { + augment "throttle/threshold-out" { + description + "Performance Metrics (PM) threshold-out packet + augmentation for a generic grouping."; + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + } + augment "throttle/threshold-in" { + description + "Performance Metrics (PM) threshold-in packet augmentation + for a generic grouping."; + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + } + augment "throttle/threshold-accelerated-advertisement" { + description + "Performance Metrics (PM) accelerated advertisement packet + augmentation for a generic grouping."; + uses one-way-performance-metrics-packet; + uses two-way-performance-metrics-packet; + } + } + } + + grouping bandwidth-profile-parameters { + description + "Common parameters to define bandwidth profiles in packet + networks."; + leaf cir { + type uint64; + units "bits per second"; + description + "Committed Information Rate (CIR)."; + } + leaf cbs { + type uint64; + units "bytes"; + description + "Committed Burst Size (CBS)."; + } + leaf eir { + type uint64; + units "bits per second"; + description + "Excess Information Rate (EIR)."; + } + leaf ebs { + type uint64; + units "bytes"; + description + "Excess Burst Size (EBS)."; + } + leaf pir { + type uint64; + units "bits per 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 per second"; + description + "Bandwidth value for Packet TE links."; + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..aef9434ed2eddc05b401519d557c137167d7e188 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-rfc8776-update-18/ietf-te-types.yang @@ -0,0 +1,4473 @@ +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: + WG List: + + Editor: Tarek Saad + + + Editor: Rakesh Gandhi + + + Editor: Vishnu Pavan Beeram + + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + "; + description + "This YANG module contains a collection of generally useful + YANG data type definitions specific to TE. + + The model 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) 2025 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 2025-01-24 { + 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: + - explicit-route-hop-with-srlg; + - 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"; + } + + /* + * 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 Shared Risk Link Group (SRLG)."; + } + + 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 + "Shared Risk Link Group (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, signalled and configured after + the failure detection. + + This restoration scheme is also known as + 'Full LSP Re-routing', with the alternate route being + computed after the failure occurs."; + 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 precomputed, presignalled and + preconfigured prior to the failure."; + } + + identity restoration-scheme-precomputed { + base restoration-scheme-type; + description + "Restoration LSP is precomputed, but not presignalled nor + preconfigured, prior to the failure. + + This restoration scheme is also known as + 'Full LSP Re-routing', with the alternate route being + pre-computed and stored for use when the failure occurs."; + 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, but not preconfigured, + prior to the failure. + + This restoration scheme is also known as + 'Pre-planned LSP Re-routing'."; + 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 Shared Risk Link Groups (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 + "RFC 8233: 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 Shared Risk Link Group (SRLG) path + computation."; + } + + identity srlg-ignore { + base path-computation-srlg-type; + description + "Ignores Shared Risk Link Groups (SRLGs) in the path + computation."; + } + + identity srlg-strict { + base path-computation-srlg-type; + description + "Includes a strict Shared Risk Link Group (SRLG) check in + the path computation."; + } + + identity srlg-preferred { + base path-computation-srlg-type; + description + "Includes a preferred Shared Risk Link Group (SRLG) check in + the path computation."; + } + + identity srlg-weighted { + base path-computation-srlg-type; + description + "Includes a weighted Shared Risk Link Group (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 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)"; + } + + /* + * 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 + "Shared Risk Link Group (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 may follow + the type 'bandwidth-ieee-float32' as defined in RFC 8294 + (e.g., 0x1p10), where the units are in bytes per second. + + 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 + "Traffic Engineering (TE) 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"; + } + + 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 + "Shared Risk Link Group (SRLG) 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 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"; + } + units "microseconds"; + description + "One-way delay or latency."; + } + leaf one-way-delay-normality { + type te-types:performance-metrics-normality; + description + "One-way delay normality."; + } + } + + grouping performance-metrics-two-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 two-way-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + description + "Two-way delay or latency."; + } + leaf two-way-delay-normality { + type te-types:performance-metrics-normality; + description + "Two-way delay normality."; + } + } + + grouping performance-metrics-one-way-bandwidth { + description + "Performance Metrics (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 Performance Metrics (PM) throttle grouping."; + leaf one-way-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "One-way delay or latency."; + } + 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 Performance Metrics (PM) throttle grouping."; + leaf two-way-delay { + type uint32 { + range "0..16777215"; + } + units "microseconds"; + default "0"; + description + "Two-way delay or latency."; + } + } + + 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 Performance Metrics (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 Performance Metrics (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"; + } + units "microseconds"; + default "0"; + description + "Offset value to be added to the measured delay value."; + } + leaf measure-interval { + type uint32; + units "seconds"; + default "30"; + description + "Interval to measure the extended metric values."; + } + leaf advertisement-interval { + type uint32; + units "seconds"; + default "0"; + description + "Interval to advertise the extended metric values."; + } + leaf suppression-interval { + type uint32 { + range "1..max"; + } + units "seconds"; + default "120"; + description + "Interval to suppress advertisement of the extended metric + values."; + reference + "RFC 8570: IS-IS Traffic Engineering (TE) Metric + Extensions, Section 6"; + } + container threshold-out { + 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."; + uses performance-metrics-thresholds; + } + container threshold-in { + 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."; + uses performance-metrics-thresholds; + } + 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 needs to be present."; + } + 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)"; + 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."; + } + } + } + case numbered-link-hop { + container numbered-link-hop { + 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)"; + 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."; + } + } + } + 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 need to be + present."; + } + 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)"; + 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."; + } + } + } + case as-number { + container as-number-hop { + description + "AS explicit route 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."; + } + } + } + case label { + description + "The label explicit route hop type."; + container label-hop { + description + "Label hop type."; + uses te-label; + } + } + } + } + + grouping explicit-route-hop-with-srlg { + description + "Augments the explicit route entry grouping with Shared Risk + Link Group (SRLG) hop type."; + uses explicit-route-hop { + augment "type" { + description + "Augmentation for a generic explicit route for Shared + Risk Link Group (SRLG) inclusion or exclusion."; + case srlg { + description + "An Shared Risk Link Group (SRLG) value to be + included or excluded."; + container srlg { + description + "Shared Risk Link Group (SRLG) container."; + leaf srlg { + type uint32; + description + "Shared Risk Link Group (SRLG) value."; + } + } + } + } + } + } + + 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 { + description + "Numbered node route hop."; + container numbered-node-hop { + must 'node-id-uri or node-id' { + description + "At least one node identifier need to 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"; + } + } + } + case numbered-link-hop { + description + "Numbered link route 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"; + } + } + } + case unnumbered-link-hop { + description + "Unnumbered link route 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 need to be + present."; + } + description + "Unnumbered link Record Route hop."; + reference + "RFC 3477: Signalling Unnumbered Links in Resource + ReSerVation Protocol - Traffic Engineering + (RSVP-TE)"; + 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"; + } + } + } + case label { + description + "The label Record Route entry types."; + 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"; + } + } + } + } + } + + 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 MUST 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-with-srlg; + } + } + } + + 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-with-srlg; + } + } + + 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 + specified '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"; + description + "List of named affinities."; + leaf name { + type string; + description + "Identifies a named affinity entry."; + } + } + } + } + } + + grouping generic-path-srlgs { + description + "Path Shared Risk Link Group (SRLG) grouping."; + container path-srlgs-lists { + description + "Path Shared Risk Link Group (SRLG) properties container."; + list path-srlgs-list { + key "usage"; + description + "List of Shared Risk Link Group (SRLG) values to be + included or excluded."; + leaf usage { + type identityref { + base route-usage-type; + } + description + "Identifies an entry in a list of Shared Risk Link Groups + (SRLGs) to either include or exclude."; + } + leaf-list values { + type srlg; + description + "List of Shared Risk Link Group (SRLG) values."; + } + } + } + container path-srlgs-names { + description + "Container for the list of named Shared Risk Link Groups + (SRLGs)."; + list path-srlgs-name { + key "usage"; + description + "List of named Shared Risk Link Groups (SRLGs) to be + included or excluded."; + leaf usage { + type identityref { + base route-usage-type; + } + description + "Identifies an entry in a list of named Shared Risk Link + Groups (SRLGs) to either include or exclude."; + } + leaf-list names { + type string; + description + "List of named Shared Risk Link Groups (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."; + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te-device.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te-device.yang new file mode 100644 index 0000000000000000000000000000000000000000..f788fa2ea4ab9bb983b5771215f21df00b8ff5df --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te-device.yang @@ -0,0 +1,595 @@ +module ietf-te-device { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-te-device"; + + /* Replace with IANA when assigned */ + + prefix te-dev; + + /* Import TE module */ + + import ietf-te { + prefix te; + reference + "RFCXXXX: A YANG Data Model for Traffic Engineering + Tunnels and Interfaces"; + } + + /* Import TE types */ + + import ietf-te-types { + prefix te-types; + reference + "draft-ietf-teas-rfc8776-update: Common YANG Data Types + for Traffic Engineering."; + } + import ietf-interfaces { + prefix if; + reference + "RFC8343: A YANG Data Model for Interface Management"; + } + import ietf-routing-types { + prefix rt-types; + reference + "RFC8294: Common YANG Data Types for the Routing Area"; + } + + organization + "IETF Traffic Engineering Architecture and Signaling (TEAS) + Working Group"; + contact + "WG Web: + WG List: + + Editor: Tarek Saad + + + Editor: Rakesh Gandhi + + + Editor: Vishnu Pavan Beeram + + + Editor: Himanshu Shah + + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + + + Editor: Oscar Gonzalez de Dios + "; + + description + "This module defines a data model for TE device configurations, + state, and RPCs. The model fully conforms to the + Network Management Datastore Architecture (NMDA). + + Copyright (c) 2023 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."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + + revision 2024-02-02 { + description + "Initial revision for the TE device YANG module."; + reference + "RFCXXXX: A YANG Data Model for Traffic Engineering Tunnels + and Interfaces"; + } + + grouping lsp-device-timers { + description + "Device TE LSP timers configs."; + leaf lsp-install-interval { + type uint32; + units "seconds"; + description + "TE LSP installation delay time."; + } + leaf lsp-cleanup-interval { + type uint32; + units "seconds"; + description + "TE LSP cleanup delay time."; + } + leaf lsp-invalidation-interval { + type uint32; + units "seconds"; + description + "TE LSP path invalidation before taking action delay time."; + } + } + + grouping te-igp-flooding-bandwidth-config { + description + "Configurable items for igp flooding bandwidth + threshold configuration."; + leaf threshold-type { + type enumeration { + enum delta { + description + "'delta' indicates that the local + system should flood IGP updates when a + change in reserved bandwidth >= the specified + delta occurs on the interface."; + } + enum threshold-crossed { + description + "THRESHOLD-CROSSED indicates that + the local system should trigger an update (and + hence flood) the reserved bandwidth when the + reserved bandwidth changes such that it crosses, + or becomes equal to one of the threshold values."; + } + } + description + "The type of threshold that should be used to specify the + values at which bandwidth is flooded. 'delta' indicates that + the local system should flood IGP updates when a change in + reserved bandwidth >= the specified delta occurs on the + interface. Where 'threshold-crossed' is specified, the local + system should trigger an update (and hence flood) the + reserved bandwidth when the reserved bandwidth changes such + that it crosses, or becomes equal to one of the threshold + values."; + } + leaf delta-percentage { + when "../threshold-type = 'delta'" { + description + "The percentage delta can only be specified when the + threshold type is specified to be a percentage delta of + the reserved bandwidth."; + } + type rt-types:percentage; + description + "The percentage of the maximum-reservable-bandwidth + considered as the delta that results in an IGP update + being flooded."; + } + leaf threshold-specification { + when "../threshold-type = 'threshold-crossed'" { + description + "The selection of whether mirrored or separate threshold + values are to be used requires user specified thresholds + to be set."; + } + type enumeration { + enum mirrored-up-down { + description + "mirrored-up-down indicates that a single set of + threshold values should be used for both increasing + and decreasing bandwidth when determining whether + to trigger updated bandwidth values to be flooded + in the IGP TE extensions."; + } + enum separate-up-down { + description + "separate-up-down indicates that a separate + threshold values should be used for the increasing + and decreasing bandwidth when determining whether + to trigger updated bandwidth values to be flooded + in the IGP TE extensions."; + } + } + description + "This value specifies whether a single set of threshold + values should be used for both increasing and decreasing + bandwidth when determining whether to trigger updated + bandwidth values to be flooded in the IGP TE extensions. + 'mirrored-up-down' indicates that a single value (or set of + values) should be used for both increasing and decreasing + values, where 'separate-up-down' specifies that the + increasing and decreasing values will be separately + specified."; + } + leaf-list up-thresholds { + when "../threshold-type = 'threshold-crossed'" + + "and ../threshold-specification = 'separate-up-down'" { + description + "A list of up-thresholds can only be specified when the + bandwidth update is triggered based on crossing a + threshold and separate up and down thresholds are + required."; + } + type rt-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth) at which bandwidth updates are to be + triggered when the bandwidth is increasing."; + } + leaf-list down-thresholds { + when "../threshold-type = 'threshold-crossed'" + + "and ../threshold-specification = 'separate-up-down'" { + description + "A list of down-thresholds can only be specified when the + bandwidth update is triggered based on crossing a + threshold and separate up and down thresholds are + required."; + } + type rt-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth) at which bandwidth updates are to be + triggered when the bandwidth is decreasing."; + } + leaf-list up-down-thresholds { + when "../threshold-type = 'threshold-crossed'" + + "and ../threshold-specification = 'mirrored-up-down'" { + description + "A list of thresholds corresponding to both increasing + and decreasing bandwidths can be specified only when an + update is triggered based on crossing a threshold, and + the same up and down thresholds are required."; + } + type rt-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth of the interface) at which bandwidth + updates are flooded - used both when the bandwidth is + increasing and decreasing."; + } + } + + /** + * TE device augmentations + */ + augment "/te:te" { + description + "TE global container."; + /* TE Interface Configuration Data */ + container interfaces { + description + "Configuration data model for TE interfaces."; + uses te-igp-flooding-bandwidth-config; + list interface { + key "name"; + description + "The list of interfaces enabled for TE."; + leaf name { + type if:interface-ref; + description + "The reference to interface enabled for TE."; + } + /* TE interface parameters */ + leaf te-metric { + type te-types:te-metric; + description + "TE interface metric."; + } + choice admin-group-type { + description + "TE interface administrative groups + representation type."; + case value-admin-groups { + choice value-admin-group-type { + description + "choice of admin-groups."; + case admin-groups { + description + "Administrative group/Resource + class/Color."; + leaf admin-group { + type te-types:admin-group; + description + "TE interface administrative group."; + } + } + case extended-admin-groups { + if-feature "te-types:extended-admin-groups"; + description + "Extended administrative group/Resource + class/Color."; + leaf extended-admin-group { + type te-types:extended-admin-group; + description + "TE interface extended administrative group."; + } + } + } + } + case named-admin-groups { + list named-admin-groups { + if-feature "te-types:extended-admin-groups"; + if-feature "te-types:named-extended-admin-groups"; + key "named-admin-group"; + description + "A list of named admin-group entries."; + leaf named-admin-group { + type leafref { + path "../../../../te:globals/" + + "te:named-admin-groups/te:named-admin-group/" + + "te:name"; + } + description + "A named admin-group entry."; + } + } + } + } + choice srlg-type { + description + "Choice of SRLG configuration."; + case value-srlgs { + list values { + key "value"; + description + "List of SRLG values that + this link is part of."; + leaf value { + type uint32 { + range "0..4294967295"; + } + description + "Value of the SRLG"; + } + } + } + case named-srlgs { + list named-srlgs { + if-feature "te-types:named-srlg-groups"; + key "named-srlg"; + description + "A list of named SRLG entries."; + leaf named-srlg { + type leafref { + path "../../../../te:globals/" + + "te:named-srlgs/te:named-srlg/te:name"; + } + description + "A named SRLG entry."; + } + } + } + } + uses te-igp-flooding-bandwidth-config; + list switching-capabilities { + key "switching-capability"; + description + "List of interface capabilities for this interface."; + leaf switching-capability { + type identityref { + base te-types:switching-capabilities; + } + description + "Switching Capability for this interface."; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "Encoding supported by this interface."; + } + } + container te-advertisements-state { + config false; + description + "TE interface advertisements state container."; + leaf flood-interval { + type uint32; + description + "The periodic flooding interval."; + } + leaf last-flooded-time { + type uint32; + units "seconds"; + description + "Time elapsed since last flooding in seconds."; + } + leaf next-flooded-time { + type uint32; + units "seconds"; + description + "Time remained for next flooding in seconds."; + } + leaf last-flooded-trigger { + type enumeration { + enum link-up { + description + "Link-up flooding trigger."; + } + enum link-down { + description + "Link-down flooding trigger."; + } + enum threshold-up { + description + "Bandwidth reservation up threshold."; + } + enum threshold-down { + description + "Bandwidth reservation down threshold."; + } + enum bandwidth-change { + description + "Bandwidth capacity change."; + } + enum user-initiated { + description + "Initiated by user."; + } + enum srlg-change { + description + "SRLG property change."; + } + enum periodic-timer { + description + "Periodic timer expired."; + } + } + default "periodic-timer"; + description + "Trigger for the last flood."; + } + list advertised-level-areas { + key "level-area"; + description + "List of level-areas that the TE interface is + advertised in."; + leaf level-area { + type uint32; + description + "The IGP area or level where the TE interface link + state is advertised in."; + } + } + } + } + } + } + + /* TE globals device augmentation */ + + augment "/te:te/te:globals" { + description + "Global TE device specific configuration parameters."; + uses lsp-device-timers; + } + + /* TE tunnels device configuration augmentation */ + + augment "/te:te/te:tunnels/te:tunnel" { + description + "Tunnel device dependent augmentation."; + leaf path-invalidation-action { + type identityref { + base te-types:path-invalidation-action-type; + } + description + "Tunnel path invalidation action."; + } + uses lsp-device-timers; + } + + /* TE LSPs device state augmentation */ + + augment "/te:te/te:lsps/te:lsp" { + description + "TE LSP device dependent augmentation."; + container lsp-timers { + when "../te:origin-type = 'ingress'" { + description + "Applicable to ingress LSPs only."; + } + description + "Ingress LSP timers."; + leaf uptime { + type uint32; + units "seconds"; + description + "The LSP uptime."; + } + leaf time-to-install { + type uint32; + units "seconds"; + description + "The time remaining for a new LSP to be instantiated + in forwarding to carry traffic."; + } + leaf time-to-destroy { + type uint32; + units "seconds"; + description + "The time remaining for a existing LSP to be deleted + from forwarding."; + } + } + container downstream-info { + when "../te:origin-type != 'egress'" { + description + "Downstream information of the LSP."; + } + description + "downstream information."; + leaf nhop { + type te-types:te-tp-id; + description + "downstream next-hop address."; + } + leaf outgoing-interface { + type if:interface-ref; + description + "downstream interface."; + } + container neighbor { + uses te-types:te-generic-node-id; + description + "downstream neighbor address."; + } + leaf label { + type rt-types:generalized-label; + description + "downstream label."; + } + } + container upstream-info { + when "../te:origin-type != 'ingress'" { + description + "Upstream information of the LSP."; + } + description + "upstream information."; + leaf phop { + type te-types:te-tp-id; + description + "upstream next-hop or previous-hop address."; + } + container neighbor { + uses te-types:te-generic-node-id; + description + "upstream neighbor address."; + } + leaf label { + type rt-types:generalized-label; + description + "upstream label."; + } + } + } + + /* TE interfaces RPCs/execution Data */ + + rpc link-state-update { + description + "Triggers a link state update for the specific interface."; + input { + choice filter-type { + mandatory true; + description + "Filter choice."; + case match-all { + leaf all { + type empty; + mandatory true; + description + "Match all TE interfaces."; + } + } + case match-one-interface { + leaf interface { + type if:interface-ref; + description + "Match a specific TE interface."; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te.yang new file mode 100644 index 0000000000000000000000000000000000000000..48b160305e76f7b192257f9996de4479a221e367 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-ietf-teas-yang-te-34/ietf-te.yang @@ -0,0 +1,1516 @@ +module ietf-te { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-te"; + + /* Replace with IANA when assigned */ + + prefix te; + + /* Import TE generic types */ + import ietf-te-types { + prefix te-types; + reference + "draft-ietf-teas-rfc8776-update: Common YANG Data Types + for Traffic Engineering."; + } + import ietf-yang-types { + prefix yang; + reference + "RFC6991: Common YANG Data Types."; + } + + 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: + WG List: + + Editor: Tarek Saad + + + Editor: Rakesh Gandhi + + + Editor: Vishnu Pavan Beeram + + + Editor: Himanshu Shah + + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + + + Editor: Oscar Gonzalez de Dios + "; + + description + "YANG data module for TE configuration, state, and RPCs. + The model fully conforms to the Network Management + Datastore Architecture (NMDA). + + Copyright (c) 2023 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."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + + revision 2024-02-02 { + description + "Initial revision for the TE generic YANG module."; + reference + "RFCXXXX: A YANG Data Model for Traffic Engineering Tunnels + and Interfaces."; + } + + typedef tunnel-ref { + type leafref { + path "/te:te/te:tunnels/te:tunnel/te:name"; + require-instance false; + } + description + "This type is used by data models that need to reference + configured TE tunnel."; + } + + /** + * TE tunnel generic groupings + */ + + grouping path-common-properties { + description + "Common path attributes."; + leaf name { + type string; + description + "TE path name."; + } + leaf path-computation-method { + type identityref { + base te-types:path-computation-method; + } + default "te-types:path-locally-computed"; + description + "The method used for computing the path, either + locally computed, queried from a server or not + computed at all (explicitly configured)."; + } + container path-computation-server { + when "derived-from-or-self(../path-computation-method, " + + "'te-types:path-externally-queried')" { + description + "The path-computation server when the path is + externally queried."; + } + uses te-types:te-generic-node-id; + description + "Address of the external path computation + server."; + } + leaf compute-only { + type empty; + description + "When present, the path is computed and updated whenever + the topology is updated. No resources are committed + or reserved in the network."; + } + leaf use-path-computation { + when "derived-from-or-self(../path-computation-method, " + + "'te-types:path-locally-computed')"; + type boolean; + default "true"; + description + "When 'true' indicates the path is dynamically computed + and/or validated against the Traffic-Engineering Database + (TED), and when 'false' indicates no path expansion or + validation against the TED is required."; + } + leaf lockdown { + type empty; + description + "When present, indicates no reoptimization to be attempted + for this path."; + } + leaf path-scope { + type identityref { + base te-types:path-scope-type; + } + default "te-types:path-scope-end-to-end"; + config false; + description + "Indicates whether the path is a segment or portion of + of the full path., or is the an end-to-end path for + the TE Tunnel."; + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping path-compute-info { + description + "Attributes used for path computation request."; + uses tunnel-associations-properties; + uses te-types:generic-path-optimization; + leaf named-path-constraint { + if-feature "te-types:named-path-constraints"; + type leafref { + path "/te:te/te:globals/te:named-path-constraints/" + + "te:named-path-constraint/te:name"; + } + description + "Reference to a globally defined named path constraint set."; + } + uses path-constraints-common; + } + + /* This grouping is re-used in path-computation rpc */ + grouping path-forward-properties { + description + "The path preference."; + leaf preference { + type uint8 { + range "1..255"; + } + default "1"; + description + "Specifies a preference for this path. The lower the number + higher the preference."; + } + leaf co-routed { + when "/te:te/te:tunnels/te:tunnel/te:bidirectional = 'true'" { + description + "Applicable to bidirectional tunnels only."; + } + type boolean; + default "false"; + description + "Indicates whether the reverse path must to be co-routed + with the primary."; + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping k-requested-paths { + description + "The k-shortest paths requests."; + leaf k-requested-paths { + type uint8; + default "1"; + description + "The number of k-shortest-paths requested from the path + computation server and returned sorted by its optimization + objective."; + } + } + + grouping path-state { + description + "TE per path state parameters."; + uses path-computation-response; + container lsp-provisioning-error-infos { + config false; + description + "LSP provisioning error information."; + list lsp-provisioning-error-info { + description + "List of LSP provisioning error info entries."; + leaf error-reason { + type identityref { + base te-types:lsp-provisioning-error-reason; + } + description + "LSP provision error type."; + } + leaf error-description { + type string; + description + "The textual representation of the error occurred during + path computation."; + } + leaf error-timestamp { + type yang:date-and-time; + description + "Timestamp of when the reported error occurred."; + } + leaf error-node-id { + type te-types:te-node-id; + description + "Node identifier of node where error occurred."; + } + leaf error-link-id { + type te-types:te-tp-id; + description + "Link ID where the error occurred."; + } + leaf lsp-id { + type uint16; + description + "The LSP-ID for which path computation was performed."; + } + } + } + container lsps { + config false; + description + "The TE LSPs container."; + list lsp { + key "node lsp-id"; + description + "List of LSPs associated with the tunnel."; + leaf tunnel-name { + type leafref { + path "/te:te/te:lsps/te:lsp/te:tunnel-name"; + } + description "TE tunnel name."; + } + leaf node { + type leafref { + path "/te:te/te:lsps/te:lsp[tunnel-name=" + + "current()/../te:tunnel-name][lsp-id=" + + "current()/../te:lsp-id]/te:node"; + } + description "The node where the LSP state resides on."; + } + leaf lsp-id { + type leafref { + path "/te:te/te:lsps/te:lsp[tunnel-name=" + + "current()/../tunnel-name]/te:lsp-id"; + } + description "The TE LSP identifier."; + } + } + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping path-computation-response { + description + "Attributes reported by path computation response."; + container computed-paths-properties { + config false; + description + "Computed path properties container."; + list computed-path-properties { + key "k-index"; + description + "List of computed paths."; + leaf k-index { + type uint8; + description + "The k-th path returned from the computation server. + A lower k value path is more optimal than higher k + value path(s)"; + } + uses te-types:generic-path-properties { + augment "path-properties" { + description + "additional path properties returned by path + computation."; + uses te-types:te-bandwidth; + leaf disjointness-type { + type te-types:te-path-disjointness; + config false; + description + "The type of resource disjointness. + When reported for a primary path, it represents the + minimum level of disjointness of all the secondary + paths. When reported for a secondary path, it + represents the disjointness of the secondary path."; + } + } + } + } + } + container computed-path-error-infos { + config false; + description + "Path computation information container."; + list computed-path-error-info { + description + "List of path computation info entries."; + leaf error-description { + type string; + description + "Textual representation of the error that occurred + during path computation."; + } + leaf error-timestamp { + type yang:date-and-time; + description + "Timestamp of last path computation attempt."; + } + leaf error-reason { + type identityref { + base te-types:path-computation-error-reason; + } + description + "Reason for the path computation error."; + } + } + } + } + + grouping protection-restoration-properties { + description + "Protection and restoration parameters."; + container protection { + description + "Protection parameters."; + leaf protection-type { + type identityref { + base te-types:lsp-protection-type; + } + default "te-types:lsp-protection-unprotected"; + description + "LSP protection type."; + } + leaf protection-reversion-disable { + type boolean; + default "false"; + description + "Disable protection reversion to working path."; + } + leaf hold-off-time { + type uint32; + units "milli-seconds"; + description + "The time between the declaration of an SF or SD condition + and the initialization of the protection switching + algorithm."; + reference + "RFC4427"; + } + leaf wait-to-revert { + type uint16; + units "seconds"; + description + "Time to wait before attempting LSP reversion."; + reference + "RFC4427"; + } + leaf aps-signal-id { + type uint8 { + range "1..255"; + } + default "1"; + description + "The APS signal number used to reference the traffic of + this tunnel. The default value for normal traffic is 1. + The default value for extra-traffic is 255. If not + specified, non-default values can be assigned by the + server, if and only if, the server controls both + endpoints."; + reference + "ITU_G.808.1"; + } + } + container restoration { + description + "Restoration parameters."; + leaf restoration-type { + type identityref { + base te-types:lsp-restoration-type; + } + description + "LSP restoration type."; + } + leaf restoration-scheme { + type identityref { + base te-types:restoration-scheme-type; + } + description + "LSP restoration scheme."; + } + leaf restoration-reversion-disable { + type boolean; + default "false"; + description + "Disable restoration reversion to working path."; + } + leaf hold-off-time { + type uint32; + units "milli-seconds"; + description + "The time between the declaration of an SF or SD condition + and the initialization of the protection switching + algorithm."; + reference + "RFC4427"; + } + leaf wait-to-restore { + type uint16; + units "seconds"; + description + "Time to wait before attempting LSP restoration."; + reference + "RFC4427"; + } + leaf wait-to-revert { + type uint16; + units "seconds"; + description + "Time to wait before attempting LSP reversion."; + reference + "RFC4427"; + } + } + } + + grouping tunnel-associations-properties { + description + "TE tunnel association grouping."; + container association-objects { + description + "TE tunnel associations."; + list association-object { + key "association-key"; + unique "type id source/id source/type"; + description + "List of association base objects."; + reference + "RFC4872"; + leaf association-key { + type string; + description + "Association key used to identify a specific + association in the list"; + } + leaf type { + type identityref { + base te-types:association-type; + } + description + "Association type."; + reference + "RFC4872"; + } + leaf id { + type uint16; + description + "Association identifier."; + reference + "RFC4872"; + } + container source { + uses te-types:te-generic-node-id; + description + "Association source."; + reference + "RFC4872"; + } + } + list association-object-extended { + key "association-key"; + unique + "type id source/id source/type global-source extended-id"; + description + "List of extended association objects."; + reference + "RFC6780"; + leaf association-key { + type string; + description + "Association key used to identify a specific + association in the list"; + } + leaf type { + type identityref { + base te-types:association-type; + } + description + "Association type."; + reference + "RFC4872, RFC6780"; + } + leaf id { + type uint16; + description + "Association identifier."; + reference + "RFC4872, RFC6780"; + } + container source { + uses te-types:te-generic-node-id; + description + "Association source."; + reference + "RFC4872, RFC6780"; + } + leaf global-source { + type uint32; + description + "Association global source."; + reference + "RFC6780"; + } + leaf extended-id { + type yang:hex-string; + description + "Association extended identifier."; + reference + "RFC6780"; + } + } + } + } + + grouping tunnel-end-point { + description + "Common grouping used to specify the tunnel source and + destination end-points."; + leaf node-id { + type nw:node-id; + description + "The TE tunnel end-point node identifier"; + } + leaf te-node-id { + type te-types:te-node-id; + description + "The TE tunnel end-point TE node identifier"; + } + leaf tunnel-tp-id { + when "../node-id or ../te-node-id" { + description + "The TE tunnel termination point identifier is local to + a node"; + } + type binary; + description + "The TE tunnel end-point TE tunnel termination point + identifier"; + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping tunnel-common-attributes { + description + "Common grouping to define the TE tunnel parameters"; + container source { + description + "TE tunnel source end-point."; + uses tunnel-end-point; + } + container destination { + description + "TE tunnel destination end-point."; + uses tunnel-end-point; + } + leaf bidirectional { + type boolean; + default "false"; + description + "Indicates a bidirectional tunnel"; + } + } + + /* This grouping is re-used in path-computation rpc */ + grouping tunnel-hierarchy-properties { + description + "A grouping for TE tunnel hierarchy information."; + container hierarchy { + description + "Container for TE hierarchy related information."; + container dependency-tunnels { + description + "List of tunnels that this tunnel can be potentially + dependent on."; + list dependency-tunnel { + key "name"; + description + "A tunnel entry that this tunnel can potentially depend + on."; + leaf name { + type tunnel-ref; + description + "Dependency tunnel name. The tunnel may not have been + instantiated yet."; + } + uses te-types:encoding-and-switching-type; + } + } + container hierarchical-link { + description + "Identifies a hierarchical link (in client layer) + that this tunnel is associated with. By default, the + topology of the hierarchical link is the same topology of + the tunnel;"; + reference + "RFC4206"; + leaf enable { + type boolean; + default "false"; + description + "Enables the hierarchical link properties supported by + this tunnel"; + } + leaf local-node-id { + type nw:node-id; + description + "The local node identifier."; + } + leaf local-te-node-id { + type te-types:te-node-id; + description + "The local TE node identifier."; + } + leaf local-link-tp-id { + type nt:tp-id; + description + "The local link termination point identifier."; + reference + "RFC8345"; + } + leaf local-te-link-tp-id { + type te-types:te-tp-id; + description + "The local TE link termination point identifier."; + } + leaf remote-node-id { + type nw:node-id; + description + "The remote node identifier."; + } + leaf remote-link-tp-id { + type nt:tp-id; + description + "The remote link termination point identifier."; + reference + "RFC8345"; + } + leaf remote-te-link-tp-id { + type te-types:te-tp-id; + description + "The remote TE link termination point identifier."; + } + leaf remote-te-node-id { + type te-types:te-node-id; + description + "Remote TE node identifier."; + } + leaf link-id { + type nt:link-id; + config false; + description + "A network topology assigned identifier to the link"; + reference + "RFC8345"; + } + leaf network-id { + type nw:network-id; + description + "The network topology identifier where the hierarchical + link supported by this TE tunnel is instantiated."; + } + uses te-types:te-topology-identifier { + description + "The TE topology identifier where the hierarchical link + supported by this TE tunnel is instantiated."; + } + } + } + } + + grouping path-constraints-common { + description + "Global named path constraints configuration + grouping."; + uses te-types:common-path-constraints-attributes; + uses te-types:generic-path-disjointness; + uses te-types:path-constraints-route-objects; + container path-in-segment { + presence "The end-to-end tunnel starts in a previous domain; + this tunnel is a segment in the current domain."; + description + "If an end-to-end tunnel crosses multiple domains using + the same technology, some additional constraints have to be + taken in consideration in each domain. + This TE tunnel segment is stitched to the upstream TE tunnel + segment."; + uses te-types:label-set-info; + } + container path-out-segment { + presence + "The end-to-end tunnel is not terminated in this domain; + this tunnel is a segment in the current domain."; + description + "If an end-to-end tunnel crosses multiple domains using + the same technology, some additional constraints have to be + taken in consideration in each domain. + This TE tunnel segment is stitched to the downstream TE + tunnel segment."; + uses te-types:label-set-info; + } + } + + /** + * TE container + */ + + container te { + description + "TE global container."; + leaf enable { + type boolean; + description + "Enables the TE component features."; + } + + /* TE Global Data */ + container globals { + description + "Globals TE system-wide configuration data container."; + container named-admin-groups { + description + "TE named admin groups container."; + list named-admin-group { + if-feature "te-types:extended-admin-groups"; + if-feature "te-types:named-extended-admin-groups"; + key "name"; + description + "List of named TE admin-groups."; + leaf name { + type string; + description + "A string name that uniquely identifies a TE + interface named admin-group."; + } + leaf bit-position { + type uint32; + description + "Bit position representing the administrative group."; + reference + "RFC3209 and RFC7308"; + } + + } + } + container named-srlgs { + description + "TE named SRLGs container."; + list named-srlg { + if-feature "te-types:named-srlg-groups"; + key "name"; + description + "A list of named SRLG groups."; + leaf name { + type string; + description + "A string name that uniquely identifies a TE + interface named SRLG."; + } + leaf value { + type te-types:srlg; + description + "An SRLG value."; + } + leaf cost { + type uint32; + description + "SRLG associated cost. Used during path to append + the path cost when traversing a link with this SRLG."; + } + } + } + container named-path-constraints { + description + "TE named path constraints container."; + list named-path-constraint { + if-feature "te-types:named-path-constraints"; + key "name"; + leaf name { + type string; + description + "A string name that uniquely identifies a + path constraint set."; + } + uses path-constraints-common; + description + "A list of named path constraints."; + } + } + } + + /* TE Tunnel Data */ + container tunnels { + description + "Tunnels TE configuration data container."; + list tunnel { + key "name"; + description + "The list of TE tunnels."; + leaf name { + type string; + description + "TE tunnel name."; + } + leaf alias { + type string; + description + "An alternate name of the TE tunnel that can be modified + anytime during its lifetime."; + } + leaf identifier { + type uint32; + description + "TE tunnel Identifier."; + reference + "RFC3209"; + } + leaf color { + type uint32; + description "The color associated with the TE tunnel."; + reference "RFC9012"; + } + leaf description { + type string; + default "None"; + description + "Textual description for this TE tunnel."; + } + leaf admin-state { + type identityref { + base te-types:tunnel-admin-state-type; + } + default "te-types:tunnel-admin-state-up"; + description + "TE tunnel administrative state."; + } + leaf operational-state { + type identityref { + base te-types:tunnel-state-type; + } + config false; + description + "TE tunnel operational state."; + } + uses te-types:encoding-and-switching-type; + uses tunnel-common-attributes; + container controller { + description + "Contains tunnel data relevant to external controller(s). + This target node may be augmented by external module(s), + for example, to add data for PCEP initiated and/or + delegated tunnels."; + leaf protocol-origin { + type identityref { + base te-types:protocol-origin-type; + } + description + "The protocol origin for instantiating the tunnel."; + } + leaf controller-entity-id { + type string; + description + "An identifier unique within the scope of visibility + that associated with the entity that controls the + tunnel."; + reference "RFC8232"; + } + } + leaf reoptimize-timer { + type uint16; + units "seconds"; + description + "Frequency of reoptimization of a traffic engineered + LSP."; + } + uses tunnel-associations-properties; + uses protection-restoration-properties; + uses te-types:tunnel-constraints; + uses tunnel-hierarchy-properties; + container primary-paths { + description + "The set of primary paths."; + reference "RFC4872"; + list primary-path { + key "name"; + description + "List of primary paths for this tunnel."; + leaf active { + type boolean; + config false; + description + "Indicates an active path that + has been selected from the primary paths list."; + } + uses path-common-properties; + uses path-forward-properties; + uses k-requested-paths; + uses path-compute-info; + uses path-state; + container primary-reverse-path { + when "../../../te:bidirectional = 'true'"; + description + "The reverse primary path properties."; + uses path-common-properties; + uses path-compute-info; + uses path-state; + container candidate-secondary-reverse-paths { + description + "The set of referenced candidate reverse secondary + paths from the full set of secondary reverse paths + which may be used for this primary path."; + list candidate-secondary-reverse-path { + key "secondary-reverse-path"; + ordered-by user; + description + "List of candidate secondary reverse path(s)"; + leaf secondary-reverse-path { + type leafref { + path "../../../../../../" + + "te:secondary-reverse-paths/" + + "te:secondary-reverse-path/te:name"; + } + description + "A reference to the secondary reverse path that + may be utilized when the containing primary + reverse path is in use."; + } + leaf active { + type boolean; + config false; + description + "Indicates an active path that has been + selected from the secondary reverse paths + list."; + } + } + } + } + container candidate-secondary-paths { + description + "The set of candidate secondary paths which may be + used for this primary path. When secondary paths are + specified in the list the path of the secondary LSP + in use must be restricted to those paths + referenced. + The priority of the secondary paths is specified + within the list. Higher priority values are less + preferred - that is to say that a path with priority + 0 is the most preferred path. In the case that the + list is empty, any secondary path may be + utilised when the current primary path is in use."; + list candidate-secondary-path { + key "secondary-path"; + ordered-by user; + description + "List of candidate secondary paths for this + tunnel."; + leaf secondary-path { + type leafref { + path "../../../../../te:secondary-paths/" + + "te:secondary-path/te:name"; + } + description + "A reference to the secondary path that may be + utilised when the containing primary path is + in use."; + } + leaf active { + type boolean; + config false; + description + "Indicates an active path that has been selected + from the candidate secondary paths."; + } + } + } + } + } + container secondary-paths { + description + "The set of secondary paths."; + reference "RFC4872"; + list secondary-path { + key "name"; + description + "List of secondary paths for this tunnel."; + uses path-common-properties; + leaf preference { + type uint8 { + range "1..255"; + } + default "1"; + description + "Specifies a preference for this path. The lower the + number higher the preference."; + } + leaf secondary-reverse-path { + type leafref { + path "../../../" + + "te:secondary-reverse-paths/" + + "te:secondary-reverse-path/te:name"; + } + description + "A reference to the reverse secondary path when + co-routed with the secondary path."; + } + uses path-compute-info; + uses protection-restoration-properties; + uses path-state; + } + } + container secondary-reverse-paths { + description + "The set of secondary reverse paths."; + list secondary-reverse-path { + key "name"; + description + "List of secondary paths for this tunnel."; + uses path-common-properties; + leaf preference { + type uint8 { + range "1..255"; + } + default "1"; + description + "Specifies a preference for this path. The lower the + number higher the preference. Paths that have the + same preference will be activated together."; + } + uses path-compute-info; + uses protection-restoration-properties; + uses path-state; + } + } + action tunnel-action { + description + "Action commands to manipulate the TE tunnel state."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 2.5"; + input { + leaf action-type { + type identityref { + base te-types:tunnel-action-type; + } + description + "The action to be invoked on the TE tunnel."; + } + } + output { + leaf action-result { + type identityref { + base te-types:te-action-result; + } + description + "The result of the tunnel action operation."; + } + } + } + action protection-external-commands { + description + "Actions to manipulate the protection external + commands of the TE tunnel."; + reference + "RFC 4427: Recovery (Protection and Restoration) + Terminology for Generalized Multi-Protocol Label + Switching (GMPLS)"; + input { + leaf protection-external-command { + type identityref { + base te-types:protection-external-commands; + } + description + "Protection external command."; + } + leaf protection-group-ingress-node { + type boolean; + default "true"; + description + "When 'true', indicates that the action is + applied on ingress node. + By default, the action applies to the ingress node + only."; + } + leaf protection-group-egress-node { + type boolean; + default "false"; + description + "When set to 'true', indicates that the action is + applied on egress node. + By default, the action applies to the ingress node + only."; + } + leaf path-name { + type string; + description + "The name of the path that the external command + applies to."; + } + leaf path-type { + type te-types:path-type; + description + "The type of the path that the external command + applies to."; + } + leaf traffic-type { + type enumeration { + enum normal-traffic { + description + "The manual-switch or forced-switch command + applies to the normal traffic (this Tunnel)."; + } + enum null-traffic { + description + "The manual-switch or forced-switch command + applies to the null traffic."; + } + enum extra-traffic { + description + "The manual-switch or forced-switch command + applies to the extra traffic (the extra-traffic + Tunnel sharing protection bandwidth with this + Tunnel)."; + } + } + description + "Indicates whether the manual-switch or forced-switch + commands applies to the normal traffic, the null + traffic or the extra-traffic."; + reference + "RFC4427"; + } + leaf extra-traffic-tunnel-ref { + type tunnel-ref; + description + "In case there are multiple extra-traffic tunnels + sharing protection bandwidth with this Tunnel + (m:n protection), represents which extra-traffic + Tunnel the manual-switch or forced-switch to + extra-traffic command applies to."; + } + } + } + } + } + + /* TE LSPs Data */ + container lsps { + config false; + description + "TE LSPs state container."; + list lsp { + key "tunnel-name lsp-id node"; + unique "source destination tunnel-id lsp-id " + + "extended-tunnel-id"; + description + "List of LSPs associated with the tunnel."; + leaf tunnel-name { + type string; + description "The TE tunnel name."; + } + leaf lsp-id { + type uint16; + description + "Identifier used in the SENDER_TEMPLATE and the + FILTER_SPEC that can be changed to allow a sender to + share resources with itself."; + reference + "RFC3209"; + } + leaf node { + type te-types:te-node-id; + description + "The node where the TE LSP state resides on."; + } + leaf source { + type te-types:te-node-id; + description + "Tunnel sender address extracted from + SENDER_TEMPLATE object."; + reference + "RFC3209"; + } + leaf destination { + type te-types:te-node-id; + description + "The tunnel endpoint address."; + reference + "RFC3209"; + } + leaf tunnel-id { + type uint16; + description + "The tunnel identifier that remains + constant over the life of the tunnel."; + reference + "RFC3209"; + } + leaf extended-tunnel-id { + type yang:dotted-quad; + description + "The LSP Extended Tunnel ID."; + reference + "RFC3209"; + } + leaf operational-state { + type identityref { + base te-types:lsp-state-type; + } + description + "The LSP operational state."; + } + leaf signaling-type { + type identityref { + base te-types:path-signaling-type; + } + description + "The signaling protocol used to set up this LSP."; + } + leaf origin-type { + type enumeration { + enum ingress { + description + "Origin ingress."; + } + enum egress { + description + "Origin egress."; + } + enum transit { + description + "Origin transit."; + } + } + description + "The origin of the LSP relative to the location of the + local switch in the path."; + } + leaf lsp-resource-status { + type enumeration { + enum primary { + description + "A primary LSP is a fully established LSP for which + the resource allocation has been committed at the + data plane."; + } + enum secondary { + description + "A secondary LSP is an LSP that has been provisioned + in the control plane only; e.g. resource allocation + has not been committed at the data plane."; + } + } + description + "LSP resource allocation state."; + reference + "RFC4872, section 4.2.1"; + } + leaf lockout-of-normal { + type boolean; + description + "When set to 'true', it represents a lockout of normal + traffic external command. When set to 'false', it + represents a clear lockout of normal traffic external + command. The lockout of normal traffic command applies + to this Tunnel."; + reference + "RFC4427"; + } + leaf freeze { + type boolean; + description + "When set to 'true', it represents a freeze external + command. When set to 'false', it represents a clear + freeze external command. The freeze command applies to + all the Tunnels which are sharing the protection + resources with this Tunnel."; + reference + "RFC4427"; + } + leaf lsp-protection-role { + type enumeration { + enum working { + description + "A working LSP must be a primary LSP whilst a + protecting LSP can be either a primary or a + secondary LSP. Also, known as protected LSPs when + working LSPs are associated with protecting LSPs."; + } + enum protecting { + description + "A secondary LSP is an LSP that has been provisioned + in the control plane only; e.g. resource allocation + has not been committed at the data plane."; + } + } + description + "LSP role type."; + reference + "RFC4872, section 4.2.1"; + } + leaf lsp-protection-state { + type identityref { + base te-types:lsp-protection-state; + } + config false; + description + "The reported protection state controlling which + tunnel is using the resources of the protecting LSP."; + } + leaf protection-group-ingress-node-id { + type te-types:te-node-id; + description + "Indicates the te-node-id of the protection group + ingress node when the APS state represents an external + command (LoP, SF, MS) applied to it or a WTR timer + running on it. If the external command is not applied to + the ingress node or the WTR timer is not running on it, + this attribute is not specified. A value 0.0.0.0 is used + when the te-node-id of the protection group ingress node + is unknown (e.g., because the ingress node is outside + the scope of control of the server)"; + } + leaf protection-group-egress-node-id { + type te-types:te-node-id; + description + "Indicates the te-node-id of the protection group egress + node when the APS state represents an external command + (LoP, SF, MS) applied to it or a WTR timer running on + it. If the external command is not applied to the + ingress node or the WTR timer is not running on it, this + attribute is not specified. A value 0.0.0.0 is used when + the te-node-id of the protection group ingress node is + unknown (e.g., because the ingress node is outside the + scope of control of the server)"; + } + container lsp-actual-route-information { + description + "RSVP recorded route object information."; + list lsp-actual-route-information { + when "../../origin-type = 'ingress'" { + description + "Applicable on ingress LSPs only."; + } + key "index"; + description + "Record route list entry."; + uses te-types:record-route-state; + } + } + } + } + } + + /* TE Tunnel RPCs/execution Data */ + + rpc tunnels-path-compute { + description + "This RPC is a generic API whose + input and output parameters are expected to be provided by + augments to this module."; + reference + "RFC 4655: A Path Computation Element (PCE)-Based + Architecture."; + input { + container path-compute-info { + /* + * An external path compute module may augment this + * target. + */ + description + "RPC input information."; + } + } + output { + container path-compute-result { + /* + * An external path compute module may augment this + * target. + */ + description + "RPC output information."; + } + } + } + + rpc tunnels-actions { + description + "RPC that manipulates the state of a TE tunnel."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels, + Section 2.5"; + input { + container tunnel-info { + description + "TE tunnel information."; + choice filter-type { + mandatory true; + description + "Filter choice."; + case all-tunnels { + leaf all { + type empty; + mandatory true; + description + "When present, applies the action on all TE + tunnels."; + } + } + case one-tunnel { + leaf tunnel { + type tunnel-ref; + description + "Apply action on the specific TE tunnel."; + } + } + } + } + container action-info { + description + "TE tunnel action information."; + leaf action { + type identityref { + base te-types:tunnel-action-type; + } + description + "The action type."; + } + leaf disruptive { + when "derived-from-or-self(../action, " + + "'te-types:tunnel-action-reoptimize')"; + type empty; + description + "When present, specifies whether or not the + reoptimization + action is allowed to be disruptive."; + } + } + } + output { + leaf action-result { + type identityref { + base te-types:te-action-result; + } + description + "The result of the tunnel action operation."; + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/draft-layer1-types/ietf-layer1-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/draft-layer1-types/ietf-layer1-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..ba3820b72746cd5027c457529aafe04a9dc84e7b --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/draft-layer1-types/ietf-layer1-types.yang @@ -0,0 +1,1361 @@ +module ietf-layer1-types { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-layer1-types"; + prefix "l1-types"; + + import ietf-routing-types { + prefix rt-types; + reference + "RFC 8294: Common YANG Data Types for the Routing Area"; + } + + organization + "IETF CCAMP Working Group"; + contact + "WG Web: + WG List: + + Editor: Haomian Zheng + + + Editor: Italo Busi + "; + + description + "This module defines Layer 1 YANG types. The model fully conforms + to the Network Management Datastore Architecture (NMDA). + + 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. + + 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."; + + revision "2024-02-22" { + description + "Initial Version"; + reference + "RFC XXXX: A YANG Data Model for Layer 1 Types"; + // RFC Editor: replace RFC XXXX with actual RFC number, + // update date information and remove this note. + } + + /* + * Identities + */ + + identity tributary-slot-granularity { + description + "Tributary Slot Granularity (TSG)."; + reference + "ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity tsg-1.25G { + base tributary-slot-granularity; + description + "1.25G tributary slot granularity."; + } + + identity tsg-2.5G { + base tributary-slot-granularity; + description + "2.5G tributary slot granularity."; + } + + identity tsg-5G { + base tributary-slot-granularity; + description + "5G tributary slot granularity."; + } + + identity odu-type { + description + "Base identity from which specific Optical Data Unit (ODU) + type is derived."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU0 { + base odu-type; + description + "ODU0 type (1.24Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU1 { + base odu-type; + description + "ODU1 type (2.49Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU2 { + base odu-type; + description + "ODU2 type (10.03Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU2e { + base odu-type; + description + "ODU2e type (10.39Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU3 { + base odu-type; + description + "ODU3 type (40.31Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODU4 { + base odu-type; + description + "ODU4 type (104.79Gb/s)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ODUflex { + base odu-type; + description + "ODUflex type (flexible bit rate, not resizable). + + It could be used for any type of ODUflex, including + ODUflex(CBR), ODUflex(GFP), ODUflex(GFP,n,k), ODUflex(IMP,s), + ODUflex(IMP) and ODUflex(FlexE-aware)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + identity ODUflex-resizable { + base odu-type; + description + "ODUflex protocol (flexible bit rate, resizable). + + It could be used only for ODUflex(GFP,n,k)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity protocol { + description + "Base identity from which specific protocol is derived."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity Ethernet { + base protocol; + description + "Ethernet protocol."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity Fibre-Channel { + base protocol; + description + "Fibre-Channel (FC) protocol."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity SDH { + base protocol; + description + "SDH protocol."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity SONET { + base protocol; + description + "SONET protocol."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity client-signal { + description + "Base identity from which specific Constant Bit Rate (CBR) + client signal is derived"; + } + + identity coding-func { + description + "Base identity from which specific coding function + is derived."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-1Gb { + base client-signal; + description + "Client signal type of 1GbE."; + reference + "IEEE 802.3-2018, Clause 36: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ETH-10Gb-LAN { + base client-signal; + description + "Client signal type of ETH-10Gb-LAN (10.3 Gb/s)."; + reference + "IEEE 802.3-2018, Clause 49: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ETH-10Gb-WAN { + base client-signal; + description + "Client signal type of ETH-10Gb-WAN (9.95 Gb/s)."; + reference + "IEEE 802.3-2018, Clause 50: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ETH-40Gb { + base client-signal; + description + "Client signal type of 40GbE."; + reference + "IEEE 802.3-2018, Clause 82: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity ETH-100Gb { + base client-signal; + description + "Client signal type of 100GbE."; + reference + "IEEE 802.3-2018, Clause 82: IEEE Standard for Ethernet + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + + identity STM-1 { + base client-signal; + base coding-func; + description + "Client signal type of STM-1; + STM-1 G.707 (N=1) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity STM-4 { + base client-signal; + base coding-func; + description + "Client signal type of STM-4; + STM-4 G.707 (N=4) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity STM-16 { + base client-signal; + base coding-func; + description + "Client signal type of STM-16; + STM-16 G.707 (N=16) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity STM-64 { + base client-signal; + base coding-func; + description + "Client signal type of STM-64; + STM-64 G.707 (N=64) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity STM-256 { + base client-signal; + base coding-func; + description + "Client signal type of STM-256; + STM-256 G.707 (N=256) coding function."; + reference + "ITU-T G.707 v7.0 (01/2007): Network node interface for the + synchronous digital hierarchy (SDH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-3 { + base client-signal; + base coding-func; + description + "Client signal type of OC3; + OC-3 GR-253-CORE (N=3) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-12 { + base client-signal; + base coding-func; + description + "Client signal type of OC12; + OC-12 GR-253-CORE (N=12) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-48 { + base client-signal; + base coding-func; + description + "Client signal type of OC48; + OC-48 GR-253-CORE (N=48) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-192 { + base client-signal; + base coding-func; + description + "Client signal type of OC192; + OC-192 GR-253-CORE (N=192) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity OC-768 { + base client-signal; + base coding-func; + description + "Client signal type of OC768; + OC-768 GR-253-CORE (N=768) coding function."; + reference + "ANSI T1.105-2001: Synchronous Optical Network (SONET) + Basic Description including Multiplex Structure, Rates, + and Formats + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-100 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-100; + FC-100 FC-FS-2 (1.0625 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-200 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-200; + FC-200 FC-FS-2 (2.125 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-400 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-400; + FC-400 FC-FS-2 (4.250 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-800 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-800; + FC-800 FC-FS-2 (8.500 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-1200 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-1200; + FC-1200 FC-10GFC (10.51875 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-1600 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-1600; + FC-1600 FC-FS-3 (14.025 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FC-3200 { + base client-signal; + base coding-func; + description + "Client signal type of Fibre Channel FC-3200; + FC-3200 FC-FS-4 (28.05 Gb/s) coding function."; + reference + "ANSI INCITS 230-1994 R1999): Information Technology - + Fibre Channel - Physical and Signaling Interface (FC-PH) + + RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks + + ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN) + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-1000X { + base coding-func; + description + "1000BASE-X PCS clause 36 coding function."; + reference + "IEEE 802.3-2018, Clause 36: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-10GW { + base coding-func; + description + "IEEE 802.3-2018, Clause 50: IEEE Standard for Ethernet + + 10GBASE-W (WAN PHY) PCS clause 49 and WIS clause 50 + coding function."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-10GR { + base coding-func; + description + "10GBASE-R (LAN PHY) PCS clause 49 coding function."; + reference + "IEEE 802.3-2018, Clause 49: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-40GR { + base coding-func; + description + "40GBASE-R PCS clause 82 coding function."; + reference + "IEEE 802.3-2018, Clause 82: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ETH-100GR { + base coding-func; + description + "100GBASE-R PCS clause 82 coding function."; + reference + "IEEE 802.3-2018, Clause 82: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity optical-interface-func { + description + "Base identity from which optical-interface-function + is derived."; + reference + "MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity SX-PMD-1000 { + base optical-interface-func; + description + "SX-PMD-clause-38 Optical Interface function for + 1000BASE-X PCS-36."; + reference + "IEEE 802.3-2018, Clause 38: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LX-PMD-1000 { + base optical-interface-func; + description + "LX-PMD-clause-38 Optical Interface function for + 1000BASE-X PCS-36."; + reference + "IEEE 802.3-2018, Clause 38: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LX10-PMD-1000 { + base optical-interface-func; + description + "LX10-PMD-clause-59 Optical Interface function for + 1000BASE-X PCS-36."; + reference + "IEEE 802.3-2018, Clause 59: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity BX10-PMD-1000 { + base optical-interface-func; + description + "BX10-PMD-clause-59 Optical Interface function for + 1000BASE-X PCS-36."; + reference + "IEEE 802.3-2018, Clause 59: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LW-PMD-10G { + base optical-interface-func; + description + "LW-PMD-clause-52 Optical Interface function for + 10GBASE-W PCS-49-WIS-50."; + reference + "IEEE 802.3-2018, Clause 52: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity EW-PMD-10G { + base optical-interface-func; + description + "EW-PMD-clause-52 Optical Interface function for + 10GBASE-W PCS-49-WIS-50."; + reference + "IEEE 802.3-2018, Clause 52: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LR-PMD-10G { + base optical-interface-func; + description + "LR-PMD-clause-52 Optical Interface function for + 10GBASE-R PCS-49."; + reference + "IEEE 802.3-2018, Clause 52: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ER-PMD-10G { + base optical-interface-func; + description + "ER-PMD-clause-52 Optical Interface function for + 10GBASE-R PCS-49."; + reference + "IEEE 802.3-2018, Clause 52: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LR4-PMD-40G { + base optical-interface-func; + description + "LR4-PMD-clause-87 Optical Interface function for + 40GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 87: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity ER4-PMD-40G { + base optical-interface-func; + description + "ER4-PMD-clause-87 Optical Interface function for + 40GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 87: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity FR-PMD-40G { + base optical-interface-func; + description + "FR-PMD-clause-89 Optical Interface function for + 40GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 89: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + identity LR4-PMD-100G { + base optical-interface-func; + description + "LR4-PMD-clause-88 Optical Interface function for + 100GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 88: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + identity ER4-PMD-100G { + base optical-interface-func; + description + "ER4-PMD-clause-88 Optical Interface function for + 100GBASE-R PCS-82."; + reference + "IEEE 802.3-2018, Clause 88: IEEE Standard for Ethernet + + MEF63: Subscriber Layer 1 Service Attributes"; + } + + /* + * Typedefs + */ + + typedef otn-tpn { + type uint16 { + range "1..4095"; + } + description + "Tributary Port Number (TPN) for OTN."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks."; + } + + typedef otn-ts { + type uint16 { + range "1..4095"; + } + description + "Tributary Slot (TS) for OTN."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of Evolving + G.709 Optical Transport Networks."; + } + + typedef otn-label-range-type { + type enumeration { + enum trib-slot { + description + "Defines a range of OTN tributary slots (TS)."; + } + enum trib-port { + description + "Defines a range of OTN tributary ports (TPN)."; + } + } + description + "Defines the type of OTN label range: TS or TPN."; + } + + typedef gfp-k { + type enumeration { + enum 2 { + description + "The ODU2.ts rate (1,249,177.230 kbit/s) is used + to compute the rate of an ODUflex(GFP,n,2)."; + } + enum 3 { + description + "The ODU3.ts rate (1,254,470.354 kbit/s) is used + to compute the rate of an ODUflex(GFP,n,3)."; + } + enum 4 { + description + "The ODU4.ts rate (1,301,467.133 kbit/s) is used + to compute the rate of an ODUflex(GFP,n,4)."; + } + } + description + "The ODUk.ts used to compute the rate of an ODUflex(GFP,n,k)."; + reference + "ITU-T G.709 v6.0 (06/2020), Table 7-8 and L.7: Interfaces for + the Optical Transport Network (OTN)"; + } + + typedef flexe-client-rate { + type union { + type uint16; + type enumeration { + enum "10G" { + description + "Represents a 10G FlexE Client signal (s=2)."; + } + enum "40G" { + description + "Represents a 40G FlexE Client signal (s=8)."; + } + } + } + description + "The FlexE Client signal rate (s x 5,156,250.000 kbit/s) + used to compute the rate of an ODUflex(IMP, s). + + Valid values for s are s=2 (10G), s=4 (40G) and + s=5 x n (n x 25G). + + In the first two cases an enumeration value + (either 10G or 40G) is used, while in the latter case + the value of n is used."; + reference + "ITU-T G.709 v6.0 (06/2020), Table 7-2: Interfaces for the + Optical Transport Network (OTN)"; + } + + typedef odtu-flex-type { + type enumeration { + enum "2" { + description + "The ODTU2.ts ODTU type."; + } + enum "3" { + description + "The ODTU3.ts ODTU type."; + } + enum "4" { + description + "The ODTU4.ts ODTU type."; + } + enum "Cn" { + description + "The ODTUCn.ts ODTU type."; + } + } + description + "The type of Optical Data Tributary Unit (ODTU), + whose nominal bitrate is used to compute the number of + Tributary Slots (TS) required by an ODUflex LSP, according to + the (19-1a) and (20-1a) formulas defined in G.709."; + reference + "ITU-T G.709 v6.0 (06/2020), Table 7-7, clause 19.6 and + clause 20.5: Interfaces for the Optical Transport + Network (OTN)"; + } + + typedef bandwidth-scientific-notation { + type string { + pattern + '0(\.0?)?([eE](\+)?0?)?|' + + '[1-9](\.[0-9]{0,6})?[eE](\+)?(9[0-6]|[1-8][0-9]|0?[0-9])?'; + } + units "bps"; + description + "Bandwidth values, expressed using the scientific notation + in bits per second. + + The encoding format is the external decimal-significant + character sequences specified in IEEE 754 and ISO/IEC 9899:1999 + for 32-bit decimal floating-point numbers: + (-1)**(S) * 10**(Exponent) * (Significant), + where Significant uses 7 digits. + + An implementation for this representation MAY use decimal32 + or binary32. The range of the Exponent is from -95 to +96 + for decimal32, and from -38 to +38 for binary32. + As a bandwidth value, the format is restricted to be + normalized, non-negative, and non-fraction: + n.dddddde{+}dd, N.DDDDDDE{+}DD, 0e0 or 0E0, + where 'd' and 'D' are decimal digits; 'n' and 'N' are + non-zero decimal digits; 'e' and 'E' indicate a power of ten. + Some examples are 0e0, 1e10, and 9.953e9."; + reference + "IEEE Std 754-2001: IEEE Standard for Floating-Point + Arithmetic + + ISO/IEC 9899:1999: Information technology - Programming + Languages - C"; + } + + /* + * Groupings + */ + + grouping otn-link-bandwidth { + description + "Bandwidth attributes for OTN links."; + container otn-bandwidth { + description + "Bandwidth attributes for OTN links."; + list odulist { + key "odu-type"; + description + "OTN bandwidth definition"; + leaf odu-type { + type identityref { + base odu-type; + } + description "ODU type"; + } + leaf number { + type uint16; + description "Number of ODUs."; + } + leaf ts-number { + when 'derived-from-or-self(../odu-type,"ODUflex") or + derived-from-or-self(../odu-type, + "ODUflex-resizable")' { + description + "Applicable when odu-type is ODUflex or + ODUflex-resizable."; + } + type uint16 { + range "1..4095"; + } + description + "The number of Tributary Slots (TS) that + could be used by all the ODUflex LSPs."; + } + } + } + } + + grouping otn-path-bandwidth { + description + "Bandwidth attributes for OTN paths."; + container otn-bandwidth { + description + "Bandwidth attributes for OTN paths."; + leaf odu-type { + type identityref { + base odu-type; + } + description "ODU type"; + } + choice oduflex-type { + when 'derived-from-or-self(./odu-type,"ODUflex") or + derived-from-or-self(./odu-type, + "ODUflex-resizable")' { + description + "Applicable when odu-type is ODUflex or + ODUflex-resizable."; + } + description + "Types of ODUflex used to compute the ODUflex + nominal bit rate."; + reference + "ITU-T G.709 v6.0 (06/2020), Table 7-2: Interfaces for the + Optical Transport Network (OTN)"; + case generic { + leaf nominal-bit-rate { + type union { + type l1-types:bandwidth-scientific-notation; + type rt-types:bandwidth-ieee-float32; + } + mandatory true; + description + "Nominal ODUflex bit rate."; + } + } + case cbr { + leaf client-type { + type identityref { + base client-signal; + } + mandatory true; + description + "The type of Constant Bit Rate (CBR) client signal + of an ODUflex(CBR)."; + } + } + case gfp-n-k { + leaf gfp-n { + type uint8 { + range "1..80"; + } + mandatory true; + description + "The value of n for an ODUflex(GFP,n,k)."; + reference + "ITU-T G.709 v6.0 (06/2020), Tables 7-8 and L.7: + Interfaces for the Optical Transport Network (OTN)"; + } + leaf gfp-k { + type gfp-k; + description + "The value of k for an ODUflex(GFP,n,k). + + If omitted, it is calculated from the value of gfp-n + as described in Table 7-8 of G.709."; + reference + "ITU-T G.709 v6.0 (06/2020), Tables 7-8 and L.7: + Interfaces for the Optical Transport Network (OTN)"; + } + } + case flexe-client { + leaf flexe-client { + type flexe-client-rate; + mandatory true; + description + "The rate of the FlexE-client for an ODUflex(IMP,s)."; + } + } + case flexe-aware { + leaf flexe-aware-n { + type uint16; + mandatory true; + description + "The rate of FlexE-aware client signal + for ODUflex(FlexE-aware)"; + } + } + case packet { + leaf opuflex-payload-rate { + type union { + type l1-types:bandwidth-scientific-notation; + type rt-types:bandwidth-ieee-float32; + } + mandatory true; + description + "Either the GFP-F encapsulated packet client nominal + bit rate for an ODUflex(GFP) or the 64b/66b encoded + packet client nominal bit rate for an ODUflex(IMP)."; + } + } + } + } + } + + grouping otn-max-path-bandwidth { + description + "Maximum bandwidth attributes for OTN paths."; + container otn-bandwidth { + description + "Maximum bandwidth attributes for OTN paths."; + leaf odu-type { + type identityref { + base odu-type; + } + description "ODU type."; + } + leaf max-ts-number { + when 'derived-from-or-self(../odu-type,"ODUflex") or + derived-from-or-self(../odu-type, + "ODUflex-resizable")' { + description + "Applicable when odu-type is ODUflex or + ODUflex-resizable."; + } + type uint16 { + range "1..4095"; + } + description + "The maximum number of Tributary Slots (TS) that could be + used by an ODUflex LSP."; + } + } + } + + grouping otn-label-range-info { + description + "Label range information for OTN. + + This grouping SHOULD be used together with the + otn-label-start-end and otn-label-step groupings to provide + OTN technology-specific label information to the models which + use the label-restriction-info grouping defined in the module + ietf-te-types."; + container otn-label-range { + description + "Label range information for OTN."; + leaf range-type { + type otn-label-range-type; + description "The type of range (e.g., TPN or TS) + to which the label range applies"; + } + leaf tsg { + type identityref { + base tributary-slot-granularity; + } + description + "Tributary slot granularity (TSG) to which the label range + applies. + + This leaf MUST be present when the range-type is TS. + + This leaf MAY be omitted when mapping an ODUk over an OTUk + Link. In this case the range-type is tpn, with only one + entry (ODUk), and the tpn range has only one value (1)."; + reference + "ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + leaf-list odu-type-list { + type identityref { + base odu-type; + } + description + "List of ODU types to which the label range applies. + + An Empty odu-type-list means that the label range + applies to all the supported ODU types."; + } + leaf priority { + type uint8 { + range 0..7; + } + description + "Priority in Interface Switching Capability + Descriptor (ISCD)."; + reference + "RFC4203: OSPF Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS)"; + } + } + } + + grouping otn-label-start-end { + description + "The OTN label-start or label-end used to specify an OTN label + range. + + This grouping is dependent on the range-type defined in the + otn-label-range-info grouping. + + This grouping SHOULD be used together with the + otn-label-range-info and otn-label-step groupings to provide + OTN technology-specific label information to the models which + use the label-restriction-info grouping defined in the module + ietf-te-types."; + container otn-label { + description + "Label start or label end for OTN. + + It is either a TPN or a TS depending on the OTN label range + type specified in the 'range-type' leaf defined in the + otn-label-range-info grouping."; + leaf tpn { + when "../../../../otn-label-range/range-type = + 'trib-port'" { + description + "Valid only when range-type represented by + trib-port."; + } + type otn-tpn; + description + "Tributary Port Number (TPN)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + leaf ts { + when "../../../../otn-label-range/range-type = + 'trib-slot'" { + description + "Valid only when range-type represented by + trib-slot."; + } + type otn-ts; + description + "Tributary Slot (TS) number."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + } + } + + grouping otn-label-hop { + description "OTN Label"; + reference + "RFC7139, section 6: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + container otn-label { + description + "Label hop for OTN."; + leaf tpn { + type otn-tpn; + description + "Tributary Port Number (TPN)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + leaf tsg { + type identityref { + base tributary-slot-granularity; + } + description "Tributary Slot Granularity (TSG)."; + reference + "ITU-T G.709 v6.0 (06/2020): Interfaces for the Optical + Transport Network (OTN)"; + } + leaf ts-list { + type string { + pattern "([1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?" + + "(,[1-9][0-9]{0,3}(-[1-9][0-9]{0,3})?)*)"; + } + description + "A list of available Tributary Slots (TS) ranging + between 1 and 4095. If multiple values or + ranges are given, they all MUST be disjoint + and MUST be in ascending order. + For example 1-20,25,50-1000."; + reference + "RFC 7139: GMPLS Signaling Extensions for Control + of Evolving G.709 Optical Transport Networks"; + } + } + } + + grouping otn-label-step { + description + "Label step for OTN. + + This grouping is dependent on the range-type defined in the + otn-label-range-info grouping. + + This grouping SHOULD be used together with the + otn-label-range-info and otn-label-start-end groupings to + provide OTN technology-specific label information to the + models which use the label-restriction-info grouping defined + in the module ietf-te-types."; + container otn-label-step { + description + "Label step for OTN. + + It is either a TPN or a TS depending on the OTN label range + type specified in the 'range-type' leaf defined in the + otn-label-range-info grouping."; + leaf tpn { + when "../../../otn-label-range/range-type = + 'trib-port'" { + description + "Valid only when range-type represented by + trib-port."; + } + type otn-tpn; + description + "Label step which represents possible increments for + Tributary Port Number (TPN)."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + leaf ts { + when "../../../otn-label-range/range-type = + 'trib-slot'" { + description + "Valid only when range-type represented by + trib-slot"; + } + type otn-ts; + description + "Label step which represents possible increments for + Tributary Slot (TS) number."; + reference + "RFC7139: GMPLS Signaling Extensions for Control of + Evolving G.709 Optical Transport Networks"; + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc6991/ietf-inet-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc6991/ietf-inet-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..a1ef0dfaa71bb591bd84ff397565eb5e6c693310 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc6991/ietf-inet-types.yang @@ -0,0 +1,458 @@ +module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2013 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 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - ip-address-no-zone + - ipv4-address-no-zone + - 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 2460."; + } + } + 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 2460: 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 2460: 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. A current list of + all assignments is available from . + + 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 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** 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 inet:ipv4-address; + type inet: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])' + + '(%[\p{N}\p{L}]+)?'; + } + 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. + + 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])))' + + '(%[\p{N}\p{L}]+)?'; + 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. + + 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 inet:ipv4-address-no-zone; + type inet: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 inet:ipv4-address { + pattern '[0-9\.]*'; + } + description + "An IPv4 address without a zone index. This type, derived from + ipv4-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + } + + typedef ipv6-address-no-zone { + type inet:ipv6-address { + pattern '[0-9a-fA-F:\.]*'; + } + description + "An IPv6 address without a zone index. This type, derived from + ipv6-address, may be used in situations where the zone is + known from the context and hence 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-prefix { + type union { + type inet:ipv4-prefix; + type inet: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 address 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."; + } + + 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 address 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 IPv6 address should have all bits that do not belong + to the prefix set to zero. + + 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."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + 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]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + 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. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + 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 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + 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, which are normalized to uppercase as described in + Section 6.2.2.1. + + 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)"; + } + +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc6991/ietf-yang-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc6991/ietf-yang-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..f6624fed83e6e59d67c277df15f6e0b82ee666a4 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc6991/ietf-yang-types.yang @@ -0,0 +1,474 @@ +module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2013 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 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - yang-identifier + - hex-string + - uuid + - dotted-quad"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier-related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifiers. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type; the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of types related to date and time***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z + all represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using + the time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually + referring to the notion of local time) uses the time-offset + -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence + happened. The specific occurrence must be defined in the + description of any schema node defined using this type. When + the specific occurrence occurred prior to the last time the + associated timeticks attribute was zero, then the timestamp + value is zero. Note that this requires all timestamp values + to be reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML-specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + /*** collection of string types ***/ + + typedef hex-string { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "A hexadecimal string with octets represented as hex digits + separated by colons. The canonical representation uses + lowercase characters."; + } + + typedef uuid { + type string { + pattern '[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}'; + } + description + "A Universally Unique IDentifier in the string representation + defined in RFC 4122. The canonical representation uses + lowercase characters. + + The following is an example of a UUID in string representation: + f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + "; + reference + "RFC 4122: A Universally Unique IDentifier (UUID) URN + Namespace"; + } + + typedef dotted-quad { + 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 + "An unsigned 32-bit number expressed in the dotted-quad + notation, i.e., four octets written as decimal numbers + and separated with the '.' (full stop) character."; + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc8294/iana-routing-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8294/iana-routing-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..e57ebd2392e6b3672dab81928d8add0372811a6e --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8294/iana-routing-types.yang @@ -0,0 +1,471 @@ +module iana-routing-types { + namespace "urn:ietf:params:xml:ns:yang:iana-routing-types"; + prefix iana-rt-types; + + organization + "IANA"; + contact + "Internet Assigned Numbers Authority + + Postal: ICANN + 12025 Waterfront Drive, Suite 300 + Los Angeles, CA 90094-2536 + United States of America + Tel: +1 310 301 5800 + "; + + description + "This module contains a collection of YANG data types + considered defined by IANA and used 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 4."; + } + + /*** Collection of IANA types related to routing ***/ + /*** IANA Address Family enumeration ***/ + + typedef address-family { + type enumeration { + enum ipv4 { + value 1; + description + "IPv4 Address Family."; + } + + enum ipv6 { + value 2; + description + "IPv6 Address Family."; + } + + enum nsap { + value 3; + description + "OSI Network Service Access Point (NSAP) Address Family."; + } + + enum hdlc { + value 4; + description + "High-Level Data Link Control (HDLC) Address Family."; + } + + enum bbn1822 { + value 5; + description + "Bolt, Beranek, and Newman Report 1822 (BBN 1822) + Address Family."; + } + + enum ieee802 { + value 6; + description + "IEEE 802 Committee Address Family + (aka Media Access Control (MAC) address)."; + } + + enum e163 { + value 7; + description + "ITU-T E.163 Address Family."; + } + enum e164 { + value 8; + description + "ITU-T E.164 (Switched Multimegabit Data Service (SMDS), + Frame Relay, ATM) Address Family."; + } + + enum f69 { + value 9; + description + "ITU-T F.69 (Telex) Address Family."; + } + + enum x121 { + value 10; + description + "ITU-T X.121 (X.25, Frame Relay) Address Family."; + } + + enum ipx { + value 11; + description + "Novell Internetwork Packet Exchange (IPX) + Address Family."; + } + + enum appletalk { + value 12; + description + "Apple AppleTalk Address Family."; + } + + enum decnet-iv { + value 13; + description + "Digital Equipment DECnet Phase IV Address Family."; + } + + enum vines { + value 14; + description + "Banyan Vines Address Family."; + } + + enum e164-nsap { + value 15; + description + "ITU-T E.164 with NSAP sub-address Address Family."; + } + + enum dns { + value 16; + description + "Domain Name System (DNS) Address Family."; + } + + enum distinguished-name { + value 17; + description + "Distinguished Name Address Family."; + } + + enum as-num { + value 18; + description + "Autonomous System (AS) Number Address Family."; + } + + enum xtp-v4 { + value 19; + description + "Xpress Transport Protocol (XTP) over IPv4 + Address Family."; + } + + enum xtp-v6 { + value 20; + description + "XTP over IPv6 Address Family."; + } + + enum xtp-native { + value 21; + description + "XTP native mode Address Family."; + } + + enum fc-port { + value 22; + description + "Fibre Channel (FC) World-Wide Port Name Address Family."; + } + enum fc-node { + value 23; + description + "FC World-Wide Node Name Address Family."; + } + + enum gwid { + value 24; + description + "ATM Gateway Identifier (GWID) Number Address Family."; + } + + enum l2vpn { + value 25; + description + "Layer 2 VPN (L2VPN) Address Family."; + } + + enum mpls-tp-section-eid { + value 26; + description + "MPLS Transport Profile (MPLS-TP) Section Endpoint + Identifier Address Family."; + } + + enum mpls-tp-lsp-eid { + value 27; + description + "MPLS-TP Label Switched Path (LSP) Endpoint Identifier + Address Family."; + } + + enum mpls-tp-pwe-eid { + value 28; + description + "MPLS-TP Pseudowire Endpoint Identifier Address Family."; + } + + enum mt-v4 { + value 29; + description + "Multi-Topology IPv4 Address Family."; + } + + enum mt-v6 { + value 30; + description + "Multi-Topology IPv6 Address Family."; + } + + enum eigrp-common-sf { + value 16384; + description + "Enhanced Interior Gateway Routing Protocol (EIGRP) + Common Service Family Address Family."; + } + + enum eigrp-v4-sf { + value 16385; + description + "EIGRP IPv4 Service Family Address Family."; + } + + enum eigrp-v6-sf { + value 16386; + description + "EIGRP IPv6 Service Family Address Family."; + } + + enum lcaf { + value 16387; + description + "Locator/ID Separation Protocol (LISP) + Canonical Address Format (LCAF) Address Family."; + } + + enum bgp-ls { + value 16388; + description + "Border Gateway Protocol - Link State (BGP-LS) + Address Family."; + } + + enum mac-48 { + value 16389; + description + "IEEE 48-bit MAC Address Family."; + } + + enum mac-64 { + value 16390; + description + "IEEE 64-bit MAC Address Family."; + } + + enum trill-oui { + value 16391; + description + "Transparent Interconnection of Lots of Links (TRILL) + IEEE Organizationally Unique Identifier (OUI) + Address Family."; + } + + enum trill-mac-24 { + value 16392; + description + "TRILL final 3 octets of 48-bit MAC Address Family."; + } + + enum trill-mac-40 { + value 16393; + description + "TRILL final 5 octets of 64-bit MAC Address Family."; + } + + enum ipv6-64 { + value 16394; + description + "First 8 octets (64 bits) of IPv6 address + Address Family."; + } + + enum trill-rbridge-port-id { + value 16395; + description + "TRILL Routing Bridge (RBridge) Port ID Address Family."; + } + + enum trill-nickname { + value 16396; + description + "TRILL Nickname Address Family."; + } + } + + description + "Enumeration containing all the IANA-defined + Address Families."; + + } + + /*** Subsequent Address Family Identifiers (SAFIs) ***/ + /*** for multiprotocol BGP enumeration ***/ + + typedef bgp-safi { + type enumeration { + enum unicast-safi { + value 1; + description + "Unicast SAFI."; + } + + enum multicast-safi { + value 2; + description + "Multicast SAFI."; + } + + enum labeled-unicast-safi { + value 4; + description + "Labeled Unicast SAFI."; + } + + enum multicast-vpn-safi { + value 5; + description + "Multicast VPN SAFI."; + } + + enum pseudowire-safi { + value 6; + description + "Multi-segment Pseudowire VPN SAFI."; + } + + enum tunnel-encap-safi { + value 7; + description + "Tunnel Encap SAFI."; + } + + enum mcast-vpls-safi { + value 8; + description + "Multicast Virtual Private LAN Service (VPLS) SAFI."; + } + + enum tunnel-safi { + value 64; + description + "Tunnel SAFI."; + } + + enum vpls-safi { + value 65; + description + "VPLS SAFI."; + } + + enum mdt-safi { + value 66; + description + "Multicast Distribution Tree (MDT) SAFI."; + } + + enum v4-over-v6-safi { + value 67; + description + "IPv4 over IPv6 SAFI."; + } + + enum v6-over-v4-safi { + value 68; + description + "IPv6 over IPv4 SAFI."; + } + + enum l1-vpn-auto-discovery-safi { + value 69; + description + "Layer 1 VPN Auto-Discovery SAFI."; + } + + enum evpn-safi { + value 70; + description + "Ethernet VPN (EVPN) SAFI."; + } + + enum bgp-ls-safi { + value 71; + description + "BGP-LS SAFI."; + } + + enum bgp-ls-vpn-safi { + value 72; + description + "BGP-LS VPN SAFI."; + } + + enum sr-te-safi { + value 73; + description + "Segment Routing - Traffic Engineering (SR-TE) SAFI."; + } + + enum labeled-vpn-safi { + value 128; + description + "MPLS Labeled VPN SAFI."; + } + + enum multicast-mpls-vpn-safi { + value 129; + description + "Multicast for BGP/MPLS IP VPN SAFI."; + } + + enum route-target-safi { + value 132; + description + "Route Target SAFI."; + } + + enum ipv4-flow-spec-safi { + value 133; + description + "IPv4 Flow Specification SAFI."; + } + + enum vpnv4-flow-spec-safi { + value 134; + description + "IPv4 VPN Flow Specification SAFI."; + } + + enum vpn-auto-discovery-safi { + value 140; + description + "VPN Auto-Discovery SAFI."; + } + } + description + "Enumeration for BGP SAFI."; + reference + "RFC 4760: Multiprotocol Extensions for BGP-4."; + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc8294/ietf-routing-types.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8294/ietf-routing-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..65c83bc848a9edd1bf6b0b4156d09ed14aa3cd28 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8294/ietf-routing-types.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: + WG List: + + Editors: Xufeng Liu + + Yingzhen Qu + + Acee Lindem + + Christian Hopps + + Lou Berger + "; + + 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 . + + 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 . + + 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."; + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc8343/ietf-interfaces.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8343/ietf-interfaces.yang new file mode 100644 index 0000000000000000000000000000000000000000..96d416753364e1f12651190655833be8da0283aa --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8343/ietf-interfaces.yang @@ -0,0 +1,1123 @@ +module ietf-interfaces { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (Network Modeling) Working Group"; + + contact + "WG Web: + WG List: + + Editor: Martin Bjorklund + "; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + 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 8343; see + the RFC itself for full legal notices."; + + revision 2018-02-20 { + description + "Updated to support NMDA."; + reference + "RFC 8343: A YANG Data Model for Interface Management"; + } + + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7223: A YANG Data Model for Interface Management"; + } + + /* + * Typedefs + */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + interfaces."; + } + + /* + * Identities + */ + + identity interface-type { + description + "Base identity from which specific interface types are + derived."; + } + + /* + * Features + */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + feature if-mib { + description + "This feature indicates that the device implements + the IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* + * Data nodes + */ + + container interfaces { + description + "Interface parameters."; + + list interface { + key "name"; + + description + "The list of interfaces on the device. + + The status of an interface is available in this list in the + operational state. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the operational + state. If the configuration of a user-controlled interface + cannot be used by the system, the configured interface is + not instantiated in the operational state. + + System-controlled interfaces created by the system are + always present in this list in the operational state, + whether or not they are configured."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + For system-controlled interfaces, this leaf is the + device-specific name of the interface. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + operational state, the server MAY reject the request if + the implementation does not support pre-provisioning of + interfaces or if the name refers to an interface that can + never exist in the system. A Network Configuration + Protocol (NETCONF) server MUST reply with an rpc-error + with the error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the 'pre-provisioning' feature is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the 'arbitrary-names' feature is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + operational state. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf description { + type string; + description + "A textual description of the interface. + + A server implementation MAY map this leaf to the ifAlias + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifAlias. The definition of + such a mechanism is outside the scope of this document. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + configuration."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the intended configuration to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the intended configuration are + reflected in ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + description + "The device will generate linkUp/linkDown SNMP + notifications for this interface."; + } + enum disabled { + value 2; + description + "The device will not generate linkUp/linkDown SNMP + notifications for this interface."; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces that do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + config false; + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + config false; + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + config false; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + config false; + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + config false; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-ref; + config false; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-ref; + config false; + + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + config false; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + config false; + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + + } + } + + /* + * Legacy typedefs + */ + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + status deprecated; + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* + * Legacy operational state data nodes + */ + + container interfaces-state { + config false; + status deprecated; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + status deprecated; + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether or not they are + configured."; + + leaf name { + type string; + status deprecated; + description + "The name of the interface. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + status deprecated; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + status deprecated; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + status deprecated; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + status deprecated; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + status deprecated; + description + "The ifIndex value for the ifEntry represented by this + interface."; + + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + status deprecated; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + status deprecated; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + status deprecated; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + status deprecated; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + status deprecated; + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + status deprecated; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + status deprecated; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + status deprecated; + + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + status deprecated; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + status deprecated; + + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + leaf out-multicast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + status deprecated; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc8345/ietf-network-topology.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8345/ietf-network-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..df3685827d5f72c2eb0671de82594ead28553468 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8345/ietf-network-topology.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: + WG List: + + Editor: Alexander Clemm + + + Editor: Jan Medved + + + Editor: Robert Varga + + + Editor: Nitin Bahadur + + + Editor: Hariharan Ananthakrishnan + + + Editor: Xufeng Liu + "; + + 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)."; + } + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc8345/ietf-network.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8345/ietf-network.yang new file mode 100644 index 0000000000000000000000000000000000000000..c67a3fa40f7b719cda6199f7a1352c8bd9e7bec8 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8345/ietf-network.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: + WG List: + + Editor: Alexander Clemm + + + Editor: Jan Medved + + + Editor: Robert Varga + + + Editor: Nitin Bahadur + + + Editor: Hariharan Ananthakrishnan + + + Editor: Xufeng Liu + "; + 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."; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc8346/ietf-l3-unicast-topology.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8346/ietf-l3-unicast-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..56941fdca9637af7766ecc1d57cf3564ddedb183 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8346/ietf-l3-unicast-topology.yang @@ -0,0 +1,359 @@ +module ietf-l3-unicast-topology { + yang-version 1.1; + namespace + "urn:ietf:params:xml:ns:yang:ietf-l3-unicast-topology"; + prefix "l3t"; + import ietf-network { + prefix "nw"; + } + import ietf-network-topology { + prefix "nt"; + } + import ietf-inet-types { + prefix "inet"; + } + import ietf-routing-types { + prefix "rt-types"; + } + organization + "IETF I2RS (Interface to the Routing System) Working Group"; + contact + "WG Web: + WG List: + Editor: Alexander Clemm + + Editor: Jan Medved + + Editor: Robert Varga + + Editor: Xufeng Liu + + Editor: Nitin Bahadur + + Editor: Hariharan Ananthakrishnan + "; + description + "This module defines a model for Layer 3 Unicast + topologies. + + 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 8346; see the RFC itself for full legal notices."; + revision "2018-02-26" { + description + "Initial revision."; + reference + "RFC 8346: A YANG Data Model for Layer 3 Topologies"; + } + + identity flag-identity { + description "Base type for flags"; + } + + typedef l3-event-type { + type enumeration { + enum "add" { + description + "A Layer 3 node, link, prefix, or termination point has + been added"; + } + enum "remove" { + description + "A Layer 3 node, link, prefix, or termination point has + been removed"; + } + enum "update" { + description + "A Layer 3 node, link, prefix, or termination point has + been updated"; + } + } + description "Layer 3 event type for notifications"; + } + + typedef prefix-flag-type { + type identityref { + base "flag-identity"; + } + description "Prefix flag attributes"; + } + + typedef node-flag-type { + type identityref { + base "flag-identity"; + } + description "Node flag attributes"; + } + + typedef link-flag-type { + type identityref { + base "flag-identity"; + } + description "Link flag attributes"; + } + + typedef l3-flag-type { + type identityref { + base "flag-identity"; + } + description "L3 flag attributes"; + } + + grouping l3-prefix-attributes { + description + "L3 prefix attributes"; + leaf prefix { + type inet:ip-prefix; + description + "IP prefix value"; + } + leaf metric { + type uint32; + description + "Prefix metric"; + } + leaf-list flag { + type prefix-flag-type; + description + "Prefix flags"; + } + } + grouping l3-unicast-topology-type { + description "Identifies the topology type to be L3 Unicast."; + container l3-unicast-topology { + presence "indicates L3 Unicast topology"; + description + "The presence of the container node indicates L3 Unicast + topology"; + } + } + grouping l3-topology-attributes { + description "Topology scope attributes"; + container l3-topology-attributes { + description "Contains topology attributes"; + leaf name { + type string; + description + "Name of the topology"; + } + leaf-list flag { + type l3-flag-type; + description + "Topology flags"; + } + } + } + grouping l3-node-attributes { + description "L3 node scope attributes"; + container l3-node-attributes { + description + "Contains node attributes"; + leaf name { + type inet:domain-name; + description + "Node name"; + } + leaf-list flag { + type node-flag-type; + description + "Node flags"; + } + leaf-list router-id { + type rt-types:router-id; + description + "Router-id for the node"; + } + list prefix { + key "prefix"; + description + "A list of prefixes along with their attributes"; + uses l3-prefix-attributes; + } + } + } + grouping l3-link-attributes { + description + "L3 link scope attributes"; + container l3-link-attributes { + description + "Contains link attributes"; + leaf name { + type string; + description + "Link Name"; + } + leaf-list flag { + type link-flag-type; + description + "Link flags"; + } + leaf metric1 { + type uint64; + description + "Link Metric 1"; + } + leaf metric2 { + type uint64; + description + "Link Metric 2"; + } + } + } + grouping l3-termination-point-attributes { + description "L3 termination point scope attributes"; + container l3-termination-point-attributes { + description + "Contains termination point attributes"; + choice termination-point-type { + description + "Indicates the termination point type"; + case ip { + leaf-list ip-address { + type inet:ip-address; + description + "IPv4 or IPv6 address."; + } + } + case unnumbered { + leaf unnumbered-id { + type uint32; + description + "Unnumbered interface identifier. + The identifier will correspond to the ifIndex value + of the interface, i.e., the ifIndex value of the + ifEntry that represents the interface in + implementations where the Interfaces Group MIB + (RFC 2863) is supported."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + } + case interface-name { + leaf interface-name { + type string; + description + "Name of the interface. The name can (but does not + have to) correspond to an interface reference of a + containing node's interface, i.e., the path name of a + corresponding interface data node on the containing + node reminiscent of data type interface-ref defined + in RFC 8343. It should be noted that data type + interface-ref of RFC 8343 cannot be used directly, + + as this data type is used to reference an interface + in a datastore of a single node in the network, not + to uniquely reference interfaces across a network."; + reference + "RFC 8343: A YANG Data Model for Interface Management"; + } + } + } + } + } + augment "/nw:networks/nw:network/nw:network-types" { + description + "Introduces new network type for L3 Unicast topology"; + uses l3-unicast-topology-type; + } + augment "/nw:networks/nw:network" { + when "nw:network-types/l3t:l3-unicast-topology" { + description + "Augmentation parameters apply only for networks with + L3 Unicast topology"; + } + description + "L3 Unicast for the network as a whole"; + uses l3-topology-attributes; + } + augment "/nw:networks/nw:network/nw:node" { + when "../nw:network-types/l3t:l3-unicast-topology" { + description + "Augmentation parameters apply only for networks with + L3 Unicast topology"; + } + description + "L3 Unicast node-level attributes "; + uses l3-node-attributes; + } + augment "/nw:networks/nw:network/nt:link" { + when "../nw:network-types/l3t:l3-unicast-topology" { + description + "Augmentation parameters apply only for networks with + L3 Unicast topology"; + } + description + "Augments topology link attributes"; + uses l3-link-attributes; + } + augment "/nw:networks/nw:network/nw:node/" + +"nt:termination-point" { + when "../../nw:network-types/l3t:l3-unicast-topology" { + description + "Augmentation parameters apply only for networks with + L3 Unicast topology"; + } + description "Augments topology termination point configuration"; + uses l3-termination-point-attributes; + } + notification l3-node-event { + description + "Notification event for L3 node"; + leaf l3-event-type { + type l3-event-type; + description + "Event type"; + } + uses nw:node-ref; + uses l3-unicast-topology-type; + uses l3-node-attributes; + } + notification l3-link-event { + description + "Notification event for L3 link"; + leaf l3-event-type { + type l3-event-type; + description + "Event type"; + } + uses nt:link-ref; + uses l3-unicast-topology-type; + uses l3-link-attributes; + } + notification l3-prefix-event { + description + "Notification event for L3 prefix"; + leaf l3-event-type { + type l3-event-type; + description + "Event type"; + } + uses nw:node-ref; + uses l3-unicast-topology-type; + container prefix { + description + "Contains L3 prefix attributes"; + uses l3-prefix-attributes; + } + } + notification termination-point-event { + description + "Notification event for L3 termination point"; + leaf l3-event-type { + type l3-event-type; + description + "Event type"; + } + uses nt:tp-ref; + uses l3-unicast-topology-type; + uses l3-termination-point-attributes; + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/rfc8795/ietf-te-topology.yang b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8795/ietf-te-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..41edbcd1f419980b0f22c507d1f5c8e3e7838d48 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/rfc8795/ietf-te-topology.yang @@ -0,0 +1,1952 @@ +module ietf-te-topology { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-te-topology"; + prefix tet; + + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-te-types { + prefix te-types; + reference + "RFC 8776: Common YANG Data Types for Traffic Engineering"; + } + 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: + WG List: + + Editor: Xufeng Liu + + + Editor: Igor Bryskin + + + Editor: Vishnu Pavan Beeram + + + Editor: Tarek Saad + + + Editor: Himanshu Shah + + + Editor: Oscar Gonzalez de Dios + "; + description + "This YANG module defines a TE topology model for representing, + retrieving, and manipulating technology-agnostic TE topologies. + + Copyright (c) 2020 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 8795; see the + RFC itself for full legal notices."; + + revision 2020-08-06 { + description + "Initial revision."; + reference + "RFC 8795: YANG Data Model for Traffic Engineering (TE) + Topologies"; + } + + /* + * Features + */ + + feature nsrlg { + description + "This feature indicates that the system supports NSRLGs + (Non-Shared Risk Link Groups)."; + } + + feature te-topology-hierarchy { + description + "This feature indicates that the system allows an underlay + and/or overlay TE topology hierarchy."; + } + + feature template { + description + "This feature indicates that the system supports + template configuration."; + } + + /* + * Typedefs + */ + + typedef geographic-coordinate-degree { + type decimal64 { + fraction-digits 8; + } + description + "Decimal degree (DD) used to express latitude and longitude + geographic coordinates."; + } + // geographic-coordinate-degree + + typedef te-info-source { + type enumeration { + enum unknown { + description + "The source is unknown."; + } + enum locally-configured { + description + "Configured entity."; + } + enum ospfv2 { + description + "OSPFv2."; + } + enum ospfv3 { + description + "OSPFv3."; + } + enum isis { + description + "IS-IS."; + } + enum bgp-ls { + description + "BGP-LS."; + reference + "RFC 7752: North-Bound Distribution of Link-State and + Traffic Engineering (TE) Information Using BGP"; + } + enum system-processed { + description + "System-processed entity."; + } + enum other { + description + "Other source."; + } + } + description + "Describes the type of source that has provided the + related information, and the source's credibility."; + } + // te-info-source + + /* + * Groupings + */ + + grouping connectivity-matrix-entry-path-attributes { + description + "Attributes of a connectivity matrix entry."; + leaf is-allowed { + type boolean; + description + "'true' - switching is allowed; + 'false' - switching is disallowed."; + } + container underlay { + if-feature "te-topology-hierarchy"; + description + "Attributes of the TE link underlay."; + reference + "RFC 4206: Label Switched Paths (LSP) Hierarchy with + Generalized Multi-Protocol Label Switching (GMPLS) + Traffic Engineering (TE)"; + uses te-link-underlay-attributes; + } + uses te-types:generic-path-constraints; + uses te-types:generic-path-optimization; + uses te-types:generic-path-properties; + } + // connectivity-matrix-entry-path-attributes + + grouping geolocation-container { + description + "Contains a GPS location."; + container geolocation { + config false; + description + "Contains a GPS location."; + leaf altitude { + type int64; + units "millimeters"; + description + "Distance above sea level."; + } + leaf latitude { + type geographic-coordinate-degree { + range "-90..90"; + } + description + "Relative position north or south on the Earth's surface."; + } + leaf longitude { + type geographic-coordinate-degree { + range "-180..180"; + } + description + "Angular distance east or west on the Earth's surface."; + } + } + // geolocation + } + // geolocation-container + + grouping information-source-state-attributes { + description + "The attributes identifying the source that has provided the + related information, and the source's credibility."; + leaf credibility-preference { + type uint16; + description + "The preference value for calculating the Traffic + Engineering database credibility value used for + tie-break selection between different information-source + values. A higher value is preferable."; + } + leaf logical-network-element { + type string; + description + "When applicable, this is the name of a logical network + element from which the information is learned."; + } + leaf network-instance { + type string; + description + "When applicable, this is the name of a network instance + from which the information is learned."; + } + } + // information-source-state-attributes + + grouping information-source-per-link-attributes { + description + "Per-node container of the attributes identifying the source + that has provided the related information, and the source's + credibility."; + leaf information-source { + type te-info-source; + config false; + description + "Indicates the type of information source."; + } + leaf information-source-instance { + type string; + config false; + description + "The name indicating the instance of the information + source."; + } + container information-source-state { + config false; + description + "Contains state attributes related to the information + source."; + uses information-source-state-attributes; + container topology { + description + "When the information is processed by the system, + the attributes in this container indicate which topology + is used to generate the result information."; + uses nt:link-ref; + } + } + } + // information-source-per-link-attributes + + grouping information-source-per-node-attributes { + description + "Per-node container of the attributes identifying the source + that has provided the related information, and the source's + credibility."; + leaf information-source { + type te-info-source; + config false; + description + "Indicates the type of information source."; + } + leaf information-source-instance { + type string; + config false; + description + "The name indicating the instance of the information + source."; + } + container information-source-state { + config false; + description + "Contains state attributes related to the information + source."; + uses information-source-state-attributes; + container topology { + description + "When the information is processed by the system, + the attributes in this container indicate which topology + is used to generate the result information."; + uses nw:node-ref; + } + } + } + // information-source-per-node-attributes + + grouping interface-switching-capability-list { + description + "List of Interface Switching Capability Descriptors (ISCDs)."; + list interface-switching-capability { + key "switching-capability encoding"; + description + "List of ISCDs for this link."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description + RFC 4203: OSPF Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS)"; + leaf switching-capability { + type identityref { + base te-types:switching-capabilities; + } + description + "Switching capability for this interface."; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "Encoding supported by this interface."; + } + uses te-link-iscd-attributes; + } + // interface-switching-capability + } + // interface-switching-capability-list + + grouping statistics-per-link { + description + "Statistics attributes per TE link."; + leaf discontinuity-time { + type yang:date-and-time; + description + "The time of the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + /* Administrative attributes */ + leaf disables { + type yang:counter32; + description + "Number of times that a link was disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that a link was enabled."; + } + leaf maintenance-clears { + type yang:counter32; + description + "Number of times that a link was taken out of maintenance."; + } + leaf maintenance-sets { + type yang:counter32; + description + "Number of times that a link was put in maintenance."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that a link was modified."; + } + /* Operational attributes */ + leaf downs { + type yang:counter32; + description + "Number of times that a link was set to an operational state + of 'down'."; + } + leaf ups { + type yang:counter32; + description + "Number of times that a link was set to an operational state + of 'up'."; + } + /* Recovery attributes */ + leaf fault-clears { + type yang:counter32; + description + "Number of times that a link experienced a fault-clear + event."; + } + leaf fault-detects { + type yang:counter32; + description + "Number of times that a link experienced fault detection."; + } + leaf protection-switches { + type yang:counter32; + description + "Number of times that a link experienced protection + switchover."; + } + leaf protection-reverts { + type yang:counter32; + description + "Number of times that a link experienced protection + reversion."; + } + leaf restoration-failures { + type yang:counter32; + description + "Number of times that a link experienced restoration + failure."; + } + leaf restoration-starts { + type yang:counter32; + description + "Number of times that a link experienced restoration + start."; + } + leaf restoration-successes { + type yang:counter32; + description + "Number of times that a link experienced restoration + success."; + } + leaf restoration-reversion-failures { + type yang:counter32; + description + "Number of times that a link experienced restoration + reversion failure."; + } + leaf restoration-reversion-starts { + type yang:counter32; + description + "Number of times that a link experienced restoration + reversion start."; + } + leaf restoration-reversion-successes { + type yang:counter32; + description + "Number of times that a link experienced restoration + reversion success."; + } + } + // statistics-per-link + + grouping statistics-per-node { + description + "Statistics attributes per TE node."; + leaf discontinuity-time { + type yang:date-and-time; + description + "The time of the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + container node { + description + "Contains statistics attributes at the TE node level."; + leaf disables { + type yang:counter32; + description + "Number of times that a node was disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that a node was enabled."; + } + leaf maintenance-sets { + type yang:counter32; + description + "Number of times that a node was put in maintenance."; + } + leaf maintenance-clears { + type yang:counter32; + description + "Number of times that a node was taken out of + maintenance."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that a node was modified."; + } + } + // node + container connectivity-matrix-entry { + description + "Contains statistics attributes at the level of a + connectivity matrix entry."; + leaf creates { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + created."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 7.2, 'create' operation"; + } + leaf deletes { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + deleted."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 7.2, 'delete' operation"; + } + leaf disables { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + enabled."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that a connectivity matrix entry was + modified."; + } + } + // connectivity-matrix-entry + } + // statistics-per-node + + grouping statistics-per-ttp { + description + "Statistics attributes per TE TTP (Tunnel Termination Point)."; + leaf discontinuity-time { + type yang:date-and-time; + description + "The time of the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + container tunnel-termination-point { + description + "Contains statistics attributes at the TE TTP level."; + /* Administrative attributes */ + leaf disables { + type yang:counter32; + description + "Number of times that a TTP was disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that a TTP was enabled."; + } + leaf maintenance-clears { + type yang:counter32; + description + "Number of times that a TTP was taken out of maintenance."; + } + leaf maintenance-sets { + type yang:counter32; + description + "Number of times that a TTP was put in maintenance."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that a TTP was modified."; + } + /* Operational attributes */ + leaf downs { + type yang:counter32; + description + "Number of times that a TTP was set to an operational state + of 'down'."; + } + leaf ups { + type yang:counter32; + description + "Number of times that a TTP was set to an operational state + of 'up'."; + } + leaf in-service-clears { + type yang:counter32; + description + "Number of times that a TTP was taken out of service + (TE tunnel was released)."; + } + leaf in-service-sets { + type yang:counter32; + description + "Number of times that a TTP was put in service by a TE + tunnel (TE tunnel was set up)."; + } + } + // tunnel-termination-point + container local-link-connectivity { + description + "Contains statistics attributes at the TE LLCL (Local Link + Connectivity List) level."; + leaf creates { + type yang:counter32; + description + "Number of times that an LLCL entry was created."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 7.2, 'create' operation"; + } + leaf deletes { + type yang:counter32; + description + "Number of times that an LLCL entry was deleted."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 7.2, 'delete' operation"; + } + leaf disables { + type yang:counter32; + description + "Number of times that an LLCL entry was disabled."; + } + leaf enables { + type yang:counter32; + description + "Number of times that an LLCL entry was enabled."; + } + leaf modifies { + type yang:counter32; + description + "Number of times that an LLCL entry was modified."; + } + } + // local-link-connectivity + } + // statistics-per-ttp + + grouping te-link-augment { + description + "Augmentation for a TE link."; + uses te-link-config; + uses te-link-state-derived; + container statistics { + config false; + description + "Statistics data."; + uses statistics-per-link; + } + } + // te-link-augment + + grouping te-link-config { + description + "TE link configuration grouping."; + choice bundle-stack-level { + description + "The TE link can be partitioned into bundled links or + component links."; + case bundle { + container bundled-links { + description + "A set of bundled links."; + reference + "RFC 4201: Link Bundling in MPLS Traffic + Engineering (TE)"; + list bundled-link { + key "sequence"; + description + "Specifies a bundled interface that is + further partitioned."; + leaf sequence { + type uint32; + description + "Identifies the sequence in the bundle."; + } + } + } + } + case component { + container component-links { + description + "A set of component links."; + list component-link { + key "sequence"; + description + "Specifies a component interface that is + sufficient to unambiguously identify the + appropriate resources."; + leaf sequence { + type uint32; + description + "Identifies the sequence in the bundle."; + } + leaf src-interface-ref { + type string; + description + "Reference to a component link interface on the + source node."; + } + leaf des-interface-ref { + type string; + description + "Reference to a component link interface on the + destination node."; + } + } + } + } + } + // bundle-stack-level + leaf-list te-link-template { + if-feature "template"; + type leafref { + path "../../../../te/templates/link-template/name"; + } + description + "The reference to a TE link template."; + } + uses te-link-config-attributes; + } + // te-link-config + + grouping te-link-config-attributes { + description + "Link configuration attributes in a TE topology."; + container te-link-attributes { + description + "Link attributes in a TE topology."; + leaf access-type { + type te-types:te-link-access-type; + description + "Link access type, which can be point-to-point or + multi-access."; + } + container external-domain { + description + "For an inter-domain link, specifies the attributes of + the remote end of the link, to facilitate the signaling at + the local end."; + uses nw:network-ref; + leaf remote-te-node-id { + type te-types:te-node-id; + description + "Remote TE node identifier, used together with + 'remote-te-link-tp-id' to identify the remote Link + Termination Point (LTP) in a different domain."; + } + leaf remote-te-link-tp-id { + type te-types:te-tp-id; + description + "Remote TE LTP identifier, used together with + 'remote-te-node-id' to identify the remote LTP in a + different domain."; + } + } + leaf is-abstract { + type empty; + description + "Present if the link is abstract."; + } + leaf name { + type string; + description + "Link name."; + } + container underlay { + if-feature "te-topology-hierarchy"; + description + "Attributes of the TE link underlay."; + reference + "RFC 4206: Label Switched Paths (LSP) Hierarchy with + Generalized Multi-Protocol Label Switching (GMPLS) + Traffic Engineering (TE)"; + uses te-link-underlay-attributes; + } + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the link."; + } + uses te-link-info-attributes; + } + // te-link-attributes + } + // te-link-config-attributes + + grouping te-link-info-attributes { + description + "Advertised TE information attributes."; + leaf link-index { + type uint64; + description + "The link identifier. If OSPF is used, this object + represents an ospfLsdbID. If IS-IS is used, this object + represents an isisLSPID. If a locally configured link is + used, this object represents a unique value, which is + locally defined in a router."; + } + leaf administrative-group { + type te-types:admin-groups; + description + "Administrative group or color of the link. + This attribute covers both administrative groups (defined + in RFCs 3630 and 5305) and Extended Administrative Groups + (defined in RFC 7308)."; + 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)"; + } + uses interface-switching-capability-list; + uses te-types:label-set-info; + leaf link-protection-type { + type identityref { + base te-types:link-protection-type; + } + description + "Link Protection Type desired for this link."; + reference + "RFC 4202: Routing Extensions in Support of + Generalized Multi-Protocol Label Switching (GMPLS)"; + } + container max-link-bandwidth { + uses te-types:te-bandwidth; + description + "Maximum bandwidth that can be seen on this link in this + direction. Units are in bytes per second."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + } + container max-resv-link-bandwidth { + uses te-types:te-bandwidth; + description + "Maximum amount of bandwidth that can be reserved in this + direction in this link. Units are in bytes per second."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + } + list unreserved-bandwidth { + key "priority"; + max-elements 8; + description + "Unreserved bandwidth for priority levels 0-7. Units are in + bytes per second."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + leaf priority { + type uint8 { + range "0..7"; + } + description + "Priority."; + } + uses te-types:te-bandwidth; + } + leaf te-default-metric { + type uint32; + description + "Traffic Engineering metric."; + reference + "RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2 + RFC 5305: IS-IS Extensions for Traffic Engineering"; + } + leaf te-delay-metric { + type uint32; + description + "Traffic Engineering delay metric."; + reference + "RFC 7471: OSPF Traffic Engineering (TE) Metric Extensions"; + } + leaf te-igp-metric { + type uint32; + description + "IGP metric used for Traffic Engineering."; + reference + "RFC 3785: Use of Interior Gateway Protocol (IGP) Metric as a + second MPLS Traffic Engineering (TE) Metric"; + } + container te-srlgs { + description + "Contains a list of SRLGs."; + leaf-list value { + type te-types:srlg; + description + "SRLG value."; + reference + "RFC 4202: Routing Extensions in Support of + Generalized Multi-Protocol Label Switching (GMPLS)"; + } + } + container te-nsrlgs { + if-feature "nsrlg"; + description + "Contains a list of NSRLGs (Non-Shared Risk Link Groups). + When an abstract TE link is configured, this list specifies + the request that underlay TE paths need to be mutually + disjoint with other TE links in the same groups."; + leaf-list id { + type uint32; + description + "NSRLG ID, uniquely configured within a topology."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + } + } + } + // te-link-info-attributes + + grouping te-link-iscd-attributes { + description + "TE link ISCD attributes."; + reference + "RFC 4203: OSPF Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS), Section 1.4"; + list max-lsp-bandwidth { + key "priority"; + max-elements 8; + description + "Maximum Label Switched Path (LSP) bandwidth at + priorities 0-7."; + leaf priority { + type uint8 { + range "0..7"; + } + description + "Priority."; + } + uses te-types:te-bandwidth; + } + } + // te-link-iscd-attributes + + grouping te-link-state-derived { + description + "Link state attributes in a TE topology."; + leaf oper-status { + type te-types:te-oper-status; + config false; + description + "The current operational state of the link."; + } + leaf is-transitional { + type empty; + config false; + description + "Present if the link is transitional; used as an + alternative approach in lieu of 'inter-layer-lock-id' + for path computation in a TE topology covering multiple + layers or multiple regions."; + reference + "RFC 5212: Requirements for GMPLS-Based Multi-Region and + Multi-Layer Networks (MRN/MLN) + RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + } + uses information-source-per-link-attributes; + list information-source-entry { + key "information-source information-source-instance"; + config false; + description + "A list of information sources learned, including the source + that is used."; + uses information-source-per-link-attributes; + uses te-link-info-attributes; + } + container recovery { + config false; + description + "Status of the recovery process."; + leaf restoration-status { + type te-types:te-recovery-status; + description + "Restoration status."; + } + leaf protection-status { + type te-types:te-recovery-status; + description + "Protection status."; + } + } + container underlay { + if-feature "te-topology-hierarchy"; + config false; + description + "State attributes for the TE link underlay."; + leaf dynamic { + type boolean; + description + "'true' if the underlay is dynamically created."; + } + leaf committed { + type boolean; + description + "'true' if the underlay is committed."; + } + } + } + // te-link-state-derived + + grouping te-link-underlay-attributes { + description + "Attributes for the TE link underlay."; + reference + "RFC 4206: Label Switched Paths (LSP) Hierarchy with + Generalized Multi-Protocol Label Switching (GMPLS) + Traffic Engineering (TE)"; + leaf enabled { + type boolean; + description + "'true' if the underlay is enabled. + 'false' if the underlay is disabled."; + } + container primary-path { + description + "The service path on the underlay topology that + supports this link."; + uses nw:network-ref; + list path-element { + key "path-element-id"; + description + "A list of path elements describing the service path."; + leaf path-element-id { + type uint32; + description + "To identify the element in a path."; + } + uses te-path-element; + } + } + // primary-path + list backup-path { + key "index"; + description + "A list of backup service paths on the underlay topology that + protect the underlay primary path. If the primary path is + not protected, the list contains zero elements. If the + primary path is protected, the list contains one or more + elements."; + leaf index { + type uint32; + description + "A sequence number to identify a backup path."; + } + uses nw:network-ref; + list path-element { + key "path-element-id"; + description + "A list of path elements describing the backup service + path."; + leaf path-element-id { + type uint32; + description + "To identify the element in a path."; + } + uses te-path-element; + } + } + // backup-path + leaf protection-type { + type identityref { + base te-types:lsp-protection-type; + } + description + "Underlay protection type desired for this link."; + } + container tunnel-termination-points { + description + "Underlay TTPs desired for this link."; + leaf source { + type binary; + description + "Source TTP identifier."; + } + leaf destination { + type binary; + description + "Destination TTP identifier."; + } + } + container tunnels { + description + "Underlay TE tunnels supporting this TE link."; + leaf sharing { + type boolean; + default "true"; + description + "'true' if the underlay tunnel can be shared with other + TE links; + 'false' if the underlay tunnel is dedicated to this + TE link. + This leaf is the default option for all TE tunnels + and may be overridden by the per-TE-tunnel value."; + } + list tunnel { + key "tunnel-name"; + description + "Zero, one, or more underlay TE tunnels that support this + TE link."; + leaf tunnel-name { + type string; + description + "A tunnel name uniquely identifies an underlay TE tunnel, + used together with the 'source-node' value for this + link."; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for LSP Tunnels"; + } + leaf sharing { + type boolean; + description + "'true' if the underlay tunnel can be shared with other + TE links; + 'false' if the underlay tunnel is dedicated to this + TE link."; + } + } + // tunnel + } + // tunnels + } + // te-link-underlay-attributes + + grouping te-node-augment { + description + "Augmentation for a TE node."; + uses te-node-config; + uses te-node-state-derived; + container statistics { + config false; + description + "Statistics data."; + uses statistics-per-node; + } + list tunnel-termination-point { + key "tunnel-tp-id"; + description + "A termination point can terminate a tunnel."; + leaf tunnel-tp-id { + type binary; + description + "TTP identifier."; + } + uses te-node-tunnel-termination-point-config; + leaf oper-status { + type te-types:te-oper-status; + config false; + description + "The current operational state of the TTP."; + } + uses geolocation-container; + container statistics { + config false; + description + "Statistics data."; + uses statistics-per-ttp; + } + // Relationship to other TTPs + list supporting-tunnel-termination-point { + key "node-ref tunnel-tp-ref"; + description + "Identifies the TTPs on which this TTP depends."; + leaf node-ref { + type inet:uri; + description + "This leaf identifies the node in which the supporting + TTP is present. + This node is either the supporting node or a node in + an underlay topology."; + } + leaf tunnel-tp-ref { + type binary; + description + "Reference to a TTP that is in either the supporting node + or a node in an underlay topology."; + } + } + // supporting-tunnel-termination-point + } + // tunnel-termination-point + } + // te-node-augment + + grouping te-node-config { + description + "TE node configuration grouping."; + leaf-list te-node-template { + if-feature "template"; + type leafref { + path "../../../../te/templates/node-template/name"; + } + description + "The reference to a TE node template."; + } + uses te-node-config-attributes; + } + // te-node-config + + grouping te-node-config-attributes { + description + "Configuration node attributes in a TE topology."; + container te-node-attributes { + description + "Contains node attributes in a TE topology."; + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the link."; + } + uses te-node-connectivity-matrices; + uses te-node-info-attributes; + } + } + // te-node-config-attributes + + grouping te-node-config-attributes-template { + description + "Configuration node attributes for a template in a TE + topology."; + container te-node-attributes { + description + "Contains node attributes in a TE topology."; + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the link."; + } + uses te-node-info-attributes; + } + } + // te-node-config-attributes-template + + grouping te-node-connectivity-matrices { + description + "Connectivity matrix on a TE node."; + container connectivity-matrices { + description + "Contains a connectivity matrix on a TE node."; + leaf number-of-entries { + type uint16; + description + "The number of connectivity matrix entries. + If this number is specified in the configuration request, + the number is the requested number of entries, which may + not all be listed in the list; + if this number is reported in the state data, + the number is the current number of operational entries."; + } + uses te-types:label-set-info; + uses connectivity-matrix-entry-path-attributes; + list connectivity-matrix { + key "id"; + description + "Represents a node's switching limitations, i.e., + limitations in the interconnecting network TE links + across the node."; + reference + "RFC 7579: General Network Element Constraint Encoding + for GMPLS-Controlled Networks"; + leaf id { + type uint32; + description + "Identifies the connectivity matrix entry."; + } + } + // connectivity-matrix + } + // connectivity-matrices + } + // te-node-connectivity-matrices + + grouping te-node-connectivity-matrix-attributes { + description + "Termination point references of a connectivity matrix entry."; + container from { + description + "Reference to a source LTP."; + leaf tp-ref { + type leafref { + path "../../../../../../nt:termination-point/nt:tp-id"; + } + description + "Relative reference to a termination point."; + } + uses te-types:label-set-info; + } + container to { + description + "Reference to a destination LTP."; + leaf tp-ref { + type leafref { + path "../../../../../../nt:termination-point/nt:tp-id"; + } + description + "Relative reference to a termination point."; + } + uses te-types:label-set-info; + } + uses connectivity-matrix-entry-path-attributes; + } + // te-node-connectivity-matrix-attributes + + grouping te-node-info-attributes { + description + "Advertised TE information attributes."; + leaf domain-id { + type uint32; + description + "Identifies the domain to which this node belongs. + This attribute is used to support inter-domain links."; + reference + "RFC 5152: A Per-Domain Path Computation Method for + Establishing Inter-Domain Traffic Engineering (TE) + Label Switched Paths (LSPs) + RFC 5316: ISIS Extensions in Support of Inter-Autonomous + System (AS) MPLS and GMPLS Traffic Engineering + RFC 5392: OSPF Extensions in Support of Inter-Autonomous + System (AS) MPLS and GMPLS Traffic Engineering"; + } + leaf is-abstract { + type empty; + description + "Present if the node is abstract; not present if the node + is actual."; + } + leaf name { + type string; + description + "Node name."; + } + leaf-list signaling-address { + type inet:ip-address; + description + "The node's signaling address."; + } + container underlay-topology { + if-feature "te-topology-hierarchy"; + description + "When an abstract node encapsulates a topology, the + attributes in this container point to said topology."; + uses nw:network-ref; + } + } + // te-node-info-attributes + + grouping te-node-state-derived { + description + "Node state attributes in a TE topology."; + leaf oper-status { + type te-types:te-oper-status; + config false; + description + "The current operational state of the node."; + } + uses geolocation-container; + leaf is-multi-access-dr { + type empty; + config false; + description + "The presence of this attribute indicates that this TE node + is a pseudonode elected as a designated router."; + reference + "RFC 1195: Use of OSI IS-IS for Routing in TCP/IP and Dual + Environments + RFC 3630: Traffic Engineering (TE) Extensions to OSPF + Version 2"; + } + uses information-source-per-node-attributes; + list information-source-entry { + key "information-source information-source-instance"; + config false; + description + "A list of information sources learned, including the source + that is used."; + uses information-source-per-node-attributes; + uses te-node-connectivity-matrices; + uses te-node-info-attributes; + } + } + // te-node-state-derived + + grouping te-node-tunnel-termination-point-config { + description + "Termination capability of a TTP on a TE node."; + uses te-node-tunnel-termination-point-config-attributes; + container local-link-connectivities { + description + "Contains an LLCL for a TTP on a TE node."; + leaf number-of-entries { + type uint16; + description + "The number of LLCL entries. + If this number is specified in the configuration request, + the number is the requested number of entries, which may + not all be listed in the list; + if this number is reported in the state data, + the number is the current number of operational entries."; + } + uses te-types:label-set-info; + uses connectivity-matrix-entry-path-attributes; + } + } + // te-node-tunnel-termination-point-config + + grouping te-node-tunnel-termination-point-config-attributes { + description + "Configuration attributes of a TTP on a TE node."; + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the TTP."; + } + leaf name { + type string; + description + "A descriptive name for the TTP."; + } + leaf switching-capability { + type identityref { + base te-types:switching-capabilities; + } + description + "Switching capability for this interface."; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "Encoding supported by this interface."; + } + leaf-list inter-layer-lock-id { + type uint32; + description + "Inter-layer lock ID, used for path computation in a TE + topology covering multiple layers or multiple regions."; + reference + "RFC 5212: Requirements for GMPLS-Based Multi-Region and + Multi-Layer Networks (MRN/MLN) + RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + } + leaf protection-type { + type identityref { + base te-types:lsp-protection-type; + } + description + "The protection type that this TTP is capable of."; + } + container client-layer-adaptation { + description + "Contains capability information to support a client-layer + adaptation in a multi-layer topology."; + list switching-capability { + key "switching-capability encoding"; + description + "List of supported switching capabilities."; + reference + "RFC 4202: Routing Extensions in Support of + Generalized Multi-Protocol Label Switching (GMPLS) + RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + leaf switching-capability { + type identityref { + base te-types:switching-capabilities; + } + description + "Switching capability for the client-layer adaptation."; + } + leaf encoding { + type identityref { + base te-types:lsp-encoding-types; + } + description + "Encoding supported by the client-layer adaptation."; + } + uses te-types:te-bandwidth; + } + } + } + // te-node-tunnel-termination-point-config-attributes + + grouping te-node-tunnel-termination-point-llc-list { + description + "LLCL of a TTP on a TE node."; + list local-link-connectivity { + key "link-tp-ref"; + description + "The termination capabilities between the TTP and the LTP. + This capability information can be used to compute + the tunnel path. + The Interface Adjustment Capability Descriptors (IACDs) + (defined in RFC 6001) on each LTP can be derived from + this list."; + reference + "RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + leaf link-tp-ref { + type leafref { + path "../../../../../nt:termination-point/nt:tp-id"; + } + description + "LTP."; + } + uses te-types:label-set-info; + uses connectivity-matrix-entry-path-attributes; + } + } + // te-node-tunnel-termination-point-llc-list + + grouping te-path-element { + description + "A group of attributes defining an element in a TE path, + such as a TE node, TE link, TE atomic resource, or label."; + uses te-types:explicit-route-hop; + } + // te-path-element + + grouping te-termination-point-augment { + description + "Augmentation for a TE termination point."; + leaf te-tp-id { + type te-types:te-tp-id; + description + "An identifier that uniquely identifies a TE termination + point."; + } + container te { + must '../te-tp-id'; + presence "TE support"; + description + "Indicates TE support."; + uses te-termination-point-config; + leaf oper-status { + type te-types:te-oper-status; + config false; + description + "The current operational state of the LTP."; + } + uses geolocation-container; + } + } + // te-termination-point-augment + + grouping te-termination-point-config { + description + "TE termination point configuration grouping."; + leaf admin-status { + type te-types:te-admin-status; + description + "The administrative state of the LTP."; + } + leaf name { + type string; + description + "A descriptive name for the LTP."; + } + uses interface-switching-capability-list; + leaf inter-domain-plug-id { + type binary; + description + "A network-wide unique number that identifies on the + network a connection that supports a given inter-domain + TE link. This is a more flexible alternative to specifying + 'remote-te-node-id' and 'remote-te-link-tp-id' on a TE link + when the provider either does not know 'remote-te-node-id' + and 'remote-te-link-tp-id' or needs to give the client the + flexibility to mix and match multiple topologies."; + } + leaf-list inter-layer-lock-id { + type uint32; + description + "Inter-layer lock ID, used for path computation in a TE + topology covering multiple layers or multiple regions."; + reference + "RFC 5212: Requirements for GMPLS-Based Multi-Region and + Multi-Layer Networks (MRN/MLN) + RFC 6001: Generalized MPLS (GMPLS) Protocol Extensions + for Multi-Layer and Multi-Region Networks (MLN/MRN)"; + } + } + // te-termination-point-config + + grouping te-topologies-augment { + description + "Augmentation for TE topologies."; + container te { + presence "TE support"; + description + "Indicates TE support."; + container templates { + description + "Configuration parameters for templates used for a TE + topology."; + list node-template { + if-feature "template"; + key "name"; + leaf name { + type te-types:te-template-name; + description + "The name to identify a TE node template."; + } + description + "The list of TE node templates used to define sharable + and reusable TE node attributes."; + uses template-attributes; + uses te-node-config-attributes-template; + } + // node-template + list link-template { + if-feature "template"; + key "name"; + leaf name { + type te-types:te-template-name; + description + "The name to identify a TE link template."; + } + description + "The list of TE link templates used to define sharable + and reusable TE link attributes."; + uses template-attributes; + uses te-link-config-attributes; + } + // link-template + } + // templates + } + // te + } + // te-topologies-augment + + grouping te-topology-augment { + description + "Augmentation for a TE topology."; + uses te-types:te-topology-identifier; + container te { + must '../te-topology-identifier/provider-id' + + ' and ../te-topology-identifier/client-id' + + ' and ../te-topology-identifier/topology-id'; + presence "TE support"; + description + "Indicates TE support."; + uses te-topology-config; + uses geolocation-container; + } + } + // te-topology-augment + + grouping te-topology-config { + description + "TE topology configuration grouping."; + leaf name { + type string; + description + "Name of the TE topology. This attribute is optional and can + be specified by the operator to describe the TE topology, + which can be useful when 'network-id' (RFC 8345) is not + descriptive and not modifiable because of being generated + by the system."; + reference + "RFC 8345: A YANG Data Model for Network Topologies"; + } + leaf preference { + type uint8 { + range "1..255"; + } + description + "Specifies a preference for this topology. A lower number + indicates a higher preference."; + } + leaf optimization-criterion { + type identityref { + base te-types:objective-function-type; + } + description + "Optimization criterion applied to this topology."; + reference + "RFC 3272: Overview and Principles of Internet Traffic + Engineering"; + } + list nsrlg { + if-feature "nsrlg"; + key "id"; + description + "List of NSRLGs (Non-Shared Risk Link Groups)."; + reference + "RFC 4872: RSVP-TE Extensions in Support of End-to-End + Generalized Multi-Protocol Label Switching (GMPLS) + Recovery"; + leaf id { + type uint32; + description + "Identifies the NSRLG entry."; + } + leaf disjointness { + type te-types:te-path-disjointness; + description + "The type of resource disjointness."; + } + } + // nsrlg + } + // te-topology-config + + grouping template-attributes { + description + "Common attributes for all templates."; + leaf priority { + type uint16; + description + "The preference value for resolving conflicts between + different templates. When two or more templates specify + values for one configuration attribute, the value from the + template with the highest priority is used. + A lower number indicates a higher priority. The highest + priority is 0."; + } + leaf reference-change-policy { + type enumeration { + enum no-action { + description + "When an attribute changes in this template, the + configuration node referring to this template does + not take any action."; + } + enum not-allowed { + description + "When any configuration object has a reference to this + template, changing this template is not allowed."; + } + enum cascade { + description + "When an attribute changes in this template, the + configuration object referring to this template applies + the new attribute value to the corresponding + configuration."; + } + } + description + "This attribute specifies the action taken for a + configuration node that has a reference to this template."; + } + } + // template-attributes + + /* + * Data nodes + */ + + augment "/nw:networks/nw:network/nw:network-types" { + description + "Introduces a new network type for a TE topology."; + container te-topology { + presence "Indicates a TE topology"; + description + "Its presence identifies the TE topology type."; + } + } + + augment "/nw:networks" { + description + "Augmentation parameters for TE topologies."; + uses te-topologies-augment; + } + + augment "/nw:networks/nw:network" { + when 'nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Configuration parameters for a TE topology."; + uses te-topology-augment; + } + + augment "/nw:networks/nw:network/nw:node" { + when '../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Configuration parameters for TE at the node level."; + leaf te-node-id { + type te-types:te-node-id; + description + "The identifier of a node in the TE topology. + A node is specific to a topology to which it belongs."; + } + container te { + must '../te-node-id' { + description + "'te-node-id' is mandatory."; + } + must 'count(../nw:supporting-node)<=1' { + description + "For a node in a TE topology, there cannot be more + than one supporting node. If multiple nodes are + abstracted, the 'underlay-topology' field is used."; + } + presence "TE support"; + description + "Indicates TE support."; + uses te-node-augment; + } + } + + augment "/nw:networks/nw:network/nt:link" { + when '../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Configuration parameters for TE at the link level."; + container te { + must 'count(../nt:supporting-link)<=1' { + description + "For a link in a TE topology, there cannot be more + than one supporting link. If one or more link paths are + abstracted, the underlay is used."; + } + presence "TE support"; + description + "Indicates TE support."; + uses te-link-augment; + } + } + + augment "/nw:networks/nw:network/nw:node/" + + "nt:termination-point" { + when '../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Configuration parameters for TE at the termination point + level."; + uses te-termination-point-augment; + } + + augment "/nw:networks/nw:network/nt:link/te/bundle-stack-level/" + + "bundle/bundled-links/bundled-link" { + when '../../../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Augmentation for a TE bundled link."; + leaf src-tp-ref { + type leafref { + path "../../../../../nw:node[nw:node-id = " + + "current()/../../../../nt:source/" + + "nt:source-node]/" + + "nt:termination-point/nt:tp-id"; + require-instance true; + } + description + "Reference to another TE termination point on the + same source node."; + } + leaf des-tp-ref { + type leafref { + path "../../../../../nw:node[nw:node-id = " + + "current()/../../../../nt:destination/" + + "nt:dest-node]/" + + "nt:termination-point/nt:tp-id"; + require-instance true; + } + description + "Reference to another TE termination point on the + same destination node."; + } + } + + augment "/nw:networks/nw:network/nw:node/te/" + + "information-source-entry/connectivity-matrices/" + + "connectivity-matrix" { + when '../../../../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Augmentation for the TE node connectivity matrix."; + uses te-node-connectivity-matrix-attributes; + } + + augment "/nw:networks/nw:network/nw:node/te/te-node-attributes/" + + "connectivity-matrices/connectivity-matrix" { + when '../../../../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Augmentation for the TE node connectivity matrix."; + uses te-node-connectivity-matrix-attributes; + } + + augment "/nw:networks/nw:network/nw:node/te/" + + "tunnel-termination-point/local-link-connectivities" { + when '../../../../nw:network-types/tet:te-topology' { + description + "Augmentation parameters apply only for networks with a + TE topology type."; + } + description + "Augmentation for TE node TTP LLCs (Local Link + Connectivities)."; + uses te-node-tunnel-termination-point-llc-list; + } +} \ No newline at end of file diff --git a/src/tests/tools/mock_nce_t_ctrl/yang/yang-repo-url.txt b/src/tests/tools/mock_nce_t_ctrl/yang/yang-repo-url.txt new file mode 100644 index 0000000000000000000000000000000000000000..df60dab3b781fe879d9a451582bec0cf7534bb59 --- /dev/null +++ b/src/tests/tools/mock_nce_t_ctrl/yang/yang-repo-url.txt @@ -0,0 +1 @@ +https://github.com/YangModels/yang diff --git a/src/tests/tools/simap_server/.gitlab-ci.yml b/src/tests/tools/simap_server/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..30c79a50addadd5827f0b450cffb418093fdaa4e --- /dev/null +++ b/src/tests/tools/simap_server/.gitlab-ci.yml @@ -0,0 +1,41 @@ +# 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. + +# Build, tag, and push the Docker image to the GitLab Docker registry +build simap_server: + stage: build + before_script: + - docker image prune --force + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + script: + - docker buildx build -t "$CI_REGISTRY_IMAGE/simap-server:test" -f ./src/tests/tools/simap_server/Dockerfile . + - docker push "$CI_REGISTRY_IMAGE/simap-server:test" + after_script: + - docker image prune --force + 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"' + - changes: + - src/common/**/*.py + - proto/*.proto + - src/src/tests/tools/simap_server/**/*.{py,in,yml,yaml,yang,sh,json} + - src/src/tests/tools/simap_server/Dockerfile + - src/src/tests/.gitlab-ci.yml + #- src/device/**/*.{py,in,yml} + #- src/device/Dockerfile + #- src/device/tests/*.py + #- src/qkd_app/**/*.{py,in,yml} + #- src/qkd_app/Dockerfile + #- src/qkd_app/tests/*.py + - .gitlab-ci.yml diff --git a/src/tests/tools/simap_server/Dockerfile b/src/tests/tools/simap_server/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f47de61a8ce25c6f0b530da9e73df661a70bcb68 --- /dev/null +++ b/src/tests/tools/simap_server/Dockerfile @@ -0,0 +1,66 @@ +# 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 git build-essential cmake libpcre2-dev python3-dev python3-cffi && \ + rm -rf /var/lib/apt/lists/* + +# Download, build and install libyang. Note that APT package is outdated +# - Ref: https://github.com/CESNET/libyang +# - Ref: https://github.com/CESNET/libyang-python/ +RUN mkdir -p /var/libyang +RUN git clone https://github.com/CESNET/libyang.git /var/libyang +WORKDIR /var/libyang +RUN git fetch +RUN git checkout v2.1.148 +RUN mkdir -p /var/libyang/build +WORKDIR /var/libyang/build +RUN cmake -D CMAKE_BUILD_TYPE:String="Release" .. +RUN make +RUN make install +RUN ldconfig + +# 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, get specific Python packages +RUN mkdir -p /var/teraflow/simap_server/ +WORKDIR /var/teraflow/simap_server/ +COPY src/common/tools/rest_conf/server/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 +COPY src/common/tools/rest_conf/server/restconf_server/ ./simap_server/ +COPY src/tests/tools/simap_server/yang/*.yang ./yang/ +COPY src/tests/tools/simap_server/startup.json ./startup.json + +# Configure RESTCONF Server +ENV RESTCONF_PREFIX="/restconf" +ENV YANG_SEARCH_PATH="./yang" +ENV STARTUP_FILE="./startup.json" + +# Configure Flask for production +ENV FLASK_ENV="production" + +# Start the service +ENTRYPOINT ["gunicorn", "--workers", "1", "--worker-class", "eventlet", "--bind", "0.0.0.0:8080", "simap_server.app:app"] diff --git a/src/tests/tools/simap_server/README.md b/src/tests/tools/simap_server/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bdea3b5bf1be4b0c9406be0c9d235daf1ea9533c --- /dev/null +++ b/src/tests/tools/simap_server/README.md @@ -0,0 +1,25 @@ +# RESTCONF/SIMAP Server + +This server implements a basic RESTCONF Server that can load, potentially, any YANG data model. +In this case, it is prepared to load a SIMAP Server based on IETF Network Topology + custom SIMAP Telemetry extensions. + + +## Build the RESTCONF/SIMAP Server Docker image +```bash +./build.sh +``` + +## Deploy the RESTCONF/SIMAP Server +```bash +./deploy.sh +``` + +## Run the RESTCONF/SIMAP Client for testing: +```bash +./run_client.sh +``` + +## Destroy the RESTCONF/SIMAP Server +```bash +./destroy.sh +``` diff --git a/src/tests/tools/simap_server/build.sh b/src/tests/tools/simap_server/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..7ec0e0c917cd9870ae4f01fa81519b99a6bfa271 --- /dev/null +++ b/src/tests/tools/simap_server/build.sh @@ -0,0 +1,22 @@ +#!/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)/../../../../ + +# Build image SIMAP Server +docker buildx build -t simap-server:test -f ./src/tests/tools/simap_server/Dockerfile . +#docker tag simap-server:test localhost:32000/tfs/simap-server:test +#docker push localhost:32000/tfs/simap-server:test diff --git a/src/tests/tools/simap_server/deploy.sh b/src/tests/tools/simap_server/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..a30b4bb1b6091bfc854c85080ca77060c288a056 --- /dev/null +++ b/src/tests/tools/simap_server/deploy.sh @@ -0,0 +1,32 @@ +#!/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. + + +# Cleanup +docker rm --force simap-server + + +# Create SIMAP Server +docker run --detach --name simap-server --publish 8080:8080 simap-server:test + + +sleep 2 + + +# Dump SIMAP Server container +docker ps -a + + +echo "Bye!" diff --git a/src/tests/tools/simap_server/destroy.sh b/src/tests/tools/simap_server/destroy.sh new file mode 100755 index 0000000000000000000000000000000000000000..51edb6bca0696ae4a25328d9149cae30add57b33 --- /dev/null +++ b/src/tests/tools/simap_server/destroy.sh @@ -0,0 +1,25 @@ +#!/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. + + +# Cleanup +docker rm --force simap-server + + +# Dump Docker containers +docker ps -a + + +echo "Bye!" diff --git a/src/tests/tools/simap_server/run_client.sh b/src/tests/tools/simap_server/run_client.sh new file mode 100755 index 0000000000000000000000000000000000000000..76aced85547c4fb2d884b69e9e1a45a52cf469b5 --- /dev/null +++ b/src/tests/tools/simap_server/run_client.sh @@ -0,0 +1,19 @@ +#!/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)/../../../ + +python -m tests.tools.simap_server.simap_client diff --git a/src/tests/tools/simap_server/simap_client/SimapClient.py b/src/tests/tools/simap_server/simap_client/SimapClient.py new file mode 100644 index 0000000000000000000000000000000000000000..725b08bd47e0bd127cf0f7c4131cb744313b149d --- /dev/null +++ b/src/tests/tools/simap_server/simap_client/SimapClient.py @@ -0,0 +1,350 @@ +# 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, Optional, Tuple +from common.tools.rest_conf.client.RestConfClient import RestConfClient + + +class TerminationPoint: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}/node={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:termination-point={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str, tp_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tp_id = tp_id + + def create(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network-topology:termination-point'][0] + + def update(self, supporting_termination_point_ids : List[Tuple[str, str, str]] = []) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + tp = {'tp-id': self._tp_id} + stps = [ + {'network-ref': snet_id, 'node-ref': snode_id, 'tp-ref': stp_id} + for snet_id,snode_id,stp_id in supporting_termination_point_ids + ] + if len(stps) > 0: tp['supporting-termination-point'] = stps + node = {'node-id': self._node_id, 'ietf-network-topology:termination-point': [tp]} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = TerminationPoint.ENDPOINT_ID.format(self._network_id, self._node_id, self._tp_id) + self._restconf_client.delete(endpoint) + + +class NodeTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/node={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + + def create( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, cpu_utilization : float, related_service_ids : List[str] = [] + ) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + telemetry = { + 'cpu-utilization': '{:.2f}'.format(cpu_utilization), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + node = {'node-id': self._node_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = NodeTelemetry.ENDPOINT.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class Node: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/node={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, node_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._node_id = node_id + self._tps : Dict[str, TerminationPoint] = dict() + self._telemetry : Optional[NodeTelemetry] = None + + @property + def telemetry(self) -> NodeTelemetry: + if self._telemetry is None: + self._telemetry = NodeTelemetry(self._restconf_client, self._network_id, self._node_id) + return self._telemetry + + def termination_points(self) -> List[Dict]: + tps : Dict = self._restconf_client.get(TerminationPoint.ENDPOINT_NO_ID) + return tps['ietf-network-topology:termination-point'].get('termination-point', list()) + + def termination_point(self, tp_id : str) -> TerminationPoint: + _tp = self._tps.get(tp_id) + if _tp is not None: return _tp + _tp = TerminationPoint(self._restconf_client, self._network_id, self._node_id, tp_id) + return self._tps.setdefault(tp_id, _tp) + + def create( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node : Dict = self._restconf_client.get(endpoint) + return node['ietf-network:node'][0] + + def update( + self, termination_point_ids : List[str] = [], + supporting_node_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + node = {'node-id': self._node_id} + tps = [{'tp-id': tp_id} for tp_id in termination_point_ids] + if len(tps) > 0: node['ietf-network-topology:termination-point'] = tps + sns = [{'network-ref': snet_id, 'node-ref': snode_id} for snet_id,snode_id in supporting_node_ids] + if len(sns) > 0: node['supporting-node'] = sns + network = {'network-id': self._network_id, 'node': [node]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Node.ENDPOINT_ID.format(self._network_id, self._node_id) + self._restconf_client.delete(endpoint) + + +class LinkTelemetry: + ENDPOINT = '/ietf-network:networks/network={:s}/ietf-network-topology:link={:s}/simap-telemetry:simap-telemetry' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + + def create( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry : Dict = self._restconf_client.get(endpoint) + return telemetry + + def update( + self, bandwidth_utilization : float, latency : float, + related_service_ids : List[str] = [] + ) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + telemetry = { + 'bandwidth-utilization': '{:.2f}'.format(bandwidth_utilization), + 'latency' : '{:.3f}'.format(latency), + } + if len(related_service_ids) > 0: telemetry['related-service-ids'] = related_service_ids + link = {'link-id': self._link_id, 'simap-telemetry:simap-telemetry': telemetry} + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = LinkTelemetry.ENDPOINT.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Link: + ENDPOINT_NO_ID = '/ietf-network:networks/network={:s}' + ENDPOINT_ID = ENDPOINT_NO_ID + '/ietf-network-topology:link={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str, link_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._link_id = link_id + self._telemetry : Optional[LinkTelemetry] = None + + @property + def telemetry(self) -> LinkTelemetry: + if self._telemetry is None: + self._telemetry = LinkTelemetry(self._restconf_client, self._network_id, self._link_id) + return self._telemetry + + def create( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link : Dict = self._restconf_client.get(endpoint) + return link['ietf-network-topology:link'][0] + + def update( + self, src_node_id : str, src_tp_id : str, dst_node_id : str, dst_tp_id : str, + supporting_link_ids : List[Tuple[str, str]] = [] + ) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + link = { + 'link-id' : self._link_id, + 'source' : {'source-node': src_node_id, 'source-tp': src_tp_id}, + 'destination': {'dest-node' : dst_node_id, 'dest-tp' : dst_tp_id}, + } + sls = [{'network-ref': snet_id, 'link-ref': slink_id} for snet_id,slink_id in supporting_link_ids] + if len(sls) > 0: link['supporting-link'] = sls + network = {'network-id': self._network_id, 'ietf-network-topology:link': [link]} + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Link.ENDPOINT_ID.format(self._network_id, self._link_id) + self._restconf_client.delete(endpoint) + + +class Network: + ENDPOINT_NO_ID = '/ietf-network:networks' + ENDPOINT_ID = ENDPOINT_NO_ID + '/network={:s}' + + def __init__(self, restconf_client : RestConfClient, network_id : str): + self._restconf_client = restconf_client + self._network_id = network_id + self._nodes : Dict[str, Node] = dict() + self._links : Dict[str, Link] = dict() + + def nodes(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Node.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('node', list()) + + def links(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Link.ENDPOINT_NO_ID.format(self._network_id)) + return reply['ietf-network:network'][0].get('ietf-network-topology:link', list()) + + def node(self, node_id : str) -> Node: + _node = self._nodes.get(node_id) + if _node is not None: return _node + _node = Node(self._restconf_client, self._network_id, node_id) + return self._nodes.setdefault(node_id, _node) + + def link(self, link_id : str) -> Link: + _link = self._links.get(link_id) + if _link is not None: return _link + _link = Link(self._restconf_client, self._network_id, link_id) + return self._links.setdefault(link_id, _link) + + def create(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.post(endpoint, payload) + + def get(self) -> Dict: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + networks : Dict = self._restconf_client.get(endpoint) + return networks['ietf-network:network'][0] + + def update(self, supporting_network_ids : List[str] = []) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + network = {'network-id': self._network_id} + sns = [{'network-ref': sn_id} for sn_id in supporting_network_ids] + if len(sns) > 0: network['supporting-network'] = sns + payload = {'ietf-network:networks': {'network': [network]}} + self._restconf_client.patch(endpoint, payload) + + def delete(self) -> None: + endpoint = Network.ENDPOINT_ID.format(self._network_id) + self._restconf_client.delete(endpoint) + + +class SimapClient: + def __init__(self, restconf_client : RestConfClient) -> None: + self._restconf_client = restconf_client + self._networks : Dict[str, Network] = dict() + + def networks(self) -> List[Dict]: + reply : Dict = self._restconf_client.get(Network.ENDPOINT_NO_ID) + return reply['ietf-network:networks'].get('network', list()) + + def network(self, network_id : str) -> Network: + _network = self._networks.get(network_id) + if _network is not None: return _network + _network = Network(self._restconf_client, network_id) + return self._networks.setdefault(network_id, _network) diff --git a/src/tests/tools/simap_server/simap_client/Tools.py b/src/tests/tools/simap_server/simap_client/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..b49110f82eea24c76dcb9a5bebe99575078925fa --- /dev/null +++ b/src/tests/tools/simap_server/simap_client/Tools.py @@ -0,0 +1,109 @@ +# 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 .SimapClient import SimapClient + +def create_simap_te(simap_client : SimapClient) -> None: + te_topo = simap_client.network('te') + te_topo.create() + + te_topo.node('ONT1').create(termination_point_ids=['200', '500']) + te_topo.node('ONT2').create(termination_point_ids=['200', '500']) + te_topo.node('OLT' ).create(termination_point_ids=['200', '201', '500', '501']) + te_topo.link('L1').create('ONT1', '500', 'OLT', '200') + te_topo.link('L2').create('ONT2', '500', 'OLT', '201') + + te_topo.node('PE1').create(termination_point_ids=['200', '500', '501']) + te_topo.node('P1' ).create(termination_point_ids=['500', '501']) + te_topo.node('P2' ).create(termination_point_ids=['500', '501']) + te_topo.node('PE2').create(termination_point_ids=['200', '500', '501']) + te_topo.link('L5' ).create('PE1', '500', 'P1', '500') + te_topo.link('L6' ).create('PE1', '501', 'P2', '500') + te_topo.link('L9' ).create('P1', '501', 'PE2', '500') + te_topo.link('L10').create('P2', '501', 'PE2', '501') + + te_topo.node('OA' ).create(termination_point_ids=['200', '500', '501']) + te_topo.node('OTN1').create(termination_point_ids=['500', '501']) + te_topo.node('OTN2').create(termination_point_ids=['500', '501']) + te_topo.node('OE' ).create(termination_point_ids=['200', '500', '501']) + te_topo.link('L7' ).create('OA', '500', 'OTN1', '500') + te_topo.link('L8' ).create('OA', '501', 'OTN2', '500') + te_topo.link('L11' ).create('OTN1', '501', 'OE', '500') + te_topo.link('L12' ).create('OTN2', '501', 'OE', '501') + + te_topo.link('L3').create('OLT', '500', 'PE1', '200') + te_topo.link('L4').create('OLT', '501', 'OA', '200') + + te_topo.node('POP1').create(termination_point_ids=['200', '201', '500']) + te_topo.link('L13').create('PE2', '200', 'POP1', '500') + + te_topo.node('POP2').create(termination_point_ids=['200', '201', '500']) + te_topo.link('L14').create('OE', '200', 'POP2', '500') + + +def create_simap_trans(simap_client : SimapClient) -> None: + simap_trans = simap_client.network('simap-trans') + simap_trans.create(supporting_network_ids=['te']) + + site_1 = simap_trans.node('site1') + site_1.create(supporting_node_ids=[('te', 'PE1')]) + site_1.termination_point('200').create(supporting_termination_point_ids=[('te', 'PE1', '200')]) + site_1.termination_point('500').create(supporting_termination_point_ids=[('te', 'PE1', '500')]) + site_1.termination_point('501').create(supporting_termination_point_ids=[('te', 'PE1', '501')]) + + site_2 = simap_trans.node('site2') + site_2.create(supporting_node_ids=[('te', 'PE2')]) + site_2.termination_point('200').create(supporting_termination_point_ids=[('te', 'PE2', '200')]) + site_2.termination_point('500').create(supporting_termination_point_ids=[('te', 'PE2', '500')]) + site_2.termination_point('501').create(supporting_termination_point_ids=[('te', 'PE2', '501')]) + + simap_trans.link('Trans-L1').create('site1', '500', 'site2', '500', supporting_link_ids=[('te', 'L5'), ('te', 'L9')]) + + +def create_simap_aggnet(simap_client : SimapClient) -> None: + simap_aggnet = simap_client.network('simap-aggnet') + simap_aggnet.create(supporting_network_ids=['te', 'simap-trans']) + + sdp_1 = simap_aggnet.node('sdp1') + sdp_1.create(supporting_node_ids=[('te', 'OLT')]) + sdp_1.termination_point('200').create(supporting_termination_point_ids=[('te', 'OLT', '200')]) + sdp_1.termination_point('201').create(supporting_termination_point_ids=[('te', 'OLT', '201')]) + sdp_1.termination_point('500').create(supporting_termination_point_ids=[('te', 'OLT', '500')]) + sdp_1.termination_point('501').create(supporting_termination_point_ids=[('te', 'OLT', '501')]) + + sdp_2 = simap_aggnet.node('sdp2') + sdp_2.create(supporting_node_ids=[('te', 'POP1')]) + sdp_2.termination_point('200').create(supporting_termination_point_ids=[('te', 'POP1', '200')]) + sdp_2.termination_point('201').create(supporting_termination_point_ids=[('te', 'POP1', '201')]) + sdp_2.termination_point('500').create(supporting_termination_point_ids=[('te', 'POP1', '500')]) + + simap_aggnet.link('AggNet-L1').create('sdp1', '500', 'sdp2', '500', supporting_link_ids=[('te', 'L3'), ('simap-trans', 'Trans-L1'), ('te', 'L13')]) + + +def create_simap_e2enet(simap_client : SimapClient) -> None: + simap_e2e = simap_client.network('simap-e2e') + simap_e2e.create(supporting_network_ids=['te', 'simap-trans']) + + sdp_1 = simap_e2e.node('sdp1') + sdp_1.create(supporting_node_ids=[('te', 'ONT1')]) + sdp_1.termination_point('200').create(supporting_termination_point_ids=[('te', 'ONT1', '200')]) + sdp_1.termination_point('500').create(supporting_termination_point_ids=[('te', 'ONT1', '500')]) + + sdp_2 = simap_e2e.node('sdp2') + sdp_2.create(supporting_node_ids=[('te', 'POP1')]) + sdp_2.termination_point('200').create(supporting_termination_point_ids=[('te', 'POP1', '200')]) + sdp_2.termination_point('201').create(supporting_termination_point_ids=[('te', 'POP1', '201')]) + sdp_2.termination_point('500').create(supporting_termination_point_ids=[('te', 'POP1', '500')]) + + simap_e2e.link('E2E-L1').create('sdp1', '500', 'sdp2', '500', supporting_link_ids=[('te', 'L1'), ('simap-aggnet', 'AggNet-L1')]) diff --git a/src/tests/tools/simap_server/simap_client/__init__.py b/src/tests/tools/simap_server/simap_client/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccc21c7db78aac26daa1f8c5ff8e1ffd3f35460 --- /dev/null +++ b/src/tests/tools/simap_server/simap_client/__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/simap_server/simap_client/__main__.py b/src/tests/tools/simap_server/simap_client/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..67803b0922f77d1f02a3cf4b5170ca428767d4fc --- /dev/null +++ b/src/tests/tools/simap_server/simap_client/__main__.py @@ -0,0 +1,60 @@ +# 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, time +from common.tools.rest_conf.client.RestConfClient import RestConfClient +from .SimapClient import SimapClient +from .Tools import create_simap_aggnet, create_simap_e2enet, create_simap_te, create_simap_trans + + +logging.basicConfig(level=logging.INFO) +logging.getLogger('RestConfClient').setLevel(logging.WARN) +LOGGER = logging.getLogger(__name__) + + +def main() -> None: + restconf_client = RestConfClient( + '127.0.0.1', port=8080, + logger=logging.getLogger('RestConfClient') + ) + simap_client = SimapClient(restconf_client) + + create_simap_te(simap_client) + create_simap_trans(simap_client) + create_simap_aggnet(simap_client) + create_simap_e2enet(simap_client) + + print('networks=', json.dumps(simap_client.networks())) + + trans_link = simap_client.network('simap-trans').link('Trans-L1') + trans_node_site1 = simap_client.network('simap-trans').node('site1') + trans_node_site2 = simap_client.network('simap-trans').node('site2') + + related_service_ids = ['trans-svc1', 'trans-svc2', 'trans-svc3'] + + for i in range(1000): + trans_link.telemetry.update(float(i), float(i), related_service_ids=related_service_ids) + trans_node_site1.telemetry.update(float(i), related_service_ids=related_service_ids) + trans_node_site2.telemetry.update(float(i), related_service_ids=related_service_ids) + + print('trans link telemetry =', json.dumps(trans_link.telemetry.get())) + print('trans site1 telemetry =', json.dumps(trans_node_site1.telemetry.get())) + print('trans site2 telemetry =', json.dumps(trans_node_site2.telemetry.get())) + + time.sleep(10) + + +if __name__ == '__main__': + main() diff --git a/src/tests/tools/simap_server/startup.json b/src/tests/tools/simap_server/startup.json new file mode 100644 index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93 --- /dev/null +++ b/src/tests/tools/simap_server/startup.json @@ -0,0 +1 @@ +{} diff --git a/src/tests/tools/simap_server/yang/ietf-inet-types.yang b/src/tests/tools/simap_server/yang/ietf-inet-types.yang new file mode 100644 index 0000000000000000000000000000000000000000..eacefb6363de1beb543567a0fa705571b7dc57a2 --- /dev/null +++ b/src/tests/tools/simap_server/yang/ietf-inet-types.yang @@ -0,0 +1,458 @@ +module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2013 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 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - ip-address-no-zone + - ipv4-address-no-zone + - 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 2460."; + } + } + 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 2460: 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 2460: 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. A current list of + all assignments is available from . + + 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 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** 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 inet:ipv4-address; + type inet: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])' + + '(%[\p{N}\p{L}]+)?'; + } + 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. + + 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])))' + + '(%[\p{N}\p{L}]+)?'; + 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. + + 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 inet:ipv4-address-no-zone; + type inet: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 inet:ipv4-address { + pattern '[0-9\.]*'; + } + description + "An IPv4 address without a zone index. This type, derived from + ipv4-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + } + + typedef ipv6-address-no-zone { + type inet:ipv6-address { + pattern '[0-9a-fA-F:\.]*'; + } + description + "An IPv6 address without a zone index. This type, derived from + ipv6-address, may be used in situations where the zone is + known from the context and hence 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-prefix { + type union { + type inet:ipv4-prefix; + type inet: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 address 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."; + } + + 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 address 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 IPv6 address should have all bits that do not belong + to the prefix set to zero. + + 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."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + 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]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + 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. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + 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 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + 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, which are normalized to uppercase as described in + Section 6.2.2.1. + + 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)"; + } + +} diff --git a/src/tests/tools/simap_server/yang/ietf-network-topology.yang b/src/tests/tools/simap_server/yang/ietf-network-topology.yang new file mode 100644 index 0000000000000000000000000000000000000000..3b1114aa017c4d283aeaf22107c7d6fe152898ec --- /dev/null +++ b/src/tests/tools/simap_server/yang/ietf-network-topology.yang @@ -0,0 +1,291 @@ +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: + WG List: + + Editor: Alexander Clemm + + + Editor: Jan Medved + + + Editor: Robert Varga + + + Editor: Nitin Bahadur + + + Editor: Hariharan Ananthakrishnan + + + Editor: Xufeng Liu + "; + + 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/tests/tools/simap_server/yang/ietf-network.yang b/src/tests/tools/simap_server/yang/ietf-network.yang new file mode 100644 index 0000000000000000000000000000000000000000..960973401ccc63ab65617235ae5df920a760ba2b --- /dev/null +++ b/src/tests/tools/simap_server/yang/ietf-network.yang @@ -0,0 +1,193 @@ +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: + WG List: + + Editor: Alexander Clemm + + + Editor: Jan Medved + + + Editor: Robert Varga + + + Editor: Nitin Bahadur + + + Editor: Hariharan Ananthakrishnan + + + Editor: Xufeng Liu + "; + + 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/tests/tools/simap_server/yang/simap-telemetry.yang b/src/tests/tools/simap_server/yang/simap-telemetry.yang new file mode 100644 index 0000000000000000000000000000000000000000..7ce09b13d7d5ac3bd8a9ddfa76aa4dc4f10ef49b --- /dev/null +++ b/src/tests/tools/simap_server/yang/simap-telemetry.yang @@ -0,0 +1,81 @@ +module simap-telemetry { + yang-version 1.1; + namespace "urn:simap:telemetry"; + prefix simap; + + import ietf-network { + prefix nw; + reference "RFC 8345"; + } + import ietf-network-topology { + prefix nt; + reference "RFC 8345"; + } + + organization + "SIMAP (example)"; + contact + "ops@simap.example"; + description + "Augments RFC 8345 network and topology objects with simple telemetry."; + + revision "2025-07-24" { + description "Initial revision."; + } + + /* --- Local typedefs --- */ + typedef percent { + type decimal64 { + fraction-digits 2; + range "0 .. 100"; + } + units "percent"; + description "0–100 percent value."; + } + + typedef milliseconds { + type decimal64 { + fraction-digits 3; + } + units "milliseconds"; + description "Latency expressed in milliseconds."; + } + + /* --- Augment link --- */ + augment "/nw:networks/nw:network/nt:link" { + description + "Add telemetry to links."; + container simap-telemetry { + description "SIMAP link telemetry."; + leaf bandwidth-utilization { + type percent; + description "Current bandwidth utilization."; + } + leaf latency { + type milliseconds; + description "One-way latency over the link."; + } + leaf-list related-service-ids { + type string; + description "Service identifiers associated with this link."; + } + } + } + + /* --- Augment node --- */ + augment "/nw:networks/nw:network/nw:node" { + description + "Add telemetry to nodes."; + container simap-telemetry { + description "SIMAP node telemetry."; + leaf cpu-utilization { + type percent; + description "Node CPU utilization."; + } + leaf-list related-service-ids { + type string; + description "Service identifiers associated with this node."; + } + } + } +} diff --git a/src/tests/tools/simap_server/yang/simap.txt b/src/tests/tools/simap_server/yang/simap.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca35944deaa9757f38ca721c52833662afe3ed44 --- /dev/null +++ b/src/tests/tools/simap_server/yang/simap.txt @@ -0,0 +1,10 @@ +module: simap-telemetry-augmentation + augment /nw:networks/nw:network/nw:link: + +--rw simap-telemetry + +--rw bandwidth-utilization? decimal64 (percent) + +--rw latency? decimal64 (milliseconds) + +--rw related-service-ids* string + augment /nw:networks/nw:network/nw:node: + +--rw simap-telemetry + +--rw cpu-utilization? decimal64 (percent) + +--rw related-service-ids* string \ No newline at end of file diff --git a/src/tests/tools/traffic_changer/Dockerfile b/src/tests/tools/traffic_changer/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9b590eb5e654e286cd37a9b98af0b45b2a4b5b38 --- /dev/null +++ b/src/tests/tools/traffic_changer/Dockerfile @@ -0,0 +1,46 @@ +# 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 --no-install-recommends build-essential && \ +# 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 specific Python packages +RUN mkdir -p /var/teraflow/ +WORKDIR /var/teraflow/ +COPY src/tests/tools/traffic_changer/requirements.in ./requirements.in +RUN pip-compile --quiet --output-file=requirements.txt requirements.in +RUN python3 -m pip install -r requirements.txt + +# Get component files +COPY src/tests/tools/traffic_changer/templates ./traffic_changer/templates +COPY src/tests/tools/traffic_changer/*.py ./traffic_changer/ + +# Configure Flask for production +ENV FLASK_ENV="production" + +# Start the service +ENTRYPOINT ["gunicorn", "--workers", "1", "--worker-class", "eventlet", "--bind", "0.0.0.0:8080", "traffic_changer.app:app"] diff --git a/src/tests/tools/traffic_changer/app.py b/src/tests/tools/traffic_changer/app.py new file mode 100644 index 0000000000000000000000000000000000000000..d3086ed224df7e37f7847afbc45779166dc19dbc --- /dev/null +++ b/src/tests/tools/traffic_changer/app.py @@ -0,0 +1,137 @@ +#!/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. + + +import enum, json, logging, requests, secrets, time +from flask import Flask, render_template, request, flash + + +logging.basicConfig( + level=logging.INFO, + format='[Worker-%(process)d][%(asctime)s] %(levelname)s:%(name)s:%(message)s', +) + +NETWORK_ID = 'admin' + +class Controller(enum.Enum): + TFS_E2E = 'TFS-E2E' + TFS_AGG = 'TFS-AGG' + TFS_IP = 'TFS-IP' + +CONTROLLER_TO_ADDRESS_PORT = { + Controller.TFS_E2E : ('10.254.0.10', 80), + Controller.TFS_AGG : ('10.254.0.11', 80), + Controller.TFS_IP : ('10.254.0.12', 80), +} + +LINK_ID_TO_CONTROLLER = { + 'L1' : Controller.TFS_E2E, + 'L2' : Controller.TFS_E2E, + 'L3' : Controller.TFS_E2E, + 'L4' : Controller.TFS_E2E, + 'L5' : Controller.TFS_IP, + 'L6' : Controller.TFS_IP, + 'L7ab' : Controller.TFS_AGG, + 'L7ba' : Controller.TFS_AGG, + 'L8ab' : Controller.TFS_AGG, + 'L8ba' : Controller.TFS_AGG, + 'L9' : Controller.TFS_IP, + 'L10' : Controller.TFS_IP, + 'L11ab' : Controller.TFS_AGG, + 'L11ba' : Controller.TFS_AGG, + 'L12ab' : Controller.TFS_AGG, + 'L12ba' : Controller.TFS_AGG, + 'L13' : Controller.TFS_AGG, + 'L14' : Controller.TFS_AGG, +} + +TARGET_URL = 'http://{:s}:{:d}/restconf/operations/affect_sample_synthesizer' + + +LOGGER = logging.getLogger(__name__) + +def log_request(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 + + +app = Flask(__name__) +app.config['SECRET_KEY'] = secrets.token_hex(64) +app.after_request(log_request) + + +@app.route('/', methods=['GET', 'POST']) +def index(): + if request.method == 'GET': + return render_template('affect_form.html', payload=None, response=None) + + link_id = request.form.get('link_id', '').strip() + bandwidth_factor = request.form.get('bandwidth_factor', '').strip() + latency_factor = request.form.get('latency_factor', '').strip() + + controller = LINK_ID_TO_CONTROLLER.get(link_id) + if controller is None: + MSG = 'link_id({:s}) not allowed. Must be one of {:s}' + allowed_link_ids = set(LINK_ID_TO_CONTROLLER.keys()) + flash(MSG.format(str(link_id), str(allowed_link_ids)), category='error') + return render_template('affect_form.html', payload=None, response=None) + + try: + bandwidth_factor = float(bandwidth_factor) + if bandwidth_factor < 0.01 or bandwidth_factor > 100.0: raise ValueError() + except Exception: + MSG = 'bandwidth_factor({:s}) must be a float in range [0.01..100.0]' + flash(MSG.format(str(bandwidth_factor)), category='error') + return render_template('affect_form.html', payload=None, response=None) + + try: + latency_factor = float(latency_factor) + if latency_factor < 0.01 or latency_factor > 100.0: raise ValueError() + except Exception: + MSG = 'latency_factor({:s}) must be a float in range [0.01..100.0]' + flash(MSG.format(str(latency_factor)), category='error') + return render_template('affect_form.html', payload=None, response=None) + + payload = { + 'network_id' : NETWORK_ID, + 'link_id' : link_id, + 'bandwidth_factor': bandwidth_factor, + 'latency_factor' : latency_factor, + } + + controller_address, controller_port = CONTROLLER_TO_ADDRESS_PORT.get(controller) + target_url = TARGET_URL.format(controller_address, controller_port) + + try: + resp = requests.post(target_url, json=payload, timeout=10) + try: + resp_content = resp.json() + except Exception: + resp_content = resp.text + + response = {'status_code': resp.status_code, 'body': resp_content, 'ok': resp.ok} + except Exception as e: + flash('Error sending request: {:s}'.format(str(e)), category='error') + response = None + + str_payload = json.dumps(payload) + return render_template('affect_form.html', payload=str_payload, response=response) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8080, debug=True, use_reloader=False) diff --git a/src/tests/tools/traffic_changer/build.sh b/src/tests/tools/traffic_changer/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..ba7df243302bceccf2c8228235c528e28a9dd82b --- /dev/null +++ b/src/tests/tools/traffic_changer/build.sh @@ -0,0 +1,22 @@ +#!/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)/../../../../ + +# Build image for Traffic Changer +docker buildx build -t traffic-changer:test -f ./src/tests/tools/traffic_changer/Dockerfile . +#docker tag traffic-changer:test localhost:32000/tfs/traffic-changer:test +#docker push localhost:32000/tfs/traffic-changer:test diff --git a/src/tests/tools/traffic_changer/deploy.sh b/src/tests/tools/traffic_changer/deploy.sh new file mode 100644 index 0000000000000000000000000000000000000000..8cc4ba3bd5e7e493b1827ac90ba3903d0375402a --- /dev/null +++ b/src/tests/tools/traffic_changer/deploy.sh @@ -0,0 +1,28 @@ +#!/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. + + +# Cleanup +docker rm --force traffic-changer || true + +# Create Traffic Changer +docker run --detach --name traffic-changer --publish 8080:8080 traffic-changer:test + +sleep 2 + +# Dump containers +docker ps -a + +echo "Bye!" diff --git a/src/tests/tools/traffic_changer/destroy.sh b/src/tests/tools/traffic_changer/destroy.sh new file mode 100644 index 0000000000000000000000000000000000000000..2f6ac62aba52872df8bca4b165b76d9e2af8b4d2 --- /dev/null +++ b/src/tests/tools/traffic_changer/destroy.sh @@ -0,0 +1,23 @@ +#!/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. + + +# Cleanup +docker rm --force traffic-changer || true + +# Dump containers +docker ps -a + +echo "Bye!" diff --git a/src/tests/tools/traffic_changer/requirements.in b/src/tests/tools/traffic_changer/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..c11cba15ea68d97d0d5797cf95792ef00ceeac28 --- /dev/null +++ b/src/tests/tools/traffic_changer/requirements.in @@ -0,0 +1,20 @@ +# 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. + + +eventlet==0.39.0 +Flask==2.1.3 +gunicorn==23.0.0 +requests==2.27.1 +werkzeug==2.3.7 diff --git a/src/tests/tools/traffic_changer/templates/affect_form.html b/src/tests/tools/traffic_changer/templates/affect_form.html new file mode 100644 index 0000000000000000000000000000000000000000..cfdb0296d637d357e3baea5101c9e0ea241cdbab --- /dev/null +++ b/src/tests/tools/traffic_changer/templates/affect_form.html @@ -0,0 +1,57 @@ + + + + + + Affect Sample Synthesizer + + + +

Affect Sample Synthesizer

+ + {% with messages = get_flashed_messages() %} + {% if messages %} +
+ {% for m in messages %} +
{{ m }}
+ {% endfor %} +
+ {% endif %} + {% endwith %} + +
+ + + + + + + + + + +
+ + {% if payload %} +

Payload

+
{{ payload }}
+ {% endif %} + + {% if response %} +

Response

+
Status: {{ response.status_code }} (ok: {{ response.ok }})
+

Body

+
{{ response.body | tojson(indent=2) }}
+ {% endif %} + + + + diff --git a/src/webui/service/static/topology_icons/client.png b/src/webui/service/static/topology_icons/client.png deleted file mode 100644 index e017c59aea31de5a9112ec89e5cb4d6cbc009ba6..0000000000000000000000000000000000000000 Binary files a/src/webui/service/static/topology_icons/client.png and /dev/null differ diff --git a/src/webui/service/static/topology_icons/datacenter.png b/src/webui/service/static/topology_icons/datacenter.png deleted file mode 100644 index 33818cf87e0f47fb6fd45b45c46f368f62ab78d2..0000000000000000000000000000000000000000 Binary files a/src/webui/service/static/topology_icons/datacenter.png and /dev/null differ diff --git a/src/webui/service/static/topology_icons/emu-computer.png b/src/webui/service/static/topology_icons/emu-computer.png new file mode 100644 index 0000000000000000000000000000000000000000..6f6b9475a73b0ec6b33cbdc3b24dd2234b0dcde2 Binary files /dev/null and b/src/webui/service/static/topology_icons/emu-computer.png differ diff --git a/src/webui/service/static/topology_icons/emu-virtual-machine.png b/src/webui/service/static/topology_icons/emu-virtual-machine.png new file mode 100644 index 0000000000000000000000000000000000000000..4101ab759e8484cd0641548002027a72af66a221 Binary files /dev/null and b/src/webui/service/static/topology_icons/emu-virtual-machine.png differ diff --git a/src/webui/service/static/topology_icons/nce-old.png b/src/webui/service/static/topology_icons/nce-old.png new file mode 100644 index 0000000000000000000000000000000000000000..0c9af8f37fbb7249fbe570a920e0bcd281582655 Binary files /dev/null and b/src/webui/service/static/topology_icons/nce-old.png differ diff --git a/src/webui/service/static/topology_icons/nce.png b/src/webui/service/static/topology_icons/nce.png index 0c9af8f37fbb7249fbe570a920e0bcd281582655..6d7557b2ea2f1ebde2f28491828e329344ccd052 100644 Binary files a/src/webui/service/static/topology_icons/nce.png and b/src/webui/service/static/topology_icons/nce.png differ diff --git a/src/webui/service/static/topology_icons/optical-fgotn.png b/src/webui/service/static/topology_icons/optical-fgotn.png new file mode 100644 index 0000000000000000000000000000000000000000..bec347ac1351ff28e00445cafef6ce5d880bcca9 Binary files /dev/null and b/src/webui/service/static/topology_icons/optical-fgotn.png differ diff --git a/src/webui/service/static/topology_icons/optical-olt.png b/src/webui/service/static/topology_icons/optical-olt.png new file mode 100644 index 0000000000000000000000000000000000000000..73aae20ffe84432eee3fea7b5fde69cce6c97cce Binary files /dev/null and b/src/webui/service/static/topology_icons/optical-olt.png differ diff --git a/src/webui/service/static/topology_icons/optical-ont.png b/src/webui/service/static/topology_icons/optical-ont.png new file mode 100644 index 0000000000000000000000000000000000000000..7b9aced95ed1790ca3134f4c9287298a1185a360 Binary files /dev/null and b/src/webui/service/static/topology_icons/optical-ont.png differ diff --git a/src/webui/service/static/topology_icons/packet-pop.png b/src/webui/service/static/topology_icons/packet-pop.png new file mode 100644 index 0000000000000000000000000000000000000000..e891a083e55f8ac7fcbd35bc5263de7e8894e1b3 Binary files /dev/null and b/src/webui/service/static/topology_icons/packet-pop.png differ diff --git a/src/webui/service/templates/js/topology.js b/src/webui/service/templates/js/topology.js index 13fdc7316342b6d013bb4aa3dde24966acd3e19e..dcbbc51be4fe53f000206401f4c77ef18b53b9c4 100644 --- a/src/webui/service/templates/js/topology.js +++ b/src/webui/service/templates/js/topology.js @@ -51,7 +51,7 @@ const svg = d3.select('#topology') ; // svg objects -var link, node, optical_link; +var link, node, optical_link, labels; // values for all forces forceProperties = { @@ -115,7 +115,18 @@ d3.json("{{ url_for('main.topology') }}", function(data) { .call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended)); // node tooltip - node.append("title").text(function(n) { return n.name; }); + //node.append("title").text(function(n) { return n.name; }); + // persistent node labels + labels = svg.append("g").attr("class", "labels") + .selectAll("text") + .data(data.devices) + .enter() + .append("text") + .text(function(d) { return d.name; }) + .attr("font-family", "sans-serif") + .attr("font-size", 12) + .attr("text-anchor", "middle") + .attr("pointer-events", "none"); // link tooltip link.append("title").text(function(l) { return l.name; }); // optical link tooltip @@ -183,6 +194,11 @@ function ticked() { node .attr('x', function(d) { return d.x-icon_width/2; }) .attr('y', function(d) { return d.y-icon_height/2; }); + if (labels) { + labels + .attr('x', function(d) { return d.x; }) + .attr('y', function(d) { return d.y + icon_height/2 + 12; }); + } } /******************** UI EVENTS ********************/ diff --git a/src/webui/service/templates/link/home.html b/src/webui/service/templates/link/home.html index d8e2522ae02ac1a8ca03baaf325a4607609ab2c8..acc7091d28bfb8e0543716c61fe82aa48804c794 100644 --- a/src/webui/service/templates/link/home.html +++ b/src/webui/service/templates/link/home.html @@ -46,6 +46,7 @@ Name Type Endpoints + Attributes @@ -79,6 +80,15 @@ {% endfor %} + +
    + {% for field_descriptor, field_value in link.attributes.ListFields() %} +
  • + {{ field_descriptor.name }} = {{ field_value }} +
  • + {% endfor %} +
+ diff --git a/src/ztp_server/service/rest_server/RestServer.py b/src/ztp_server/service/rest_server/RestServer.py index e1d50c9eb95d50e538408182038f921e108d3a13..9691dc8617efc1981e8eacc09c66e7530753db34 100755 --- a/src/ztp_server/service/rest_server/RestServer.py +++ b/src/ztp_server/service/rest_server/RestServer.py @@ -14,10 +14,10 @@ from common.Constants import ServiceNameEnum from common.Settings import get_service_baseurl_http, get_service_port_http -from common.tools.service.GenericRestServer import GenericRestServer +from common.tools.rest_api.server.GenericRestServer import GenericRestServer class RestServer(GenericRestServer): def __init__(self, cls_name: str = __name__) -> None: bind_port = get_service_port_http(ServiceNameEnum.ZTP_SERVER) base_url = get_service_baseurl_http(ServiceNameEnum.ZTP_SERVER) - super().__init__(bind_port, base_url, cls_name=cls_name) + super().__init__(bind_port, base_url=base_url, cls_name=cls_name) diff --git a/src/ztp_server/tests/PrepareTestScenario.py b/src/ztp_server/tests/PrepareTestScenario.py index cda9702f78258d5e81cca9d6ddfd30ac19c56bda..72389ee9742be68f36d05fa8b1557819febd1b4f 100644 --- a/src/ztp_server/tests/PrepareTestScenario.py +++ b/src/ztp_server/tests/PrepareTestScenario.py @@ -21,7 +21,7 @@ from common.Settings import ( ENVVAR_SUFIX_SERVICE_PORT_HTTP, get_env_var_name ) -from common.tools.service.GenericRestServer import GenericRestServer +from common.tools.rest_api.server.GenericRestServer import GenericRestServer from ztp_server.service.rest_server.ztpServer_plugins.ztp_provisioning_api import register_ztp_provisioning from ztp_server.client.ZtpClient import ZtpClient @@ -35,7 +35,7 @@ os.environ[get_env_var_name(ServiceNameEnum.ZTP_SERVER, ENVVAR_SUFIX_SERVICE_POR @pytest.fixture(scope='session') def ztp_server_application(): - _rest_server = GenericRestServer(ZTP_SERVICE_PORT, None, bind_address='127.0.0.1') + _rest_server = GenericRestServer(ZTP_SERVICE_PORT, bind_address='127.0.0.1') register_ztp_provisioning(_rest_server) _rest_server.start() time.sleep(1) # bring time for the server to start