diff --git a/my_deploy.sh b/my_deploy.sh index 7dd5e5c3ee13cbce2b701b5b4e703823dfb2c28f..4e705622bce9d9642277aec35e51fc61f394ae61 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -29,7 +29,14 @@ export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_gene #export TFS_COMPONENTS="${TFS_COMPONENTS} bgpls_speaker" # Uncomment to activate Optical Controller -#export TFS_COMPONENTS="${TFS_COMPONENTS} opticalcontroller" +# To manage optical connections, "service" requires "opticalcontroller" to be deployed +# before "service", thus we "hack" the TFS_COMPONENTS environment variable prepending the +# "opticalcontroller" only if "service" is already in TFS_COMPONENTS, and re-export it. +#if [[ "$TFS_COMPONENTS" == *"service"* ]]; then +# BEFORE="${TFS_COMPONENTS% service*}" +# AFTER="${TFS_COMPONENTS#* service}" +# export TFS_COMPONENTS="${BEFORE} opticalcontroller service ${AFTER}" +#fi # Uncomment to activate ZTP #export TFS_COMPONENTS="${TFS_COMPONENTS} ztp" diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index a102fa17629bd866d96883230a542a6e7a4d92ff..379705372be0cbe1050311f264dd04ffbe6b7656 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -305,7 +305,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetOpticalConfig(self, request : Empty, context : grpc.ServicerContext) -> OpticalConfigList: result = get_opticalconfig(self.db_engine) - return OpticalConfigList(OpticalConfigs=result) + return OpticalConfigList(opticalconfigs=result) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetOpticalConfig(self, request : OpticalConfig, context : grpc.ServicerContext) -> OpticalConfigId: @@ -315,6 +315,4 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SelectOpticalConfig(self, request : OpticalConfigId, context : grpc.ServicerContext) -> OpticalConfig: result = select_opticalconfig(self.db_engine, request) - optical_config_id = OpticalConfigId() - optical_config_id.CopyFrom(result.OpticalConfig_id) - return OpticalConfig(config=result.config, OpticalConfig_id=optical_config_id) + return OpticalConfig(config=result.config, opticalconfig_id=result.opticalconfig_id) diff --git a/src/opticalcontroller/OpticalController.py b/src/opticalcontroller/OpticalController.py index c2805695a75933c73d4ad367176bee8b504d4460..326862f93453674d5dff5566c2184dba1f00565c 100644 --- a/src/opticalcontroller/OpticalController.py +++ b/src/opticalcontroller/OpticalController.py @@ -85,6 +85,7 @@ class AddFlexLightpath(Resource): if rsa is not None: flow_id, optical_band_id = rsa.rsa_fs_computation(src, dst, bitrate, bidir, band) print (f"flow_id {flow_id} and optical_band_id {optical_band_id} ") + LOGGER.debug('flow_id={:s} rsa.db_flows={:s}'.format(str(flow_id), str(rsa.db_flows))) if flow_id is not None: if rsa.db_flows[flow_id]["op-mode"] == 0: return 'No path found', 404 diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index 5c5747970c6accf3c76da6d4ffb60de48edbcdac..fdfcb903b59e4780e1c66ba3a9ee698bf4bea6f9 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -122,17 +122,16 @@ class TaskExecutor: optical_config_id = OpticalConfigId() optical_config_id.opticalconfig_uuid = device.device_id.device_uuid.uuid optical_config = OpticalConfig() - setting = settings.value if settings else "" + setting = settings.value if settings else '' - new_config = {} try: result = self._context_client.SelectOpticalConfig(optical_config_id) - new_config = json.loads(result.config) - if result is not None : + if result is not None: + new_config = json.loads(result.config) new_config["new_config"] = setting new_config["is_opticalband"] = is_opticalband new_config["flow"] = flows - result.config = str(new_config) + result.config = json.dumps(new_config) optical_config.CopyFrom(result) self._device_client.ConfigureOpticalDevice(optical_config) self._store_grpc_object(CacheableObjectType.DEVICE, device_key, device) diff --git a/src/service/service/tools/OpticalTools.py b/src/service/service/tools/OpticalTools.py index 20652437194b9ef498f5b83bbe996863ba49c911..1837bf688c3ab4f61d49c4f481928896a6f4dbd7 100644 --- a/src/service/service/tools/OpticalTools.py +++ b/src/service/service/tools/OpticalTools.py @@ -13,35 +13,58 @@ # limitations under the License. # -import json -import requests -import uuid -from common.Constants import * +import functools, json, logging, requests, uuid from typing import List +from common.Constants import ServiceNameEnum from common.proto.context_pb2 import( Device, DeviceId, Service, Connection, EndPointId, TopologyId, ContextId, Uuid, ConfigRule, ConfigActionEnum, ConfigRule_Custom ) from common.proto.pathcomp_pb2 import PathCompReply from common.Settings import ( - ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, find_environment_variables, get_env_var_name + ENVVAR_SUFIX_SERVICE_BASEURL_HTTP, ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, + find_environment_variables, get_env_var_name +) +from service.service.tools.replies import ( + reply_uni_txt, optical_band_uni_txt, reply_bid_txt, optical_band_bid_txt ) -from service.service.tools.replies import reply_uni_txt, optical_band_uni_txt, reply_bid_txt, optical_band_bid_txt log = logging.getLogger(__name__) -testing = False +TESTING = False + +get_optical_controller_setting = functools.partial(get_env_var_name, ServiceNameEnum.OPTICALCONTROLLER) +VAR_NAME_OPTICAL_CTRL_BASEURL_HTTP = get_optical_controller_setting(ENVVAR_SUFIX_SERVICE_BASEURL_HTTP) +VAR_NAME_OPTICAL_CTRL_SCHEMA = get_optical_controller_setting('SCHEMA') +VAR_NAME_OPTICAL_CTRL_HOST = get_optical_controller_setting(ENVVAR_SUFIX_SERVICE_HOST) +VAR_NAME_OPTICAL_CTRL_PORT = get_optical_controller_setting(ENVVAR_SUFIX_SERVICE_PORT_GRPC) + +OPTICAL_CTRL_BASE_URL = '{:s}://{:s}:{:d}/OpticalTFS' + +def get_optical_controller_base_url() -> str: + settings = find_environment_variables([ + VAR_NAME_OPTICAL_CTRL_BASEURL_HTTP, + VAR_NAME_OPTICAL_CTRL_SCHEMA, + VAR_NAME_OPTICAL_CTRL_HOST, + VAR_NAME_OPTICAL_CTRL_PORT, + ]) + base_url = settings.get(VAR_NAME_OPTICAL_CTRL_BASEURL_HTTP) + if base_url is not None: + log.debug('Optical Controller: base_url={:s}'.format(str(base_url))) + return base_url + + host = settings.get(VAR_NAME_OPTICAL_CTRL_HOST) + port = int(settings.get(VAR_NAME_OPTICAL_CTRL_PORT, 80)) + + MSG = 'Optical Controller not found: settings={:s}' + if host is None: raise Exception(MSG.format(str(settings))) + if port is None: raise Exception(MSG.format(str(settings))) -VAR_NAME_OPTICAL_CONTROLLER_HOST = get_env_var_name(ServiceNameEnum.OPTICALCONTROLLER, ENVVAR_SUFIX_SERVICE_HOST) -VAR_NAME_OPTICAL_CONTROLLER_PORT = get_env_var_name(ServiceNameEnum.OPTICALCONTROLLER, ENVVAR_SUFIX_SERVICE_PORT_GRPC) + schema = settings.get(VAR_NAME_OPTICAL_CTRL_SCHEMA, 'http') + base_url = OPTICAL_CTRL_BASE_URL.format(schema, host, port) + log.debug('Optical Controller: base_url={:s}'.format(str(base_url))) + return base_url -opticalcontrollers_url = find_environment_variables([ - VAR_NAME_OPTICAL_CONTROLLER_HOST, - VAR_NAME_OPTICAL_CONTROLLER_PORT, -]) -OPTICAL_IP = opticalcontrollers_url.get(VAR_NAME_OPTICAL_CONTROLLER_HOST) -OPTICAL_PORT = opticalcontrollers_url.get(VAR_NAME_OPTICAL_CONTROLLER_PORT) -log.info(str(OPTICAL_IP), str(OPTICAL_PORT)) def get_uuids_from_names(devices: List[Device], device_name: str, port_name: str): device_uuid = "" @@ -79,17 +102,18 @@ def get_device_name_from_uuid(devices: List[Device], device_uuid: str): def add_lightpath(src, dst, bitrate, bidir, ob_band) -> str: - if not testing: + if not TESTING: urlx = "" headers = {"Content-Type": "application/json"} + base_url = get_optical_controller_base_url() if ob_band is None: if bidir is None: bidir = 1 - urlx = "http://{}:{}/OpticalTFS/AddFlexLightpath/{}/{}/{}/{}".format(OPTICAL_IP, OPTICAL_PORT, src, dst, bitrate, bidir) + urlx = "{:s}/AddFlexLightpath/{:s}/{:s}/{:s}/{:s}".format(base_url, src, dst, str(bitrate), str(bidir)) else: if bidir is None: bidir = 1 - urlx = "http://{}:{}/OpticalTFS/AddFlexLightpath/{}/{}/{}/{}/{}".format(OPTICAL_IP, OPTICAL_PORT, src, dst, bitrate, bidir, ob_band) + urlx = "{:s}/AddFlexLightpath/{:s}/{:s}/{:s}/{:s}/{:s}".format(base_url, src, dst, str(bitrate), str(bidir), str(ob_band)) r = requests.put(urlx, headers=headers) reply = r.text return reply @@ -101,8 +125,9 @@ def add_lightpath(src, dst, bitrate, bidir, ob_band) -> str: def get_optical_band(idx) -> str: - if not testing: - urlx = "http://{}:{}/OpticalTFS/GetOpticalBand/{}".format(OPTICAL_IP, OPTICAL_PORT, idx) + if not TESTING: + base_url = get_optical_controller_base_url() + urlx = "{:s}/GetOpticalBand/{:s}".format(base_url, str(idx)) headers = {"Content-Type": "application/json"} r = requests.get(urlx, headers=headers) reply = r.text @@ -116,8 +141,9 @@ def get_optical_band(idx) -> str: def delete_lightpath(flow_id, src, dst, bitrate) -> str: reply = "200" - if not testing: - urlx = "http://{}:{}/OpticalTFS/DelLightpath/{}/{}/{}/{}".format(OPTICAL_IP, OPTICAL_PORT, flow_id, src, dst, bitrate) + if not TESTING: + base_url = get_optical_controller_base_url() + urlx = "{:s}/DelLightpath/{:s}/{:s}/{:s}/{:s}".format(base_url, str(flow_id), src, dst, str(bitrate)) headers = {"Content-Type": "application/json"} r = requests.delete(urlx, headers=headers) @@ -126,7 +152,8 @@ def delete_lightpath(flow_id, src, dst, bitrate) -> str: def get_lightpaths() -> str: - urlx = "http://{}:{}/OpticalTFS/GetLightpaths".format(OPTICAL_IP, OPTICAL_PORT) + base_url = get_optical_controller_base_url() + urlx = "{:s}/GetLightpaths".format(base_url) headers = {"Content-Type": "application/json"} r = requests.get(urlx, headers=headers) diff --git a/src/tests/.gitlab-ci.yml b/src/tests/.gitlab-ci.yml index 41b8bb36ca8d3ef7eae444cdc9525cfdf4e48d02..b7345bbd10d16e98e07960f88d15724aa940ed06 100644 --- a/src/tests/.gitlab-ci.yml +++ b/src/tests/.gitlab-ci.yml @@ -19,4 +19,4 @@ include: - local: '/src/tests/ecoc22/.gitlab-ci.yml' #- local: '/src/tests/nfvsdn22/.gitlab-ci.yml' #- local: '/src/tests/ofc23/.gitlab-ci.yml' - #- local: '/src/tests/ofc24/.gitlab-ci.yml' + - local: '/src/tests/ofc24/.gitlab-ci.yml' diff --git a/src/tests/ecoc22/Dockerfile b/src/tests/ecoc22/Dockerfile index 3ac134a384a5b61d4dca05fcf9076680c74fe649..28fc91d5ead5cb3020936c7a270fe7856231db86 100644 --- a/src/tests/ecoc22/Dockerfile +++ b/src/tests/ecoc22/Dockerfile @@ -72,8 +72,6 @@ COPY src/device/__init__.py device/__init__.py COPY src/device/client/. device/client/ COPY src/monitoring/__init__.py monitoring/__init__.py COPY src/monitoring/client/. monitoring/client/ -COPY src/monitoring/__init__.py monitoring/__init__.py -COPY src/monitoring/client/. monitoring/client/ COPY src/e2e_orchestrator/__init__.py e2e_orchestrator/__init__.py COPY src/e2e_orchestrator/client/. e2e_orchestrator/client/ COPY src/service/__init__.py service/__init__.py diff --git a/src/tests/ofc22/.gitlab-ci.yml b/src/tests/ofc22/.gitlab-ci.yml index 013a389bc438cdac8307f71288f74b0a3afd9737..810e591690b4c9698aad628bef4c63a270c02022 100644 --- a/src/tests/ofc22/.gitlab-ci.yml +++ b/src/tests/ofc22/.gitlab-ci.yml @@ -96,7 +96,7 @@ end2end_test ofc22: - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/sliceservice -c server - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/nbiservice -c server - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/monitoringservice -c server - - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/ztpservice -c server + - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/ztpservice -c ztpservice - if docker ps -a | grep ${TEST_NAME}; then docker rm -f ${TEST_NAME}; fi - docker images --filter="dangling=true" --quiet | xargs -r docker rmi #coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' diff --git a/src/tests/ofc22/Dockerfile b/src/tests/ofc22/Dockerfile index 4817bd93a313b74851e01bba53174dd14f280a18..4cba83466de077890c39226b58d997230ec844b6 100644 --- a/src/tests/ofc22/Dockerfile +++ b/src/tests/ofc22/Dockerfile @@ -72,8 +72,6 @@ COPY src/device/__init__.py device/__init__.py COPY src/device/client/. device/client/ COPY src/monitoring/__init__.py monitoring/__init__.py COPY src/monitoring/client/. monitoring/client/ -COPY src/monitoring/__init__.py monitoring/__init__.py -COPY src/monitoring/client/. monitoring/client/ COPY src/e2e_orchestrator/__init__.py e2e_orchestrator/__init__.py COPY src/e2e_orchestrator/client/. e2e_orchestrator/client/ COPY src/service/__init__.py service/__init__.py diff --git a/src/tests/ofc24/.gitlab-ci.yml b/src/tests/ofc24/.gitlab-ci.yml index 0b5593b160ee14941546242dd3a4c3571407dedf..f169bf7ee08e2faaeff30776080c8128d82e9fe5 100644 --- a/src/tests/ofc24/.gitlab-ci.yml +++ b/src/tests/ofc24/.gitlab-ci.yml @@ -13,9 +13,9 @@ # limitations under the License. # Build, tag, and push the Docker image to the GitLab Docker registry -build ofc22: +build ofc24: variables: - TEST_NAME: 'ofc22' + TEST_NAME: 'ofc24' stage: build before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY @@ -36,23 +36,65 @@ build ofc22: - .gitlab-ci.yml # Deploy TeraFlowSDN and Execute end-2-end test -end2end_test ofc22: +end2end_test ofc24: variables: - TEST_NAME: 'ofc22' + TEST_NAME: 'ofc24' stage: end2end_test # Disable to force running it after all other tasks #needs: - # - build ofc22 + # - build ofc24 before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + - docker rm -f na-t1 na-t2 na-r1 na-r2 + - docker network rm -f na-br + script: # Download Docker image to run the test - docker pull "${CI_REGISTRY_IMAGE}/${TEST_NAME}:latest" + - docker pull asgamb1/oc23bgp.img:latest + - docker pull asgamb1/flexscale-node.img:latest # Check MicroK8s is ready - microk8s status --wait-ready - kubectl get pods --all-namespaces + # Deploy Optical Device Node Agents + - > + docker network create -d bridge --subnet=172.254.253.0/24 --gateway=172.254.253.254 + --ip-range=172.254.253.0/24 na-br + - > + docker run -dit --init --name na-t1 --network=na-br --ip 172.254.253.101 + --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-tp.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" + --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_t1.xml:/confd/examples.confd/OC23/platform.xml" + asgamb1/oc23bgp.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh + - > + docker run -dit --init --name na-t2 --network=na-br --ip 172.254.253.102 + --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-tp.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" + --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_t2.xml:/confd/examples.confd/OC23/platform.xml" + asgamb1/oc23bgp.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh + - > + docker run -dit --init --name na-r1 --network=na-br --ip 172.254.253.201 + --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-mg-on.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" + --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_r1.xml:/confd/examples.confd/OC23/platform.xml" + asgamb1/flexscale-node.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh + - > + docker run -dit --init --name na-r2 --network=na-br --ip 172.254.253.202 + --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/startNetconfAgent-mg-on.sh:/confd/examples.confd/OC23/startNetconfAgent.sh" + --volume "$PWD/src/tests/${TEST_NAME}/node-agents-config/platform_r2.xml:/confd/examples.confd/OC23/platform.xml" + asgamb1/flexscale-node.img:latest /confd/examples.confd/OC23/startNetconfAgent.sh + + + # Wait for initialization of Optical Device Node Agents + - sleep 3 + - docker ps -a + - while ! docker logs na-t1 2>&1 | grep -q '*** ConfD OpenConfig NETCONF agent ***'; do sleep 1; done + - while ! docker logs na-t2 2>&1 | grep -q '*** ConfD OpenConfig NETCONF agent ***'; do sleep 1; done + - while ! docker logs na-r1 2>&1 | grep -q '*** ConfD OpenConfig NETCONF agent ***'; do sleep 1; done + - while ! docker logs na-r2 2>&1 | grep -q '*** ConfD OpenConfig NETCONF agent ***'; do sleep 1; done + - sleep 3 + - docker ps -a + + # Configure TeraFlowSDN deployment # Uncomment if DEBUG log level is needed for the components #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/contextservice.yaml @@ -86,7 +128,9 @@ end2end_test ofc22: --volume "$PWD/tfs_runtime_env_vars.sh:/var/teraflow/tfs_runtime_env_vars.sh" --volume "$PWD/src/tests/${TEST_NAME}:/opt/results" $CI_REGISTRY_IMAGE/${TEST_NAME}:latest + after_script: + # Dump TeraFlowSDN component logs - source src/tests/${TEST_NAME}/deploy_specs.sh - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/contextservice -c server - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/deviceservice -c server @@ -94,8 +138,23 @@ end2end_test ofc22: - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/serviceservice -c server - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/sliceservice -c server - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/nbiservice -c server + - kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/opticalcontrollerservice -c server - if docker ps -a | grep ${TEST_NAME}; then docker rm -f ${TEST_NAME}; fi + + # Dump Optical Device Node Agents container status and logs + - docker ps -a + - docker logs na-t1 + - docker logs na-t2 + - docker logs na-r1 + - docker logs na-r2 + + # Destroy Optical Device Node Agents + - docker rm -f na-t1 na-t2 na-r1 na-r2 + - docker network rm -f na-br + + # Clean old docker images - docker images --filter="dangling=true" --quiet | xargs -r docker rmi + #coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' diff --git a/src/tests/ofc24/Dockerfile b/src/tests/ofc24/Dockerfile index 8efa0c72c3bdc72bd336d10a4ffdbc0af025fc25..bef7d25fee87f2539dfb81aaab47d73b4f0fe287 100644 --- a/src/tests/ofc24/Dockerfile +++ b/src/tests/ofc24/Dockerfile @@ -70,10 +70,8 @@ COPY src/context/__init__.py context/__init__.py COPY src/context/client/. context/client/ COPY src/device/__init__.py device/__init__.py COPY src/device/client/. device/client/ -#COPY src/monitoring/__init__.py monitoring/__init__.py -#COPY src/monitoring/client/. monitoring/client/ -#COPY src/monitoring/__init__.py monitoring/__init__.py -#COPY src/monitoring/client/. monitoring/client/ +COPY src/monitoring/__init__.py monitoring/__init__.py +COPY src/monitoring/client/. monitoring/client/ COPY src/e2e_orchestrator/__init__.py e2e_orchestrator/__init__.py COPY src/e2e_orchestrator/client/. e2e_orchestrator/client/ COPY src/service/__init__.py service/__init__.py @@ -82,18 +80,21 @@ COPY src/slice/__init__.py slice/__init__.py COPY src/slice/client/. slice/client/ COPY src/tests/*.py ./tests/ COPY src/tests/ofc24/__init__.py ./tests/ofc24/__init__.py -COPY src/tests/ofc24/descriptors_topology.json ./tests/ofc24/descriptors_topology.json +COPY src/tests/ofc24/descriptors/topology.json ./tests/ofc24/descriptors/topology.json +COPY src/tests/ofc24/descriptors/service-unidir.json ./tests/ofc24/descriptors/service-unidir.json +COPY src/tests/ofc24/descriptors/service-bidir.json ./tests/ofc24/descriptors/service-bidir.json COPY src/tests/ofc24/tests/. ./tests/ofc24/tests/ -COPY src/tests/tools/. ./tests/tools/ RUN tee ./run_tests.sh <&1 | grep -q '*** ConfD OpenConfig NETCONF agent ***'; do sleep 1; done while ! docker logs na-t2 2>&1 | grep -q '*** ConfD OpenConfig NETCONF agent ***'; do sleep 1; done while ! docker logs na-r1 2>&1 | grep -q '*** ConfD OpenConfig NETCONF agent ***'; do sleep 1; done while ! docker logs na-r2 2>&1 | grep -q '*** ConfD OpenConfig NETCONF agent ***'; do sleep 1; done -sleep 2 +sleep 3 docker ps -a @@ -69,16 +68,10 @@ echo echo "Dump Node Agent status:" echo "-----------------------" docker ps -a -#docker logs na-t1 -#docker logs na-t2 -#docker logs na-r1 -#docker logs na-r2 - +docker logs na-t1 +docker logs na-t2 +docker logs na-r1 +docker logs na-r2 -#echo -#echo "Post-test clean-up:" -#echo "-------------------" -#docker rm -f na-t1 na-t2 na-r1 na-r2 -#docker network rm na-br echo "Done!" diff --git a/src/tests/ofc24/deploy_specs.sh b/src/tests/ofc24/deploy_specs.sh index ca5494de25ea17c08c2df1f2d62923c59b0e81e2..4ade7592363981bb8d51612fa9339b93b53dd4c3 100755 --- a/src/tests/ofc24/deploy_specs.sh +++ b/src/tests/ofc24/deploy_specs.sh @@ -30,7 +30,14 @@ export TFS_COMPONENTS="context device pathcomp service slice nbi webui" #export TFS_COMPONENTS="${TFS_COMPONENTS} bgpls_speaker" # Uncomment to activate Optical Controller -export TFS_COMPONENTS="${TFS_COMPONENTS} opticalcontroller" +# To manage optical connections, "service" requires "opticalcontroller" to be deployed +# before "service", thus we "hack" the TFS_COMPONENTS environment variable prepending the +# "opticalcontroller" only if "service" is already in TFS_COMPONENTS, and re-export it. +if [[ "$TFS_COMPONENTS" == *"service"* ]]; then + BEFORE="${TFS_COMPONENTS% service*}" + AFTER="${TFS_COMPONENTS#* service}" + export TFS_COMPONENTS="${BEFORE} opticalcontroller service ${AFTER}" +fi # Uncomment to activate ZTP #export TFS_COMPONENTS="${TFS_COMPONENTS} ztp" @@ -63,7 +70,7 @@ export TFS_K8S_NAMESPACE="tfs" export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" # Uncomment to monitor performance of components -export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/servicemonitors.yaml" +#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" diff --git a/src/tests/ofc24/1.context.json b/src/tests/ofc24/descriptors/old/1.context.json similarity index 100% rename from src/tests/ofc24/1.context.json rename to src/tests/ofc24/descriptors/old/1.context.json diff --git a/src/tests/ofc24/2.device1.json b/src/tests/ofc24/descriptors/old/2.device1.json similarity index 95% rename from src/tests/ofc24/2.device1.json rename to src/tests/ofc24/descriptors/old/2.device1.json index 3e31f31eb84630415f96b6af1e6ae5d34bdb1c89..c5a189e3165f4d170fb1c9e8b13314a202a84630 100755 --- a/src/tests/ofc24/2.device1.json +++ b/src/tests/ofc24/descriptors/old/2.device1.json @@ -41,7 +41,7 @@ "action": 1, "custom": { "resource_key": "_connect/address", - "resource_value": "10.0.2.15" + "resource_value": "10.0.2.4" } }, { diff --git a/src/tests/ofc24/3.device2.json b/src/tests/ofc24/descriptors/old/3.device2.json similarity index 95% rename from src/tests/ofc24/3.device2.json rename to src/tests/ofc24/descriptors/old/3.device2.json index 812affa7b8540b67f83d6f3c9bb9b5442c44fd0d..a38fc290508788637a1ebc1cef7ddc54514a46fe 100755 --- a/src/tests/ofc24/3.device2.json +++ b/src/tests/ofc24/descriptors/old/3.device2.json @@ -41,7 +41,7 @@ "action": 1, "custom": { "resource_key": "_connect/address", - "resource_value": "10.0.2.15" + "resource_value": "10.0.2.4" } }, { diff --git a/src/tests/ofc24/4.device3_R1.json b/src/tests/ofc24/descriptors/old/4.device3_R1.json similarity index 96% rename from src/tests/ofc24/4.device3_R1.json rename to src/tests/ofc24/descriptors/old/4.device3_R1.json index 3a57ba79cd2ff8aa6d4b666ac382932ade2f20e0..1c110f20ecb6c2d5081044c961dbf50e3d4c816d 100755 --- a/src/tests/ofc24/4.device3_R1.json +++ b/src/tests/ofc24/descriptors/old/4.device3_R1.json @@ -107,7 +107,7 @@ "action": 1, "custom": { "resource_key": "_connect/address", - "resource_value": "10.0.2.15" + "resource_value": "10.0.2.4" } }, { diff --git a/src/tests/ofc24/5.device4_R2.json b/src/tests/ofc24/descriptors/old/5.device4_R2.json similarity index 96% rename from src/tests/ofc24/5.device4_R2.json rename to src/tests/ofc24/descriptors/old/5.device4_R2.json index 9b1968d095c3e2c28c058b22f7295d7d1cbda380..43ebda5c6c139b4f2d8b2a51e7f42a257a38b4d4 100755 --- a/src/tests/ofc24/5.device4_R2.json +++ b/src/tests/ofc24/descriptors/old/5.device4_R2.json @@ -108,7 +108,7 @@ "action": 1, "custom": { "resource_key": "_connect/address", - "resource_value": "10.0.2.15" + "resource_value": "10.0.2.4" } }, { diff --git a/src/tests/ofc24/6.links.json b/src/tests/ofc24/descriptors/old/6.links.json similarity index 100% rename from src/tests/ofc24/6.links.json rename to src/tests/ofc24/descriptors/old/6.links.json diff --git a/src/tests/ofc24/7.service-bidir.json b/src/tests/ofc24/descriptors/service-bidir.json similarity index 100% rename from src/tests/ofc24/7.service-bidir.json rename to src/tests/ofc24/descriptors/service-bidir.json diff --git a/src/tests/ofc24/7.service-unidir.json b/src/tests/ofc24/descriptors/service-unidir.json similarity index 100% rename from src/tests/ofc24/7.service-unidir.json rename to src/tests/ofc24/descriptors/service-unidir.json diff --git a/src/tests/ofc24/descriptors_topology.json b/src/tests/ofc24/descriptors/topology.json similarity index 96% rename from src/tests/ofc24/descriptors_topology.json rename to src/tests/ofc24/descriptors/topology.json index 6ae521c7642b24d428b26b11c29882ca46995014..85bbad55eb8608d2d2a7abad7fffab8fffdae682 100644 --- a/src/tests/ofc24/descriptors_topology.json +++ b/src/tests/ofc24/descriptors/topology.json @@ -16,8 +16,8 @@ }} ], "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.15"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "2023"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "172.254.253.101"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "2022"}}, {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": { "username": "admin", "password": "admin", "force_running": false, "hostkey_verify": false, "look_for_keys": false, "allow_agent": false, "commit_per_rule": false, @@ -36,8 +36,8 @@ }} ], "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.15"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "2024"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "172.254.253.102"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "2022"}}, {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": { "username": "admin", "password": "admin", "force_running": false, "hostkey_verify": false, "look_for_keys": false, "allow_agent": false, "commit_per_rule": false, @@ -68,8 +68,8 @@ }} ], "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.15"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "2025"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "172.254.253.201"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "2022"}}, {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": { "username": "admin", "password": "admin", "force_running": false, "hostkey_verify": false, "look_for_keys": false, "allow_agent": false, "commit_per_rule": false, @@ -105,8 +105,8 @@ }} ], "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.15"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "2026"}}, + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "172.254.253.202"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "2022"}}, {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": { "username": "admin", "password": "admin", "force_running": false, "hostkey_verify": false, "look_for_keys": false, "allow_agent": false, "commit_per_rule": false, diff --git a/src/tests/ofc24/destroy-node-agents.sh b/src/tests/ofc24/destroy-node-agents.sh new file mode 100755 index 0000000000000000000000000000000000000000..19e7fc9a9398734aa967cfb5618d125c545500a2 --- /dev/null +++ b/src/tests/ofc24/destroy-node-agents.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +echo +echo "Clean-up:" +echo "---------" +docker rm -f na-t1 na-t2 na-r1 na-r2 +docker network rm na-br + +echo "Done!" diff --git a/src/tests/ofc24/platform_r1.xml b/src/tests/ofc24/node-agents-config/platform_r1.xml old mode 100755 new mode 100644 similarity index 100% rename from src/tests/ofc24/platform_r1.xml rename to src/tests/ofc24/node-agents-config/platform_r1.xml diff --git a/src/tests/ofc24/platform_r2.xml b/src/tests/ofc24/node-agents-config/platform_r2.xml old mode 100755 new mode 100644 similarity index 100% rename from src/tests/ofc24/platform_r2.xml rename to src/tests/ofc24/node-agents-config/platform_r2.xml diff --git a/src/tests/ofc24/platform_t1.xml b/src/tests/ofc24/node-agents-config/platform_t1.xml old mode 100755 new mode 100644 similarity index 100% rename from src/tests/ofc24/platform_t1.xml rename to src/tests/ofc24/node-agents-config/platform_t1.xml diff --git a/src/tests/ofc24/platform_t2.xml b/src/tests/ofc24/node-agents-config/platform_t2.xml old mode 100755 new mode 100644 similarity index 100% rename from src/tests/ofc24/platform_t2.xml rename to src/tests/ofc24/node-agents-config/platform_t2.xml diff --git a/src/tests/ofc24/node-agents-config/startNetconfAgent-mg-on.sh b/src/tests/ofc24/node-agents-config/startNetconfAgent-mg-on.sh new file mode 100755 index 0000000000000000000000000000000000000000..e54496b408f055853aa396e28aa040a2052293fa --- /dev/null +++ b/src/tests/ofc24/node-agents-config/startNetconfAgent-mg-on.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +echo 'Cleaning...' +make clean + +echo 'Rebuilding...' +make all + +echo 'Initializing database...' +cp platform.xml confd-cdb/ + +echo 'Starting ConfD...' +make start2 + +echo 'ConfD Ready!!' diff --git a/src/tests/ofc24/node-agents-config/startNetconfAgent-tp.sh b/src/tests/ofc24/node-agents-config/startNetconfAgent-tp.sh new file mode 100755 index 0000000000000000000000000000000000000000..4e2ec068662b13c82248575478b4c88832d45e5f --- /dev/null +++ b/src/tests/ofc24/node-agents-config/startNetconfAgent-tp.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +echo 'Cleaning...' +make clean + +echo 'Rebuilding...' +make all + +echo 'Initializing database...' +cp platform.xml confd-cdb/ +cp interfaces.xml confd-cdb/ +cp bgp.xml confd-cdb/ + +echo 'Starting ConfD...' +make start2 + +echo 'ConfD Ready!!' diff --git a/src/tests/ofc24/run-tests-locally.sh b/src/tests/ofc24/run-tests-locally.sh new file mode 100755 index 0000000000000000000000000000000000000000..14cf78500d7d61789129b89cdfffe0f28e793aa5 --- /dev/null +++ b/src/tests/ofc24/run-tests-locally.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source ~/tfs-ctrl/tfs_runtime_env_vars.sh +pytest --verbose --log-level=INFO ~/tfs-ctrl/ofc24/tests/test_functional_bootstrap.py +pytest --verbose --log-level=INFO ~/tfs-ctrl/ofc24/tests/test_functional_create_service_unidir.py +pytest --verbose --log-level=INFO ~/tfs-ctrl/ofc24/tests/test_functional_delete_service_unidir.py +pytest --verbose --log-level=INFO ~/tfs-ctrl/ofc24/tests/test_functional_create_service_bidir.py +pytest --verbose --log-level=INFO ~/tfs-ctrl/ofc24/tests/test_functional_delete_service_bidir.py +pytest --verbose --log-level=INFO ~/tfs-ctrl/ofc24/tests/test_functional_cleanup.py diff --git a/src/tests/ofc24/startExtraNetConfigAgent.sh b/src/tests/ofc24/startExtraNetConfigAgent.sh deleted file mode 100755 index d9428585ef1040bb0440bf6db100b7f2bc71c970..0000000000000000000000000000000000000000 --- a/src/tests/ofc24/startExtraNetConfigAgent.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -DOCKER_CONTAINER=$1 -DOCKER_PORT=$2 - -if [ -n "$DOCKER_CONTAINER" ] && [ -n "$DOCKER_PORT" ];then - sudo docker stop "$DOCKER_CONTAINER" -t 1 - sudo docker rm "$DOCKER_CONTAINER" - - echo "Creating TPs" - screen -dmS t1 -T xterm sh -c "docker run -p 10.0.2.15:"$DOCKER_PORT":2022 -v ~/tfs-ctrl/tempOC/files:/files --name $DOCKER_CONTAINER -it asgamb1/oc23bgp.img:latest" - sleep 2 - if [ "$( docker container inspect -f '{{.State.Running}}' "$DOCKER_CONTAINER")" = "true" ]; then - docker exec "$DOCKER_CONTAINER" cp /files/demoECOC21_4.xml demoECOC21.xml - docker exec "$DOCKER_CONTAINER" /confd/examples.confd/OC23/startNetconfAgent.sh - else - echo "your container is not running yet" - fi -else - echo "Please define the docker container name and port" -fi diff --git a/src/tests/ofc24/startNetconfAgent.sh b/src/tests/ofc24/startNetconfAgent.sh deleted file mode 100755 index 10b721883799c4fd257e1f627ff1480259037702..0000000000000000000000000000000000000000 --- a/src/tests/ofc24/startNetconfAgent.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -make clean -make all -#make init -cp init_openconfig-platform.xml confd-cdb/ -#cp init_flex-scale-mg-on.xml confd-cdb/ -make start2 diff --git a/src/tests/ofc24/start_topo.sh b/src/tests/ofc24/start_topo.sh deleted file mode 100755 index c924064763c14e4da45344cd21f4d9c81c9640a9..0000000000000000000000000000000000000000 --- a/src/tests/ofc24/start_topo.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash -# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -IMAGE_NAME="asgamb1/oc23bgp.img:latest" -DOCKER_CONTAINER=$1 -DOCKER_PORT="2022" - -sudo docker stop na1 -t 1 -sudo docker stop na2 -t 1 -sudo docker stop na3 -t 1 -sudo docker stop na4 -t 1 - -sudo docker rm na2 -sudo docker rm na1 -sudo docker rm na3 -sudo docker rm na4 - -echo "Creating Transponder Agents" - -# if ! docker image inspect "$IMAGE_NAME" >/dev/null 2>&1 ; then -# echo "asgamb1/oc23bgp.img:latest not existed ! " -# screen -dmS t3 -T xterm sh -c "docker run -p 10.0.2.15:2025:2022 -v ~/tempOC/files:/files --name na -it $IMAGE_NAME bash" -# echo 'start downloading asgamb1/oc23bgp.img:latest , it may take few minutes ! .... ' -# while [ "$(docker image inspect asgamb1/oc23bgp.img:latest 2>/dev/null)" == "[]" ]; do -# sleep 1 -# done - -#fi - - - -screen -dmS t1 -T xterm sh -c "docker run -p 127.0.0.1:2023:2022 -v ~/tempOC/files:/files --name $DOCKER_CONTAINER -it asgamb1/oc23bgp.img:latest bash" -sleep 2 -if [ "$( docker container inspect -f '{{.State.Running}}' "$DOCKER_CONTAINER")" = "true" ]; then - docker exec "$DOCKER_CONTAINER" cp /files/demoECOC21_4.xml demoECOC21.xml - docker exec "$DOCKER_CONTAINER" /confd/examples.confd/OC23/startNetconfAgent.sh -else - echo "your container is not running yet" -fi - -echo " It may take a while , Hang on ..." -source "./startExtraNetConfigAgent.sh" "na1" "2023" -sleep 3 - -source "./startExtraNetConfigAgent.sh" "na2" "2024" -sleep 3 - -bash -c "cp /tempOC/files/plat_r1.xml /confd/examples.confd/OC23/init_openconfig-platform.xml; ./startNetconfAgent.sh" -bash -c "cp /tempOC/files/plat_r2.xml /confd/examples.confd/OC23/init_openconfig-platform.xml; ./startNetconfAgent.sh" -screen -dmS t3 -T xterm sh -c 'docker run -p 10.0.2.15:2025:2022 -v ~/tfs-ctrl/tempOC/files:/files --name na3 -it asgamb1/flexscale-node.img:latest ./startNetconfAgent.sh' -screen -dmS t4 -T xterm sh -c 'docker run -p 10.0.2.15:2026:2022 -v ~/tfs-ctrl/tempOC/files:/files --name na4 -it asgamb1/flexscale-node.img:latest ./startNetconfAgent.sh' diff --git a/src/tests/ofc24/tests/test_functional_bootstrap.py b/src/tests/ofc24/tests/test_functional_bootstrap.py index bc648d16de57c8c287c7d601f994cb81ed45bc04..e6562f8a2f95f3d1f504b2bbc5dd6dc3b4c47077 100644 --- a/src/tests/ofc24/tests/test_functional_bootstrap.py +++ b/src/tests/ofc24/tests/test_functional_bootstrap.py @@ -24,7 +24,7 @@ from tests.Fixtures import context_client, device_client # pylint: disable=unuse LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) -DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'descriptors_topology.json') +DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'descriptors', 'topology.json') ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) def test_scenario_bootstrap( diff --git a/src/tests/ofc24/tests/test_functional_cleanup.py b/src/tests/ofc24/tests/test_functional_cleanup.py index 5f1ce23f13051759e0e688a42c7118eaff8d3c72..281b0969d708d07be8bdda0aca83b8976d6fa1dd 100644 --- a/src/tests/ofc24/tests/test_functional_cleanup.py +++ b/src/tests/ofc24/tests/test_functional_cleanup.py @@ -24,7 +24,7 @@ from tests.Fixtures import context_client, device_client # pylint: disable=un LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) -DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'descriptors_topology.json') +DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'descriptors', 'topology.json') ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) def test_scenario_cleanup( diff --git a/src/tests/ofc24/tests/test_functional_create_service.py b/src/tests/ofc24/tests/test_functional_create_service.py deleted file mode 100644 index 74c74483eb82325afec2cae833ebd460566da153..0000000000000000000000000000000000000000 --- a/src/tests/ofc24/tests/test_functional_create_service.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging, os, random -from common.Constants import DEFAULT_CONTEXT_NAME -from common.proto.context_pb2 import ContextId, Empty, ServiceTypeEnum -from common.proto.kpi_sample_types_pb2 import KpiSampleType -from common.tools.descriptor.Loader import DescriptorLoader -from common.tools.grpc.Tools import grpc_message_to_json_string -from common.tools.object_factory.Context import json_context_id -from context.client.ContextClient import ContextClient -from monitoring.client.MonitoringClient import MonitoringClient -from tests.Fixtures import context_client, monitoring_client # pylint: disable=unused-import -from tests.tools.mock_osm.MockOSM import MockOSM -from .Fixtures import osm_wim # pylint: disable=unused-import -from .Objects import WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE - -LOGGER = logging.getLogger(__name__) -LOGGER.setLevel(logging.DEBUG) - -DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'descriptors_emulated.json') -ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) - -def test_service_creation(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name - # Load descriptors and validate the base scenario - descriptor_loader = DescriptorLoader(descriptors_file=DESCRIPTOR_FILE, context_client=context_client) - descriptor_loader.validate() - - # Verify the scenario has no services/slices - response = context_client.GetContext(ADMIN_CONTEXT_ID) - assert len(response.service_ids) == 0 - assert len(response.slice_ids) == 0 - - # Create Connectivity Service - service_uuid = osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS) - osm_wim.get_connectivity_service_status(service_uuid) - - # Ensure slices and services are created - response = context_client.ListSlices(ADMIN_CONTEXT_ID) - LOGGER.info('Slices[{:d}] = {:s}'.format(len(response.slices), grpc_message_to_json_string(response))) - assert len(response.slices) == 1 # OSM slice - - response = context_client.ListServices(ADMIN_CONTEXT_ID) - LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) - assert len(response.services) == 2 # 1xL3NM + 1xTAPI - - 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))) - - if service.service_type == ServiceTypeEnum.SERVICETYPE_L3NM: - assert len(response.connections) == 1 # 1 connection per service - elif service.service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE: - assert len(response.connections) == 1 # 1 connection per service - else: - str_service = grpc_message_to_json_string(service) - raise Exception('Unexpected ServiceType: {:s}'.format(str_service)) - - -def test_scenario_kpi_values_created( - monitoring_client: MonitoringClient, # pylint: disable=redefined-outer-name -) -> None: - """ - This test validates that KPI values have been inserted into the monitoring database. - We short k KPI descriptors to test. - """ - response = monitoring_client.GetKpiDescriptorList(Empty()) - kpi_descriptors = random.choices(response.kpi_descriptor_list, k=2) - - for kpi_descriptor in kpi_descriptors: - MSG = 'KPI(kpi_uuid={:s}, device_uuid={:s}, endpoint_uuid={:s}, service_uuid={:s}, kpi_sample_type={:s})...' - LOGGER.info(MSG.format( - str(kpi_descriptor.kpi_id.kpi_id.uuid), str(kpi_descriptor.device_id.device_uuid.uuid), - str(kpi_descriptor.endpoint_id.endpoint_uuid.uuid), str(kpi_descriptor.service_id.service_uuid.uuid), - str(KpiSampleType.Name(kpi_descriptor.kpi_sample_type)))) - response = monitoring_client.GetInstantKpi(kpi_descriptor.kpi_id) - kpi_uuid = response.kpi_id.kpi_id.uuid - assert kpi_uuid == kpi_descriptor.kpi_id.kpi_id.uuid - kpi_value_type = response.kpi_value.WhichOneof('value') - if kpi_value_type is None: - MSG = ' KPI({:s}): No instant value found' - LOGGER.warning(MSG.format(str(kpi_uuid))) - else: - kpi_timestamp = response.timestamp.timestamp - assert kpi_timestamp > 0 - assert kpi_value_type == 'floatVal' - kpi_value = getattr(response.kpi_value, kpi_value_type) - MSG = ' KPI({:s}): timestamp={:s} value_type={:s} value={:s}' - LOGGER.info(MSG.format(str(kpi_uuid), str(kpi_timestamp), str(kpi_value_type), str(kpi_value))) diff --git a/src/tests/ofc24/tests/test_functional_create_service_bidir.py b/src/tests/ofc24/tests/test_functional_create_service_bidir.py new file mode 100644 index 0000000000000000000000000000000000000000..e910c946d509a814415019c01a19b1058934e47a --- /dev/null +++ b/src/tests/ofc24/tests/test_functional_create_service_bidir.py @@ -0,0 +1,72 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging, os +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import ContextId, ServiceStatusEnum, ServiceTypeEnum +from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results +from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.object_factory.Context import json_context_id +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from service.client.ServiceClient import ServiceClient +from tests.Fixtures import context_client, device_client, service_client # pylint: disable=unused-import + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'descriptors', 'service-bidir.json') +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) + +def test_service_creation_bidir( + context_client : ContextClient, # pylint: disable=redefined-outer-name + device_client : DeviceClient, # pylint: disable=redefined-outer-name + service_client : ServiceClient, # pylint: disable=redefined-outer-name +): + # Load descriptors and validate the base scenario + descriptor_loader = DescriptorLoader( + descriptors_file=DESCRIPTOR_FILE, context_client=context_client, device_client=device_client, + service_client=service_client + ) + results = descriptor_loader.process() + check_descriptor_load_results(results, descriptor_loader) + + # Verify the scenario has 1 service and 0 slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.service_ids) == 1 + assert len(response.slice_ids) == 0 + + # Check there are no slices + response = context_client.ListSlices(ADMIN_CONTEXT_ID) + LOGGER.warning('Slices[{:d}] = {:s}'.format(len(response.slices), grpc_message_to_json_string(response))) + assert len(response.slices) == 0 + + # Check there is 1 service + response = context_client.ListServices(ADMIN_CONTEXT_ID) + LOGGER.warning('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 1 + + for service in response.services: + service_id = service.service_id + assert service.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE + + response = context_client.ListConnections(service_id) + LOGGER.warning(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) + + if service.service_type == ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY: + assert len(response.connections) == 2 + else: + str_service = grpc_message_to_json_string(service) + raise Exception('Unexpected ServiceType: {:s}'.format(str_service)) diff --git a/src/tests/ofc24/tests/test_functional_create_service_unidir.py b/src/tests/ofc24/tests/test_functional_create_service_unidir.py new file mode 100644 index 0000000000000000000000000000000000000000..5b2550ae1e6f7ea9d51aec70af7af7b5c1360dc4 --- /dev/null +++ b/src/tests/ofc24/tests/test_functional_create_service_unidir.py @@ -0,0 +1,72 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging, os +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import ContextId, ServiceStatusEnum, ServiceTypeEnum +from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results +from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.object_factory.Context import json_context_id +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from service.client.ServiceClient import ServiceClient +from tests.Fixtures import context_client, device_client, service_client # pylint: disable=unused-import + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'descriptors', 'service-bidir.json') +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) + +def test_service_creation_unidir( + context_client : ContextClient, # pylint: disable=redefined-outer-name + device_client : DeviceClient, # pylint: disable=redefined-outer-name + service_client : ServiceClient, # pylint: disable=redefined-outer-name +): + # Load descriptors and validate the base scenario + descriptor_loader = DescriptorLoader( + descriptors_file=DESCRIPTOR_FILE, context_client=context_client, device_client=device_client, + service_client=service_client + ) + results = descriptor_loader.process() + check_descriptor_load_results(results, descriptor_loader) + + # Verify the scenario has 1 service and 0 slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.service_ids) == 1 + assert len(response.slice_ids) == 0 + + # Check there are no slices + response = context_client.ListSlices(ADMIN_CONTEXT_ID) + LOGGER.warning('Slices[{:d}] = {:s}'.format(len(response.slices), grpc_message_to_json_string(response))) + assert len(response.slices) == 0 + + # Check there is 1 service + response = context_client.ListServices(ADMIN_CONTEXT_ID) + LOGGER.warning('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 1 + + for service in response.services: + service_id = service.service_id + assert service.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE + + response = context_client.ListConnections(service_id) + LOGGER.warning(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) + + if service.service_type == ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY: + assert len(response.connections) == 2 + else: + str_service = grpc_message_to_json_string(service) + raise Exception('Unexpected ServiceType: {:s}'.format(str_service)) diff --git a/src/tests/ofc24/tests/test_functional_delete_service.py b/src/tests/ofc24/tests/test_functional_delete_service.py deleted file mode 100644 index daff29064f07e8117a4503fc243c7acac9f88bc6..0000000000000000000000000000000000000000 --- a/src/tests/ofc24/tests/test_functional_delete_service.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging, os -from common.Constants import DEFAULT_CONTEXT_NAME -from common.proto.context_pb2 import ContextId, ServiceTypeEnum -from common.tools.descriptor.Loader import DescriptorLoader -from common.tools.grpc.Tools import grpc_message_to_json_string -from common.tools.object_factory.Context import json_context_id -from context.client.ContextClient import ContextClient -from tests.Fixtures import context_client # pylint: disable=unused-import -from tests.tools.mock_osm.MockOSM import MockOSM -from .Fixtures import osm_wim # pylint: disable=unused-import - -LOGGER = logging.getLogger(__name__) -LOGGER.setLevel(logging.DEBUG) - -DESCRIPTOR_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'descriptors_emulated.json') -ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) - -def test_service_removal(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name - # Ensure slices and services are created - response = context_client.ListSlices(ADMIN_CONTEXT_ID) - LOGGER.info('Slices[{:d}] = {:s}'.format(len(response.slices), grpc_message_to_json_string(response))) - assert len(response.slices) == 1 # OSM slice - - response = context_client.ListServices(ADMIN_CONTEXT_ID) - LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) - assert len(response.services) == 2 # 1xL3NM + 1xTAPI - - service_uuids = set() - 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))) - - if service.service_type == ServiceTypeEnum.SERVICETYPE_L3NM: - assert len(response.connections) == 1 # 1 connection per service - service_uuid = service_id.service_uuid.uuid - service_uuids.add(service_uuid) - osm_wim.conn_info[service_uuid] = {} - elif service.service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE: - assert len(response.connections) == 1 # 1 connection per service - else: - str_service = grpc_message_to_json_string(service) - raise Exception('Unexpected ServiceType: {:s}'.format(str_service)) - - # Identify service to delete - assert len(service_uuids) == 1 # assume a single L3NM service has been created - service_uuid = set(service_uuids).pop() - - # Delete Connectivity Service - osm_wim.delete_connectivity_service(service_uuid) - - # Verify the scenario has no services/slices - response = context_client.GetContext(ADMIN_CONTEXT_ID) - assert len(response.service_ids) == 0 - assert len(response.slice_ids) == 0 - - # Load descriptors and validate the base scenario - descriptor_loader = DescriptorLoader(descriptors_file=DESCRIPTOR_FILE, context_client=context_client) - descriptor_loader.validate() diff --git a/src/tests/ofc24/tests/test_functional_delete_service_bidir.py b/src/tests/ofc24/tests/test_functional_delete_service_bidir.py new file mode 100644 index 0000000000000000000000000000000000000000..a337336a87535a89aa6ca176d56e40d33dcb1aca --- /dev/null +++ b/src/tests/ofc24/tests/test_functional_delete_service_bidir.py @@ -0,0 +1,78 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from typing import Set, Tuple +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import ContextId, ServiceId, ServiceStatusEnum, ServiceTypeEnum +from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Service import json_service_id +from context.client.ContextClient import ContextClient +from service.client.ServiceClient import ServiceClient +from tests.Fixtures import context_client, service_client # pylint: disable=unused-import + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) + +def test_service_removal_bidir( + context_client : ContextClient, # pylint: disable=redefined-outer-name + service_client : ServiceClient, # pylint: disable=redefined-outer-name +): + # Verify the scenario has 1 service and 0 slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.service_ids) == 1 + assert len(response.slice_ids) == 0 + + # Check there are no slices + response = context_client.ListSlices(ADMIN_CONTEXT_ID) + LOGGER.warning('Slices[{:d}] = {:s}'.format(len(response.slices), grpc_message_to_json_string(response))) + assert len(response.slices) == 0 + + # Check there is 1 service + response = context_client.ListServices(ADMIN_CONTEXT_ID) + LOGGER.warning('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 1 + + context_service_uuids : Set[Tuple[str, str]] = set() + for service in response.services: + service_id = service.service_id + assert service.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE + + response = context_client.ListConnections(service_id) + LOGGER.warning(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) + + if service.service_type == ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY: + assert len(response.connections) == 2 + context_uuid = service_id.context_id.context_uuid.uuid + service_uuid = service_id.service_uuid.uuid + context_service_uuids.add((context_uuid, service_uuid)) + else: + str_service = grpc_message_to_json_string(service) + raise Exception('Unexpected ServiceType: {:s}'.format(str_service)) + + # Identify service to delete + assert len(context_service_uuids) == 1 + context_uuid, service_uuid = set(context_service_uuids).pop() + + # Delete Service + service_client.DeleteService(ServiceId(**json_service_id(service_uuid, json_context_id(context_uuid)))) + + # Verify the scenario has no services/slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.service_ids) == 0 + assert len(response.slice_ids) == 0 diff --git a/src/tests/ofc24/tests/test_functional_delete_service_unidir.py b/src/tests/ofc24/tests/test_functional_delete_service_unidir.py new file mode 100644 index 0000000000000000000000000000000000000000..9b0381c492bd1c0783aaf9872037bfefdd25fa37 --- /dev/null +++ b/src/tests/ofc24/tests/test_functional_delete_service_unidir.py @@ -0,0 +1,78 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from typing import Set, Tuple +from common.Constants import DEFAULT_CONTEXT_NAME +from common.proto.context_pb2 import ContextId, ServiceId, ServiceStatusEnum, ServiceTypeEnum +from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Service import json_service_id +from context.client.ContextClient import ContextClient +from service.client.ServiceClient import ServiceClient +from tests.Fixtures import context_client, service_client # pylint: disable=unused-import + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) + +def test_service_removal_unidir( + context_client : ContextClient, # pylint: disable=redefined-outer-name + service_client : ServiceClient, # pylint: disable=redefined-outer-name +): + # Verify the scenario has 1 service and 0 slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.service_ids) == 1 + assert len(response.slice_ids) == 0 + + # Check there are no slices + response = context_client.ListSlices(ADMIN_CONTEXT_ID) + LOGGER.warning('Slices[{:d}] = {:s}'.format(len(response.slices), grpc_message_to_json_string(response))) + assert len(response.slices) == 0 + + # Check there is 1 service + response = context_client.ListServices(ADMIN_CONTEXT_ID) + LOGGER.warning('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 1 + + context_service_uuids : Set[Tuple[str, str]] = set() + for service in response.services: + service_id = service.service_id + assert service.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE + + response = context_client.ListConnections(service_id) + LOGGER.warning(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) + + if service.service_type == ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY: + assert len(response.connections) == 2 + context_uuid = service_id.context_id.context_uuid.uuid + service_uuid = service_id.service_uuid.uuid + context_service_uuids.add((context_uuid, service_uuid)) + else: + str_service = grpc_message_to_json_string(service) + raise Exception('Unexpected ServiceType: {:s}'.format(str_service)) + + # Identify service to delete + assert len(context_service_uuids) == 1 + context_uuid, service_uuid = set(context_service_uuids).pop() + + # Delete Service + service_client.DeleteService(ServiceId(**json_service_id(service_uuid, json_context_id(context_uuid)))) + + # Verify the scenario has no services/slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.service_ids) == 0 + assert len(response.slice_ids) == 0