diff --git a/oeccpsc22 b/oeccpsc22 new file mode 120000 index 0000000000000000000000000000000000000000..4f55befad3e8730c8b7eb1a4cf2fbc7600d1878b --- /dev/null +++ b/oeccpsc22 @@ -0,0 +1 @@ +src/tests/oeccpsc22/ \ No newline at end of file diff --git a/src/tests/.gitlab-ci.yml b/src/tests/.gitlab-ci.yml index e663b09ec1d79a14f76d37a4ac906e534667ac26..6fcac64803e92b238eb1a63ce92814e67e3138ab 100644 --- a/src/tests/.gitlab-ci.yml +++ b/src/tests/.gitlab-ci.yml @@ -14,4 +14,5 @@ # include the individual .gitlab-ci.yml of each integration test include: - - local: '/src/tests/ofc22_bootstrap_monitor_l3vpn/.gitlab-ci.yml' + - local: '/src/tests/ofc22/.gitlab-ci.yml' + - local: '/src/tests/oeccpsc22/.gitlab-ci.yml' diff --git a/src/tests/oeccpsc22/README.md b/src/tests/oeccpsc22/README.md new file mode 100644 index 0000000000000000000000000000000000000000..42e0228a52bdf9dfc21bc0358b78fb98677ed458 --- /dev/null +++ b/src/tests/oeccpsc22/README.md @@ -0,0 +1,8 @@ +# OECC/PSC'22 Paper - Interdomain slices +This functional test reproduces the experiment in paper "... paper title ..." presented at OECC/PSC'22 conference +[OECC/PSC'22](... demo link ...). + +## Functional test folder +This functional test can be found in folder `./src/tests/oeccpsc22/`. A convenience alias `./oeccpsc22/` pointing to that folder has been defined. + +# TO BE WRITTEN diff --git a/src/tests/oeccpsc22/__init__.py b/src/tests/oeccpsc22/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7 --- /dev/null +++ b/src/tests/oeccpsc22/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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/oeccpsc22/deploy_in_kubernetes.sh b/src/tests/oeccpsc22/deploy_in_kubernetes.sh new file mode 100755 index 0000000000000000000000000000000000000000..f40257f341c006e24e24ed961f5d1a0367ef5a11 --- /dev/null +++ b/src/tests/oeccpsc22/deploy_in_kubernetes.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# OECC/PSC 22 deployment settings + +export REGISTRY_IMAGE="" +export COMPONENTS="context device service compute monitoring interdomain webui" # slice +export IMAGE_TAG="oeccpsc22" +export K8S_HOSTNAME="kubernetes-master" +#export GRAFANA_PASSWORD="admin123+" + +# Deploy TeraFlow instance 1 +export K8S_NAMESPACE="oeccpsc22-1" +export EXTRA_MANIFESTS="./oeccpsc22/expose_services_teraflow_1.yaml" +./deploy_in_kubernetes.sh + +# Deploy TeraFlow instance 2 +export K8S_NAMESPACE="oeccpsc22-2" +export EXTRA_MANIFESTS="./oeccpsc22/expose_services_teraflow_2.yaml" +./deploy_in_kubernetes.sh diff --git a/src/tests/oeccpsc22/expose_services_teraflow_1.yaml b/src/tests/oeccpsc22/expose_services_teraflow_1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6d2f0bed72633608a2a0cd80235e7ce4b3d723c5 --- /dev/null +++ b/src/tests/oeccpsc22/expose_services_teraflow_1.yaml @@ -0,0 +1,101 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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: Service +metadata: + name: remote-teraflow +spec: + type: ExternalName + externalName: interdomainservice.oeccpsc22-2.svc.cluster.local + ports: + - name: grpc + protocol: TCP + port: 10010 +--- +apiVersion: v1 +kind: Service +metadata: + name: contextservice-public + labels: + app: contextservice +spec: + type: NodePort + selector: + app: contextservice + ports: + - name: grpc + protocol: TCP + port: 1010 + targetPort: 1010 + nodePort: 30111 + - name: redis + protocol: TCP + port: 6379 + targetPort: 6379 + nodePort: 30631 +--- +apiVersion: v1 +kind: Service +metadata: + name: deviceservice-public + labels: + app: deviceservice +spec: + type: NodePort + selector: + app: deviceservice + ports: + - name: grpc + protocol: TCP + port: 2020 + targetPort: 2020 + nodePort: 30221 +--- +apiVersion: v1 +kind: Service +metadata: + name: computeservice-public +spec: + type: NodePort + selector: + app: computeservice + ports: + - name: http + protocol: TCP + port: 8080 + targetPort: 8080 + nodePort: 30881 +--- +apiVersion: v1 +kind: Service +metadata: + name: webuiservice-public + labels: + app: webuiservice +spec: + type: NodePort + selector: + app: webuiservice + ports: + - name: http + protocol: TCP + port: 8004 + targetPort: 8004 + nodePort: 30801 + - name: grafana + protocol: TCP + port: 3000 + targetPort: 3000 + nodePort: 30301 diff --git a/src/tests/oeccpsc22/expose_services_teraflow_2.yaml b/src/tests/oeccpsc22/expose_services_teraflow_2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..32974848ea91560d9fbfe511cae99010a7471a5d --- /dev/null +++ b/src/tests/oeccpsc22/expose_services_teraflow_2.yaml @@ -0,0 +1,101 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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: Service +metadata: + name: remote-teraflow +spec: + type: ExternalName + externalName: interdomainservice.oeccpsc22-2.svc.cluster.local + ports: + - name: grpc + protocol: TCP + port: 10010 +--- +apiVersion: v1 +kind: Service +metadata: + name: contextservice-public + labels: + app: contextservice +spec: + type: NodePort + selector: + app: contextservice + ports: + - name: grpc + protocol: TCP + port: 1010 + targetPort: 1010 + nodePort: 30112 + - name: redis + protocol: TCP + port: 6379 + targetPort: 6379 + nodePort: 30632 +--- +apiVersion: v1 +kind: Service +metadata: + name: deviceservice-public + labels: + app: deviceservice +spec: + type: NodePort + selector: + app: deviceservice + ports: + - name: grpc + protocol: TCP + port: 2020 + targetPort: 2020 + nodePort: 30222 +--- +apiVersion: v1 +kind: Service +metadata: + name: computeservice-public +spec: + type: NodePort + selector: + app: computeservice + ports: + - name: http + protocol: TCP + port: 8080 + targetPort: 8080 + nodePort: 30882 +--- +apiVersion: v1 +kind: Service +metadata: + name: webuiservice-public + labels: + app: webuiservice +spec: + type: NodePort + selector: + app: webuiservice + ports: + - name: http + protocol: TCP + port: 8004 + targetPort: 8004 + nodePort: 30802 + - name: grafana + protocol: TCP + port: 3000 + targetPort: 3000 + nodePort: 30302 diff --git a/src/tests/oeccpsc22/run_test_01_bootstrap.sh b/src/tests/oeccpsc22/run_test_01_bootstrap.sh new file mode 100755 index 0000000000000000000000000000000000000000..7b816984a17f7f5a30ce8eaafc6d831c615ce3e0 --- /dev/null +++ b/src/tests/oeccpsc22/run_test_01_bootstrap.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc +COVERAGEFILE=$PROJECTDIR/coverage/.coverage + +# Configure the correct folder on the .coveragerc file +cat $PROJECTDIR/coverage/.coveragerc.template | sed s+~/teraflow/controller+$PROJECTDIR+g > $RCFILE + +# Destroy old coverage file +rm -f $COVERAGEFILE + +# Set the name of the Kubernetes namespace and hostname to use. +K8S_NAMESPACE_D1="oeccpsc22-1" +K8S_NAMESPACE_D2="oeccpsc22-2" +# K8S_HOSTNAME="kubernetes-master" +# dynamically gets the name of the K8s master node +K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` + +# Flush Context database +kubectl --namespace $K8S_NAMESPACE_D1 exec -it deployment/contextservice --container redis -- redis-cli FLUSHALL +kubectl --namespace $K8S_NAMESPACE_D2 exec -it deployment/contextservice --container redis -- redis-cli FLUSHALL + +export D1_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D1_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') + +export D2_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D2_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') + +# Useful flags for pytest: +#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG + +# Run functional test and analyze coverage of code at same time + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + tests/oeccpsc22/tests/test_functional_bootstrap.py diff --git a/src/tests/oeccpsc22/run_test_02_create_service.sh b/src/tests/oeccpsc22/run_test_02_create_service.sh new file mode 100755 index 0000000000000000000000000000000000000000..c01f64741fe54e4e5a889080f4e680660435c093 --- /dev/null +++ b/src/tests/oeccpsc22/run_test_02_create_service.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc +COVERAGEFILE=$PROJECTDIR/coverage/.coverage + +# Set the name of the Kubernetes namespace and hostname to use. +K8S_NAMESPACE="oeccpsc22" +# dynamically gets the name of the K8s master node +K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` + +export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +# Useful flags for pytest: +#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG + +# Run functional test and analyze coverage of code at same time + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + tests/oeccpsc22/tests/test_functional_create_service.py diff --git a/src/tests/oeccpsc22/run_test_03_delete_service.sh b/src/tests/oeccpsc22/run_test_03_delete_service.sh new file mode 100755 index 0000000000000000000000000000000000000000..1782a143bdf6b3257ae8dbf75d94803dcde2275c --- /dev/null +++ b/src/tests/oeccpsc22/run_test_03_delete_service.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc +COVERAGEFILE=$PROJECTDIR/coverage/.coverage + +# Set the name of the Kubernetes namespace and hostname to use. +K8S_NAMESPACE="oeccpsc22" +# dynamically gets the name of the K8s master node +K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` + +export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +# Useful flags for pytest: +#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG + +# Run functional test and analyze coverage of code at same time + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + tests/oeccpsc22/tests/test_functional_delete_service.py diff --git a/src/tests/oeccpsc22/run_test_04_cleanup.sh b/src/tests/oeccpsc22/run_test_04_cleanup.sh new file mode 100755 index 0000000000000000000000000000000000000000..14b4024be28dba06add413fb775b0a6ed090af74 --- /dev/null +++ b/src/tests/oeccpsc22/run_test_04_cleanup.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc +COVERAGEFILE=$PROJECTDIR/coverage/.coverage + +# Set the name of the Kubernetes namespace and hostname to use. +K8S_NAMESPACE="oeccpsc22" +# dynamically gets the name of the K8s master node +K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` + +export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +# Useful flags for pytest: +#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG + +# Run functional test and analyze coverage of code at same time + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + tests/oeccpsc22/tests/test_functional_cleanup.py diff --git a/src/tests/oeccpsc22/show_deploy.sh b/src/tests/oeccpsc22/show_deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..90d6914890cfd37db37ed3b3ea8266372c067c20 --- /dev/null +++ b/src/tests/oeccpsc22/show_deploy.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Deploy TeraFlow instance 1 +printf "TeraFlow Instance 1:\n--------------------\n" +export K8S_NAMESPACE="oeccpsc22-1" +kubectl --namespace $K8S_NAMESPACE get all + +printf "\n\n" + +# Deploy TeraFlow instance 2 +printf "TeraFlow Instance 2:\n--------------------\n" +export K8S_NAMESPACE="oeccpsc22-2" +kubectl --namespace $K8S_NAMESPACE get all diff --git a/src/tests/oeccpsc22/tests/.gitignore b/src/tests/oeccpsc22/tests/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..76cb708d1b532c9b69166e55f36bcb912fd5e370 --- /dev/null +++ b/src/tests/oeccpsc22/tests/.gitignore @@ -0,0 +1,2 @@ +# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc. +Credentials.py diff --git a/src/tests/oeccpsc22/tests/Objects_Domain_1.py b/src/tests/oeccpsc22/tests/Objects_Domain_1.py new file mode 100644 index 0000000000000000000000000000000000000000..af353c6e9ca3c863387e5d72dbef005fcb516a5a --- /dev/null +++ b/src/tests/oeccpsc22/tests/Objects_Domain_1.py @@ -0,0 +1,121 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID +from common.tools.object_factory.Context import json_context, json_context_id +from common.tools.object_factory.Device import ( + json_device_emulated_connect_rules, json_device_emulated_packet_router_disabled, json_device_id) +from common.tools.object_factory.Link import json_link, json_link_id +from common.tools.object_factory.Topology import json_topology, json_topology_id +from .Tools import get_link_uuid, json_endpoint_ids + +# ----- Context -------------------------------------------------------------------------------------------------------- +D1_CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID) +D1_CONTEXT = json_context(DEFAULT_CONTEXT_UUID) + +# ----- Topology ------------------------------------------------------------------------------------------------------- +D1_TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=D1_CONTEXT_ID) +D1_TOPOLOGY = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=D1_CONTEXT_ID) + +# ----- Devices -------------------------------------------------------------------------------------------------------- +# Assume all devices have the same architecture of endpoints +DEVICE_ENDPOINT_DEFS = [ + # Trunk ports + ('1/1', '25Gbps', []), ('1/2', '25Gbps', []), ('1/3', '25Gbps', []), ('1/4', '25Gbps', []), + # Inter-domain ports + ('2/1', '100Gbps', []), ('2/2', '100Gbps', []), + # Access ports + ('3/1', '10Gbps', []), ('3/2', '10Gbps', []), ('3/3', '10Gbps', []), ('3/4', '10Gbps', []), + ('3/5', '10Gbps', []), ('3/6', '10Gbps', []), ('3/7', '10Gbps', []), ('3/8', '10Gbps', []), +] + +DEVICE_D1R1_UUID = 'D1-R1' +DEVICE_D1R1_ID = json_device_id(DEVICE_D1R1_UUID) +DEVICE_D1R1 = json_device_emulated_packet_router_disabled(DEVICE_D1R1_UUID) +DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D1R2_UUID = 'D1-R2' +DEVICE_D1R2_ID = json_device_id(DEVICE_D1R2_UUID) +DEVICE_D1R2 = json_device_emulated_packet_router_disabled(DEVICE_D1R2_UUID) +DEVICE_D1R2_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D1R3_UUID = 'D1-R3' +DEVICE_D1R3_ID = json_device_id(DEVICE_D1R3_UUID) +DEVICE_D1R3 = json_device_emulated_packet_router_disabled(DEVICE_D1R3_UUID) +DEVICE_D1R3_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D1R4_UUID = 'D1-R4' +DEVICE_D1R4_ID = json_device_id(DEVICE_D1R4_UUID) +DEVICE_D1R4 = json_device_emulated_packet_router_disabled(DEVICE_D1R4_UUID) +DEVICE_D1R4_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +# Virtual devices on remote domains +DEVICE_D2R1_UUID = 'D2-R1' +DEVICE_D2R1_ID = json_device_id(DEVICE_D2R1_UUID) +DEVICE_D2R1 = json_device_emulated_packet_router_disabled(DEVICE_D2R1_UUID) +DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +ENDPOINT_IDS = {} +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R1_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R2_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R3_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R4_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R1_ID, DEVICE_ENDPOINT_DEFS)) + + +# ----- Links ---------------------------------------------------------------------------------------------------------- +# Intra-domain links +LINK_D1R1_D1R2_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/1']) +LINK_D1R1_D1R2_ID = json_link_id(LINK_D1R1_D1R2_UUID) +LINK_D1R1_D1R2 = json_link(LINK_D1R1_D1R2_UUID, [ + ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/1']]) + +LINK_D1R2_D1R3_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/1']) +LINK_D1R2_D1R3_ID = json_link_id(LINK_D1R2_D1R3_UUID) +LINK_D1R2_D1R3 = json_link(LINK_D1R2_D1R3_UUID, [ + ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/1']]) + +LINK_D1R3_D1R4_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/1']) +LINK_D1R3_D1R4_ID = json_link_id(LINK_D1R3_D1R4_UUID) +LINK_D1R3_D1R4 = json_link(LINK_D1R3_D1R4_UUID, [ + ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/1']]) + +LINK_D1R4_D1R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/1']) +LINK_D1R4_D1R1_ID = json_link_id(LINK_D1R4_D1R1_UUID) +LINK_D1R4_D1R1 = json_link(LINK_D1R4_D1R1_UUID, [ + ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/1']]) + +# Inter-domain links +LINK_D1R4_D2R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['2/1']) +LINK_D1R4_D2R1_ID = json_link_id(LINK_D1R4_D2R1_UUID) +LINK_D1R4_D2R1 = json_link(LINK_D1R4_D2R1_UUID, [ + ENDPOINT_IDS[DEVICE_D1R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['2/1']]) + +# ----- Object Collections --------------------------------------------------------------------------------------------- + +D1_CONTEXTS = [D1_CONTEXT] +D1_TOPOLOGIES = [D1_TOPOLOGY] + +D1_DEVICES = [ + (DEVICE_D1R1, DEVICE_D1R1_CONNECT_RULES), + (DEVICE_D1R2, DEVICE_D1R2_CONNECT_RULES), + (DEVICE_D1R3, DEVICE_D1R3_CONNECT_RULES), + (DEVICE_D1R4, DEVICE_D1R4_CONNECT_RULES), + (DEVICE_D2R1, DEVICE_D2R1_CONNECT_RULES), +] + +D1_LINKS = [ + LINK_D1R1_D1R2, LINK_D1R2_D1R3, LINK_D1R3_D1R4, LINK_D1R4_D1R1, + LINK_D1R4_D2R1, +] diff --git a/src/tests/oeccpsc22/tests/Objects_Domain_2.py b/src/tests/oeccpsc22/tests/Objects_Domain_2.py new file mode 100644 index 0000000000000000000000000000000000000000..f7798925096f9fa6b49c81c7a6e628afac23bed8 --- /dev/null +++ b/src/tests/oeccpsc22/tests/Objects_Domain_2.py @@ -0,0 +1,121 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID +from common.tools.object_factory.Context import json_context, json_context_id +from common.tools.object_factory.Device import ( + json_device_emulated_connect_rules, json_device_emulated_packet_router_disabled, json_device_id) +from common.tools.object_factory.Link import json_link, json_link_id +from common.tools.object_factory.Topology import json_topology, json_topology_id +from .Tools import get_link_uuid, json_endpoint_ids + +# ----- Context -------------------------------------------------------------------------------------------------------- +D2_CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID) +D2_CONTEXT = json_context(DEFAULT_CONTEXT_UUID) + +# ----- Topology ------------------------------------------------------------------------------------------------------- +D2_TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=D2_CONTEXT_ID) +D2_TOPOLOGY = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=D2_CONTEXT_ID) + +# ----- Devices -------------------------------------------------------------------------------------------------------- +# Assume all devices have the same architecture of endpoints +DEVICE_ENDPOINT_DEFS = [ + # Trunk ports + ('1/1', '25Gbps', []), ('1/2', '25Gbps', []), ('1/3', '25Gbps', []), ('1/4', '25Gbps', []), + # Inter-domain ports + ('2/1', '100Gbps', []), ('2/2', '100Gbps', []), + # Access ports + ('3/1', '10Gbps', []), ('3/2', '10Gbps', []), ('3/3', '10Gbps', []), ('3/4', '10Gbps', []), + ('3/5', '10Gbps', []), ('3/6', '10Gbps', []), ('3/7', '10Gbps', []), ('3/8', '10Gbps', []), +] + +DEVICE_D2R1_UUID = 'D2-R1' +DEVICE_D2R1_ID = json_device_id(DEVICE_D2R1_UUID) +DEVICE_D2R1 = json_device_emulated_packet_router_disabled(DEVICE_D2R1_UUID) +DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D2R2_UUID = 'D2-R2' +DEVICE_D2R2_ID = json_device_id(DEVICE_D2R2_UUID) +DEVICE_D2R2 = json_device_emulated_packet_router_disabled(DEVICE_D2R2_UUID) +DEVICE_D2R2_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D2R3_UUID = 'D2-R3' +DEVICE_D2R3_ID = json_device_id(DEVICE_D2R3_UUID) +DEVICE_D2R3 = json_device_emulated_packet_router_disabled(DEVICE_D2R3_UUID) +DEVICE_D2R3_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D2R4_UUID = 'D2-R4' +DEVICE_D2R4_ID = json_device_id(DEVICE_D2R4_UUID) +DEVICE_D2R4 = json_device_emulated_packet_router_disabled(DEVICE_D2R4_UUID) +DEVICE_D2R4_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +# Virtual devices on remote domains +DEVICE_D1R1_UUID = 'D1-R1' +DEVICE_D1R1_ID = json_device_id(DEVICE_D1R1_UUID) +DEVICE_D1R1 = json_device_emulated_packet_router_disabled(DEVICE_D1R1_UUID) +DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +ENDPOINT_IDS = {} +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R1_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R2_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R3_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R4_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R1_ID, DEVICE_ENDPOINT_DEFS)) + + +# ----- Links ---------------------------------------------------------------------------------------------------------- +# Intra-domain links +LINK_D2R1_D2R2_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/1']) +LINK_D2R1_D2R2_ID = json_link_id(LINK_D2R1_D2R2_UUID) +LINK_D2R1_D2R2 = json_link(LINK_D2R1_D2R2_UUID, [ + ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/1']]) + +LINK_D2R2_D2R3_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/1']) +LINK_D2R2_D2R3_ID = json_link_id(LINK_D2R2_D2R3_UUID) +LINK_D2R2_D2R3 = json_link(LINK_D2R2_D2R3_UUID, [ + ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/1']]) + +LINK_D2R3_D2R4_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/1']) +LINK_D2R3_D2R4_ID = json_link_id(LINK_D2R3_D2R4_UUID) +LINK_D2R3_D2R4 = json_link(LINK_D2R3_D2R4_UUID, [ + ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/1']]) + +LINK_D2R4_D2R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/1']) +LINK_D2R4_D2R1_ID = json_link_id(LINK_D2R4_D2R1_UUID) +LINK_D2R4_D2R1 = json_link(LINK_D2R4_D2R1_UUID, [ + ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/1']]) + +# Inter-domain links +LINK_D2R4_D1R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['2/1']) +LINK_D2R4_D1R1_ID = json_link_id(LINK_D2R4_D1R1_UUID) +LINK_D2R4_D1R1 = json_link(LINK_D2R4_D1R1_UUID, [ + ENDPOINT_IDS[DEVICE_D2R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['2/1']]) + +# ----- Object Collections --------------------------------------------------------------------------------------------- + +D2_CONTEXTS = [D2_CONTEXT] +D2_TOPOLOGIES = [D2_TOPOLOGY] + +D2_DEVICES = [ + (DEVICE_D2R1, DEVICE_D2R1_CONNECT_RULES), + (DEVICE_D2R2, DEVICE_D2R2_CONNECT_RULES), + (DEVICE_D2R3, DEVICE_D2R3_CONNECT_RULES), + (DEVICE_D2R4, DEVICE_D2R4_CONNECT_RULES), + (DEVICE_D1R1, DEVICE_D1R1_CONNECT_RULES), +] + +D2_LINKS = [ + LINK_D2R1_D2R2, LINK_D2R2_D2R3, LINK_D2R3_D2R4, LINK_D2R4_D2R1, + LINK_D2R4_D1R1, +] diff --git a/src/tests/oeccpsc22/tests/Objects_Service.py b/src/tests/oeccpsc22/tests/Objects_Service.py new file mode 100644 index 0000000000000000000000000000000000000000..b9ec2a691e03381ff54dc08053063cad87cdeb3b --- /dev/null +++ b/src/tests/oeccpsc22/tests/Objects_Service.py @@ -0,0 +1,35 @@ + + +# ----- WIM Service Settings ------------------------------------------------------------------------------------------- +WIM_SEP_R1_ID = compose_service_endpoint_id(ENDPOINT_ID_R1_13_1_2) +WIM_SEP_R1_ROUTER_ID = '10.10.10.1' +WIM_SEP_R1_ROUTER_DIST = '65000:111' +WIM_SEP_R1_SITE_ID = '1' +WIM_SEP_R1_BEARER = compose_bearer(ENDPOINT_ID_R1_13_1_2, WIM_SEP_R1_ROUTER_ID, WIM_SEP_R1_ROUTER_DIST) +WIM_SRV_R1_VLAN_ID = 400 + +WIM_SEP_R3_ID = compose_service_endpoint_id(ENDPOINT_ID_R3_13_1_2) +WIM_SEP_R3_ROUTER_ID = '20.20.20.1' +WIM_SEP_R3_ROUTER_DIST = '65000:222' +WIM_SEP_R3_SITE_ID = '2' +WIM_SEP_R3_BEARER = compose_bearer(ENDPOINT_ID_R3_13_1_2, WIM_SEP_R3_ROUTER_ID, WIM_SEP_R3_ROUTER_DIST) +WIM_SRV_R3_VLAN_ID = 500 + +WIM_USERNAME = 'admin' +WIM_PASSWORD = 'admin' + +WIM_MAPPING = [ + {'device-id': DEVICE_R1_UUID, 'service_endpoint_id': WIM_SEP_R1_ID, + 'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_R1_BEARER}, 'site-id': WIM_SEP_R1_SITE_ID}}, + {'device-id': DEVICE_R3_UUID, 'service_endpoint_id': WIM_SEP_R3_ID, + 'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_R3_BEARER}, 'site-id': WIM_SEP_R3_SITE_ID}}, +] +WIM_SERVICE_TYPE = 'ELINE' +WIM_SERVICE_CONNECTION_POINTS = [ + {'service_endpoint_id': WIM_SEP_R1_ID, + 'service_endpoint_encapsulation_type': 'dot1q', + 'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_R1_VLAN_ID}}, + {'service_endpoint_id': WIM_SEP_R3_ID, + 'service_endpoint_encapsulation_type': 'dot1q', + 'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_R3_VLAN_ID}}, +] diff --git a/src/tests/oeccpsc22/tests/Tools.py b/src/tests/oeccpsc22/tests/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..a782b6bb3e541e4331f5f95164e69def5640f556 --- /dev/null +++ b/src/tests/oeccpsc22/tests/Tools.py @@ -0,0 +1,25 @@ +from typing import Dict, List, Tuple +from common.tools.object_factory.EndPoint import json_endpoint_id + +def json_endpoint_ids(device_id : Dict, endpoint_descriptors : List[Tuple[str, str, List[int]]]): + return { + device_id['device_uuid']['uuid']: { + ep_uuid: json_endpoint_id(device_id, ep_uuid, topology_id=None) + for ep_uuid, _, _ in endpoint_descriptors + } + } + +def get_link_uuid(a_endpoint_id : Dict, z_endpoint_id : Dict) -> str: + return '{:s}/{:s}=={:s}/{:s}'.format( + a_endpoint_id['device_id']['device_uuid']['uuid'], a_endpoint_id['endpoint_uuid']['uuid'], + a_endpoint_id['device_id']['device_uuid']['uuid'], z_endpoint_id['endpoint_uuid']['uuid']) + +def compose_service_endpoint_id(endpoint_id): + device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] + endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] + return ':'.join([device_uuid, endpoint_uuid]) + +def compose_bearer(endpoint_id): + device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] + endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] + return ':'.join([device_uuid, endpoint_uuid]) diff --git a/src/tests/oeccpsc22/tests/__init__.py b/src/tests/oeccpsc22/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7 --- /dev/null +++ b/src/tests/oeccpsc22/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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/oeccpsc22/tests/test_functional_bootstrap.py b/src/tests/oeccpsc22/tests/test_functional_bootstrap.py new file mode 100644 index 0000000000000000000000000000000000000000..b09b558cd96bf794b26dadc99913100bb0ca9de1 --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_bootstrap.py @@ -0,0 +1,208 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy, logging, pytest +from common.Settings import get_setting +from common.tests.EventTools import EVENT_CREATE, check_events +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Link import json_link_id +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.proto.context_pb2 import Context, ContextId, Device, Empty, Link, Topology +from device.client.DeviceClient import DeviceClient +from .Objects_Domain_1 import D1_CONTEXT_ID, D1_CONTEXTS, D1_DEVICES, D1_LINKS, D1_TOPOLOGIES +from .Objects_Domain_2 import D2_CONTEXT_ID, D2_CONTEXTS, D2_DEVICES, D2_LINKS, D2_TOPOLOGIES + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +@pytest.fixture(scope='session') +def d1_context_client(): + _client = ContextClient( + get_setting('D1_CONTEXTSERVICE_SERVICE_HOST'), get_setting('D1_CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +@pytest.fixture(scope='session') +def d1_device_client(): + _client = DeviceClient( + get_setting('D1_DEVICESERVICE_SERVICE_HOST'), get_setting('D1_DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +@pytest.fixture(scope='session') +def d2_context_client(): + _client = ContextClient( + get_setting('D2_CONTEXTSERVICE_SERVICE_HOST'), get_setting('D2_CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +@pytest.fixture(scope='session') +def d2_device_client(): + _client = DeviceClient( + get_setting('D2_DEVICESERVICE_SERVICE_HOST'), get_setting('D2_DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +def test_scenario_empty( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == 0 + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == 0 + + response = context_client.ListLinks(Empty()) + assert len(response.links) == 0 + + # ----- List entities - Ensure database is empty ------------------------------------------------------------------- + per_domain(d1_context_client) + per_domain(d2_context_client) + + +def test_prepare_scenario( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, context_client): + for context in contexts: + context_uuid = context['context_id']['context_uuid']['uuid'] + LOGGER.info('Adding Context {:s}'.format(context_uuid)) + response = context_client.SetContext(Context(**context)) + assert response.context_uuid.uuid == context_uuid + + for topology in topologies: + context_uuid = topology['topology_id']['context_id']['context_uuid']['uuid'] + topology_uuid = topology['topology_id']['topology_uuid']['uuid'] + LOGGER.info('Adding Topology {:s}/{:s}'.format(context_uuid, topology_uuid)) + response = context_client.SetTopology(Topology(**topology)) + assert response.context_id.context_uuid.uuid == context_uuid + assert response.topology_uuid.uuid == topology_uuid + + # ----- Create Contexts and Topologies ----------------------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, d2_context_client) + + +def test_scenario_ready( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == 0 + + response = context_client.ListLinks(Empty()) + assert len(response.links) == 0 + + response = context_client.ListServices(ContextId(**context_id)) + assert len(response.services) == 0 + + # ----- List entities - Ensure scenario is ready ------------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_CONTEXT_ID, d2_context_client) + + +def test_devices_bootstraping( + d1_device_client : DeviceClient, # pylint: disable=redefined-outer-name + d2_device_client : DeviceClient): # pylint: disable=redefined-outer-name + + def per_domain(devices, device_client): + for device, connect_rules in devices: + device_uuid = device['device_id']['device_uuid']['uuid'] + LOGGER.info('Adding Device {:s}'.format(device_uuid)) + device_with_connect_rules = copy.deepcopy(device) + device_with_connect_rules['device_config']['config_rules'].extend(connect_rules) + response = device_client.AddDevice(Device(**device_with_connect_rules)) + assert response.device_uuid.uuid == device_uuid + + # ----- Create Devices and Validate Collected Events --------------------------------------------------------------- + per_domain(D1_DEVICES, d1_device_client) + per_domain(D2_DEVICES, d2_device_client) + + +def test_devices_bootstrapped( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, devices, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(devices) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == 0 + + response = context_client.ListServices(ContextId(**context_id)) + assert len(response.services) == 0 + + # ----- List entities - Ensure bevices are created ----------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_DEVICES, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_CONTEXT_ID, d2_context_client) + + +def test_links_creation( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(links, context_client): + for link in links: + link_uuid = link['link_id']['link_uuid']['uuid'] + LOGGER.info('Adding Link {:s}'.format(link_uuid)) + response = context_client.SetLink(Link(**link)) + assert response.link_uuid.uuid == link_uuid + + # ----- Create Links and Validate Collected Events ----------------------------------------------------------------- + per_domain(D1_LINKS, d1_context_client) + per_domain(D2_LINKS, d2_context_client) + + +def test_links_created( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, devices, links, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(devices) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(links) + + response = context_client.ListServices(ContextId(**context_id)) + assert len(response.services) == 0 + + # ----- List entities - Ensure links are created ------------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_DEVICES, D1_LINKS, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_LINKS, D2_CONTEXT_ID, d2_context_client) diff --git a/src/tests/oeccpsc22/tests/test_functional_cleanup.py b/src/tests/oeccpsc22/tests/test_functional_cleanup.py new file mode 100644 index 0000000000000000000000000000000000000000..eb78a585079e3ee757a836433bf23423a3ad899d --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_cleanup.py @@ -0,0 +1,123 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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, pytest +from common.Settings import get_setting +from common.tests.EventTools import EVENT_REMOVE, check_events +from common.tools.object_factory.Context import json_context_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.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.proto.context_pb2 import ContextId, DeviceId, Empty, LinkId, TopologyId +from device.client.DeviceClient import DeviceClient +from .Objects import CONTEXT_ID, CONTEXTS, DEVICES, LINKS, TOPOLOGIES + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def device_client(): + _client = DeviceClient(get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +def test_services_removed(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure service is removed ------------------------------------------------------------------ + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + assert len(response.services) == 0 + + +def test_scenario_cleanup( + context_client : ContextClient, device_client : DeviceClient): # pylint: disable=redefined-outer-name + + # ----- Start the EventsCollector ---------------------------------------------------------------------------------- + events_collector = EventsCollector(context_client) + events_collector.start() + + expected_events = [] + + # ----- Delete Links and Validate Collected Events ----------------------------------------------------------------- + for link in LINKS: + link_id = link['link_id'] + link_uuid = link_id['link_uuid']['uuid'] + LOGGER.info('Deleting Link {:s}'.format(link_uuid)) + context_client.RemoveLink(LinkId(**link_id)) + expected_events.append(('LinkEvent', EVENT_REMOVE, json_link_id(link_uuid))) + + # ----- Delete Devices and Validate Collected Events --------------------------------------------------------------- + for device, _ in DEVICES: + device_id = device['device_id'] + device_uuid = device_id['device_uuid']['uuid'] + LOGGER.info('Deleting Device {:s}'.format(device_uuid)) + device_client.DeleteDevice(DeviceId(**device_id)) + expected_events.append(('DeviceEvent', EVENT_REMOVE, json_device_id(device_uuid))) + + # ----- Delete Topologies and Validate Collected Events ------------------------------------------------------------ + for topology in TOPOLOGIES: + topology_id = topology['topology_id'] + context_uuid = topology_id['context_id']['context_uuid']['uuid'] + topology_uuid = topology_id['topology_uuid']['uuid'] + LOGGER.info('Deleting Topology {:s}/{:s}'.format(context_uuid, topology_uuid)) + context_client.RemoveTopology(TopologyId(**topology_id)) + context_id = json_context_id(context_uuid) + expected_events.append(('TopologyEvent', EVENT_REMOVE, json_topology_id(topology_uuid, context_id=context_id))) + + # ----- Delete Contexts and Validate Collected Events -------------------------------------------------------------- + for context in CONTEXTS: + context_id = context['context_id'] + context_uuid = context_id['context_uuid']['uuid'] + LOGGER.info('Deleting Context {:s}'.format(context_uuid)) + context_client.RemoveContext(ContextId(**context_id)) + expected_events.append(('ContextEvent', EVENT_REMOVE, json_context_id(context_uuid))) + + # ----- Validate Collected Events ---------------------------------------------------------------------------------- + check_events(events_collector, expected_events) + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + +def test_scenario_empty_again(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure database is empty again ------------------------------------------------------------- + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == 0 + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == 0 + + response = context_client.ListLinks(Empty()) + assert len(response.links) == 0 diff --git a/src/tests/oeccpsc22/tests/test_functional_create_service.py b/src/tests/oeccpsc22/tests/test_functional_create_service.py new file mode 100644 index 0000000000000000000000000000000000000000..f3389fdbfce4e9262ffddbad876bb86f9b300551 --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_create_service.py @@ -0,0 +1,129 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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, pytest +from common.DeviceTypes import DeviceTypeEnum +from common.Settings import get_setting +from common.tests.EventTools import EVENT_CREATE, EVENT_UPDATE, check_events +from common.tools.object_factory.Connection import json_connection_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Service import json_service_id +from common.tools.grpc.Tools import grpc_message_to_json_string +from compute.tests.mock_osm.MockOSM import MockOSM +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.proto.context_pb2 import ContextId, Empty +from .Objects import ( + CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, + WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME) + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DEVTYPE_EMU_PR = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value +DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value + + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def osm_wim(): + wim_url = 'http://{:s}:{:s}'.format( + get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP'))) + return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) + + +def test_scenario_is_correct(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure links are created ------------------------------------------------------------------- + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + assert len(response.services) == 0 + + +def test_service_creation(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name + # ----- Start the EventsCollector ---------------------------------------------------------------------------------- + events_collector = EventsCollector(context_client, log_events_received=True) + events_collector.start() + + # ----- Create Service --------------------------------------------------------------------------------------------- + service_uuid = osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS) + osm_wim.get_connectivity_service_status(service_uuid) + + # ----- Validate collected events ---------------------------------------------------------------------------------- + + packet_connection_uuid = '{:s}:{:s}'.format(service_uuid, DEVTYPE_EMU_PR) + optical_connection_uuid = '{:s}:optical:{:s}'.format(service_uuid, DEVTYPE_EMU_OLS) + optical_service_uuid = '{:s}:optical'.format(service_uuid) + + expected_events = [ + # Create packet service and add first endpoint + ('ServiceEvent', EVENT_CREATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + ('ServiceEvent', EVENT_UPDATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + + # Configure OLS controller, create optical service, create optical connection + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_O1_UUID)), + ('ServiceEvent', EVENT_CREATE, json_service_id(optical_service_uuid, context_id=CONTEXT_ID)), + ('ConnectionEvent', EVENT_CREATE, json_connection_id(optical_connection_uuid)), + + # Configure endpoint packet devices, add second endpoint to service, create connection + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R1_UUID)), + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R3_UUID)), + ('ServiceEvent', EVENT_UPDATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + ('ConnectionEvent', EVENT_CREATE, json_connection_id(packet_connection_uuid)), + ] + check_events(events_collector, expected_events) + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + +def test_scenario_service_created(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure service is created ------------------------------------------------------------------ + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 2 # L3NM + TAPI + for service in response.services: + service_id = service.service_id + response = context_client.ListConnections(service_id) + LOGGER.info(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) + assert len(response.connections) == 1 # one connection per service diff --git a/src/tests/oeccpsc22/tests/test_functional_delete_service.py b/src/tests/oeccpsc22/tests/test_functional_delete_service.py new file mode 100644 index 0000000000000000000000000000000000000000..51e91a5967e1696fa2fdfe7dd06d2efb46642248 --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_delete_service.py @@ -0,0 +1,134 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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, pytest +from common.DeviceTypes import DeviceTypeEnum +from common.Settings import get_setting +from common.tests.EventTools import EVENT_REMOVE, EVENT_UPDATE, check_events +from common.tools.object_factory.Connection import json_connection_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Service import json_service_id +from common.tools.grpc.Tools import grpc_message_to_json_string +from compute.tests.mock_osm.MockOSM import MockOSM +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.proto.context_pb2 import ContextId, Empty +from .Objects import ( + CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, WIM_MAPPING, + WIM_PASSWORD, WIM_USERNAME) + + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DEVTYPE_EMU_PR = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value +DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value + + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def osm_wim(): + wim_url = 'http://{:s}:{:s}'.format( + get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP'))) + return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) + + +def test_scenario_is_correct(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure service is created ------------------------------------------------------------------ + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 2 # L3NM + TAPI + for service in response.services: + service_id = service.service_id + response = context_client.ListConnections(service_id) + LOGGER.info(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) + assert len(response.connections) == 1 # one connection per service + + +def test_service_removal(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name + # ----- Start the EventsCollector ---------------------------------------------------------------------------------- + events_collector = EventsCollector(context_client, log_events_received=True) + events_collector.start() + + # ----- Delete Service --------------------------------------------------------------------------------------------- + response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) + LOGGER.info('Services[{:d}] = {:s}'.format(len(response.service_ids), grpc_message_to_json_string(response))) + assert len(response.service_ids) == 2 # L3NM + TAPI + service_uuids = set() + for service_id in response.service_ids: + service_uuid = service_id.service_uuid.uuid + if service_uuid.endswith(':optical'): continue + service_uuids.add(service_uuid) + osm_wim.conn_info[service_uuid] = {} + + assert len(service_uuids) == 1 # assume a single service has been created + service_uuid = set(service_uuids).pop() + + osm_wim.delete_connectivity_service(service_uuid) + + # ----- Validate collected events ---------------------------------------------------------------------------------- + packet_connection_uuid = '{:s}:{:s}'.format(service_uuid, DEVTYPE_EMU_PR) + optical_connection_uuid = '{:s}:optical:{:s}'.format(service_uuid, DEVTYPE_EMU_OLS) + optical_service_uuid = '{:s}:optical'.format(service_uuid) + + expected_events = [ + ('ConnectionEvent', EVENT_REMOVE, json_connection_id(packet_connection_uuid)), + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R1_UUID)), + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R3_UUID)), + ('ServiceEvent', EVENT_REMOVE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + ('ConnectionEvent', EVENT_REMOVE, json_connection_id(optical_connection_uuid)), + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_O1_UUID)), + ('ServiceEvent', EVENT_REMOVE, json_service_id(optical_service_uuid, context_id=CONTEXT_ID)), + ] + check_events(events_collector, expected_events) + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + +def test_services_removed(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure service is removed ------------------------------------------------------------------ + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + assert len(response.services) == 0