diff --git a/deploy/all.sh b/deploy/all.sh index c169bc92c0d9a6dea87de919ad20b4cf3afc1199..2001fd65790939273b1ab4c4470dfe58121c57b8 100755 --- a/deploy/all.sh +++ b/deploy/all.sh @@ -175,6 +175,10 @@ export GRAF_EXT_PORT_HTTP=${GRAF_EXT_PORT_HTTP:-"3000"} # Deploy TeraFlowSDN ./deploy/tfs.sh +#Configure Subscription WS +./deploy/subscription_ws_ip.sh +./deploy/subscription_ws_e2e.sh + # Show deploy summary ./deploy/show.sh diff --git a/deploy/subscription_ws_e2e.sh b/deploy/subscription_ws_e2e.sh new file mode 100755 index 0000000000000000000000000000000000000000..6088871d19481c42ca16889b914e6ccacf2a284d --- /dev/null +++ b/deploy/subscription_ws_e2e.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +######################################################################################################################## +# Read deployment settings +######################################################################################################################## + +# If not already set, set the namespace where CockroackDB will be deployed. +export SUBSCRIPTION_WS_NAMESPACE=${SUBSCRIPTION_WS_NAMESPACE:-"tfs-e2e"} + +# If not already set, set the external port interface will be exposed to. +export SUBSCRIPTION_WS_EXT_PORT=${SUBSCRIPTION_WS_EXT_PORT:-"8761"} + + +######################################################################################################################## +# Automated steps start here +######################################################################################################################## + + +echo "Subscription WebSocket Port Mapping" +echo ">>> ExposeSubscription WebSocket port (${SUBSCRIPTION_WS_EXT_PORT}->${SUBSCRIPTION_WS_EXT_PORT})" +PATCH='{"data": {"'${SUBSCRIPTION_WS_EXT_PORT}'": "'${SUBSCRIPTION_WS_NAMESPACE}'/nbiservice:'${SUBSCRIPTION_WS_EXT_PORT}'"}}' +kubectl patch configmap nginx-ingress-tcp-microk8s-conf-e2e --namespace ingress --patch "${PATCH}" + +PORT_MAP='{"containerPort": '${SUBSCRIPTION_WS_EXT_PORT}', "hostPort": '${SUBSCRIPTION_WS_EXT_PORT}'}' +CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' +PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' +kubectl patch daemonset nginx-ingress-microk8s-controller-e2e --namespace ingress --patch "${PATCH}" +echo + + + diff --git a/deploy/subscription_ws_ip.sh b/deploy/subscription_ws_ip.sh new file mode 100755 index 0000000000000000000000000000000000000000..f5e775d457e6e31429a41e1e1c44c47c36a9c8ce --- /dev/null +++ b/deploy/subscription_ws_ip.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +######################################################################################################################## +# Read deployment settings +######################################################################################################################## + +# If not already set, set the namespace where CockroackDB will be deployed. +export SUBSCRIPTION_WS_NAMESPACE=${SUBSCRIPTION_WS_NAMESPACE:-"tfs-ip"} + +# If not already set, set the external port interface will be exposed to. +export SUBSCRIPTION_WS_INT_PORT=${SUBSCRIPTION_WS_INT_PORT:-"8762"} +######################################################################################################################## +# Automated steps start here +######################################################################################################################## + + + + +echo "Subscription WebSocket Port Mapping" +echo ">>> ExposeSubscription WebSocket port (${SUBSCRIPTION_WS_INT_PORT}->${SUBSCRIPTION_WS_INT_PORT})" +PATCH='{"data": {"'${SUBSCRIPTION_WS_INT_PORT}'": "'${SUBSCRIPTION_WS_NAMESPACE}'/nbiservice:'${SUBSCRIPTION_WS_INT_PORT}'"}}' +kubectl patch configmap nginx-ingress-tcp-microk8s-conf-ip --namespace ingress --patch "${PATCH}" + +PORT_MAP='{"containerPort": '${SUBSCRIPTION_WS_INT_PORT}', "hostPort": '${SUBSCRIPTION_WS_INT_PORT}'}' +CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' +PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' +kubectl patch daemonset nginx-ingress-microk8s-controller-ip --namespace ingress --patch "${PATCH}" +echo + + + diff --git a/deploy/tfs.sh b/deploy/tfs.sh index 3fdbe77fb502c42aaf7dd507ab239f6b3bb20056..1ae20f07f0570b049f2bad4ac40c540211b5d6f4 100755 --- a/deploy/tfs.sh +++ b/deploy/tfs.sh @@ -279,8 +279,8 @@ for COMPONENT in $TFS_COMPONENTS; do echo " Adapting '$COMPONENT' manifest file..." MANIFEST="$TMP_MANIFESTS_FOLDER/${COMPONENT}service.yaml" - # cp ./manifests/"${COMPONENT}"service.yaml "$MANIFEST" - cat ./manifests/"${COMPONENT}"service.yaml | linkerd inject - --proxy-cpu-request "10m" --proxy-cpu-limit "1" --proxy-memory-request "64Mi" --proxy-memory-limit "256Mi" > "$MANIFEST" + cp ./manifests/"${COMPONENT}"service.yaml "$MANIFEST" + #cat ./manifests/"${COMPONENT}"service.yaml | linkerd inject - --proxy-cpu-request "10m" --proxy-cpu-limit "1" --proxy-memory-request "64Mi" --proxy-memory-limit "256Mi" > "$MANIFEST" if [ "$COMPONENT" == "pathcomp" ]; then IMAGE_URL=$(echo "$TFS_REGISTRY_IMAGES/$COMPONENT-frontend:$TFS_IMAGE_TAG" | sed 's,//,/,g' | sed 's,http:/,,g') diff --git a/ecoc24 b/ecoc24 new file mode 120000 index 0000000000000000000000000000000000000000..37c97d3a77727ec098303cc9a5e04d711a30fcec --- /dev/null +++ b/ecoc24 @@ -0,0 +1 @@ +src/tests/ecoc24/ \ No newline at end of file diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml index 3abc4f208da8b4820b589b798a328c4a971f55f0..4da515936e0adaba9f1fcd230dbb2fad1ed1bd80 100644 --- a/manifests/contextservice.yaml +++ b/manifests/contextservice.yaml @@ -58,7 +58,7 @@ spec: command: ["/bin/grpc_health_probe", "-addr=:1010"] resources: requests: - cpu: 250m + cpu: 200m memory: 128Mi limits: cpu: 1000m @@ -83,25 +83,25 @@ spec: protocol: TCP port: 9192 targetPort: 9192 ---- -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: contextservice-hpa -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: contextservice - minReplicas: 1 - maxReplicas: 20 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 - #behavior: - # scaleDown: - # stabilizationWindowSeconds: 30 +#--- +#apiVersion: autoscaling/v2 +#kind: HorizontalPodAutoscaler +#metadata: +# name: contextservice-hpa +#spec: +# scaleTargetRef: +# apiVersion: apps/v1 +# kind: Deployment +# name: contextservice +# minReplicas: 1 +# maxReplicas: 20 +# metrics: +# - type: Resource +# resource: +# name: cpu +# target: +# type: Utilization +# averageUtilization: 80 +# #behavior: +# # scaleDown: +# # stabilizationWindowSeconds: 30 diff --git a/manifests/deviceservice.yaml b/manifests/deviceservice.yaml index e49ba23995b07f4e7e956a69f8d5383c8e976e89..4801ba1fb8c39d5f3a829d9e1f6899ffb88cbb24 100644 --- a/manifests/deviceservice.yaml +++ b/manifests/deviceservice.yaml @@ -53,7 +53,7 @@ spec: command: ["/bin/grpc_health_probe", "-addr=:2020"] resources: requests: - cpu: 250m + cpu: 200m memory: 128Mi limits: cpu: 1000m diff --git a/manifests/e2e_orchestratorservice.yaml b/manifests/e2e_orchestratorservice.yaml index b6354c7a8aef7dc565b61d84703f905055b9303f..1161615dae7cac2953aa12753acf489431014bcd 100644 --- a/manifests/e2e_orchestratorservice.yaml +++ b/manifests/e2e_orchestratorservice.yaml @@ -20,8 +20,14 @@ spec: selector: matchLabels: app: e2e-orchestratorservice + replicas: 1 template: metadata: + annotations: + config.linkerd.io/skip-outbound-ports: "8761" + config.linkerd.io/skip-inbound-ports: "8761" + + labels: app: e2e-orchestratorservice spec: @@ -33,6 +39,7 @@ spec: ports: - containerPort: 10050 - containerPort: 9192 + - containerPort: 8761 env: - name: LOG_LEVEL value: "INFO" @@ -44,7 +51,7 @@ spec: command: ["/bin/grpc_health_probe", "-addr=:10050"] resources: requests: - cpu: 250m + cpu: 200m memory: 128Mi limits: cpu: 1000m @@ -67,25 +74,6 @@ spec: - name: metrics port: 9192 targetPort: 9192 ---- -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: e2e-orchestratorservice-hpa -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: e2e-orchestratorservice - minReplicas: 1 - maxReplicas: 20 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 - #behavior: - # scaleDown: - # stabilizationWindowSeconds: 30 + - name: ws + port: 8761 + targetPort: 8761 diff --git a/manifests/nbiservice.yaml b/manifests/nbiservice.yaml index d3892118a3d8330335b58459a0953bb45e4854ea..a6b8f7214816205732c7f1fdc290a17c4985ef26 100644 --- a/manifests/nbiservice.yaml +++ b/manifests/nbiservice.yaml @@ -23,6 +23,9 @@ spec: replicas: 1 template: metadata: + annotations: + config.linkerd.io/skip-inbound-ports: "8762" + config.linkerd.io/skip-outbound-ports: "8762" labels: app: nbiservice spec: @@ -35,9 +38,10 @@ spec: - containerPort: 8080 - containerPort: 9090 - containerPort: 9192 + - containerPort: 8762 env: - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:9090"] @@ -75,3 +79,7 @@ spec: protocol: TCP port: 9192 targetPort: 9192 + - name: ws + protocol: TCP + port: 8762 + targetPort: 8762 diff --git a/manifests/pathcompservice.yaml b/manifests/pathcompservice.yaml index 1f54c569ce861a9ac957a2e276eb0e5f227d514a..8184d900fbb5ad5033c56dd08eadf2ab15e7026a 100644 --- a/manifests/pathcompservice.yaml +++ b/manifests/pathcompservice.yaml @@ -47,7 +47,7 @@ spec: command: ["/bin/grpc_health_probe", "-addr=:10020"] resources: requests: - cpu: 250m + cpu: 200m memory: 128Mi limits: cpu: 1000m @@ -100,25 +100,25 @@ spec: protocol: TCP port: 9192 targetPort: 9192 ---- -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: pathcompservice-hpa -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: pathcompservice - minReplicas: 1 - maxReplicas: 20 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 - #behavior: - # scaleDown: - # stabilizationWindowSeconds: 30 +#--- +#apiVersion: autoscaling/v2 +#kind: HorizontalPodAutoscaler +#metadata: +# name: pathcompservice-hpa +#spec: +# scaleTargetRef: +# apiVersion: apps/v1 +# kind: Deployment +# name: pathcompservice +# minReplicas: 1 +# maxReplicas: 20 +# metrics: +# - type: Resource +# resource: +# name: cpu +# target: +# type: Utilization +# averageUtilization: 80 +# #behavior: +# # scaleDown: +# # stabilizationWindowSeconds: 30 diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index 1dd383d615bb167ae6de15ae03c404a50bdee942..22a1f78287f20309cab6570385d48e92d6216f49 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -45,7 +45,7 @@ spec: command: ["/bin/grpc_health_probe", "-addr=:3030"] resources: requests: - cpu: 250m + cpu: 200m memory: 128Mi limits: cpu: 1000m @@ -70,25 +70,25 @@ spec: protocol: TCP port: 9192 targetPort: 9192 ---- -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: serviceservice-hpa -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: serviceservice - minReplicas: 1 - maxReplicas: 20 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 - #behavior: - # scaleDown: - # stabilizationWindowSeconds: 30 +#--- +#apiVersion: autoscaling/v2 +#kind: HorizontalPodAutoscaler +#metadata: +# name: serviceservice-hpa +#spec: +# scaleTargetRef: +# apiVersion: apps/v1 +# kind: Deployment +# name: serviceservice +# minReplicas: 1 +# maxReplicas: 20 +# metrics: +# - type: Resource +# resource: +# name: cpu +# target: +# type: Utilization +# averageUtilization: 80 +# #behavior: +# # scaleDown: +# # stabilizationWindowSeconds: 30 diff --git a/manifests/sliceservice.yaml b/manifests/sliceservice.yaml index 96a324fdffc3c1aa249d5ec592b08fe90b710313..9eaa830bb4fc4db32d090908c0102f40cdda44a1 100644 --- a/manifests/sliceservice.yaml +++ b/manifests/sliceservice.yaml @@ -50,7 +50,7 @@ spec: command: ["/bin/grpc_health_probe", "-addr=:4040"] resources: requests: - cpu: 250m + cpu: 200m memory: 128Mi limits: cpu: 1000m @@ -75,25 +75,25 @@ spec: protocol: TCP port: 9192 targetPort: 9192 ---- -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: sliceservice-hpa -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: sliceservice - minReplicas: 1 - maxReplicas: 20 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 80 - #behavior: - # scaleDown: - # stabilizationWindowSeconds: 30 +#--- +#apiVersion: autoscaling/v2 +#kind: HorizontalPodAutoscaler +#metadata: +# name: sliceservice-hpa +#spec: +# scaleTargetRef: +# apiVersion: apps/v1 +# kind: Deployment +# name: sliceservice +# minReplicas: 1 +# maxReplicas: 20 +# metrics: +# - type: Resource +# resource: +# name: cpu +# target: +# type: Utilization +# averageUtilization: 80 +# #behavior: +# # scaleDown: +# # stabilizationWindowSeconds: 30 diff --git a/manifests/vnt_managerservice.yaml b/manifests/vnt_managerservice.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f7a6213fe5f9de711de8bc5dc2ceeaf14446cd6e --- /dev/null +++ b/manifests/vnt_managerservice.yaml @@ -0,0 +1,73 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vnt-managerservice +spec: + selector: + matchLabels: + app: vnt-managerservice + replicas: 1 + template: + metadata: + annotations: + config.linkerd.io/skip-outbound-ports: "8765" + config.linkerd.io/skip-inbound-ports: "8765" + labels: + app: vnt-managerservice + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: labs.etsi.org:5050/tfs/controller/vnt_manager:latest + imagePullPolicy: Always + ports: + - containerPort: 10070 + - containerPort: 9192 + env: + - name: LOG_LEVEL + value: "INFO" + readinessProbe: + exec: + command: ["/bin/grpc_health_probe", "-addr=:10070"] + livenessProbe: + exec: + command: ["/bin/grpc_health_probe", "-addr=:10070"] + resources: + requests: + cpu: 200m + memory: 128Mi + limits: + cpu: 1000m + memory: 1024Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: vnt-managerservice + labels: + app: vnt-managerservice +spec: + type: ClusterIP + selector: + app: vnt-managerservice + ports: + - name: grpc + port: 10070 + targetPort: 10070 + - name: metrics + port: 9192 + targetPort: 9192 diff --git a/manifests/webuiservice.yaml b/manifests/webuiservice.yaml index a519aa4a2f8a1e81f1b7f2a1be1965ec0b8bb386..1060cf6d84e1cf5128adc33e86f0cde89e2f50a3 100644 --- a/manifests/webuiservice.yaml +++ b/manifests/webuiservice.yaml @@ -61,44 +61,44 @@ spec: limits: cpu: 1000m memory: 1024Mi - - name: grafana - image: grafana/grafana:8.5.22 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 3000 - name: http-grafana - protocol: TCP - env: - - name: GF_SERVER_ROOT_URL - value: "http://0.0.0.0:3000/grafana/" - - name: GF_SERVER_SERVE_FROM_SUB_PATH - value: "true" - readinessProbe: - failureThreshold: 60 - httpGet: - #path: /robots.txt - path: /login - port: 3000 - scheme: HTTP - initialDelaySeconds: 1 - periodSeconds: 1 - successThreshold: 1 - timeoutSeconds: 2 - livenessProbe: - failureThreshold: 60 - initialDelaySeconds: 1 - periodSeconds: 1 - successThreshold: 1 - tcpSocket: - port: 3000 - timeoutSeconds: 1 - resources: - requests: - cpu: 250m - memory: 512Mi - limits: - cpu: 500m - memory: 1024Mi +# - name: grafana +# image: grafana/grafana:8.5.22 +# imagePullPolicy: IfNotPresent +# ports: +# - containerPort: 3000 +# name: http-grafana +# protocol: TCP +# env: +# - name: GF_SERVER_ROOT_URL +# value: "http://0.0.0.0:3000/grafana/" +# - name: GF_SERVER_SERVE_FROM_SUB_PATH +# value: "true" +# readinessProbe: +# failureThreshold: 60 +# httpGet: +# #path: /robots.txt +# path: /login +# port: 3000 +# scheme: HTTP +# initialDelaySeconds: 1 +# periodSeconds: 1 +# successThreshold: 1 +# timeoutSeconds: 2 +# livenessProbe: +# failureThreshold: 60 +# initialDelaySeconds: 1 +# periodSeconds: 1 +# successThreshold: 1 +# tcpSocket: +# port: 3000 +# timeoutSeconds: 1 +# resources: +# requests: +# cpu: 250m +# memory: 512Mi +# limits: +# cpu: 500m +# memory: 1024Mi --- apiVersion: v1 kind: Service diff --git a/my_deploy.sh b/my_deploy.sh index 8417f6eae510391e65d5f91202e59cccf32e1f98..91fbc6a5fc5e31bfe94548b436b52bf05ea3a940 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -57,7 +57,10 @@ export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_gene #export TFS_COMPONENTS="${TFS_COMPONENTS} forecaster" # Uncomment to activate E2E Orchestrator -#export TFS_COMPONENTS="${TFS_COMPONENTS} e2e_orchestrator" +export TFS_COMPONENTS="${TFS_COMPONENTS} e2e_orchestrator" + +# Uncomment to activate VNT Manager +export TFS_COMPONENTS="${TFS_COMPONENTS} vnt_manager" # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" diff --git a/proto/context.proto b/proto/context.proto index 87f69132df022e2aa4a0766dc9f0a7a7fae36d59..685a76670927f7e8b5ee0134f49d5d7b49343f57 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -259,6 +259,7 @@ message Link { string name = 2; repeated EndPointId link_endpoint_ids = 3; LinkAttributes attributes = 4; + bool virtual = 5; } message LinkIdList { diff --git a/proto/e2eorchestrator.proto b/proto/e2eorchestrator.proto index d8f539f9fa7a7adaeaf48add127dd3077c904375..29a2b7736aa8067b6745c9685fe62164f9d2eb8b 100644 --- a/proto/e2eorchestrator.proto +++ b/proto/e2eorchestrator.proto @@ -20,7 +20,9 @@ import "context.proto"; service E2EOrchestratorService { - rpc Compute(E2EOrchestratorRequest) returns (E2EOrchestratorReply) {} + rpc Compute(E2EOrchestratorRequest) returns (E2EOrchestratorReply) {} + rpc PushTopology(context.Topology) returns (context.Empty) {} + } message E2EOrchestratorRequest { diff --git a/proto/vnt_manager.proto b/proto/vnt_manager.proto new file mode 100644 index 0000000000000000000000000000000000000000..39a52c405833cb68190fd78093b3c6eef8fa82ff --- /dev/null +++ b/proto/vnt_manager.proto @@ -0,0 +1,37 @@ +// Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// protocol buffers documentation: https://developers.google.com/protocol-buffers/docs/proto3 +syntax = "proto3"; +package vnt_manager; +import "context.proto"; + + +service VNTManagerService { + rpc VNTSubscript (VNTSubscriptionRequest) returns (VNTSubscriptionReply) {} + rpc ListVirtualLinkIds (context.Empty) returns (context.LinkIdList) {} + rpc ListVirtualLinks (context.Empty) returns (context.LinkList) {} + rpc GetVirtualLink (context.LinkId) returns (context.Link) {} + rpc SetVirtualLink (context.Link) returns (context.LinkId) {} + rpc RemoveVirtualLink (context.LinkId) returns (context.Empty) {} +} + +message VNTSubscriptionRequest { + string host = 1; + string port = 2; +} + +message VNTSubscriptionReply { + string subscription = 1; +} diff --git a/scripts/show_logs_e2eorchestrator.sh b/scripts/show_logs_e2eorchestrator.sh old mode 100644 new mode 100755 index 5ac39c6cba06e57720368dd37454986fdc0e1e29..bf1fb5987e4a65fd231f057f71cc3ffcffc14655 --- a/scripts/show_logs_e2eorchestrator.sh +++ b/scripts/show_logs_e2eorchestrator.sh @@ -24,4 +24,4 @@ export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} # Automated steps start here ######################################################################################################################## -kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/e2eorchestratorservice -c server +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/e2e-orchestratorservice -c server diff --git a/scripts/show_logs_vntmanager.sh b/scripts/show_logs_vntmanager.sh new file mode 100755 index 0000000000000000000000000000000000000000..e39cb35c5d33be012634a890836917b33206aa8d --- /dev/null +++ b/scripts/show_logs_vntmanager.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################################################################################################## +# 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/vnt_managerservice -c server diff --git a/src/common/Constants.py b/src/common/Constants.py index de9ac45a4089a7847c37ceeeeab000f51566a3a3..63b9ed4d03b9fb27d6e7f6f289f13a5f45ff589a 100644 --- a/src/common/Constants.py +++ b/src/common/Constants.py @@ -58,8 +58,9 @@ class ServiceNameEnum(Enum): CACHING = 'caching' TE = 'te' FORECASTER = 'forecaster' - E2EORCHESTRATOR = 'e2eorchestrator' + E2EORCHESTRATOR = 'e2e-orchestrator' OPTICALCONTROLLER = 'opticalcontroller' + VNTMANAGER = 'vnt-manager' BGPLS = 'bgpls-speaker' # Used for test and debugging only @@ -89,6 +90,7 @@ DEFAULT_SERVICE_GRPC_PORTS = { ServiceNameEnum.FORECASTER .value : 10040, ServiceNameEnum.E2EORCHESTRATOR .value : 10050, ServiceNameEnum.OPTICALCONTROLLER .value : 10060, + ServiceNameEnum.VNTMANAGER .value : 10070, ServiceNameEnum.BGPLS .value : 20030, # Used for test and debugging only diff --git a/src/e2e_orchestrator/Dockerfile b/src/e2e_orchestrator/Dockerfile index 1ead42a2fd85316b8f8a45aded98f91d861e6b8d..f1355fd9cf0c1e6c9cc95d3491937ccdd80510ef 100644 --- a/src/e2e_orchestrator/Dockerfile +++ b/src/e2e_orchestrator/Dockerfile @@ -79,6 +79,7 @@ RUN python3 -m pip install -r e2e_orchestrator/requirements.txt # Add component files into working directory COPY --chown=teraflow:teraflow ./src/context/. context COPY --chown=teraflow:teraflow ./src/e2e_orchestrator/. e2e_orchestrator +COPY --chown=teraflow:teraflow ./src/service/. service # Start the service ENTRYPOINT ["python", "-m", "e2e_orchestrator.service"] diff --git a/src/e2e_orchestrator/requirements.in b/src/e2e_orchestrator/requirements.in index 6553c5a41eb9bcf20b7841d8af1fb84be61a27fc..8b5c877e77cd1140c7dd326cd1be16d42c73ab36 100644 --- a/src/e2e_orchestrator/requirements.in +++ b/src/e2e_orchestrator/requirements.in @@ -12,4 +12,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -networkx \ No newline at end of file +networkx +websockets==12.0 \ No newline at end of file diff --git a/src/e2e_orchestrator/service/E2EOrchestratorServiceServicerImpl.py b/src/e2e_orchestrator/service/E2EOrchestratorServiceServicerImpl.py index 0ba2eba3390ab13f38ce80affd4faaeba61e1b87..07ece21c618e92f67efe19ffeca74ac0beffc5d5 100644 --- a/src/e2e_orchestrator/service/E2EOrchestratorServiceServicerImpl.py +++ b/src/e2e_orchestrator/service/E2EOrchestratorServiceServicerImpl.py @@ -12,33 +12,159 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging - -import networkx as nx -import grpc import copy - -from common.Constants import ServiceNameEnum -from common.method_wrappers.Decorator import (MetricsPool, MetricTypeEnum, safe_and_metered_rpc_method) +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.e2eorchestrator_pb2 import E2EOrchestratorRequest, E2EOrchestratorReply -from common.proto.context_pb2 import Empty, Connection, EndPointId +from common.proto.context_pb2 import ( + Empty, Connection, EndPointId, Link, TopologyDetails, Topology, Context, Service, ServiceTypeEnum, + ServiceStatusEnum) from common.proto.e2eorchestrator_pb2_grpc import E2EOrchestratorServiceServicer from context.client.ContextClient import ContextClient +from service.client.ServiceClient import ServiceClient from context.service.database.uuids.EndPoint import endpoint_get_uuid +from context.service.database.uuids.Device import device_get_uuid +from common.proto.vnt_manager_pb2 import VNTSubscriptionRequest +from common.tools.grpc.Tools import grpc_message_to_json_string +import grpc +import json +import logging +import networkx as nx +from threading import Thread +from websockets.sync.client import connect +from websockets.sync.server import serve +from common.Constants import DEFAULT_CONTEXT_NAME LOGGER = logging.getLogger(__name__) +logging.getLogger("websockets").propagate = True METRICS_POOL = MetricsPool("E2EOrchestrator", "RPC") + context_client: ContextClient = ContextClient() +service_client: ServiceClient = ServiceClient() + +EXT_HOST = "nbiservice.tfs-ip.svc.cluster.local" +EXT_PORT = "8762" + +OWN_HOST = "e2e-orchestratorservice.tfs-e2e.svc.cluster.local" +OWN_PORT = "8761" + +ALL_HOSTS = "0.0.0.0" + +class SubscriptionServer(Thread): + def __init__(self): + Thread.__init__(self) + + def run(self): + url = "ws://" + EXT_HOST + ":" + EXT_PORT + request = VNTSubscriptionRequest() + request.host = OWN_HOST + request.port = OWN_PORT + try: + LOGGER.debug("Trying to connect to {}".format(url)) + websocket = connect(url) + except Exception as ex: + LOGGER.error('Error connecting to {}'.format(url)) + else: + with websocket: + LOGGER.debug("Connected to {}".format(url)) + send = grpc_message_to_json_string(request) + websocket.send(send) + LOGGER.debug("Sent: {}".format(send)) + try: + message = websocket.recv() + LOGGER.debug("Received message from WebSocket: {}".format(message)) + except Exception as ex: + LOGGER.info('Exception receiving from WebSocket: {}'.format(ex)) + + self._events_server() + + + def _events_server(self): + all_hosts = "0.0.0.0" + + try: + server = serve(self._event_received, all_hosts, int(OWN_PORT)) + except Exception as ex: + LOGGER.error('Error starting server on {}:{}'.format(all_hosts, OWN_PORT)) + LOGGER.error('Exception!: {}'.format(ex)) + else: + with server: + LOGGER.info("Running events server...: {}:{}".format(all_hosts, OWN_PORT)) + server.serve_forever() + + + def _event_received(self, connection): + for message in connection: + message_json = json.loads(message) + + if 'link_id' in message_json: + link = Link(**message_json) + + service = Service() + service.service_id.service_uuid.uuid = link.link_id.link_uuid.uuid + service.service_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME + service.service_type = ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY + service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + service_client.CreateService(service) + + links = context_client.ListLinks(Empty()).links + a_device_uuid = device_get_uuid(link.link_endpoint_ids[0].device_id) + a_endpoint_uuid = endpoint_get_uuid(link.link_endpoint_ids[0])[2] + z_device_uuid = device_get_uuid(link.link_endpoint_ids[1].device_id) + z_endpoint_uuid = endpoint_get_uuid(link.link_endpoint_ids[1])[2] + + + for _link in links: + for _endpoint_id in _link.link_endpoint_ids: + if _endpoint_id.device_id.device_uuid.uuid == a_device_uuid and \ + _endpoint_id.endpoint_uuid.uuid == a_endpoint_uuid: + a_ep_id = _endpoint_id + elif _endpoint_id.device_id.device_uuid.uuid == z_device_uuid and \ + _endpoint_id.endpoint_uuid.uuid == z_endpoint_uuid: + z_ep_id = _endpoint_id + + + service.service_endpoint_ids.append(copy.deepcopy(a_ep_id)) + service.service_endpoint_ids.append(copy.deepcopy(z_ep_id)) + + service_client.UpdateService(service) + connection.send(grpc_message_to_json_string(link)) + + else: + topology_details = TopologyDetails(**message_json) + + context = Context() + context.context_id.context_uuid.uuid = topology_details.topology_id.context_id.context_uuid.uuid + context_client.SetContext(context) + + topology = Topology() + topology.topology_id.topology_uuid.uuid = topology_details.topology_id.topology_uuid.uuid + context_client.SetTopology(topology) + + for device in topology_details.devices: + context_client.SetDevice(device) + + for link in topology_details.links: + context_client.SetLink(link) + class E2EOrchestratorServiceServicerImpl(E2EOrchestratorServiceServicer): def __init__(self): LOGGER.debug("Creating Servicer...") - LOGGER.debug("Servicer Created") + try: + LOGGER.debug("Requesting subscription") + sub_server = SubscriptionServer() + sub_server.start() + LOGGER.debug("Servicer Created") + + except Exception as ex: + LOGGER.info("Exception!: {}".format(ex)) + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def Compute(self, request: E2EOrchestratorRequest, context: grpc.ServicerContext) -> E2EOrchestratorReply: endpoints_ids = [] @@ -90,4 +216,4 @@ class E2EOrchestratorServiceServicerImpl(E2EOrchestratorServiceServicer): path.connections.append(conn) - return path + diff --git a/src/e2e_orchestrator/service/__main__.py b/src/e2e_orchestrator/service/__main__.py index 5f20fd72f062127e12fc41352e7213fa320d4a94..b4763627d6de04e49765c047d560cc5626fbc17f 100644 --- a/src/e2e_orchestrator/service/__main__.py +++ b/src/e2e_orchestrator/service/__main__.py @@ -43,13 +43,6 @@ def main(): logging.basicConfig(level=log_level) LOGGER = logging.getLogger(__name__) - wait_for_environment_variables( - [ - get_env_var_name(ServiceNameEnum.E2EORCHESTRATOR, ENVVAR_SUFIX_SERVICE_HOST), - get_env_var_name(ServiceNameEnum.E2EORCHESTRATOR, ENVVAR_SUFIX_SERVICE_PORT_GRPC), - ] - ) - signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) diff --git a/src/nbi/Dockerfile b/src/nbi/Dockerfile index 1435e9757226e290e92f208ef48a7182eb106c55..8693296a5ed885f1cf9fa417c5a29c2638d0ff40 100644 --- a/src/nbi/Dockerfile +++ b/src/nbi/Dockerfile @@ -89,6 +89,8 @@ COPY src/service/__init__.py service/__init__.py COPY src/service/client/. service/client/ COPY src/slice/__init__.py slice/__init__.py COPY src/slice/client/. slice/client/ +COPY src/vnt_manager/__init__.py vnt_manager/__init__.py +COPY src/vnt_manager/client/. vnt_manager/client/ RUN mkdir -p /var/teraflow/tests/tools COPY src/tests/tools/mock_osm/. tests/tools/mock_osm/ diff --git a/src/nbi/requirements.in b/src/nbi/requirements.in index 78d941974c62e32251373a805056068608b0bda2..0999912a220ac69e3b40050db4dab86e01567ec4 100644 --- a/src/nbi/requirements.in +++ b/src/nbi/requirements.in @@ -24,3 +24,4 @@ pyang==2.6.0 git+https://github.com/robshakir/pyangbind.git requests==2.27.1 werkzeug==2.3.7 +websockets==12.0 diff --git a/src/nbi/service/NbiServiceServicerImpl.py b/src/nbi/service/NbiServiceServicerImpl.py index b1d62afb11709d9ee237bc1f840b803674923c00..79d8b8ca0074fe63cde141d41a3e6fde475415ee 100644 --- a/src/nbi/service/NbiServiceServicerImpl.py +++ b/src/nbi/service/NbiServiceServicerImpl.py @@ -20,7 +20,7 @@ from common.proto.nbi_pb2_grpc import NbiServiceServicer LOGGER = logging.getLogger(__name__) -METRICS_POOL = MetricsPool('Compute', 'RPC') +METRICS_POOL = MetricsPool('NBI', 'RPC') class NbiServiceServicerImpl(NbiServiceServicer): def __init__(self): diff --git a/src/nbi/service/__main__.py b/src/nbi/service/__main__.py index 362b0116d6f0bdbc4d1fa2025c09ac23c828617f..a4cc992a3ac2bbe23db58d5bf0c0cfe719f1a17c 100644 --- a/src/nbi/service/__main__.py +++ b/src/nbi/service/__main__.py @@ -26,6 +26,7 @@ from .rest_server.nbi_plugins.ietf_l3vpn import register_ietf_l3vpn from .rest_server.nbi_plugins.ietf_network import register_ietf_network from .rest_server.nbi_plugins.ietf_network_slice import register_ietf_nss from .rest_server.nbi_plugins.tfs_api import register_tfs_api +from .context_subscription import register_context_subscription terminate = threading.Event() LOGGER = None @@ -70,6 +71,8 @@ def main(): register_tfs_api(rest_server) rest_server.start() + register_context_subscription() + # Wait for Ctrl+C or termination signal while not terminate.wait(timeout=1.0): pass diff --git a/src/nbi/service/context_subscription/__init__.py b/src/nbi/service/context_subscription/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a69e14e72bb297faab9ce08e60a7456b7c3cddc9 --- /dev/null +++ b/src/nbi/service/context_subscription/__init__.py @@ -0,0 +1,65 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from websockets.sync.server import serve +from common.proto.vnt_manager_pb2 import VNTSubscriptionRequest +from common.proto.context_pb2 import Empty + +from context.client.ContextClient import ContextClient +from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME +from common.tools.object_factory.Topology import json_topology_id +from common.tools.object_factory.Context import json_context_id +from common.proto.context_pb2 import ContextId, TopologyId +import json +import os +from vnt_manager.client.VNTManagerClient import VNTManagerClient + +JSON_ADMIN_CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_NAME) +ADMIN_CONTEXT_ID = ContextId(**JSON_ADMIN_CONTEXT_ID) +ADMIN_TOPOLOGY_ID = TopologyId(**json_topology_id(DEFAULT_TOPOLOGY_NAME, context_id=JSON_ADMIN_CONTEXT_ID)) + +vnt_manager_client: VNTManagerClient = VNTManagerClient() +context_client: ContextClient = ContextClient() + +HOST = "0.0.0.0" +PORT = 8762 + +LOGGER = logging.getLogger(__name__) + + +def register_context_subscription(): + with serve(subcript_to_vnt_manager, HOST, PORT, logger=LOGGER) as server: + LOGGER.info("Running subscription server...: {}:{}".format(HOST, str(PORT))) + server.serve_forever() + LOGGER.info("Exiting subscription server...") + + +def subcript_to_vnt_manager(websocket): + for message in websocket: + LOGGER.debug("Message received: {}".format(message)) + message_json = json.loads(message) + request = VNTSubscriptionRequest() + request.host = message_json['host'] + request.port = message_json['port'] + LOGGER.debug("Received gRPC from ws: {}".format(request)) + + try: + vntm_reply = vnt_manager_client.VNTSubscript(request) + LOGGER.debug("Received gRPC from vntm: {}".format(vntm_reply)) + except Exception as e: + LOGGER.error('Could not subscript to VTNManager: {}'.format(e)) + + websocket.send(vntm_reply.subscription) diff --git a/src/nbi/service/rest_server/nbi_plugins/tfs_api/Resources.py b/src/nbi/service/rest_server/nbi_plugins/tfs_api/Resources.py index ce60bdea3a7ab08b8dc24dd2e7c2efe4ecf81ae0..86c1544ad08f8386983371f90ae1b7287bd07a03 100644 --- a/src/nbi/service/rest_server/nbi_plugins/tfs_api/Resources.py +++ b/src/nbi/service/rest_server/nbi_plugins/tfs_api/Resources.py @@ -19,15 +19,24 @@ from common.proto.context_pb2 import Empty from common.tools.grpc.Tools import grpc_message_to_json from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient +from vnt_manager.client.VNTManagerClient import VNTManagerClient +import logging + + + from .Tools import ( - format_grpc_to_json, grpc_connection_id, grpc_context_id, grpc_device_id, grpc_link_id, grpc_policy_rule_id, + format_grpc_to_json, grpc_connection_id, grpc_context_id, grpc_device_id, grpc_link, grpc_link_id, grpc_policy_rule_id, grpc_service_id, grpc_service, grpc_slice_id, grpc_topology_id) +LOGGER = logging.getLogger(__name__) + + class _Resource(Resource): def __init__(self) -> None: super().__init__() self.client = ContextClient() self.service_client = ServiceClient() + self.vntmanager_client = VNTManagerClient() class ContextIds(_Resource): def get(self): @@ -186,6 +195,27 @@ class Link(_Resource): def get(self, link_uuid : str): return format_grpc_to_json(self.client.GetLink(grpc_link_id(link_uuid))) +class VirtualLinkIds(_Resource): + def get(self): + return format_grpc_to_json(self.vntmanager_client.ListVirtualLinkIds(Empty())) + +class VirtualLinks(_Resource): + def get(self): + return format_grpc_to_json(self.vntmanager_client.ListVirtualLinks(Empty())) + +class VirtualLink(_Resource): + def get(self, virtual_link_uuid : str): + return format_grpc_to_json(self.vntmanager_client.GetVirtualLink(grpc_link_id(virtual_link_uuid))) + def post(self, virtual_link_uuid : str): # pylint: disable=unused-argument + link_json = request.get_json() + link = grpc_link(link_json) + return format_grpc_to_json(self.vntmanager_client.SetVirtualLink(link)) + def put(self, virtual_link_uuid : str): # pylint: disable=unused-argument + link = request.get_json() + return format_grpc_to_json(self.vntmanager_client.SetVirtualLink(grpc_link(link))) + def delete(self, virtual_link_uuid : str): + return format_grpc_to_json(self.vntmanager_client.RemoveVirtualLink(grpc_link_id(virtual_link_uuid))) + class ConnectionIds(_Resource): def get(self, context_uuid : str, service_uuid : str): return format_grpc_to_json(self.client.ListConnectionIds(grpc_service_id(context_uuid, service_uuid))) diff --git a/src/nbi/service/rest_server/nbi_plugins/tfs_api/Tools.py b/src/nbi/service/rest_server/nbi_plugins/tfs_api/Tools.py index 1f69ffffb8c97a83591ec626920b57f40d032783..32873295906f69fedeed967161fac12d6071b3ff 100644 --- a/src/nbi/service/rest_server/nbi_plugins/tfs_api/Tools.py +++ b/src/nbi/service/rest_server/nbi_plugins/tfs_api/Tools.py @@ -14,7 +14,7 @@ from flask.json import jsonify from common.proto.context_pb2 import ( - ConnectionId, ContextId, DeviceId, LinkId, ServiceId, SliceId, TopologyId, Service, ServiceStatusEnum + ConnectionId, ContextId, DeviceId, Link, LinkId, ServiceId, SliceId, TopologyId, Service, ServiceStatusEnum ) from common.proto.policy_pb2 import PolicyRuleId from common.tools.grpc.Tools import grpc_message_to_json @@ -24,7 +24,7 @@ from common.tools.object_factory.ConfigRule import json_config_rule from common.tools.object_factory.Constraint import json_constraint_custom from common.tools.object_factory.EndPoint import json_endpoint_id from common.tools.object_factory.Device import json_device_id -from common.tools.object_factory.Link import json_link_id +from common.tools.object_factory.Link import json_link_id, json_link from common.tools.object_factory.PolicyRule import json_policyrule_id from common.tools.object_factory.Service import json_service_id, json_service from common.tools.object_factory.Slice import json_slice_id @@ -46,6 +46,9 @@ def grpc_device_id(device_uuid): def grpc_link_id(link_uuid): return LinkId(**json_link_id(link_uuid)) +def grpc_link(link): + return Link(**json_link(link['link_id']['link_uuid']['uuid'], link['link_endpoint_ids'])) + def grpc_service_id(context_uuid, service_uuid): return ServiceId(**json_service_id(service_uuid, context_id=json_context_id(context_uuid))) diff --git a/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py b/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py index 41e8ff1ea634869e69258c20f81f7c3db9767eb5..5a57c7cee066add443e420062bbd31594141a9a2 100644 --- a/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py @@ -19,6 +19,7 @@ from .Resources import ( Device, DeviceIds, Devices, DummyContexts, Link, LinkIds, Links, + VirtualLink, VirtualLinkIds, VirtualLinks, PolicyRule, PolicyRuleIds, PolicyRules, Service, ServiceIds, Services, Slice, SliceIds, Slices, @@ -30,38 +31,42 @@ URL_PREFIX = '/tfs-api' # Use 'path' type since some identifiers might contain char '/' and Flask is unable to recognize them in 'string' type. RESOURCES = [ # (endpoint_name, resource_class, resource_url) - ('api.context_ids', ContextIds, '/context_ids'), - ('api.contexts', Contexts, '/contexts'), - ('api.dummy_contexts', DummyContexts, '/dummy_contexts'), - ('api.context', Context, '/context/'), + ('api.context_ids', ContextIds, '/context_ids'), + ('api.contexts', Contexts, '/contexts'), + ('api.dummy_contexts', DummyContexts, '/dummy_contexts'), + ('api.context', Context, '/context/'), - ('api.topology_ids', TopologyIds, '/context//topology_ids'), - ('api.topologies', Topologies, '/context//topologies'), - ('api.topology', Topology, '/context//topology/'), + ('api.topology_ids', TopologyIds, '/context//topology_ids'), + ('api.topologies', Topologies, '/context//topologies'), + ('api.topology', Topology, '/context//topology/'), - ('api.service_ids', ServiceIds, '/context//service_ids'), - ('api.services', Services, '/context//services'), - ('api.service', Service, '/context//service/'), + ('api.service_ids', ServiceIds, '/context//service_ids'), + ('api.services', Services, '/context//services'), + ('api.service', Service, '/context//service/'), - ('api.slice_ids', SliceIds, '/context//slice_ids'), - ('api.slices', Slices, '/context//slices'), - ('api.slice', Slice, '/context//slice/'), + ('api.slice_ids', SliceIds, '/context//slice_ids'), + ('api.slices', Slices, '/context//slices'), + ('api.slice', Slice, '/context//slice/'), - ('api.device_ids', DeviceIds, '/device_ids'), - ('api.devices', Devices, '/devices'), - ('api.device', Device, '/device/'), + ('api.device_ids', DeviceIds, '/device_ids'), + ('api.devices', Devices, '/devices'), + ('api.device', Device, '/device/'), - ('api.link_ids', LinkIds, '/link_ids'), - ('api.links', Links, '/links'), - ('api.link', Link, '/link/'), + ('api.link_ids', LinkIds, '/link_ids'), + ('api.links', Links, '/links'), + ('api.link', Link, '/link/'), - ('api.connection_ids', ConnectionIds, '/context//service//connection_ids'), - ('api.connections', Connections, '/context//service//connections'), - ('api.connection', Connection, '/connection/'), + ('api.virtual_link_ids', VirtualLinkIds, '/virtual_link_ids'), + ('api.virtual_links', VirtualLinks, '/virtual_links'), + ('api.virtual_link', VirtualLink, '/virtual_link/'), - ('api.policyrule_ids', PolicyRuleIds, '/policyrule_ids'), - ('api.policyrules', PolicyRules, '/policyrules'), - ('api.policyrule', PolicyRule, '/policyrule/'), + ('api.connection_ids', ConnectionIds, '/context//service//connection_ids'), + ('api.connections', Connections, '/context//service//connections'), + ('api.connection', Connection, '/connection/'), + + ('api.policyrule_ids', PolicyRuleIds, '/policyrule_ids'), + ('api.policyrules', PolicyRules, '/policyrules'), + ('api.policyrule', PolicyRule, '/policyrule/'), ] def register_tfs_api(rest_server : RestServer): diff --git a/src/tests/ecoc24/__init__.py b/src/tests/ecoc24/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a5ccb6078c2c18285753cacb045f9614017f8485 --- /dev/null +++ b/src/tests/ecoc24/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/tests/ecoc24/delete.sh b/src/tests/ecoc24/delete.sh new file mode 100755 index 0000000000000000000000000000000000000000..20b9ce9aa177046ee56ee60c8bfe8bf2ebb93dfb --- /dev/null +++ b/src/tests/ecoc24/delete.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Delete old namespaces +kubectl delete namespace tfs-e2e tfs-ip + +# Delete secondary ingress controllers +kubectl delete -f src/tests/ecoc24/nginx-ingress-controller-e2e.yaml +kubectl delete -f src/tests/ecoc24/nginx-ingress-controller-ip.yaml diff --git a/src/tests/ecoc24/deploy.sh b/src/tests/ecoc24/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..06c54455d4ec5185c6d52c4d648f9c09fd273c8e --- /dev/null +++ b/src/tests/ecoc24/deploy.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Delete old namespaces +kubectl delete namespace tfs-e2e tfs-ip + +# Delete secondary ingress controllers +kubectl delete -f ./src/tests/ecoc24/nginx-ingress-controller-e2e.yaml +kubectl delete -f ./src/tests/ecoc24/nginx-ingress-controller-ip.yaml + +# Create secondary ingress controllers +kubectl apply -f ./src/tests/ecoc24/nginx-ingress-controller-e2e.yaml +kubectl apply -f ./src/tests/ecoc24/nginx-ingress-controller-ip.yaml + + +# Deploy TFS for ip +source ./src/tests/ecoc24/deploy_specs_ip.sh +./deploy/all.sh +mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_ip.sh + + +# Deploy TFS for e2e +source ./src/tests/ecoc24/deploy_specs_e2e.sh +./deploy/all.sh +mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_e2e.sh + diff --git a/src/tests/ecoc24/deploy_e2e.sh b/src/tests/ecoc24/deploy_e2e.sh new file mode 100755 index 0000000000000000000000000000000000000000..01800e0431ec01437b15e70a6dca8617694c609f --- /dev/null +++ b/src/tests/ecoc24/deploy_e2e.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Delete old namespaces +kubectl delete namespace tfs-e2e + +# Delete secondary ingress controllers +kubectl delete -f src/tests/ecoc24/nginx-ingress-controller-e2e.yaml + +# Create secondary ingress controllers +kubectl apply -f src/tests/ecoc24/nginx-ingress-controller-e2e.yaml + +# Deploy TFS for E2E +source src/tests/ecoc24/deploy_specs_e2e.sh +./deploy/all.sh +mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_e2e.sh diff --git a/src/tests/ecoc24/deploy_ip.sh b/src/tests/ecoc24/deploy_ip.sh new file mode 100755 index 0000000000000000000000000000000000000000..34e4e894e07b44f2b2d048ade6bc4f2c48555caa --- /dev/null +++ b/src/tests/ecoc24/deploy_ip.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Delete old namespaces +kubectl delete namespace tfs-ip + +# Delete secondary ingress controllers +kubectl delete -f src/tests/ecoc24/nginx-ingress-controller-ip.yaml + +# Create secondary ingress controllers +kubectl apply -f src/tests/ecoc24/nginx-ingress-controller-ip.yaml + +# Deploy TFS for IP +source src/tests/ecoc24/deploy_specs_ip.sh +./deploy/all.sh +mv tfs_runtime_env_vars.sh tfs_runtime_env_vars_ip.sh diff --git a/src/tests/ecoc24/deploy_specs_e2e.sh b/src/tests/ecoc24/deploy_specs_e2e.sh new file mode 100755 index 0000000000000000000000000000000000000000..f50bc28db8fadd36cc5d5b45a60627a211f4e5a7 --- /dev/null +++ b/src/tests/ecoc24/deploy_specs_e2e.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# ----- TeraFlowSDN ------------------------------------------------------------ + +# Set the URL of the internal MicroK8s Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +# export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_generator" +export TFS_COMPONENTS="context device pathcomp service slice nbi webui" + +# Uncomment to activate Monitoring +#export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" + +# Uncomment to activate ZTP and Policy Manager +#export TFS_COMPONENTS="${TFS_COMPONENTS} ztp 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 E2E Orchestrator +export TFS_COMPONENTS="${TFS_COMPONENTS} e2e_orchestrator" + +# 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-e2e" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="src/tests/ecoc24/tfs-ingress-e2e.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 the database name to be used by Context. +export CRDB_DATABASE="tfs_e2e" + +# 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-e2e" + +# Set the external port NATS Client interface will be exposed to. +export NATS_EXT_PORT_CLIENT="4223" + +# Set the external port NATS HTTP Mgmt GUI interface will be exposed to. +export NATS_EXT_PORT_HTTP="8223" + +# Disable flag for re-deploying NATS from scratch. +export NATS_REDEPLOY="" + + +# ----- QuestDB ---------------------------------------------------------------- + +# Set the namespace where QuestDB will be deployed. +export QDB_NAMESPACE="qdb-e2e" + +# Set the external port QuestDB Postgre SQL interface will be exposed to. +export QDB_EXT_PORT_SQL="8813" + +# Set the external port QuestDB Influx Line Protocol interface will be exposed to. +export QDB_EXT_PORT_ILP="9011" + +# Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to. +export QDB_EXT_PORT_HTTP="9001" + +# Set the database username to be used for QuestDB. +export QDB_USERNAME="admin" + +# Set the database user's password to be used for QuestDB. +export QDB_PASSWORD="quest" + +# Set the table name to be used by Monitoring for KPIs. +export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis" + +# Set the table name to be used by Slice for plotting groups. +export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups" + +# Disable flag for dropping tables if they exist. +export QDB_DROP_TABLES_IF_EXIST="YES" + +# Disable flag for re-deploying QuestDB from scratch. +export QDB_REDEPLOY="" + + +# ----- 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/ecoc24/deploy_specs_ip.sh b/src/tests/ecoc24/deploy_specs_ip.sh new file mode 100755 index 0000000000000000000000000000000000000000..65e8db4ab4c4b54686be50164471148099c4b819 --- /dev/null +++ b/src/tests/ecoc24/deploy_specs_ip.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# ----- TeraFlowSDN ------------------------------------------------------------ + +# Set the URL of the internal MicroK8s Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +# export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_generator" +export TFS_COMPONENTS="context device pathcomp service slice nbi webui" + +# Uncomment to activate Monitoring +#export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" + +# Uncomment to activate ZTP and Policy Manager +#export TFS_COMPONENTS="${TFS_COMPONENTS} ztp 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 VNT Manager +export TFS_COMPONENTS="${TFS_COMPONENTS} vnt_manager" + +# 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-ip" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="src/tests/ecoc24/tfs-ingress-ip.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 the database name to be used by Context. +export CRDB_DATABASE="tfs_ip" + +# 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-ip" + +# Set the external port NATS Client interface will be exposed to. +export NATS_EXT_PORT_CLIENT="4224" + +# Set the external port NATS HTTP Mgmt GUI interface will be exposed to. +export NATS_EXT_PORT_HTTP="8224" + +# Disable flag for re-deploying NATS from scratch. +export NATS_REDEPLOY="" + + +# ----- QuestDB ---------------------------------------------------------------- + +# Set the namespace where QuestDB will be deployed. +export QDB_NAMESPACE="qdb-ip" + +# Set the external port QuestDB Postgre SQL interface will be exposed to. +export QDB_EXT_PORT_SQL="8814" + +# Set the external port QuestDB Influx Line Protocol interface will be exposed to. +export QDB_EXT_PORT_ILP="9012" + +# Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to. +export QDB_EXT_PORT_HTTP="9002" + +# Set the database username to be used for QuestDB. +export QDB_USERNAME="admin" + +# Set the database user's password to be used for QuestDB. +export QDB_PASSWORD="quest" + +# Set the table name to be used by Monitoring for KPIs. +export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis" + +# Set the table name to be used by Slice for plotting groups. +export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups" + +# Disable flag for dropping tables if they exist. +export QDB_DROP_TABLES_IF_EXIST="YES" + +# Disable flag for re-deploying QuestDB from scratch. +export QDB_REDEPLOY="" + + +# ----- 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/ecoc24/descriptors/emulated/dc-2-dc-service.json b/src/tests/ecoc24/descriptors/emulated/dc-2-dc-service.json new file mode 100644 index 0000000000000000000000000000000000000000..44c80ad46f727cbe9af7fd78f73184e756d32a92 --- /dev/null +++ b/src/tests/ecoc24/descriptors/emulated/dc-2-dc-service.json @@ -0,0 +1,48 @@ +{ + "services": [ + { + "service_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "service_uuid": { + "uuid": "dc-2-dc-svc" + } + }, + "service_type": 2, + "service_status": {"service_status": 1}, + "service_endpoint_ids": [ + {"device_id":{"device_uuid":{"uuid":"DC1"}},"endpoint_uuid":{"uuid":"int"}}, + {"device_id":{"device_uuid":{"uuid":"DC2"}},"endpoint_uuid":{"uuid":"int"}} + ], + "service_constraints": [ + {"sla_capacity": {"capacity_gbps": 10.0}}, + {"sla_latency": {"e2e_latency_ms": 15.2}} + ], + "service_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "/settings", "resource_value": { + "address_families": ["IPV4"], "bgp_as": 65000, "bgp_route_target": "65000:123", + "mtu": 1512, "vlan_id": 300 + }}}, + {"action": 1, "custom": {"resource_key": "/device[PE1]/endpoint[1/1]/settings", "resource_value": { + "route_distinguisher": "65000:123", "router_id": "10.0.0.1", + "address_ip": "3.3.1.1", "address_prefix": 24, "sub_interface_index": 1, "vlan_id": 300 + }}}, + {"action": 1, "custom": {"resource_key": "/device[PE2]/endpoint[1/1]/settings", "resource_value": { + "route_distinguisher": "65000:123", "router_id": "10.0.0.2", + "address_ip": "3.3.2.1", "address_prefix": 24, "sub_interface_index": 1, "vlan_id": 300 + }}}, + {"action": 1, "custom": {"resource_key": "/device[PE3]/endpoint[1/1]/settings", "resource_value": { + "route_distinguisher": "65000:123", "router_id": "10.0.0.3", + "address_ip": "3.3.3.1", "address_prefix": 24, "sub_interface_index": 1, "vlan_id": 300 + }}}, + {"action": 1, "custom": {"resource_key": "/device[PE4]/endpoint[1/1]/settings", "resource_value": { + "route_distinguisher": "65000:123", "router_id": "10.0.0.4", + "address_ip": "3.3.4.1", "address_prefix": 24, "sub_interface_index": 1, "vlan_id": 300 + }}} + ]} + } + ] +} diff --git a/src/tests/ecoc24/descriptors/emulated/descriptor_ip.json b/src/tests/ecoc24/descriptors/emulated/descriptor_ip.json new file mode 100644 index 0000000000000000000000000000000000000000..516a8bdebba9b8a7dcdc0a245d52dd17b65cce07 --- /dev/null +++ b/src/tests/ecoc24/descriptors/emulated/descriptor_ip.json @@ -0,0 +1,34 @@ +{ + "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": "IP1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper/internal", "uuid": "CTP1"}, + {"sample_types": [], "type": "copper/internal", "uuid": "CTP2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "CTP3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "IP2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 0, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper/internal", "uuid": "CTP1"}, + {"sample_types": [], "type": "copper/internal", "uuid": "CTP2"}, + {"sample_types": [], "type": "copper/internal", "uuid": "CTP3"} + ]}}} + ]} + } + ] +} diff --git a/src/tests/ecoc24/descriptors/emulated/link_mapping.json b/src/tests/ecoc24/descriptors/emulated/link_mapping.json new file mode 100644 index 0000000000000000000000000000000000000000..9ac5a25994c867bb5387e00363e7b3e0908e030d --- /dev/null +++ b/src/tests/ecoc24/descriptors/emulated/link_mapping.json @@ -0,0 +1,34 @@ +{ + "contexts": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}} + ], + "topologies": [ + {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}} + ], + "links": [ + {"link_id": {"link_uuid": {"uuid": "IP1_CTP1-MGON1_OTP1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "IP1"}}, "endpoint_uuid": {"uuid": "CTP1"}}, + {"device_id": {"device_uuid": {"uuid": "MG-ON1"}}, "endpoint_uuid": {"uuid": "OTP1"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "IP1_CTP2-MGON1_OTP2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "IP1"}}, "endpoint_uuid": {"uuid": "CTP2"}}, + {"device_id": {"device_uuid": {"uuid": "MG-ON1"}}, "endpoint_uuid": {"uuid": "OTP2"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "IP1CTP3-MGON1_OTP3"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "IP1"}}, "endpoint_uuid": {"uuid": "CTP3"}}, + {"device_id": {"device_uuid": {"uuid": "MG-ON1"}}, "endpoint_uuid": {"uuid": "OTP3"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "IP2_CTP1-MGON3_OTP1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "IP2"}}, "endpoint_uuid": {"uuid": "CTP1"}}, + {"device_id": {"device_uuid": {"uuid": "MG-ON3"}}, "endpoint_uuid": {"uuid": "OTP1"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "IP2_CTP2-MGON3_OTP2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "IP2"}}, "endpoint_uuid": {"uuid": "CTP2"}}, + {"device_id": {"device_uuid": {"uuid": "MG-ON3"}}, "endpoint_uuid": {"uuid": "OTP2"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "IP2_CTP3-MGON3_OTP3"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "IP2"}}, "endpoint_uuid": {"uuid": "CTP3"}}, + {"device_id": {"device_uuid": {"uuid": "MG-ON3"}}, "endpoint_uuid": {"uuid": "OTP3"}} + ]} + ] +} diff --git a/src/tests/ecoc24/dump_logs.sh b/src/tests/ecoc24/dump_logs.sh new file mode 100755 index 0000000000000000000000000000000000000000..0d8d517f1af5fe45b6cc8df8e6bffdc5e8752db9 --- /dev/null +++ b/src/tests/ecoc24/dump_logs.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +rm -rf tmp/exec + +echo "Collecting logs for E2E..." +mkdir -p tmp/exec/e2e +kubectl --namespace tfs-e2e logs deployments/contextservice server > tmp/exec/e2e/context.log +kubectl --namespace tfs-e2e logs deployments/deviceservice server > tmp/exec/e2e/device.log +kubectl --namespace tfs-e2e logs deployments/serviceservice server > tmp/exec/e2e/service.log +kubectl --namespace tfs-e2e logs deployments/pathcompservice frontend > tmp/exec/e2e/pathcomp-frontend.log +kubectl --namespace tfs-e2e logs deployments/pathcompservice backend > tmp/exec/e2e/pathcomp-backend.log +kubectl --namespace tfs-e2e logs deployments/sliceservice server > tmp/exec/e2e/slice.log +printf "\n" + +echo "Collecting logs for IP..." +mkdir -p tmp/exec/ip +kubectl --namespace tfs-ip logs deployments/contextservice server > tmp/exec/ip/context.log +kubectl --namespace tfs-ip logs deployments/deviceservice server > tmp/exec/ip/device.log +kubectl --namespace tfs-ip logs deployments/serviceservice server > tmp/exec/ip/service.log +kubectl --namespace tfs-ip logs deployments/pathcompservice frontend > tmp/exec/ip/pathcomp-frontend.log +kubectl --namespace tfs-ip logs deployments/pathcompservice backend > tmp/exec/ip/pathcomp-backend.log +kubectl --namespace tfs-ip logs deployments/sliceservice server > tmp/exec/ip/slice.log +printf "\n" + +echo "Done!" diff --git a/src/tests/ecoc24/fast_redeploy.sh b/src/tests/ecoc24/fast_redeploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..51f0a1a16129813d3a4fe930eddbac11bbad22ff --- /dev/null +++ b/src/tests/ecoc24/fast_redeploy.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +kubectl delete namespace tfs-e2e tfs-ip + +echo "Deploying tfs-e2e ..." +kubectl delete -f src/tests/ecoc24/nginx-ingress-controller-e2e.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl create namespace tfs-e2e > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl apply -f src/tests/ecoc24/nginx-ingress-controller-e2e.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl --namespace tfs-e2e apply -f ./tmp/tfs-e2e/manifests/contextservice.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl --namespace tfs-e2e apply -f ./tmp/tfs-e2e/manifests/deviceservice.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl --namespace tfs-e2e apply -f ./tmp/tfs-e2e/manifests/e2e_orchestratorservice.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl --namespace tfs-e2e apply -f ./tmp/tfs-e2e/manifests/pathcompservice.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl --namespace tfs-e2e apply -f ./tmp/tfs-e2e/manifests/serviceservice.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl --namespace tfs-e2e apply -f ./tmp/tfs-e2e/manifests/sliceservice.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl --namespace tfs-e2e apply -f ./tmp/tfs-e2e/manifests/webuiservice.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +kubectl --namespace tfs-e2e apply -f src/tests/ecoc24/tfs-ingress-e2e.yaml > ./tmp/tfs-e2e/logs/deploy-tfs-e2e.log +printf "\n" + +echo "Deploying tfs-ip ..." +kubectl delete -f src/tests/ecoc24/nginx-ingress-controller-ip.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl create namespace tfs-ip > ./tmp/logs/deploy-tfs-ip.log +kubectl apply -f src/tests/ecoc24/nginx-ingress-controller-ip.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl --namespace tfs-ip apply -f ./tmp/tfs-ip/manifests/contextservice.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl --namespace tfs-ip apply -f ./tmp/tfs-ip/manifests/deviceservice.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl --namespace tfs-ip apply -f ./tmp/tfs-ip/manifests/pathcompservice.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl --namespace tfs-ip apply -f ./tmp/tfs-ip/manifests/serviceservice.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl --namespace tfs-ip apply -f ./tmp/tfs-ip/manifests/sliceservice.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl --namespace tfs-ip apply -f ./tmp/tfs-ip/manifests/vnt_managerservice.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl --namespace tfs-ip apply -f ./tmp/tfs-ip/manifests/webuiservice.yaml > ./tmp/logs/deploy-tfs-ip.log +kubectl --namespace tfs-ip apply -f src/tests/ecoc24/tfs-ingress-ip.yaml > ./tmp/logs/deploy-tfs-ip.log +printf "\n" + +echo "Waiting tfs-e2e ..." +kubectl wait --namespace tfs-e2e --for='condition=available' --timeout=300s deployment/contextservice +kubectl wait --namespace tfs-e2e --for='condition=available' --timeout=300s deployment/deviceservice +kubectl wait --namespace tfs-e2e --for='condition=available' --timeout=300s deployment/e2e-orchestratorservice +kubectl wait --namespace tfs-e2e --for='condition=available' --timeout=300s deployment/pathcompservice +kubectl wait --namespace tfs-e2e --for='condition=available' --timeout=300s deployment/serviceservice +kubectl wait --namespace tfs-e2e --for='condition=available' --timeout=300s deployment/sliceservice +kubectl wait --namespace tfs-e2e --for='condition=available' --timeout=300s deployment/webuiservice +printf "\n" + +echo "Waiting tfs-ip ..." +kubectl wait --namespace tfs-ip --for='condition=available' --timeout=300s deployment/contextservice +kubectl wait --namespace tfs-ip --for='condition=available' --timeout=300s deployment/deviceservice +kubectl wait --namespace tfs-ip --for='condition=available' --timeout=300s deployment/pathcompservice +kubectl wait --namespace tfs-ip --for='condition=available' --timeout=300s deployment/serviceservice +kubectl wait --namespace tfs-ip --for='condition=available' --timeout=300s deployment/sliceservice +kubectl wait --namespace tfs-ip --for='condition=available' --timeout=300s deployment/vnt-managerservice +kubectl wait --namespace tfs-ip --for='condition=available' --timeout=300s deployment/webuiservice +printf "\n" + +echo "Done!" diff --git a/src/tests/ecoc24/nginx-ingress-controller-e2e.yaml b/src/tests/ecoc24/nginx-ingress-controller-e2e.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c4fc966d13fef5f0cf38dff1fb3bc86942d4687a --- /dev/null +++ b/src/tests/ecoc24/nginx-ingress-controller-e2e.yaml @@ -0,0 +1,138 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-load-balancer-microk8s-conf-e2e + namespace: ingress +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-ingress-udp-microk8s-conf-e2e + namespace: ingress +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-ingress-tcp-microk8s-conf-e2e + namespace: ingress +--- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: tfs-ingress-class-e2e + annotations: + ingressclass.kubernetes.io/is-default-class: "false" +spec: + controller: tfs.etsi.org/controller-class-e2e +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: nginx-ingress-microk8s-controller-e2e + namespace: ingress + labels: + microk8s-application: nginx-ingress-microk8s-e2e +spec: + selector: + matchLabels: + name: nginx-ingress-microk8s-e2e + updateStrategy: + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + name: nginx-ingress-microk8s-e2e + spec: + terminationGracePeriodSeconds: 60 + restartPolicy: Always + serviceAccountName: nginx-ingress-microk8s-serviceaccount + containers: + - image: k8s.gcr.io/ingress-nginx/controller:v1.2.0 + imagePullPolicy: IfNotPresent + name: nginx-ingress-microk8s + livenessProbe: + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + timeoutSeconds: 5 + lifecycle: + preStop: + exec: + command: + - /wait-shutdown + securityContext: + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + runAsUser: 101 # www-data + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + ports: + - name: http + containerPort: 80 + hostPort: 8001 + protocol: TCP + - name: https + containerPort: 443 + hostPort: 4431 + protocol: TCP + - name: health + containerPort: 10254 + hostPort: 12541 + protocol: TCP + - name: ws + containerPort: 8761 + hostPort: 8761 + protocol: TCP + args: + - /nginx-ingress-controller + - --configmap=$(POD_NAMESPACE)/nginx-load-balancer-microk8s-conf-e2e + - --tcp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-tcp-microk8s-conf-e2e + - --udp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-udp-microk8s-conf-e2e + - --election-id=ingress-controller-leader-e2e + - --controller-class=tfs.etsi.org/controller-class-e2e + - --ingress-class=tfs-ingress-class-e2e + - ' ' + - --publish-status-address=127.0.0.1 diff --git a/src/tests/ecoc24/nginx-ingress-controller-ip.yaml b/src/tests/ecoc24/nginx-ingress-controller-ip.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f8946a8a33a79bae0fd8587c2f3eff1b00028b9b --- /dev/null +++ b/src/tests/ecoc24/nginx-ingress-controller-ip.yaml @@ -0,0 +1,138 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-load-balancer-microk8s-conf-ip + namespace: ingress +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-ingress-udp-microk8s-conf-ip + namespace: ingress +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-ingress-tcp-microk8s-conf-ip + namespace: ingress +--- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: tfs-ingress-class-ip + annotations: + ingressclass.kubernetes.io/is-default-class: "false" +spec: + controller: tfs.etsi.org/controller-class-ip +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: nginx-ingress-microk8s-controller-ip + namespace: ingress + labels: + microk8s-application: nginx-ingress-microk8s-ip +spec: + selector: + matchLabels: + name: nginx-ingress-microk8s-ip + updateStrategy: + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + name: nginx-ingress-microk8s-ip + spec: + terminationGracePeriodSeconds: 60 + restartPolicy: Always + serviceAccountName: nginx-ingress-microk8s-serviceaccount + containers: + - image: k8s.gcr.io/ingress-nginx/controller:v1.2.0 + imagePullPolicy: IfNotPresent + name: nginx-ingress-microk8s + livenessProbe: + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + timeoutSeconds: 5 + lifecycle: + preStop: + exec: + command: + - /wait-shutdown + securityContext: + capabilities: + add: + - NET_BIND_SERVICE + drop: + - ALL + runAsUser: 101 # www-data + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + ports: + - name: http + containerPort: 80 + hostPort: 8002 + protocol: TCP + - name: https + containerPort: 443 + hostPort: 4432 + protocol: TCP + - name: health + containerPort: 10254 + hostPort: 12542 + protocol: TCP + - name: ws + containerPort: 8762 + hostPort: 8762 + protocol: TCP + args: + - /nginx-ingress-controller + - --configmap=$(POD_NAMESPACE)/nginx-load-balancer-microk8s-conf-ip + - --tcp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-tcp-microk8s-conf-ip + - --udp-services-configmap=$(POD_NAMESPACE)/nginx-ingress-udp-microk8s-conf-ip + - --election-id=ingress-controller-leader-ip + - --controller-class=tfs.etsi.org/controller-class-ip + - --ingress-class=tfs-ingress-class-ip + - ' ' + - --publish-status-address=127.0.0.1 diff --git a/src/tests/ecoc24/show_deploy.sh b/src/tests/ecoc24/show_deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..97504777444d4f2623d687fcf84ab22d5006685a --- /dev/null +++ b/src/tests/ecoc24/show_deploy.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################################################################################################## +# Automated steps start here +######################################################################################################################## + +echo "Deployment Resources:" +kubectl --namespace tfs-e2e get all +printf "\n" + +echo "Deployment Ingress:" +kubectl --namespace tfs-e2e get ingress +printf "\n" + +echo "Deployment Resources:" +kubectl --namespace tfs-ip get all +printf "\n" + +echo "Deployment Ingress:" +kubectl --namespace tfs-ip get ingress +printf "\n" diff --git a/src/tests/ecoc24/tfs-ingress-e2e.yaml b/src/tests/ecoc24/tfs-ingress-e2e.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c7d5551f048332801a36921f79ae9814026bac1b --- /dev/null +++ b/src/tests/ecoc24/tfs-ingress-e2e.yaml @@ -0,0 +1,60 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: tfs-ingress-e2e + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 +spec: + ingressClassName: tfs-ingress-class-e2e + rules: + - http: + paths: + - path: /webui(/|$)(.*) + pathType: Prefix + backend: + service: + name: webuiservice + port: + number: 8004 + - path: /grafana(/|$)(.*) + pathType: Prefix + backend: + service: + name: webuiservice + port: + number: 3000 + - path: /context(/|$)(.*) + pathType: Prefix + backend: + service: + name: contextservice + port: + number: 8080 + - path: /()(restconf/.*) + pathType: Prefix + backend: + service: + name: nbiservice + port: + number: 8080 + - path: /()(tfs-api/.*) + pathType: Prefix + backend: + service: + name: nbiservice + port: + number: 8080 diff --git a/src/tests/ecoc24/tfs-ingress-ip.yaml b/src/tests/ecoc24/tfs-ingress-ip.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d6aa56e93a3febf6a3956854c661a121d806eb88 --- /dev/null +++ b/src/tests/ecoc24/tfs-ingress-ip.yaml @@ -0,0 +1,60 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: tfs-ingress-ip + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 +spec: + ingressClassName: tfs-ingress-class-ip + rules: + - http: + paths: + - path: /webui(/|$)(.*) + pathType: Prefix + backend: + service: + name: webuiservice + port: + number: 8004 + - path: /grafana(/|$)(.*) + pathType: Prefix + backend: + service: + name: webuiservice + port: + number: 3000 + - path: /context(/|$)(.*) + pathType: Prefix + backend: + service: + name: contextservice + port: + number: 8080 + - path: /()(restconf/.*) + pathType: Prefix + backend: + service: + name: nbiservice + port: + number: 8080 + - path: /()(tfs-api/.*) + pathType: Prefix + backend: + service: + name: nbiservice + port: + number: 8080 \ No newline at end of file diff --git a/src/vnt_manager/.gitlab-ci.yml b/src/vnt_manager/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..85192054ede48cefa74ff2ebca4b4ce3aa61aa14 --- /dev/null +++ b/src/vnt_manager/.gitlab-ci.yml @@ -0,0 +1,38 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# build, tag and push the Docker image to the gitlab registry +build vntmanager: + variables: + IMAGE_NAME: 'vntmanager' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: build + before_script: + - 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 images --filter="dangling=true" --quiet | xargs -r docker rmi + 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/vnt_manager/Config.py b/src/vnt_manager/Config.py new file mode 100644 index 0000000000000000000000000000000000000000..719815a4233422a18cc00318739b6b5af32b6ebc --- /dev/null +++ b/src/vnt_manager/Config.py @@ -0,0 +1,13 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/vnt_manager/Dockerfile b/src/vnt_manager/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..1c7137fbe5603795e5c51cf649ff89ae95f60033 --- /dev/null +++ b/src/vnt_manager/Dockerfile @@ -0,0 +1,84 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM python:3.9-slim + +# Install dependencies +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install wget g++ && \ + rm -rf /var/lib/apt/lists/* + +# Set Python to show logs as they occur +ENV PYTHONUNBUFFERED=0 +ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python + +# Download the gRPC health probe +RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe + +# Creating a user for security reasons +RUN groupadd -r teraflow && useradd -u 1001 --no-log-init -r -m -g teraflow teraflow +USER teraflow + +# set working directory +RUN mkdir -p /home/teraflow/controller/common/ +WORKDIR /home/teraflow/controller + +# Get Python packages per module +ENV VIRTUAL_ENV=/home/teraflow/venv +RUN python3 -m venv ${VIRTUAL_ENV} +ENV PATH="${VIRTUAL_ENV}/bin:${PATH}" + +# 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 +COPY --chown=teraflow:teraflow 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 /home/teraflow/controller/common +COPY --chown=teraflow:teraflow src/common/. ./ +RUN rm -rf proto + +# Create proto sub-folder, copy .proto files, and generate Python code +RUN mkdir -p /home/teraflow/controller/common/proto +WORKDIR /home/teraflow/controller/common/proto +RUN touch __init__.py +COPY --chown=teraflow:teraflow 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 module sub-folders +RUN mkdir -p /home/teraflow/controller/vnt_manager +WORKDIR /home/teraflow/controller + +# Get Python packages per module +COPY --chown=teraflow:teraflow ./src/vnt_manager/requirements.in vnt_manager/requirements.in +# consider common and specific requirements to avoid inconsistencies with dependencies +RUN pip-compile --quiet --output-file=vnt_manager/requirements.txt vnt_manager/requirements.in common_requirements.in +RUN python3 -m pip install -r vnt_manager/requirements.txt + +# Add component files into working directory +COPY --chown=teraflow:teraflow ./src/context/. context +COPY --chown=teraflow:teraflow ./src/vnt_manager/. vnt_manager + +# Start the service +ENTRYPOINT ["python", "-m", "vnt_manager.service"] diff --git a/src/vnt_manager/__init__.py b/src/vnt_manager/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..719815a4233422a18cc00318739b6b5af32b6ebc --- /dev/null +++ b/src/vnt_manager/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/vnt_manager/client/VNTManagerClient.py b/src/vnt_manager/client/VNTManagerClient.py new file mode 100644 index 0000000000000000000000000000000000000000..74f4778edd2ebf03facfb0eb5d820dae0504c0a7 --- /dev/null +++ b/src/vnt_manager/client/VNTManagerClient.py @@ -0,0 +1,105 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +import grpc + +from common.Constants import ServiceNameEnum +from common.proto.context_pb2 import Empty +from common.proto.vnt_manager_pb2 import VNTSubscriptionRequest, VNTSubscriptionReply +from common.proto.vnt_manager_pb2_grpc import VNTManagerServiceStub +from common.Settings import get_service_host, get_service_port_grpc +from common.tools.client.RetryDecorator import delay_exponential, retry +from common.tools.grpc.Tools import grpc_message_to_json +# from common.proto.e2eorchestrator_pb2 import E2EOrchestratorRequest, E2EOrchestratorReply +from common.proto.context_pb2 import ( + Link, LinkId, LinkIdList, LinkList, +) +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 VNTManagerClient: + def __init__(self, host=None, port=None): + if not host: + host = get_service_host(ServiceNameEnum.VNTMANAGER) + if not port: + port = get_service_port_grpc(ServiceNameEnum.VNTMANAGER) + 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 = VNTManagerServiceStub(self.channel) + + def close(self): + if self.channel is not None: + self.channel.close() + self.channel = None + self.stub = None + + @RETRY_DECORATOR + def VNTSubscript(self, request: VNTSubscriptionRequest) -> VNTSubscriptionReply: + LOGGER.info("Subscript request: {:s}".format(str(grpc_message_to_json(request)))) + response = self.stub.VNTSubscript(request) + LOGGER.info("Subscript result: {:s}".format(str(grpc_message_to_json(response)))) + return response + + @RETRY_DECORATOR + def ListVirtualLinkIds(self, request: Empty) -> LinkIdList: + LOGGER.debug('ListVirtualLinkIds request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.ListVirtualLinkIds(request) + LOGGER.debug('ListVirtualLinkIds result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def ListVirtualLinks(self, request: Empty) -> LinkList: + LOGGER.debug('ListVirtualLinks request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.ListVirtualLinks(request) + LOGGER.debug('ListVirtualLinks result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def GetVirtualLink(self, request: LinkId) -> Link: + LOGGER.debug('GetVirtualLink request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.GetVirtualLink(request) + LOGGER.debug('GetVirtualLink result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def SetVirtualLink(self, request: Link) -> LinkId: + LOGGER.debug('SetVirtualLink request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.SetVirtualLink(request) + LOGGER.debug('SetVirtualLink result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def RemoveVirtualLink(self, request: LinkId) -> Empty: + LOGGER.debug('RemoveVirtualLink request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.RemoveVirtualLink(request) + LOGGER.debug('RemoveVirtualLink result: {:s}'.format(grpc_message_to_json_string(response))) + return response diff --git a/src/vnt_manager/client/__init__.py b/src/vnt_manager/client/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..719815a4233422a18cc00318739b6b5af32b6ebc --- /dev/null +++ b/src/vnt_manager/client/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/vnt_manager/requirements.in b/src/vnt_manager/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..95d80d85c397cacb3e90a223e07f47b4816983c6 --- /dev/null +++ b/src/vnt_manager/requirements.in @@ -0,0 +1,16 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +networkx +websockets==12.0 \ No newline at end of file diff --git a/src/vnt_manager/service/VNTManagerService.py b/src/vnt_manager/service/VNTManagerService.py new file mode 100644 index 0000000000000000000000000000000000000000..d42e270f9644292594ea7049312e8e7a32a47353 --- /dev/null +++ b/src/vnt_manager/service/VNTManagerService.py @@ -0,0 +1,35 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from common.Constants import ServiceNameEnum +from common.proto.vnt_manager_pb2_grpc import add_VNTManagerServiceServicer_to_server +from common.Settings import get_service_port_grpc +from common.tools.service.GenericGrpcService import GenericGrpcService +from .VNTManagerServiceServicerImpl import VNTManagerServiceServicerImpl + +LOGGER = logging.getLogger(__name__) + + +class VNTManagerService(GenericGrpcService): + def __init__(self, cls_name: str = __name__): + port = get_service_port_grpc(ServiceNameEnum.VNTMANAGER) + super().__init__(port, cls_name=cls_name) + self.vntmanager_servicer = VNTManagerServiceServicerImpl() + + def install_servicers(self): + add_VNTManagerServiceServicer_to_server( + self.vntmanager_servicer, self.server + ) diff --git a/src/vnt_manager/service/VNTManagerServiceServicerImpl.py b/src/vnt_manager/service/VNTManagerServiceServicerImpl.py new file mode 100644 index 0000000000000000000000000000000000000000..1b7b85f4c27dcad6aca426669ae3c5cffb7c99c7 --- /dev/null +++ b/src/vnt_manager/service/VNTManagerServiceServicerImpl.py @@ -0,0 +1,200 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +import networkx as nx +import grpc +import time +from websockets.sync.client import connect +from common.method_wrappers.Decorator import (MetricsPool, MetricTypeEnum, safe_and_metered_rpc_method) +from common.proto.vnt_manager_pb2 import VNTSubscriptionRequest, VNTSubscriptionReply +from common.proto.vnt_manager_pb2_grpc import VNTManagerServiceServicer +from context.client.ContextClient import ContextClient +from common.proto.context_pb2 import ( + Empty, + Event, EventTypeEnum, + Link, LinkEvent, LinkId, LinkIdList, LinkList, +) +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Topology import json_topology_id +from common.proto.context_pb2 import ContextId, TopologyId +import threading +from common.proto.context_pb2 import ( + ConnectionEvent, ContextEvent, DeviceEvent, EventTypeEnum, ServiceEvent, TopologyEvent) +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from common.tests.EventTools import EVENT_CREATE, EVENT_UPDATE, check_events +from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME, INTERDOMAIN_TOPOLOGY_NAME +from typing import Any, Dict, Set +from common.proto.dlt_gateway_pb2 import DltRecordEvent, DltRecordOperationEnum, DltRecordTypeEnum +from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.grpc.Tools import grpc_message_to_json +import json + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool("VNTManager", "RPC") + +context_client: ContextClient = ContextClient() + +JSON_ADMIN_CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_NAME) +ADMIN_CONTEXT_ID = ContextId(**JSON_ADMIN_CONTEXT_ID) +ADMIN_TOPOLOGY_ID = TopologyId(**json_topology_id(DEFAULT_TOPOLOGY_NAME, context_id=JSON_ADMIN_CONTEXT_ID)) + +GET_EVENT_TIMEOUT = 0.5 + + + +HOST = "10.1.1.83" +PORT = str(8765) + + +WEBSOCKET = None + +def send_msg(msg): + try: + WEBSOCKET.send(msg) + except Exception as e: + LOGGER.info(e) + + +class VNTMEventDispatcher(threading.Thread): + def __init__(self, host, port) -> None: + LOGGER.debug('Creating VTNM connector...') + self.host = host + self.port = port + super().__init__(name='VNTMEventDispatcher', daemon=True) + self._terminate = threading.Event() + LOGGER.debug('VNTM connector created') + + def start(self) -> None: + self._terminate.clear() + return super().start() + + def stop(self): + self._terminate.set() + + def run(self) -> None: + global WEBSOCKET + + time.sleep(5) + events_collector = EventsCollector( + context_client, log_events_received=True, + activate_context_collector = False, + activate_topology_collector = True, + activate_device_collector = False, + activate_link_collector = False, + activate_service_collector = False, + activate_slice_collector = False, + activate_connection_collector = False,) + events_collector.start() + + + url = "ws://" + str(self.host) + ":" + str(self.port) + LOGGER.debug('Connecting to {}'.format(url)) + + try: + LOGGER.info("Connecting to events server...: {}".format(url)) + WEBSOCKET = connect(url) + except Exception as ex: + LOGGER.error('Error connecting to {}'.format(url)) + else: + LOGGER.info('Connected to {}'.format(url)) + context_id = json_context_id(DEFAULT_CONTEXT_NAME) + topology_id = json_topology_id(DEFAULT_TOPOLOGY_NAME, context_id) + + try: + topology_details = context_client.GetTopologyDetails(TopologyId(**topology_id)) + except Exception as ex: + LOGGER.warning('No topology found') + else: + send_msg(grpc_message_to_json_string(topology_details)) + + while not self._terminate.is_set(): + + event = events_collector.get_event(block=True, timeout=GET_EVENT_TIMEOUT) + if event is None: continue + topology_details = context_client.GetTopologyDetails(TopologyId(**topology_id)) + + to_send = grpc_message_to_json_string(topology_details) + + send_msg(to_send) + + LOGGER.info('Exiting') + events_collector.stop() + + +class VNTManagerServiceServicerImpl(VNTManagerServiceServicer): + def __init__(self): + LOGGER.debug("Creating Servicer...") + LOGGER.debug("Servicer Created") + self.links = [] + + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def VNTSubscript(self, request: VNTSubscriptionRequest, context: grpc.ServicerContext) -> VNTSubscriptionReply: + LOGGER.info("Subscript request: {:s}".format(str(grpc_message_to_json(request)))) + reply = VNTSubscriptionReply() + reply.subscription = "OK" + + event_dispatcher = VNTMEventDispatcher(request.host, int(request.port)) + + self.host = request.host + self.port = request.port + event_dispatcher.start() + + return reply + + """ @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def ListVirtualLinkIds(self, request : Empty, context : grpc.ServicerContext) -> LinkIdList: + return [link for link in context_client.ListLinks(Empty()) if link.virtual] + + return LinkIdList(link_ids=[link.link_id for link in self.links]) + """ + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def ListVirtualLinks(self, request : Empty, context : grpc.ServicerContext) -> LinkList: + return [link for link in context_client.ListLinks(Empty()).links if link.virtual] + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def GetVirtualLink(self, request : LinkId, context : grpc.ServicerContext) -> Link: + link = context_client.GetLink(request) + return link if link.virtual else Empty() + + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def SetVirtualLink(self, request : Link, context : grpc.ServicerContext) -> LinkId: + try: + send_msg(grpc_message_to_json_string(request)) + message = WEBSOCKET.recv() + + + + + + + + + message_json = json.loads(message) + link = Link(**message_json) + context_client.SetLink(link) + except Exception as e: + LOGGER.error('Exception setting virtual link={}\n\t{}'.format(request.link_id.link_uuid.uuid, e)) + return request.link_id + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def RemoveVirtualLink(self, request : LinkId, context : grpc.ServicerContext) -> Empty: + # TODO + return Empty() + diff --git a/src/vnt_manager/service/__init__.py b/src/vnt_manager/service/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..719815a4233422a18cc00318739b6b5af32b6ebc --- /dev/null +++ b/src/vnt_manager/service/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/vnt_manager/service/__main__.py b/src/vnt_manager/service/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..40918ecce622ae31bedfb9924976dc36c17f8c98 --- /dev/null +++ b/src/vnt_manager/service/__main__.py @@ -0,0 +1,74 @@ +# Copyright 2022-2024 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import signal +import sys +import threading + +from prometheus_client import start_http_server + +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 .VNTManagerService import VNTManagerService + +terminate = threading.Event() +LOGGER = None + + +def signal_handler(signal, frame): # pylint: disable=redefined-outer-name + LOGGER.warning("Terminate signal received") + terminate.set() + + +def main(): + global LOGGER # pylint: disable=global-statement + + log_level = get_log_level() + logging.basicConfig(level=log_level) + LOGGER = logging.getLogger(__name__) + + + 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) + + # Starting VNTManager service + grpc_service = VNTManagerService() + grpc_service.start() + LOGGER.info("Started...") + # Wait for Ctrl+C or termination signal + + while not terminate.wait(timeout=1): + pass + + + LOGGER.info("Terminating...") + grpc_service.stop() + + LOGGER.info("Bye") + return 0 + + +if __name__ == "__main__": + sys.exit(main())