From 2d918155fca7eca14643becc1d997102f7caadf9 Mon Sep 17 00:00:00 2001 From: Carlos Manso Date: Fri, 22 Dec 2023 15:17:16 +0100 Subject: [PATCH 1/3] etsi_bwm fixes and unit tests --- scripts/run_tests_locally-etsi_bwm.sh | 24 ++ src/common/tests/MockServicerImpl_Context.py | 2 +- src/nbi/.gitlab-ci.yml | 2 +- .../nbi_plugins/etsi_bwm/Resources.py | 23 +- .../rest_server/nbi_plugins/etsi_bwm/Tools.py | 9 +- .../nbi_plugins/etsi_bwm/__init__.py | 2 +- src/nbi/tests/Constants.py | 4 +- src/nbi/tests/PrepareTestScenario.py | 119 +++++- src/nbi/tests/data/topology-dummy.json | 362 ++++++++++++++++++ src/nbi/tests/test_etsi_bwm.py | 224 +++++++++++ 10 files changed, 743 insertions(+), 28 deletions(-) create mode 100755 scripts/run_tests_locally-etsi_bwm.sh create mode 100644 src/nbi/tests/data/topology-dummy.json create mode 100644 src/nbi/tests/test_etsi_bwm.py diff --git a/scripts/run_tests_locally-etsi_bwm.sh b/scripts/run_tests_locally-etsi_bwm.sh new file mode 100755 index 000000000..a057ee28f --- /dev/null +++ b/scripts/run_tests_locally-etsi_bwm.sh @@ -0,0 +1,24 @@ +#!/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. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc + +# Run unitary tests and analyze coverage of code at same time +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + nbi/tests/test_etsi_bwm.py diff --git a/src/common/tests/MockServicerImpl_Context.py b/src/common/tests/MockServicerImpl_Context.py index 837445b5a..d253f266b 100644 --- a/src/common/tests/MockServicerImpl_Context.py +++ b/src/common/tests/MockServicerImpl_Context.py @@ -494,7 +494,7 @@ class MockServicerImpl_Context(ContextServiceServicer): def RemoveService(self, request: ServiceId, context : grpc.ServicerContext) -> Empty: LOGGER.debug('[RemoveService] request={:s}'.format(grpc_message_to_json_string(request))) - context_uuid = str(request.service_id.context_id.context_uuid.uuid) + context_uuid = str(request.context_id.context_uuid.uuid) container_name = 'service[{:s}]'.format(context_uuid) service_uuid = request.service_uuid.uuid reply = self._del(request, container_name, service_uuid, 'service_id', TOPIC_SERVICE, context) diff --git a/src/nbi/.gitlab-ci.yml b/src/nbi/.gitlab-ci.yml index 0b12f9f08..01854e5a0 100644 --- a/src/nbi/.gitlab-ci.yml +++ b/src/nbi/.gitlab-ci.yml @@ -56,7 +56,7 @@ unit_test nbi: - sleep 5 - docker ps -a - docker logs $IMAGE_NAME - - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml" + - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py $IMAGE_NAME/tests/test_etsi_bwm.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml" - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' after_script: diff --git a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Resources.py b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Resources.py index 38534b754..4c858990b 100644 --- a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Resources.py +++ b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Resources.py @@ -12,14 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import copy +import copy, json, logging from common.Constants import DEFAULT_CONTEXT_NAME -from flask_restful import Resource, request from context.client.ContextClient import ContextClient +from flask_restful import Resource, request from service.client.ServiceClient import ServiceClient from .Tools import ( format_grpc_to_json, grpc_context_id, grpc_service_id, bwInfo_2_service, service_2_bwInfo) +LOGGER = logging.getLogger(__name__) class _Resource(Resource): @@ -39,7 +40,6 @@ class BwInfo(_Resource): bwinfo = request.get_json() service = bwInfo_2_service(self.client, bwinfo) stripped_service = copy.deepcopy(service) - stripped_service.ClearField('service_endpoint_ids') stripped_service.ClearField('service_constraints') stripped_service.ClearField('service_config') @@ -57,19 +57,24 @@ class BwInfoId(_Resource): return service_2_bwInfo(service) def put(self, allocationId: str): - json_data = request.get_json() + json_data = json.loads(request.get_json()) service = bwInfo_2_service(self.client, json_data) - response = self.service_client.UpdateService(service) - return format_grpc_to_json(response) + self.service_client.UpdateService(service) + service = self.client.GetService(grpc_service_id(DEFAULT_CONTEXT_NAME, json_data['appInsId'])) + response_bwm = service_2_bwInfo(service) + + return response_bwm def patch(self, allocationId: str): json_data = request.get_json() if not 'appInsId' in json_data: json_data['appInsId'] = allocationId service = bwInfo_2_service(self.client, json_data) - response = self.service_client.UpdateService(service) - return format_grpc_to_json(response) + self.service_client.UpdateService(service) + service = self.client.GetService(grpc_service_id(DEFAULT_CONTEXT_NAME, json_data['appInsId'])) + response_bwm = service_2_bwInfo(service) + + return response_bwm def delete(self, allocationId: str): self.service_client.DeleteService(grpc_service_id(DEFAULT_CONTEXT_NAME, allocationId)) - return diff --git a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Tools.py b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Tools.py index 023d1006c..a1b66f032 100644 --- a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Tools.py +++ b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Tools.py @@ -27,13 +27,12 @@ LOGGER = logging.getLogger(__name__) def service_2_bwInfo(service: Service) -> dict: response = {} # allocationDirection = '??' # String: 00 = Downlink (towards the UE); 01 = Uplink (towards the application/session); 10 = Symmetrical - response['appInsId'] = service.service_id.context_id.context_uuid.uuid # String: Application instance identifier + response['appInsId'] = service.service_id.service_uuid.uuid # String: Application instance identifier for constraint in service.service_constraints: if constraint.WhichOneof('constraint') == 'sla_capacity': response['fixedAllocation'] = str(constraint.sla_capacity.capacity_gbps*1000) # String: Size of requested fixed BW allocation in [bps] break - for config_rule in service.service_config.config_rules: for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'sourceIp', 'sourcePort', 'dstPort', 'protocol', 'sessionFilter']: if config_rule.custom.resource_key == key: @@ -42,7 +41,6 @@ def service_2_bwInfo(service: Service) -> dict: else: response[key] = json.loads(config_rule.custom.resource_value) - unixtime = time.time() response['timeStamp'] = { # Time stamp to indicate when the corresponding information elements are sent "seconds": int(unixtime), @@ -53,7 +51,6 @@ def service_2_bwInfo(service: Service) -> dict: def bwInfo_2_service(client, bwInfo: dict) -> Service: service = Service() - for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'timeStamp', 'sessionFilter']: if key not in bwInfo: continue @@ -82,10 +79,6 @@ def bwInfo_2_service(client, bwInfo: dict) -> Service: ep_id.endpoint_uuid.uuid = ep['uuid'] ep_id.device_id.device_uuid.uuid = device.device_id.device_uuid.uuid service.service_endpoint_ids.append(ep_id) - - if len(service.service_endpoint_ids) < 2: - LOGGER.error('No endpoints matched') - return None service.service_type = ServiceTypeEnum.SERVICETYPE_L3NM diff --git a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/__init__.py b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/__init__.py index 5525c58ad..f34432d82 100644 --- a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/__init__.py @@ -15,7 +15,7 @@ from nbi.service.rest_server.RestServer import RestServer from .Resources import BwInfo, BwInfoId -URL_PREFIX = '/bwm/v1' +URL_PREFIX = '/restconf/bwm/v1' # Use 'path' type since some identifiers might contain char '/' and Flask is unable to recognize them in 'string' type. RESOURCES = [ diff --git a/src/nbi/tests/Constants.py b/src/nbi/tests/Constants.py index d177a219a..d46ce2cf7 100644 --- a/src/nbi/tests/Constants.py +++ b/src/nbi/tests/Constants.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -WIM_USERNAME = 'admin' -WIM_PASSWORD = 'admin' +USERNAME = 'admin' +PASSWORD = 'admin' # Ref: https://osm.etsi.org/wikipub/index.php/WIM WIM_MAPPING = [ diff --git a/src/nbi/tests/PrepareTestScenario.py b/src/nbi/tests/PrepareTestScenario.py index 8a868851f..825d45684 100644 --- a/src/nbi/tests/PrepareTestScenario.py +++ b/src/nbi/tests/PrepareTestScenario.py @@ -12,16 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, pytest, time +import logging, os, pytest, requests, time, enum +from typing import Dict, List, Optional, Set, Union, Any from common.Constants import ServiceNameEnum from common.Settings import ( - ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_HTTP, get_env_var_name, get_service_port_http) + ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_HTTP, + get_env_var_name, get_service_baseurl_http, get_service_port_http +) +from context.client.ContextClient import ContextClient from nbi.service.rest_server.RestServer import RestServer from nbi.service.rest_server.nbi_plugins.debug_api import register_debug_api from nbi.service.rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn +from nbi.service.rest_server.nbi_plugins.etsi_bwm import register_etsi_bwm_api from nbi.tests.MockService_Dependencies import MockService_Dependencies +from service.client.ServiceClient import ServiceClient +from slice.client.SliceClient import SliceClient from tests.tools.mock_osm.MockOSM import MockOSM -from .Constants import WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD +from .Constants import USERNAME, PASSWORD, WIM_MAPPING LOCAL_HOST = '127.0.0.1' MOCKSERVICE_PORT = 10000 @@ -38,10 +45,11 @@ def mock_service(): _service.stop() @pytest.fixture(scope='session') -def nbi_service_rest(mock_service): # pylint: disable=redefined-outer-name +def nbi_service_rest(mock_service : MockService_Dependencies): # pylint: disable=redefined-outer-name, unused-argument _rest_server = RestServer() register_debug_api(_rest_server) register_ietf_l2vpn(_rest_server) + register_etsi_bwm_api(_rest_server) _rest_server.start() time.sleep(1) # bring time for the server to start yield _rest_server @@ -49,6 +57,105 @@ def nbi_service_rest(mock_service): # pylint: disable=redefined-outer-name _rest_server.join() @pytest.fixture(scope='session') -def osm_wim(nbi_service_rest): # pylint: disable=redefined-outer-name +def osm_wim(nbi_service_rest : RestServer): # pylint: disable=redefined-outer-name, unused-argument wim_url = 'http://{:s}:{:d}'.format(LOCAL_HOST, NBI_SERVICE_PORT) - return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) + return MockOSM(wim_url, WIM_MAPPING, USERNAME, PASSWORD) + +@pytest.fixture(scope='session') +def context_client(mock_service : MockService_Dependencies): # pylint: disable=redefined-outer-name, unused-argument + _client = ContextClient() + yield _client + _client.close() + +@pytest.fixture(scope='session') +def service_client(mock_service : MockService_Dependencies): # pylint: disable=redefined-outer-name, unused-argument + _client = ServiceClient() + yield _client + _client.close() + +@pytest.fixture(scope='session') +def slice_client(mock_service : MockService_Dependencies): # pylint: disable=redefined-outer-name, unused-argument + _client = SliceClient() + yield _client + _client.close() + +class RestRequestMethod(enum.Enum): + GET = 'get' + POST = 'post' + PUT = 'put' + DELETE = 'delete' + PATCH = 'patch' + + +EXPECTED_STATUS_CODES : Set[int] = { + requests.codes['OK' ], + requests.codes['CREATED' ], + requests.codes['ACCEPTED' ], + requests.codes['NO_CONTENT'], +} + +def do_rest_request( + method : RestRequestMethod, url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Union[Dict, List]: + base_url = get_service_baseurl_http(ServiceNameEnum.NBI) or '' + request_url = 'http://{:s}:{:s}@{:s}:{:d}{:s}{:s}'.format( + USERNAME, PASSWORD, LOCAL_HOST, NBI_SERVICE_PORT, str(base_url), url + ) + if logger is not None: + msg = 'Request: {:s} {:s}'.format(str(method.value).upper(), str(request_url)) + if body is not None: msg += ' body={:s}'.format(str(body)) + logger.warning(msg) + method = getattr(requests, method.value) + reply = method(request_url, timeout=timeout, json=body, allow_redirects=allow_redirects) + if logger is not None: + logger.warning('Reply: {:s}'.format(str(reply.text))) + assert reply.status_code in expected_status_codes, 'Reply failed with status code {:d}'.format(reply.status_code) + return reply.json() + +def do_rest_get_request( + url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Union[Dict, List]: + return do_rest_request( + RestRequestMethod.GET, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_post_request(url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Union[Dict, List]: + return do_rest_request( + RestRequestMethod.POST, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_put_request(url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Union[Dict, List]: + return do_rest_request( + RestRequestMethod.PUT, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_delete_request(url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Union[Dict, List]: + return do_rest_request( + RestRequestMethod.DELETE, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) + +def do_rest_patch_request(url : str, body : Optional[Any] = None, timeout : int = 10, + allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES, + logger : Optional[logging.Logger] = None +) -> Union[Dict, List]: + return do_rest_request( + RestRequestMethod.PATCH, url, body=body, timeout=timeout, allow_redirects=allow_redirects, + expected_status_codes=expected_status_codes, logger=logger + ) \ No newline at end of file diff --git a/src/nbi/tests/data/topology-dummy.json b/src/nbi/tests/data/topology-dummy.json new file mode 100644 index 000000000..3ee8bf69a --- /dev/null +++ b/src/nbi/tests/data/topology-dummy.json @@ -0,0 +1,362 @@ +{ + "dummy_mode": true, + + "contexts": [ + {"context_id": {"context_uuid": {"uuid": "admin"}}, "name": "admin"} + ], + + "topologies": [ + { + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"} + }, + "name": "admin" + }, + { + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + }, + "name": "providerId-10-clientId-0-topologyId-1", + "device_ids": [ + {"device_uuid": {"uuid": "10.0.10.1"}}, + {"device_uuid": {"uuid": "10.0.20.1"}}, + {"device_uuid": {"uuid": "10.0.30.1"}}, + {"device_uuid": {"uuid": "10.0.40.1"}} + ] + }, + { + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-2"} + }, + "name": "providerId-10-clientId-0-topologyId-2", + "device_ids": [ + {"device_uuid": {"uuid": "10.0.10.1"}}, + {"device_uuid": {"uuid": "10.0.20.1"}}, + {"device_uuid": {"uuid": "10.0.30.1"}}, + {"device_uuid": {"uuid": "10.0.40.1"}} + ] + } + ], + + "devices": [ + { + "device_config": { + "config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": "{\n\"endpoints\": [\n{\n\"context_uuid\": \"admin\",\n\"name\": \"mgmt\",\n\"topology_uuid\": \"admin\",\n\"type\": \"mgmt\",\n\"uuid\": \"mgmt\"\n}\n]\n}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[mgmt]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"mgmt\", \"topology_uuid\": \"admin\", \"type\": \"mgmt\", \"uuid\": \"mgmt\"}"}} + ] + }, + "device_drivers": [0], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "nce-t"}}, + "endpoint_uuid": {"uuid": "mgmt"}, + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}} + }, + "endpoint_type": "mgmt", + "name": "mgmt" + } + ], + "device_id": {"device_uuid": {"uuid": "nce-t"}}, + "device_operational_status": 2, + "device_type": "emu-open-line-system", + "name": "nce-t" + }, + { + "controller_id": {"device_uuid": {"uuid": "nce-t"}}, + "device_config": { + "config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": "{\n\"endpoints\": [\n{\n\"context_uuid\": \"admin\",\n\"name\": \"mgmt\",\n\"topology_uuid\": \"admin\",\n\"type\": \"mgmt\",\n\"uuid\": \"mgmt\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"200\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-2\",\n\"type\": \"copper\",\n\"uuid\": \"uuid-200\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"500\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\",\n\"type\": \"optical\",\n\"uuid\": \"uuid-500\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"501\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\",\n\"type\": \"optical\",\n\"uuid\": \"uuid-501\"\n}\n]\n}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[mgmt]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"mgmt\", \"topology_uuid\": \"admin\", \"type\": \"mgmt\", \"uuid\": \"mgmt\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-200]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"200\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-2\", \"type\": \"copper\", \"uuid\": \"uuid-200\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-500]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"500\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\", \"type\": \"optical\", \"uuid\": \"uuid-500\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-501]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"501\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\", \"type\": \"optical\", \"uuid\": \"uuid-501\"}"}} + ] + }, + "device_drivers": [0], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, + "endpoint_uuid": {"uuid": "uuid-200"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-2"} + } + }, + "endpoint_type": "copper", + "name": "200" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, + "endpoint_uuid": {"uuid": "uuid-500"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + } + }, + "endpoint_type": "optical", + "name": "500" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, + "endpoint_uuid": {"uuid": "mgmt"}, + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}} + }, + "endpoint_type": "mgmt", + "name": "mgmt" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, + "endpoint_uuid": {"uuid": "uuid-501"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + } + }, + "endpoint_type": "optical", + "name": "501" + } + ], + "device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, + "device_operational_status": 2, + "device_type": "emu-packet-router", + "name": "10.0.30.1" + }, + { + "controller_id": {"device_uuid": {"uuid": "nce-t"}}, + "device_config": { + "config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": "{\n\"endpoints\": [\n{\n\"context_uuid\": \"admin\",\n\"name\": \"mgmt\",\n\"topology_uuid\": \"admin\",\n\"type\": \"mgmt\",\n\"uuid\": \"mgmt\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"500\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\",\n\"type\": \"optical\",\n\"uuid\": \"uuid-500\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"501\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\",\n\"type\": \"optical\",\n\"uuid\": \"uuid-501\"\n}\n]\n}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[mgmt]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"mgmt\", \"topology_uuid\": \"admin\", \"type\": \"mgmt\", \"uuid\": \"mgmt\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-500]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"500\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\", \"type\": \"optical\", \"uuid\": \"uuid-500\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-501]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"501\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\", \"type\": \"optical\", \"uuid\": \"uuid-501\"}"}} + ] + }, + "device_drivers": [0], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, + "endpoint_uuid": {"uuid": "uuid-501"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + } + }, + "endpoint_type": "optical", + "name": "501" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, + "endpoint_uuid": {"uuid": "uuid-500"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + } + }, + "endpoint_type": "optical", + "name": "500" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, + "endpoint_uuid": {"uuid": "mgmt"}, + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}} + }, + "endpoint_type": "mgmt", + "name": "mgmt" + } + ], + "device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, + "device_operational_status": 2, + "device_type": "emu-optical-roadm", + "name": "10.0.40.1" + }, + { + "controller_id": {"device_uuid": {"uuid": "nce-t"}}, + "device_config": { + "config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": "{\n\"endpoints\": [\n{\n\"context_uuid\": \"admin\",\n\"name\": \"mgmt\",\n\"topology_uuid\": \"admin\",\n\"type\": \"mgmt\",\n\"uuid\": \"mgmt\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"200\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-2\",\n\"type\": \"copper\",\n\"uuid\": \"uuid-200\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"500\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\",\n\"type\": \"optical\",\n\"uuid\": \"uuid-500\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"501\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\",\n\"type\": \"optical\",\n\"uuid\": \"uuid-501\"\n}\n]\n}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[mgmt]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"mgmt\", \"topology_uuid\": \"admin\", \"type\": \"mgmt\", \"uuid\": \"mgmt\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-200]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"200\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-2\", \"type\": \"copper\", \"uuid\": \"uuid-200\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-500]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"500\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\", \"type\": \"optical\", \"uuid\": \"uuid-500\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-501]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"501\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\", \"type\": \"optical\", \"uuid\": \"uuid-501\"}"}} + ] + }, + "device_drivers": [0], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, + "endpoint_uuid": {"uuid": "mgmt"}, + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}} + }, + "endpoint_type": "mgmt", + "name": "mgmt" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, + "endpoint_uuid": {"uuid": "uuid-501"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + } + }, + "endpoint_type": "optical", + "name": "501" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, + "endpoint_uuid": {"uuid": "uuid-200"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-2"} + } + }, + "endpoint_type": "copper", + "name": "200" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, + "endpoint_uuid": {"uuid": "uuid-500"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + } + }, + "endpoint_type": "optical", + "name": "500" + } + ], + "device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, + "device_operational_status": 2, + "device_type": "emu-packet-router", + "name": "10.0.10.1" + }, + { + "controller_id": {"device_uuid": {"uuid": "nce-t"}}, + "device_config": { + "config_rules": [ + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "_connect/settings", "resource_value": "{\n\"endpoints\": [\n{\n\"context_uuid\": \"admin\",\n\"name\": \"mgmt\",\n\"topology_uuid\": \"admin\",\n\"type\": \"mgmt\",\n\"uuid\": \"mgmt\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"500\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\",\n\"type\": \"optical\",\n\"uuid\": \"uuid-500\"\n},\n{\n\"context_uuid\": \"admin\",\n\"name\": \"501\",\n\"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\",\n\"type\": \"optical\",\n\"uuid\": \"uuid-501\"\n}\n]\n}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[mgmt]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"mgmt\", \"topology_uuid\": \"admin\", \"type\": \"mgmt\", \"uuid\": \"mgmt\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-500]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"500\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\", \"type\": \"optical\", \"uuid\": \"uuid-500\"}"}}, + {"action": "CONFIGACTION_SET", "custom": {"resource_key": "/endpoints/endpoint[uuid-501]", "resource_value": "{\"context_uuid\": \"admin\", \"name\": \"501\", \"topology_uuid\": \"providerId-10-clientId-0-topologyId-1\", \"type\": \"optical\", \"uuid\": \"uuid-501\"}"}} + ] + }, + "device_drivers": [0], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, + "endpoint_uuid": {"uuid": "uuid-500"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + } + }, + "endpoint_type": "optical", + "name": "500" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, + "endpoint_uuid": {"uuid": "uuid-501"}, + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"} + } + }, + "endpoint_type": "optical", + "name": "501" + }, + { + "endpoint_id": { + "device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, + "endpoint_uuid": {"uuid": "mgmt"}, + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}} + }, + "endpoint_type": "mgmt", + "name": "mgmt" + } + ], + "device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, + "device_operational_status": 2, + "device_type": "emu-optical-roadm", + "name": "10.0.20.1" + } + ], + + "links": [ + {"link_id": {"link_uuid": {"uuid": "nce-t/mgmt==10.0.10.1/mgmt"}}, "name": "nce-t/mgmt==10.0.10.1/mgmt", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "nce-t" }}, "endpoint_uuid": {"uuid": "mgmt"}}, + {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "mgmt"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "nce-t/mgmt==10.0.20.1/mgmt"}}, "name": "nce-t/mgmt==10.0.20.1/mgmt", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "nce-t" }}, "endpoint_uuid": {"uuid": "mgmt"}}, + {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "mgmt"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "nce-t/mgmt==10.0.30.1/mgmt"}}, "name": "nce-t/mgmt==10.0.30.1/mgmt", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "nce-t" }}, "endpoint_uuid": {"uuid": "mgmt"}}, + {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "mgmt"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "nce-t/mgmt==10.0.40.1/mgmt"}}, "name": "nce-t/mgmt==10.0.40.1/mgmt", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "nce-t" }}, "endpoint_uuid": {"uuid": "mgmt"}}, + {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "mgmt"}} + ]}, + + {"link_id": {"link_uuid": {"uuid": "10.0.10.1/501==10.0.20.1/501"}}, "name": "10.0.10.1-501", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "uuid-501"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}, + {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "uuid-501"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}} + ]}, + {"link_id": {"link_uuid": {"uuid": "10.0.20.1/501==10.0.10.1/501"}}, "name": "10.0.20.1-501", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "uuid-501"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}, + {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "uuid-501"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}} + ]}, + + {"link_id": {"link_uuid": {"uuid": "10.0.10.1/500==10.0.40.1/500"}}, "name": "10.0.10.1-500", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "uuid-500"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}, + {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "uuid-500"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}} + ]}, + {"link_id": {"link_uuid": {"uuid": "10.0.40.1/500==10.0.10.1/500"}}, "name": "10.0.40.1-500", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "uuid-500"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}, + {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "uuid-500"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}} + ]}, + + {"link_id": {"link_uuid": {"uuid": "10.0.20.1/500==10.0.30.1/500"}}, "name": "10.0.20.1-500", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "uuid-500"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}, + {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "uuid-500"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}} + ]}, + {"link_id": {"link_uuid": {"uuid": "10.0.30.1/500==10.0.20.1/500"}}, "name": "10.0.30.1-500", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "uuid-500"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}, + {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "uuid-500"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}} + ]}, + + {"link_id": {"link_uuid": {"uuid": "10.0.40.1/501==10.0.30.1/501"}}, "name": "10.0.40.1-501", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "uuid-501"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}, + {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "uuid-501"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}} + ]}, + {"link_id": {"link_uuid": {"uuid": "10.0.30.1/501==10.0.40.1/501"}}, "name": "10.0.30.1-501", "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "uuid-501"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}, + {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "uuid-501"}, "topology_id": {"topology_uuid": {"uuid": "providerId-10-clientId-0-topologyId-1"}, "context_id": {"context_uuid": {"uuid": "admin"}}}} + ]} + ] +} diff --git a/src/nbi/tests/test_etsi_bwm.py b/src/nbi/tests/test_etsi_bwm.py new file mode 100644 index 000000000..6d77aa749 --- /dev/null +++ b/src/nbi/tests/test_etsi_bwm.py @@ -0,0 +1,224 @@ +# 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. + +from typing import Dict +import json, logging, pytest +from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME +from common.proto.context_pb2 import ContextId, TopologyId +from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results, validate_empty_scenario +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from nbi.service.rest_server import RestServer +from .PrepareTestScenario import do_rest_delete_request, do_rest_post_request, do_rest_get_request, do_rest_put_request, do_rest_patch_request, mock_service, nbi_service_rest, context_client + + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DESCRIPTOR_FILE = 'nbi/tests/data/topology-dummy.json' + +JSON_ADMIN_CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_NAME) +ADMIN_CONTEXT_ID = ContextId(**JSON_ADMIN_CONTEXT_ID) +ADMIN_TOPOLOGY_ID = TopologyId(**json_topology_id(DEFAULT_TOPOLOGY_NAME, context_id=JSON_ADMIN_CONTEXT_ID)) +BASE_URL = '/restconf/bwm/v1' + +@pytest.fixture(scope='session') +def storage() -> Dict: + yield dict() + +def compare_dicts(dict1, dict2): + # Function to recursively sort dictionaries + def recursively_sort(d): + if isinstance(d, dict): + return {k: recursively_sort(v) for k, v in sorted(d.items())} + if isinstance(d, list): + return [recursively_sort(item) for item in d] + return d + + # Sort dictionaries to ignore the order of fields + sorted_dict1 = recursively_sort(dict1) + sorted_dict2 = recursively_sort(dict2) + + if sorted_dict1 != sorted_dict2: + LOGGER.error(sorted_dict1) + LOGGER.error(sorted_dict2) + + return sorted_dict1 != sorted_dict2 + +def check_timestamps(bwm_service): + assert 'timeStamp' in bwm_service + assert 'seconds' in bwm_service['timeStamp'] + assert 'nanoseconds' in bwm_service['timeStamp'] + +def test_prepare_environment(context_client : ContextClient) -> None: # pylint: disable=redefined-outer-name + validate_empty_scenario(context_client) + descriptor_loader = DescriptorLoader(descriptors_file=DESCRIPTOR_FILE, context_client=context_client) + results = descriptor_loader.process() + check_descriptor_load_results(results, descriptor_loader) + descriptor_loader.validate() + + # Verify the scenario has no services/slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.topology_ids) == 3 + assert len(response.service_ids ) == 0 + assert len(response.slice_ids ) == 0 + +def test_get_allocations_empty(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument + URL = BASE_URL + '/bw_allocations' + retrieved_data = do_rest_get_request(URL, logger=LOGGER, expected_status_codes={200}) + LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) + assert len(retrieved_data) == 0 + +def test_allocation(nbi_service_rest : RestServer, storage : Dict): + URL = BASE_URL + '/bw_allocations' + data = { + "allocationDirection":"string", + "appInsId":"service_uuid_01", + "fixedAllocation":"123000.0", + "fixedBWPriority":"SEE_DESCRIPTION", + "requestType":0, + "sessionFilter":[ + { + "dstAddress":"192.168.3.2", + "dstPort":["b"], + "protocol":"string", + "sourceIp":"192.168.1.2", + "sourcePort":["a"] + } + ] + } + retrieved_data = do_rest_post_request(URL, body=data, logger=LOGGER, expected_status_codes={200}) + LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) + storage['service_uuid_01'] = 'service_uuid_01' + + +def test_get_allocations(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument + assert 'service_uuid_01' in storage + URL = BASE_URL + '/bw_allocations' + retrieved_data = do_rest_get_request(URL, logger=LOGGER, expected_status_codes={200}) + LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) + assert len(retrieved_data) == 1 + good_result = [ + { + "appInsId":"service_uuid_01", + "fixedAllocation":"123000.0", + "allocationDirection":"string", + "fixedBWPriority":"SEE_DESCRIPTION", + "requestType":"0", + "sessionFilter":[ + { + "dstAddress":"192.168.3.2", + "dstPort":["b"], + "protocol":"string", + "sourceIp":"192.168.1.2", + "sourcePort":["a"] + } + ], + } + ] + compare_dicts(retrieved_data, good_result) + check_timestamps(retrieved_data[0]) + + +def test_get_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument + assert 'service_uuid_01' in storage + URL = BASE_URL + '/bw_allocations/service_uuid_01' + retrieved_data = do_rest_get_request(URL, logger=LOGGER, expected_status_codes={200}) + LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) + good_result = { + "appInsId":"service_uuid_01", + "fixedAllocation":"123000.0", + "allocationDirection":"string", + "fixedBWPriority":"SEE_DESCRIPTION", + "requestType":"0", + "sessionFilter":[ + { + "dstAddress":"192.168.3.2", + "dstPort":["b"], + "protocol":"string", + "sourceIp":"192.168.1.2", + "sourcePort":["a"] + } + ] + } + + compare_dicts(retrieved_data, good_result) + check_timestamps(retrieved_data) + + +def test_put_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument + assert 'service_uuid_01' in storage + URL = BASE_URL + '/bw_allocations/service_uuid_01' + changed_allocation = { + "appInsId":"service_uuid_01", + "fixedAllocation":"200.0", + "allocationDirection":"parriba", + "fixedBWPriority":"NOPRIORITY", + "requestType":"0", + "sessionFilter":[ + { + "dstAddress":"192.168.3.2", + "dstPort":["b"], + "protocol":"string", + "sourceIp":"192.168.1.2", + "sourcePort":["a"] + } + ] + } + retrieved_data = do_rest_put_request(URL, body=json.dumps(changed_allocation), logger=LOGGER, expected_status_codes={200}) + compare_dicts(retrieved_data, changed_allocation) + check_timestamps(retrieved_data) + + +def test_patch_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument + assert 'service_uuid_01' in storage + URL = BASE_URL + '/bw_allocations/service_uuid_01' + difference = { + "fixedBWPriority":"FULLPRIORITY", + } + changed_allocation = { + "appInsId":"service_uuid_01", + "fixedAllocation":"200.0", + "allocationDirection":"parriba", + "fixedBWPriority":"FULLPRIORITY", + "requestType":"0", + "sessionFilter":[ + { + "dstAddress":"192.168.3.2", + "dstPort":["b"], + "protocol":"string", + "sourceIp":"192.168.1.2", + "sourcePort":["a"] + } + ] + } + + retrieved_data = do_rest_patch_request(URL, body=changed_allocation, logger=LOGGER, expected_status_codes={200}) + compare_dicts(retrieved_data, changed_allocation) + check_timestamps(retrieved_data) + + + +def test_delete_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument + assert 'service_uuid_01' in storage + URL = BASE_URL + '/bw_allocations/service_uuid_01' + do_rest_delete_request(URL, logger=LOGGER, expected_status_codes={200}) + + +def test_get_allocations_empty_final(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument + URL = BASE_URL + '/bw_allocations' + retrieved_data = do_rest_get_request(URL, logger=LOGGER, expected_status_codes={200}) + LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) + assert len(retrieved_data) == 0 -- GitLab From 8729a0dd7a183e9efb42dc74deeb422f0acaa09d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 22 Dec 2023 14:34:55 +0000 Subject: [PATCH 2/3] NBI Component - ETSI BMW: - Fixed unitary test launch scripts --- scripts/run_tests_locally-nbi-all.sh | 6 ++++++ ...ocally-etsi_bwm.sh => run_tests_locally-nbi-etsi-bwm.sh} | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) rename scripts/{run_tests_locally-etsi_bwm.sh => run_tests_locally-nbi-etsi-bwm.sh} (84%) diff --git a/scripts/run_tests_locally-nbi-all.sh b/scripts/run_tests_locally-nbi-all.sh index ec2987d77..48961b0fe 100755 --- a/scripts/run_tests_locally-nbi-all.sh +++ b/scripts/run_tests_locally-nbi-all.sh @@ -22,8 +22,14 @@ RCFILE=$PROJECTDIR/coverage/.coveragerc # Run unitary tests and analyze coverage of code at same time # helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0 +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + nbi/tests/test_etsi_bwm.py + coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ nbi/tests/test_ietf_l2vpn.py +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + nbi/tests/test_ietf_l3vpn.py + coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ nbi/tests/test_ietf_network.py diff --git a/scripts/run_tests_locally-etsi_bwm.sh b/scripts/run_tests_locally-nbi-etsi-bwm.sh similarity index 84% rename from scripts/run_tests_locally-etsi_bwm.sh rename to scripts/run_tests_locally-nbi-etsi-bwm.sh index a057ee28f..dc5f79ffa 100755 --- a/scripts/run_tests_locally-etsi_bwm.sh +++ b/scripts/run_tests_locally-nbi-etsi-bwm.sh @@ -19,6 +19,7 @@ PROJECTDIR=`pwd` cd $PROJECTDIR/src RCFILE=$PROJECTDIR/coverage/.coveragerc -# Run unitary tests and analyze coverage of code at same time +# Run unitary tests and analyze coverage of code at same time +# helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0 coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ nbi/tests/test_etsi_bwm.py -- GitLab From c7e957fb13a87f452980c2ee9dd4fee7eb3f0420 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 22 Dec 2023 15:23:01 +0000 Subject: [PATCH 3/3] NBI Component - ETSI BMW: - Cosmetic cleanup in unitary test data - Multiple bug fixes - Fixed unitary tests - Added new Python requirement --- src/nbi/requirements.in | 1 + .../nbi_plugins/etsi_bwm/Resources.py | 10 +- .../rest_server/nbi_plugins/etsi_bwm/Tools.py | 12 +- src/nbi/tests/data/topology-dummy.json | 2 +- src/nbi/tests/test_etsi_bwm.py | 218 ++++++++++-------- src/nbi/tests/test_ietf_network.py | 2 +- 6 files changed, 137 insertions(+), 108 deletions(-) diff --git a/src/nbi/requirements.in b/src/nbi/requirements.in index 52094c61f..6e3eb9440 100644 --- a/src/nbi/requirements.in +++ b/src/nbi/requirements.in @@ -13,6 +13,7 @@ # limitations under the License. deepdiff==6.7.* +deepmerge==1.1.* Flask==2.1.3 Flask-HTTPAuth==4.5.0 Flask-RESTful==0.3.9 diff --git a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Resources.py b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Resources.py index 4c858990b..3fccbbb55 100644 --- a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Resources.py +++ b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Resources.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import copy, json, logging +import copy, deepmerge, json, logging from common.Constants import DEFAULT_CONTEXT_NAME from context.client.ContextClient import ContextClient from flask_restful import Resource, request @@ -69,8 +69,14 @@ class BwInfoId(_Resource): json_data = request.get_json() if not 'appInsId' in json_data: json_data['appInsId'] = allocationId - service = bwInfo_2_service(self.client, json_data) + + service = self.client.GetService(grpc_service_id(DEFAULT_CONTEXT_NAME, json_data['appInsId'])) + current_bwm = service_2_bwInfo(service) + new_bmw = deepmerge.always_merger.merge(current_bwm, json_data) + + service = bwInfo_2_service(self.client, new_bmw) self.service_client.UpdateService(service) + service = self.client.GetService(grpc_service_id(DEFAULT_CONTEXT_NAME, json_data['appInsId'])) response_bwm = service_2_bwInfo(service) diff --git a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Tools.py b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Tools.py index a1b66f032..a78d28193 100644 --- a/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Tools.py +++ b/src/nbi/service/rest_server/nbi_plugins/etsi_bwm/Tools.py @@ -15,8 +15,11 @@ import json import logging import time +from decimal import ROUND_HALF_EVEN, Decimal from flask.json import jsonify -from common.proto.context_pb2 import ContextId, Empty, EndPointId, ServiceId, ServiceTypeEnum, Service, Constraint, Constraint_SLA_Capacity, ConfigRule, ConfigRule_Custom, ConfigActionEnum +from common.proto.context_pb2 import ( + ContextId, Empty, EndPointId, ServiceId, ServiceTypeEnum, Service, Constraint, Constraint_SLA_Capacity, + ConfigRule, ConfigRule_Custom, ConfigActionEnum) from common.tools.grpc.Tools import grpc_message_to_json from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Service import json_service_id @@ -30,7 +33,10 @@ def service_2_bwInfo(service: Service) -> dict: response['appInsId'] = service.service_id.service_uuid.uuid # String: Application instance identifier for constraint in service.service_constraints: if constraint.WhichOneof('constraint') == 'sla_capacity': - response['fixedAllocation'] = str(constraint.sla_capacity.capacity_gbps*1000) # String: Size of requested fixed BW allocation in [bps] + # String: Size of requested fixed BW allocation in [bps] + fixed_allocation = Decimal(constraint.sla_capacity.capacity_gbps * 1.e9) + fixed_allocation = fixed_allocation.quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN) + response['fixedAllocation'] = str(fixed_allocation) break for config_rule in service.service_config.config_rules: @@ -89,7 +95,7 @@ def bwInfo_2_service(client, bwInfo: dict) -> Service: if 'fixedAllocation' in bwInfo: capacity = Constraint_SLA_Capacity() - capacity.capacity_gbps = float(bwInfo['fixedAllocation']) + capacity.capacity_gbps = float(bwInfo['fixedAllocation']) / 1.e9 constraint = Constraint() constraint.sla_capacity.CopyFrom(capacity) service.service_constraints.append(constraint) diff --git a/src/nbi/tests/data/topology-dummy.json b/src/nbi/tests/data/topology-dummy.json index 4c0f58255..4735bf446 100644 --- a/src/nbi/tests/data/topology-dummy.json +++ b/src/nbi/tests/data/topology-dummy.json @@ -2837,4 +2837,4 @@ } } ] -} +} \ No newline at end of file diff --git a/src/nbi/tests/test_etsi_bwm.py b/src/nbi/tests/test_etsi_bwm.py index 6d77aa749..8925897a7 100644 --- a/src/nbi/tests/test_etsi_bwm.py +++ b/src/nbi/tests/test_etsi_bwm.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import deepdiff, json, logging, pytest from typing import Dict -import json, logging, pytest from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME from common.proto.context_pb2 import ContextId, TopologyId from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results, validate_empty_scenario @@ -21,8 +21,11 @@ from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Topology import json_topology_id from context.client.ContextClient import ContextClient from nbi.service.rest_server import RestServer -from .PrepareTestScenario import do_rest_delete_request, do_rest_post_request, do_rest_get_request, do_rest_put_request, do_rest_patch_request, mock_service, nbi_service_rest, context_client - +from .PrepareTestScenario import ( # pylint: disable=unused-import + # be careful, order of symbols is important here! + do_rest_delete_request, do_rest_get_request, do_rest_patch_request, do_rest_post_request, do_rest_put_request, + mock_service, nbi_service_rest, context_client +) LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) @@ -38,24 +41,24 @@ BASE_URL = '/restconf/bwm/v1' def storage() -> Dict: yield dict() -def compare_dicts(dict1, dict2): - # Function to recursively sort dictionaries - def recursively_sort(d): - if isinstance(d, dict): - return {k: recursively_sort(v) for k, v in sorted(d.items())} - if isinstance(d, list): - return [recursively_sort(item) for item in d] - return d - - # Sort dictionaries to ignore the order of fields - sorted_dict1 = recursively_sort(dict1) - sorted_dict2 = recursively_sort(dict2) - - if sorted_dict1 != sorted_dict2: - LOGGER.error(sorted_dict1) - LOGGER.error(sorted_dict2) - - return sorted_dict1 != sorted_dict2 +#def compare_dicts(dict1, dict2): +# # Function to recursively sort dictionaries +# def recursively_sort(d): +# if isinstance(d, dict): +# return {k: recursively_sort(v) for k, v in sorted(d.items())} +# if isinstance(d, list): +# return [recursively_sort(item) for item in d] +# return d +# +# # Sort dictionaries to ignore the order of fields +# sorted_dict1 = recursively_sort(dict1) +# sorted_dict2 = recursively_sort(dict2) +# +# if sorted_dict1 != sorted_dict2: +# LOGGER.error(sorted_dict1) +# LOGGER.error(sorted_dict2) +# +# return sorted_dict1 != sorted_dict2 def check_timestamps(bwm_service): assert 'timeStamp' in bwm_service @@ -71,7 +74,7 @@ def test_prepare_environment(context_client : ContextClient) -> None: # pylint: # Verify the scenario has no services/slices response = context_client.GetContext(ADMIN_CONTEXT_ID) - assert len(response.topology_ids) == 3 + assert len(response.topology_ids) == 1 assert len(response.service_ids ) == 0 assert len(response.slice_ids ) == 0 @@ -81,23 +84,21 @@ def test_get_allocations_empty(nbi_service_rest : RestServer, storage : Dict): # LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) assert len(retrieved_data) == 0 -def test_allocation(nbi_service_rest : RestServer, storage : Dict): +def test_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument URL = BASE_URL + '/bw_allocations' data = { - "allocationDirection":"string", - "appInsId":"service_uuid_01", - "fixedAllocation":"123000.0", - "fixedBWPriority":"SEE_DESCRIPTION", - "requestType":0, - "sessionFilter":[ - { - "dstAddress":"192.168.3.2", - "dstPort":["b"], - "protocol":"string", - "sourceIp":"192.168.1.2", - "sourcePort":["a"] - } - ] + "appInsId" : "service_uuid_01", + "allocationDirection" : "00", + "fixedAllocation" : "123000.0", + "fixedBWPriority" : "SEE_DESCRIPTION", + "requestType" : 0, + "sessionFilter" : [{ + "sourceIp" : "192.168.1.2", + "sourcePort" : ["a"], + "protocol" : "string", + "dstAddress" : "192.168.3.2", + "dstPort" : ["b"], + }] } retrieved_data = do_rest_post_request(URL, body=data, logger=LOGGER, expected_status_codes={200}) LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) @@ -112,24 +113,25 @@ def test_get_allocations(nbi_service_rest : RestServer, storage : Dict): # pylin assert len(retrieved_data) == 1 good_result = [ { - "appInsId":"service_uuid_01", - "fixedAllocation":"123000.0", - "allocationDirection":"string", - "fixedBWPriority":"SEE_DESCRIPTION", - "requestType":"0", - "sessionFilter":[ - { - "dstAddress":"192.168.3.2", - "dstPort":["b"], - "protocol":"string", - "sourceIp":"192.168.1.2", - "sourcePort":["a"] - } - ], + "appInsId" : "service_uuid_01", + "fixedAllocation" : "123000.0", + "allocationDirection" : "00", + "fixedBWPriority" : "SEE_DESCRIPTION", + "requestType" : "0", + "sessionFilter" : [{ + "sourceIp" : "192.168.1.2", + "sourcePort" : ["a"], + "protocol" : "string", + "dstAddress" : "192.168.3.2", + "dstPort" : ["b"], + }], } ] - compare_dicts(retrieved_data, good_result) check_timestamps(retrieved_data[0]) + del retrieved_data[0]['timeStamp'] + diff_data = deepdiff.DeepDiff(good_result, retrieved_data) + LOGGER.error('Differences:\n{:s}'.format(str(diff_data.pretty()))) + assert len(diff_data) == 0 def test_get_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument @@ -138,48 +140,49 @@ def test_get_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint retrieved_data = do_rest_get_request(URL, logger=LOGGER, expected_status_codes={200}) LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) good_result = { - "appInsId":"service_uuid_01", - "fixedAllocation":"123000.0", - "allocationDirection":"string", - "fixedBWPriority":"SEE_DESCRIPTION", - "requestType":"0", - "sessionFilter":[ - { - "dstAddress":"192.168.3.2", - "dstPort":["b"], - "protocol":"string", - "sourceIp":"192.168.1.2", - "sourcePort":["a"] - } - ] + "appInsId" : "service_uuid_01", + "fixedAllocation" : "123000.0", + "allocationDirection": "00", + "fixedBWPriority" : "SEE_DESCRIPTION", + "requestType" : "0", + "sessionFilter" : [{ + "sourceIp" : "192.168.1.2", + "sourcePort" : ["a"], + "protocol" : "string", + "dstAddress" : "192.168.3.2", + "dstPort" : ["b"], + }] } - - compare_dicts(retrieved_data, good_result) check_timestamps(retrieved_data) + del retrieved_data['timeStamp'] + diff_data = deepdiff.DeepDiff(good_result, retrieved_data) + LOGGER.error('Differences:\n{:s}'.format(str(diff_data.pretty()))) + assert len(diff_data) == 0 def test_put_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument assert 'service_uuid_01' in storage URL = BASE_URL + '/bw_allocations/service_uuid_01' changed_allocation = { - "appInsId":"service_uuid_01", - "fixedAllocation":"200.0", - "allocationDirection":"parriba", - "fixedBWPriority":"NOPRIORITY", - "requestType":"0", - "sessionFilter":[ - { - "dstAddress":"192.168.3.2", - "dstPort":["b"], - "protocol":"string", - "sourceIp":"192.168.1.2", - "sourcePort":["a"] - } - ] + "appInsId" : "service_uuid_01", + "fixedAllocation" : "200.0", + "allocationDirection": "00", + "fixedBWPriority" : "NOPRIORITY", + "requestType" : "0", + "sessionFilter" : [{ + "sourceIp" : "192.168.1.2", + "sourcePort" : ["a"], + "protocol" : "string", + "dstAddress" : "192.168.3.2", + "dstPort" : ["b"], + }] } retrieved_data = do_rest_put_request(URL, body=json.dumps(changed_allocation), logger=LOGGER, expected_status_codes={200}) - compare_dicts(retrieved_data, changed_allocation) check_timestamps(retrieved_data) + del retrieved_data['timeStamp'] + diff_data = deepdiff.DeepDiff(changed_allocation, retrieved_data) + LOGGER.error('Differences:\n{:s}'.format(str(diff_data.pretty()))) + assert len(diff_data) == 0 def test_patch_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument @@ -189,26 +192,25 @@ def test_patch_allocation(nbi_service_rest : RestServer, storage : Dict): # pyli "fixedBWPriority":"FULLPRIORITY", } changed_allocation = { - "appInsId":"service_uuid_01", - "fixedAllocation":"200.0", - "allocationDirection":"parriba", - "fixedBWPriority":"FULLPRIORITY", - "requestType":"0", - "sessionFilter":[ - { - "dstAddress":"192.168.3.2", - "dstPort":["b"], - "protocol":"string", - "sourceIp":"192.168.1.2", - "sourcePort":["a"] - } - ] + "appInsId" : "service_uuid_01", + "fixedAllocation" : "200.0", + "allocationDirection": "00", + "fixedBWPriority" : "FULLPRIORITY", + "requestType" : "0", + "sessionFilter" : [{ + "sourceIp" : "192.168.1.2", + "sourcePort" : ["a"], + "protocol" : "string", + "dstAddress" : "192.168.3.2", + "dstPort" : ["b"], + }] } - - retrieved_data = do_rest_patch_request(URL, body=changed_allocation, logger=LOGGER, expected_status_codes={200}) - compare_dicts(retrieved_data, changed_allocation) + retrieved_data = do_rest_patch_request(URL, body=difference, logger=LOGGER, expected_status_codes={200}) check_timestamps(retrieved_data) - + del retrieved_data['timeStamp'] + diff_data = deepdiff.DeepDiff(changed_allocation, retrieved_data) + LOGGER.error('Differences:\n{:s}'.format(str(diff_data.pretty()))) + assert len(diff_data) == 0 def test_delete_allocation(nbi_service_rest : RestServer, storage : Dict): # pylint: disable=redefined-outer-name, unused-argument @@ -222,3 +224,17 @@ def test_get_allocations_empty_final(nbi_service_rest : RestServer, storage : Di retrieved_data = do_rest_get_request(URL, logger=LOGGER, expected_status_codes={200}) LOGGER.debug('retrieved_data={:s}'.format(json.dumps(retrieved_data, sort_keys=True))) assert len(retrieved_data) == 0 + + +def test_cleanup_environment(context_client : ContextClient) -> None: # pylint: disable=redefined-outer-name + # Verify the scenario has no services/slices + response = context_client.GetContext(ADMIN_CONTEXT_ID) + assert len(response.topology_ids) == 1 + 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() + descriptor_loader.unload() + validate_empty_scenario(context_client) diff --git a/src/nbi/tests/test_ietf_network.py b/src/nbi/tests/test_ietf_network.py index e41a88af0..fb39a9192 100644 --- a/src/nbi/tests/test_ietf_network.py +++ b/src/nbi/tests/test_ietf_network.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict import deepdiff, json, logging, operator +from typing import Dict from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import ContextId from common.tools.descriptor.Loader import DescriptorLoader, check_descriptor_load_results, validate_empty_scenario -- GitLab