diff --git a/src/tests/ofc24/.gitlab-ci.yml b/src/tests/ofc24/.gitlab-ci.yml index 6d52579649bc6dc00ab498fef09c92ceed8f939a..6dc32a181de0978151971a02f1d7d6c67125558d 100644 --- a/src/tests/ofc24/.gitlab-ci.yml +++ b/src/tests/ofc24/.gitlab-ci.yml @@ -45,16 +45,55 @@ end2end_test ofc24: # - 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 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 - - ./src/tests/${TEST_NAME}/deploy-node-agents.sh + - > + 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 @@ -89,6 +128,7 @@ end2end_test ofc24: --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 diff --git a/src/tests/ofc24/Dockerfile b/src/tests/ofc24/Dockerfile index df42fe3382e9aeb4f7c753d2086dc9d920362321..db1d1d9ef277659657b46d64392e6ce519eec5f7 100644 --- a/src/tests/ofc24/Dockerfile +++ b/src/tests/ofc24/Dockerfile @@ -92,10 +92,12 @@ RUN tee ./run_tests.sh <<EOF #!/bin/bash source /var/teraflow/tfs_runtime_env_vars.sh export PYTHONPATH=/var/teraflow -pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_bootstrap.py --junitxml=/opt/results/report_bootstrap.xml -pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_create_service.py --junitxml=/opt/results/report_create_service.xml -pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_delete_service.py --junitxml=/opt/results/report_delete_service.xml -pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_cleanup.py --junitxml=/opt/results/report_cleanup.xml +pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_bootstrap.py --junitxml=/opt/results/report_bootstrap.xml +pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_create_service_unidir.py --junitxml=/opt/results/report_create_service_unidir.xml +pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_delete_service_unidir.py --junitxml=/opt/results/report_delete_service_unidir.xml +pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_create_service_bidir.py --junitxml=/opt/results/report_create_service_bidir.xml +pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_delete_service_bidir.py --junitxml=/opt/results/report_delete_service_bidir.xml +pytest --verbose --log-level=INFO /var/teraflow/tests/ofc24/tests/test_functional_cleanup.py --junitxml=/opt/results/report_cleanup.xml EOF RUN chmod ug+x ./run_tests.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_bidir.py similarity index 61% rename from src/tests/ofc24/tests/test_functional_create_service.py rename to src/tests/ofc24/tests/test_functional_create_service_bidir.py index 74c74483eb82325afec2cae833ebd460566da153..19c05425b607688fd6d14d3aea7b58b1883ca7c5 100644 --- a/src/tests/ofc24/tests/test_functional_create_service.py +++ b/src/tests/ofc24/tests/test_functional_create_service_bidir.py @@ -12,15 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, os, random +import logging, os 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.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 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 @@ -29,10 +27,10 @@ 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') +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(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name +def test_service_creation_bidir(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() @@ -68,35 +66,3 @@ def test_service_creation(context_client : ContextClient, osm_wim : MockOSM): # 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_unidir.py b/src/tests/ofc24/tests/test_functional_create_service_unidir.py new file mode 100644 index 0000000000000000000000000000000000000000..35d9664f72e13d3a4cf31e16e727c727ed4e8c2b --- /dev/null +++ b/src/tests/ofc24/tests/test_functional_create_service_unidir.py @@ -0,0 +1,68 @@ +# 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, 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', 'service-bidir.json') +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) + +def test_service_creation_unidir(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)) diff --git a/src/tests/ofc24/tests/test_functional_delete_service.py b/src/tests/ofc24/tests/test_functional_delete_service_bidir.py similarity index 95% rename from src/tests/ofc24/tests/test_functional_delete_service.py rename to src/tests/ofc24/tests/test_functional_delete_service_bidir.py index daff29064f07e8117a4503fc243c7acac9f88bc6..64a6161f62877225959e6af501a7b9bf10e0dc7a 100644 --- a/src/tests/ofc24/tests/test_functional_delete_service.py +++ b/src/tests/ofc24/tests/test_functional_delete_service_bidir.py @@ -26,10 +26,10 @@ 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') +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_removal(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name +def test_service_removal_bidir(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))) 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..ebdb602559039353cc787addf58412e6edafc4f8 --- /dev/null +++ b/src/tests/ofc24/tests/test_functional_delete_service_unidir.py @@ -0,0 +1,74 @@ +# 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', 'service-unidir.json') +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) + +def test_service_removal_unidir(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()