Loading src/common/tests/LoadScenario.pydeleted 100644 → 0 +0 −50 Original line number Diff line number Diff line # 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 common.tools.descriptor.Loader import DescriptorLoader, compose_notifications from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from service.client.ServiceClient import ServiceClient from slice.client.SliceClient import SliceClient LOGGER = logging.getLogger(__name__) LOGGERS = { 'success': LOGGER.info, 'danger' : LOGGER.error, 'error' : LOGGER.error, } def load_scenario_from_descriptor( descriptor_file : str, context_client : ContextClient, device_client : DeviceClient, service_client : ServiceClient, slice_client : SliceClient ) -> DescriptorLoader: with open(descriptor_file, 'r', encoding='UTF-8') as f: descriptors = f.read() descriptor_loader = DescriptorLoader( descriptors, context_client=context_client, device_client=device_client, service_client=service_client, slice_client=slice_client) results = descriptor_loader.process() num_errors = 0 for message,level in compose_notifications(results): LOGGERS.get(level)(message) if level != 'success': num_errors += 1 if num_errors > 0: MSG = 'Failed to load descriptors in file {:s}' raise Exception(MSG.format(str(descriptor_file))) return descriptor_loader No newline at end of file src/common/tools/descriptor/Loader.py +133 −24 Original line number Diff line number Diff line Loading @@ -15,25 +15,30 @@ # SDN controller descriptor loader # Usage example (WebUI): # descriptors = json.loads(descriptors_data_from_client) # descriptors = json.loads( # descriptors=descriptors_data_from_client, num_workers=10, # context_client=..., device_client=..., service_client=..., slice_client=...) # descriptor_loader = DescriptorLoader(descriptors) # results = descriptor_loader.process() # for message,level in compose_notifications(results): # flash(message, level) # Usage example (pytest): # with open('path/to/descriptor.json', 'r', encoding='UTF-8') as f: # descriptors = json.loads(f.read()) # descriptor_loader = DescriptorLoader( # descriptors, context_client=..., device_client=..., service_client=..., slice_client=...) # descriptors_file='path/to/descriptor.json', num_workers=10, # context_client=..., device_client=..., service_client=..., slice_client=...) # results = descriptor_loader.process() # loggers = {'success': LOGGER.info, 'danger': LOGGER.error, 'error': LOGGER.error} # for message,level in compose_notifications(results): # loggers.get(level)(message) # check_results(results, descriptor_loader) # descriptor_loader.validate() # # do test ... # descriptor_loader.unload() import concurrent.futures, json, logging, operator from typing import Any, Dict, List, Optional, Tuple, Union from common.proto.context_pb2 import Connection, Context, Device, Link, Service, Slice, Topology from common.proto.context_pb2 import ( Connection, Context, ContextId, Device, DeviceId, Empty, Link, LinkId, Service, ServiceId, Slice, SliceId, Topology, TopologyId) 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 Loading @@ -44,6 +49,11 @@ from .Tools import ( get_descriptors_add_topologies, split_devices_by_rules) LOGGER = logging.getLogger(__name__) LOGGERS = { 'success': LOGGER.info, 'danger' : LOGGER.error, 'error' : LOGGER.error, } ENTITY_TO_TEXT = { # name => singular, plural Loading @@ -67,25 +77,26 @@ TypeResults = List[Tuple[str, str, int, List[str]]] # entity_name, action, num_o TypeNotification = Tuple[str, str] # message, level TypeNotificationList = List[TypeNotification] def compose_notifications(results : TypeResults) -> TypeNotificationList: notifications = [] for entity_name, action_name, num_ok, error_list in results: entity_name_singluar,entity_name_plural = ENTITY_TO_TEXT[entity_name] action_infinitive, action_past = ACTION_TO_TEXT[action_name] num_err = len(error_list) for error in error_list: notifications.append((f'Unable to {action_infinitive} {entity_name_singluar} {error}', 'error')) if num_ok : notifications.append((f'{str(num_ok)} {entity_name_plural} {action_past}', 'success')) if num_err: notifications.append((f'{str(num_err)} {entity_name_plural} failed', 'danger')) return notifications class DescriptorLoader: def __init__( self, descriptors : Union[str, Dict], num_workers : int = 1, self, descriptors : Optional[Union[str, Dict]] = None, descriptors_file : Optional[str] = None, num_workers : int = 1, context_client : Optional[ContextClient] = None, device_client : Optional[DeviceClient] = None, service_client : Optional[ServiceClient] = None, slice_client : Optional[SliceClient] = None ) -> None: if (descriptors is None) == (descriptors_file is None): raise Exception('Exactly one of "descriptors" or "descriptors_file" is required') if descriptors_file is not None: with open(descriptors_file, 'r', encoding='UTF-8') as f: self.__descriptors = json.loads(f.read()) self.__descriptor_file_path = descriptors_file else: # descriptors is not None self.__descriptors = json.loads(descriptors) if isinstance(descriptors, str) else descriptors self.__descriptor_file_path = '<dict>' self.__num_workers = num_workers self.__dummy_mode = self.__descriptors.get('dummy_mode' , False) self.__contexts = self.__descriptors.get('contexts' , []) self.__topologies = self.__descriptors.get('topologies' , []) Loading @@ -95,8 +106,6 @@ class DescriptorLoader: self.__slices = self.__descriptors.get('slices' , []) self.__connections = self.__descriptors.get('connections', []) self.__num_workers = num_workers self.__contexts_add = None self.__topologies_add = None self.__devices_add = None Loading @@ -111,6 +120,24 @@ class DescriptorLoader: self.__results : TypeResults = list() @property def descriptor_file_path(self) -> Optional[str]: return self.__descriptor_file_path @property def num_workers(self) -> int: return self.__num_workers @property def context_client(self) -> Optional[ContextClient]: return self.__ctx_cli @property def device_client(self) -> Optional[DeviceClient]: return self.__dev_cli @property def service_client(self) -> Optional[ServiceClient]: return self.__svc_cli @property def slice_client(self) -> Optional[SliceClient]: return self.__slc_cli @property def contexts(self) -> List[Dict]: return self.__contexts Loading Loading @@ -269,3 +296,85 @@ class DescriptorLoader: error_list = [str_error for _,str_error in sorted(error_list, key=operator.itemgetter(0))] self.__results.append((entity_name, action_name, num_ok, error_list)) def validate(self) -> None: self.__ctx_cli.connect() contexts = self.__ctx_cli.ListContexts(Empty()) assert len(contexts.contexts) == self.num_contexts for context_uuid, num_topologies in self.num_topologies.items(): response = self.__ctx_cli.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = self.__ctx_cli.ListDevices(Empty()) assert len(response.devices) == self.num_devices response = self.__ctx_cli.ListLinks(Empty()) assert len(response.links) == self.num_links for context_uuid, num_services in self.num_services.items(): response = self.__ctx_cli.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == num_services for context_uuid, num_slices in self.num_slices.items(): response = self.__ctx_cli.ListSlices(ContextId(**json_context_id(context_uuid))) assert len(response.slices) == num_slices def unload(self) -> None: self.__ctx_cli.connect() self.__dev_cli.connect() self.__svc_cli.connect() self.__slc_cli.connect() for _, slice_list in self.slices.items(): for slice_ in slice_list: self.__slc_cli.DeleteSlice(SliceId(**slice_['slice_id'])) for _, service_list in self.services.items(): for service in service_list: self.__svc_cli.DeleteService(ServiceId(**service['service_id'])) for link in self.links: self.__ctx_cli.RemoveLink(LinkId(**link['link_id'])) for device in self.devices: self.__dev_cli.DeleteDevice(DeviceId(**device['device_id'])) for _, topology_list in self.topologies.items(): for topology in topology_list: self.__ctx_cli.RemoveTopology(TopologyId(**topology['topology_id'])) for context in self.contexts: self.__ctx_cli.RemoveContext(ContextId(**context['context_id'])) def compose_notifications(results : TypeResults) -> TypeNotificationList: notifications = [] for entity_name, action_name, num_ok, error_list in results: entity_name_singluar,entity_name_plural = ENTITY_TO_TEXT[entity_name] action_infinitive, action_past = ACTION_TO_TEXT[action_name] num_err = len(error_list) for error in error_list: notifications.append((f'Unable to {action_infinitive} {entity_name_singluar} {error}', 'error')) if num_ok : notifications.append((f'{str(num_ok)} {entity_name_plural} {action_past}', 'success')) if num_err: notifications.append((f'{str(num_err)} {entity_name_plural} failed', 'danger')) return notifications def check_descriptor_load_results(results : TypeResults, descriptor_loader : DescriptorLoader) -> None: num_errors = 0 for message,level in compose_notifications(results): LOGGERS.get(level)(message) if level != 'success': num_errors += 1 if num_errors > 0: MSG = 'Failed to load descriptors from "{:s}"' raise Exception(MSG.format(str(descriptor_loader.descriptor_file_path))) def validate_empty_scenario(context_client : ContextClient) -> None: response = context_client.ListContexts(Empty()) assert len(response.contexts) == 0 response = context_client.ListDevices(Empty()) assert len(response.devices) == 0 response = context_client.ListLinks(Empty()) assert len(response.links) == 0 src/tests/benchmark/policy/tests/test_functional_bootstrap.py +13 −32 Original line number Diff line number Diff line Loading @@ -13,10 +13,10 @@ # limitations under the License. import logging, time from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import ContextId, Empty from common.proto.monitoring_pb2 import KpiDescriptorList from common.tests.LoadScenario import load_scenario_from_descriptor from common.tools.grpc.Tools import grpc_message_to_json_string 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 context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient Loading @@ -27,44 +27,25 @@ LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) DESCRIPTOR_FILE = 'ofc22/descriptors_emulated.json' ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) def test_scenario_bootstrap( context_client : ContextClient, # pylint: disable=redefined-outer-name device_client : DeviceClient, # pylint: disable=redefined-outer-name ) -> None: # ----- List entities - Ensure database is empty ------------------------------------------------------------------- response = context_client.ListContexts(Empty()) assert len(response.contexts) == 0 validate_empty_scenario(context_client) response = context_client.ListDevices(Empty()) assert len(response.devices) == 0 response = context_client.ListLinks(Empty()) assert len(response.links) == 0 # ----- Load Scenario ---------------------------------------------------------------------------------------------- descriptor_loader = load_scenario_from_descriptor( DESCRIPTOR_FILE, context_client, device_client, None, None) # ----- List entities - Ensure scenario is ready ------------------------------------------------------------------- response = context_client.ListContexts(Empty()) assert len(response.contexts) == descriptor_loader.num_contexts for context_uuid, num_topologies in descriptor_loader.num_topologies.items(): response = context_client.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = context_client.ListDevices(Empty()) assert len(response.devices) == descriptor_loader.num_devices descriptor_loader = DescriptorLoader( descriptors_file=DESCRIPTOR_FILE, context_client=context_client, device_client=device_client) results = descriptor_loader.process() check_descriptor_load_results(results, descriptor_loader) descriptor_loader.validate() response = context_client.ListLinks(Empty()) assert len(response.links) == descriptor_loader.num_links # 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 for context_uuid, _ in descriptor_loader.num_services.items(): response = context_client.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == 0 def test_scenario_kpis_created( context_client : ContextClient, # pylint: disable=redefined-outer-name Loading src/tests/benchmark/policy/tests/test_functional_cleanup.py +16 −52 Original line number Diff line number Diff line Loading @@ -13,9 +13,10 @@ # limitations under the License. import logging from common.tools.descriptor.Loader import DescriptorLoader from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import ContextId from common.tools.descriptor.Loader import DescriptorLoader, validate_empty_scenario from common.tools.object_factory.Context import json_context_id from common.proto.context_pb2 import ContextId, DeviceId, Empty, LinkId, TopologyId from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from tests.Fixtures import context_client, device_client # pylint: disable=unused-import Loading @@ -24,57 +25,20 @@ LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) DESCRIPTOR_FILE = 'ofc22/descriptors_emulated.json' ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) def test_services_removed( def test_scenario_cleanup( context_client : ContextClient, # pylint: disable=redefined-outer-name device_client : DeviceClient, # pylint: disable=redefined-outer-name ) -> None: # ----- List entities - Ensure service is removed ------------------------------------------------------------------ with open(DESCRIPTOR_FILE, 'r', encoding='UTF-8') as f: descriptors = f.read() descriptor_loader = DescriptorLoader(descriptors) response = context_client.ListContexts(Empty()) assert len(response.contexts) == descriptor_loader.num_contexts for context_uuid, num_topologies in descriptor_loader.num_topologies.items(): response = context_client.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = context_client.ListDevices(Empty()) assert len(response.devices) == descriptor_loader.num_devices response = context_client.ListLinks(Empty()) assert len(response.links) == descriptor_loader.num_links for context_uuid, _ in descriptor_loader.num_services.items(): response = context_client.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == 0 # ----- Delete Links, Devices, Topologies, Contexts ---------------------------------------------------------------- for link in descriptor_loader.links: context_client.RemoveLink(LinkId(**link['link_id'])) for device in descriptor_loader.devices: device_client .DeleteDevice(DeviceId(**device['device_id'])) for context_uuid, topology_list in descriptor_loader.topologies.items(): for topology in topology_list: context_client.RemoveTopology(TopologyId(**topology['topology_id'])) for context in descriptor_loader.contexts: context_client.RemoveContext(ContextId(**context['context_id'])) # ----- List entities - Ensure database is empty again ------------------------------------------------------------- response = context_client.ListContexts(Empty()) assert len(response.contexts) == 0 response = context_client.ListDevices(Empty()) assert len(response.devices) == 0 response = context_client.ListLinks(Empty()) assert len(response.links) == 0 # 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, device_client=device_client) descriptor_loader.validate() descriptor_loader.unload() validate_empty_scenario(context_client) src/tests/benchmark/policy/tests/test_functional_create_service.py +35 −57 Original line number Diff line number Diff line Loading @@ -13,15 +13,15 @@ # limitations under the License. import logging, random from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import ContextId, Empty 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, device_client, monitoring_client # pylint: disable=unused-import 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 Loading @@ -29,67 +29,45 @@ from .Objects import WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) DEVTYPE_EMU_PR = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value DESCRIPTOR_FILE = 'ofc22/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 # ----- List entities - Ensure scenario is ready ------------------------------------------------------------------- with open(DESCRIPTOR_FILE, 'r', encoding='UTF-8') as f: descriptors = f.read() descriptor_loader = DescriptorLoader(descriptors) response = context_client.ListContexts(Empty()) assert len(response.contexts) == descriptor_loader.num_contexts for context_uuid, num_topologies in descriptor_loader.num_topologies.items(): response = context_client.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = context_client.ListDevices(Empty()) assert len(response.devices) == descriptor_loader.num_devices response = context_client.ListLinks(Empty()) assert len(response.links) == descriptor_loader.num_links for context_uuid, num_services in descriptor_loader.num_services.items(): response = context_client.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == 0 # 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 Service --------------------------------------------------------------------------------------------- # 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 # ----- List entities - Ensure service is created ------------------------------------------------------------------ response = context_client.ListContexts(Empty()) assert len(response.contexts) == descriptor_loader.num_contexts for context_uuid, num_topologies in descriptor_loader.num_topologies.items(): response = context_client.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = context_client.ListDevices(Empty()) assert len(response.devices) == descriptor_loader.num_devices response = context_client.ListLinks(Empty()) assert len(response.links) == descriptor_loader.num_links for context_uuid, num_services in descriptor_loader.num_services.items(): response = context_client.ListServices(ContextId(**json_context_id(context_uuid))) 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*num_services # OLS & L3NM => (L3NM + TAPI) 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))) assert len(response.connections) == 1 # one connection per service 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( Loading Loading
src/common/tests/LoadScenario.pydeleted 100644 → 0 +0 −50 Original line number Diff line number Diff line # 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 common.tools.descriptor.Loader import DescriptorLoader, compose_notifications from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from service.client.ServiceClient import ServiceClient from slice.client.SliceClient import SliceClient LOGGER = logging.getLogger(__name__) LOGGERS = { 'success': LOGGER.info, 'danger' : LOGGER.error, 'error' : LOGGER.error, } def load_scenario_from_descriptor( descriptor_file : str, context_client : ContextClient, device_client : DeviceClient, service_client : ServiceClient, slice_client : SliceClient ) -> DescriptorLoader: with open(descriptor_file, 'r', encoding='UTF-8') as f: descriptors = f.read() descriptor_loader = DescriptorLoader( descriptors, context_client=context_client, device_client=device_client, service_client=service_client, slice_client=slice_client) results = descriptor_loader.process() num_errors = 0 for message,level in compose_notifications(results): LOGGERS.get(level)(message) if level != 'success': num_errors += 1 if num_errors > 0: MSG = 'Failed to load descriptors in file {:s}' raise Exception(MSG.format(str(descriptor_file))) return descriptor_loader No newline at end of file
src/common/tools/descriptor/Loader.py +133 −24 Original line number Diff line number Diff line Loading @@ -15,25 +15,30 @@ # SDN controller descriptor loader # Usage example (WebUI): # descriptors = json.loads(descriptors_data_from_client) # descriptors = json.loads( # descriptors=descriptors_data_from_client, num_workers=10, # context_client=..., device_client=..., service_client=..., slice_client=...) # descriptor_loader = DescriptorLoader(descriptors) # results = descriptor_loader.process() # for message,level in compose_notifications(results): # flash(message, level) # Usage example (pytest): # with open('path/to/descriptor.json', 'r', encoding='UTF-8') as f: # descriptors = json.loads(f.read()) # descriptor_loader = DescriptorLoader( # descriptors, context_client=..., device_client=..., service_client=..., slice_client=...) # descriptors_file='path/to/descriptor.json', num_workers=10, # context_client=..., device_client=..., service_client=..., slice_client=...) # results = descriptor_loader.process() # loggers = {'success': LOGGER.info, 'danger': LOGGER.error, 'error': LOGGER.error} # for message,level in compose_notifications(results): # loggers.get(level)(message) # check_results(results, descriptor_loader) # descriptor_loader.validate() # # do test ... # descriptor_loader.unload() import concurrent.futures, json, logging, operator from typing import Any, Dict, List, Optional, Tuple, Union from common.proto.context_pb2 import Connection, Context, Device, Link, Service, Slice, Topology from common.proto.context_pb2 import ( Connection, Context, ContextId, Device, DeviceId, Empty, Link, LinkId, Service, ServiceId, Slice, SliceId, Topology, TopologyId) 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 Loading @@ -44,6 +49,11 @@ from .Tools import ( get_descriptors_add_topologies, split_devices_by_rules) LOGGER = logging.getLogger(__name__) LOGGERS = { 'success': LOGGER.info, 'danger' : LOGGER.error, 'error' : LOGGER.error, } ENTITY_TO_TEXT = { # name => singular, plural Loading @@ -67,25 +77,26 @@ TypeResults = List[Tuple[str, str, int, List[str]]] # entity_name, action, num_o TypeNotification = Tuple[str, str] # message, level TypeNotificationList = List[TypeNotification] def compose_notifications(results : TypeResults) -> TypeNotificationList: notifications = [] for entity_name, action_name, num_ok, error_list in results: entity_name_singluar,entity_name_plural = ENTITY_TO_TEXT[entity_name] action_infinitive, action_past = ACTION_TO_TEXT[action_name] num_err = len(error_list) for error in error_list: notifications.append((f'Unable to {action_infinitive} {entity_name_singluar} {error}', 'error')) if num_ok : notifications.append((f'{str(num_ok)} {entity_name_plural} {action_past}', 'success')) if num_err: notifications.append((f'{str(num_err)} {entity_name_plural} failed', 'danger')) return notifications class DescriptorLoader: def __init__( self, descriptors : Union[str, Dict], num_workers : int = 1, self, descriptors : Optional[Union[str, Dict]] = None, descriptors_file : Optional[str] = None, num_workers : int = 1, context_client : Optional[ContextClient] = None, device_client : Optional[DeviceClient] = None, service_client : Optional[ServiceClient] = None, slice_client : Optional[SliceClient] = None ) -> None: if (descriptors is None) == (descriptors_file is None): raise Exception('Exactly one of "descriptors" or "descriptors_file" is required') if descriptors_file is not None: with open(descriptors_file, 'r', encoding='UTF-8') as f: self.__descriptors = json.loads(f.read()) self.__descriptor_file_path = descriptors_file else: # descriptors is not None self.__descriptors = json.loads(descriptors) if isinstance(descriptors, str) else descriptors self.__descriptor_file_path = '<dict>' self.__num_workers = num_workers self.__dummy_mode = self.__descriptors.get('dummy_mode' , False) self.__contexts = self.__descriptors.get('contexts' , []) self.__topologies = self.__descriptors.get('topologies' , []) Loading @@ -95,8 +106,6 @@ class DescriptorLoader: self.__slices = self.__descriptors.get('slices' , []) self.__connections = self.__descriptors.get('connections', []) self.__num_workers = num_workers self.__contexts_add = None self.__topologies_add = None self.__devices_add = None Loading @@ -111,6 +120,24 @@ class DescriptorLoader: self.__results : TypeResults = list() @property def descriptor_file_path(self) -> Optional[str]: return self.__descriptor_file_path @property def num_workers(self) -> int: return self.__num_workers @property def context_client(self) -> Optional[ContextClient]: return self.__ctx_cli @property def device_client(self) -> Optional[DeviceClient]: return self.__dev_cli @property def service_client(self) -> Optional[ServiceClient]: return self.__svc_cli @property def slice_client(self) -> Optional[SliceClient]: return self.__slc_cli @property def contexts(self) -> List[Dict]: return self.__contexts Loading Loading @@ -269,3 +296,85 @@ class DescriptorLoader: error_list = [str_error for _,str_error in sorted(error_list, key=operator.itemgetter(0))] self.__results.append((entity_name, action_name, num_ok, error_list)) def validate(self) -> None: self.__ctx_cli.connect() contexts = self.__ctx_cli.ListContexts(Empty()) assert len(contexts.contexts) == self.num_contexts for context_uuid, num_topologies in self.num_topologies.items(): response = self.__ctx_cli.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = self.__ctx_cli.ListDevices(Empty()) assert len(response.devices) == self.num_devices response = self.__ctx_cli.ListLinks(Empty()) assert len(response.links) == self.num_links for context_uuid, num_services in self.num_services.items(): response = self.__ctx_cli.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == num_services for context_uuid, num_slices in self.num_slices.items(): response = self.__ctx_cli.ListSlices(ContextId(**json_context_id(context_uuid))) assert len(response.slices) == num_slices def unload(self) -> None: self.__ctx_cli.connect() self.__dev_cli.connect() self.__svc_cli.connect() self.__slc_cli.connect() for _, slice_list in self.slices.items(): for slice_ in slice_list: self.__slc_cli.DeleteSlice(SliceId(**slice_['slice_id'])) for _, service_list in self.services.items(): for service in service_list: self.__svc_cli.DeleteService(ServiceId(**service['service_id'])) for link in self.links: self.__ctx_cli.RemoveLink(LinkId(**link['link_id'])) for device in self.devices: self.__dev_cli.DeleteDevice(DeviceId(**device['device_id'])) for _, topology_list in self.topologies.items(): for topology in topology_list: self.__ctx_cli.RemoveTopology(TopologyId(**topology['topology_id'])) for context in self.contexts: self.__ctx_cli.RemoveContext(ContextId(**context['context_id'])) def compose_notifications(results : TypeResults) -> TypeNotificationList: notifications = [] for entity_name, action_name, num_ok, error_list in results: entity_name_singluar,entity_name_plural = ENTITY_TO_TEXT[entity_name] action_infinitive, action_past = ACTION_TO_TEXT[action_name] num_err = len(error_list) for error in error_list: notifications.append((f'Unable to {action_infinitive} {entity_name_singluar} {error}', 'error')) if num_ok : notifications.append((f'{str(num_ok)} {entity_name_plural} {action_past}', 'success')) if num_err: notifications.append((f'{str(num_err)} {entity_name_plural} failed', 'danger')) return notifications def check_descriptor_load_results(results : TypeResults, descriptor_loader : DescriptorLoader) -> None: num_errors = 0 for message,level in compose_notifications(results): LOGGERS.get(level)(message) if level != 'success': num_errors += 1 if num_errors > 0: MSG = 'Failed to load descriptors from "{:s}"' raise Exception(MSG.format(str(descriptor_loader.descriptor_file_path))) def validate_empty_scenario(context_client : ContextClient) -> None: response = context_client.ListContexts(Empty()) assert len(response.contexts) == 0 response = context_client.ListDevices(Empty()) assert len(response.devices) == 0 response = context_client.ListLinks(Empty()) assert len(response.links) == 0
src/tests/benchmark/policy/tests/test_functional_bootstrap.py +13 −32 Original line number Diff line number Diff line Loading @@ -13,10 +13,10 @@ # limitations under the License. import logging, time from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import ContextId, Empty from common.proto.monitoring_pb2 import KpiDescriptorList from common.tests.LoadScenario import load_scenario_from_descriptor from common.tools.grpc.Tools import grpc_message_to_json_string 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 context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient Loading @@ -27,44 +27,25 @@ LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) DESCRIPTOR_FILE = 'ofc22/descriptors_emulated.json' ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) def test_scenario_bootstrap( context_client : ContextClient, # pylint: disable=redefined-outer-name device_client : DeviceClient, # pylint: disable=redefined-outer-name ) -> None: # ----- List entities - Ensure database is empty ------------------------------------------------------------------- response = context_client.ListContexts(Empty()) assert len(response.contexts) == 0 validate_empty_scenario(context_client) response = context_client.ListDevices(Empty()) assert len(response.devices) == 0 response = context_client.ListLinks(Empty()) assert len(response.links) == 0 # ----- Load Scenario ---------------------------------------------------------------------------------------------- descriptor_loader = load_scenario_from_descriptor( DESCRIPTOR_FILE, context_client, device_client, None, None) # ----- List entities - Ensure scenario is ready ------------------------------------------------------------------- response = context_client.ListContexts(Empty()) assert len(response.contexts) == descriptor_loader.num_contexts for context_uuid, num_topologies in descriptor_loader.num_topologies.items(): response = context_client.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = context_client.ListDevices(Empty()) assert len(response.devices) == descriptor_loader.num_devices descriptor_loader = DescriptorLoader( descriptors_file=DESCRIPTOR_FILE, context_client=context_client, device_client=device_client) results = descriptor_loader.process() check_descriptor_load_results(results, descriptor_loader) descriptor_loader.validate() response = context_client.ListLinks(Empty()) assert len(response.links) == descriptor_loader.num_links # 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 for context_uuid, _ in descriptor_loader.num_services.items(): response = context_client.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == 0 def test_scenario_kpis_created( context_client : ContextClient, # pylint: disable=redefined-outer-name Loading
src/tests/benchmark/policy/tests/test_functional_cleanup.py +16 −52 Original line number Diff line number Diff line Loading @@ -13,9 +13,10 @@ # limitations under the License. import logging from common.tools.descriptor.Loader import DescriptorLoader from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import ContextId from common.tools.descriptor.Loader import DescriptorLoader, validate_empty_scenario from common.tools.object_factory.Context import json_context_id from common.proto.context_pb2 import ContextId, DeviceId, Empty, LinkId, TopologyId from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from tests.Fixtures import context_client, device_client # pylint: disable=unused-import Loading @@ -24,57 +25,20 @@ LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) DESCRIPTOR_FILE = 'ofc22/descriptors_emulated.json' ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_NAME)) def test_services_removed( def test_scenario_cleanup( context_client : ContextClient, # pylint: disable=redefined-outer-name device_client : DeviceClient, # pylint: disable=redefined-outer-name ) -> None: # ----- List entities - Ensure service is removed ------------------------------------------------------------------ with open(DESCRIPTOR_FILE, 'r', encoding='UTF-8') as f: descriptors = f.read() descriptor_loader = DescriptorLoader(descriptors) response = context_client.ListContexts(Empty()) assert len(response.contexts) == descriptor_loader.num_contexts for context_uuid, num_topologies in descriptor_loader.num_topologies.items(): response = context_client.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = context_client.ListDevices(Empty()) assert len(response.devices) == descriptor_loader.num_devices response = context_client.ListLinks(Empty()) assert len(response.links) == descriptor_loader.num_links for context_uuid, _ in descriptor_loader.num_services.items(): response = context_client.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == 0 # ----- Delete Links, Devices, Topologies, Contexts ---------------------------------------------------------------- for link in descriptor_loader.links: context_client.RemoveLink(LinkId(**link['link_id'])) for device in descriptor_loader.devices: device_client .DeleteDevice(DeviceId(**device['device_id'])) for context_uuid, topology_list in descriptor_loader.topologies.items(): for topology in topology_list: context_client.RemoveTopology(TopologyId(**topology['topology_id'])) for context in descriptor_loader.contexts: context_client.RemoveContext(ContextId(**context['context_id'])) # ----- List entities - Ensure database is empty again ------------------------------------------------------------- response = context_client.ListContexts(Empty()) assert len(response.contexts) == 0 response = context_client.ListDevices(Empty()) assert len(response.devices) == 0 response = context_client.ListLinks(Empty()) assert len(response.links) == 0 # 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, device_client=device_client) descriptor_loader.validate() descriptor_loader.unload() validate_empty_scenario(context_client)
src/tests/benchmark/policy/tests/test_functional_create_service.py +35 −57 Original line number Diff line number Diff line Loading @@ -13,15 +13,15 @@ # limitations under the License. import logging, random from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import ContextId, Empty 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, device_client, monitoring_client # pylint: disable=unused-import 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 Loading @@ -29,67 +29,45 @@ from .Objects import WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) DEVTYPE_EMU_PR = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value DESCRIPTOR_FILE = 'ofc22/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 # ----- List entities - Ensure scenario is ready ------------------------------------------------------------------- with open(DESCRIPTOR_FILE, 'r', encoding='UTF-8') as f: descriptors = f.read() descriptor_loader = DescriptorLoader(descriptors) response = context_client.ListContexts(Empty()) assert len(response.contexts) == descriptor_loader.num_contexts for context_uuid, num_topologies in descriptor_loader.num_topologies.items(): response = context_client.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = context_client.ListDevices(Empty()) assert len(response.devices) == descriptor_loader.num_devices response = context_client.ListLinks(Empty()) assert len(response.links) == descriptor_loader.num_links for context_uuid, num_services in descriptor_loader.num_services.items(): response = context_client.ListServices(ContextId(**json_context_id(context_uuid))) assert len(response.services) == 0 # 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 Service --------------------------------------------------------------------------------------------- # 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 # ----- List entities - Ensure service is created ------------------------------------------------------------------ response = context_client.ListContexts(Empty()) assert len(response.contexts) == descriptor_loader.num_contexts for context_uuid, num_topologies in descriptor_loader.num_topologies.items(): response = context_client.ListTopologies(ContextId(**json_context_id(context_uuid))) assert len(response.topologies) == num_topologies response = context_client.ListDevices(Empty()) assert len(response.devices) == descriptor_loader.num_devices response = context_client.ListLinks(Empty()) assert len(response.links) == descriptor_loader.num_links for context_uuid, num_services in descriptor_loader.num_services.items(): response = context_client.ListServices(ContextId(**json_context_id(context_uuid))) 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*num_services # OLS & L3NM => (L3NM + TAPI) 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))) assert len(response.connections) == 1 # one connection per service 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( Loading