diff --git a/proto/pathcomp.proto b/proto/pathcomp.proto index 6dabbaa3f2e52ba5f1c45ac28a871718f350436c..2570456bba28fd4baa02628aedabed1cc63db10a 100644 --- a/proto/pathcomp.proto +++ b/proto/pathcomp.proto @@ -26,5 +26,12 @@ message PathCompRequest { } message PathCompReply { - repeated context.Connection connections = 1; + // Services requested completed with possible missing fields, and + // sub-services required for supporting requested services on the + // underlying layers. + repeated context.Service services = 1; + + // Connections supporting the requested services and sub-services + // required for the underlying layers. + repeated context.Connection connections = 2; } diff --git a/src/common/tests/MockServicerImpl_Context.py b/src/common/tests/MockServicerImpl_Context.py index 38f932f4dc9638abf3e526e6867800dd01cca154..8b4560016bc2cfa54d5ac30e7147b4df59e04e72 100644 --- a/src/common/tests/MockServicerImpl_Context.py +++ b/src/common/tests/MockServicerImpl_Context.py @@ -18,8 +18,8 @@ from common.tools.grpc.Tools import grpc_message_to_json_string from context.proto.context_pb2 import ( Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId, ContextIdList, ContextList, Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList, Empty, Link, LinkEvent, - LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Topology, - TopologyEvent, TopologyId, TopologyIdList, TopologyList) + LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, + SliceId, SliceIdList, SliceList, Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList) from context.proto.context_pb2_grpc import ContextServiceServicer LOGGER = logging.getLogger(__name__) @@ -92,12 +92,12 @@ class MockServicerImpl_Context(ContextServiceServicer): def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: LOGGER.info('[ListTopologyIds] request={:s}'.format(grpc_message_to_json_string(request))) - topologies = get_entries(self.database, 'topology[{:s}]'.format(str(request.context_id.context_uuid.uuid))) + topologies = get_entries(self.database, 'topology[{:s}]'.format(str(request.context_uuid.uuid))) return TopologyIdList(topology_ids=[topology.topology_id for topology in topologies]) def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: LOGGER.info('[ListTopologies] request={:s}'.format(grpc_message_to_json_string(request))) - topologies = get_entries(self.database, 'topology[{:s}]'.format(str(request.context_id.context_uuid.uuid))) + topologies = get_entries(self.database, 'topology[{:s}]'.format(str(request.context_uuid.uuid))) return TopologyList(topologies=[topology for topology in topologies]) def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: @@ -171,16 +171,48 @@ class MockServicerImpl_Context(ContextServiceServicer): LOGGER.info('[GetLinkEvents] request={:s}'.format(grpc_message_to_json_string(request))) + # ----- Slice ------------------------------------------------------------------------------------------------------ + + def ListSliceIds(self, request: ContextId, context : grpc.ServicerContext) -> SliceIdList: + LOGGER.info('[ListSliceIds] request={:s}'.format(grpc_message_to_json_string(request))) + slices = get_entries(self.database, 'slice[{:s}]'.format(str(request.context_uuid.uuid))) + return SliceIdList(slice_ids=[slice.slice_id for slice in slices]) + + def ListSlices(self, request: ContextId, context : grpc.ServicerContext) -> SliceList: + LOGGER.info('[ListSlices] request={:s}'.format(grpc_message_to_json_string(request))) + slices = get_entries(self.database, 'slice[{:s}]'.format(str(request.context_uuid.uuid))) + return SliceList(slices=[slice for slice in slices]) + + def GetSlice(self, request: SliceId, context : grpc.ServicerContext) -> Slice: + LOGGER.info('[GetSlice] request={:s}'.format(grpc_message_to_json_string(request))) + container_name = 'slice[{:s}]'.format(str(request.context_id.context_uuid.uuid)) + return get_entry(context, self.database, container_name, request.slice_uuid.uuid) + + def SetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: + LOGGER.info('[SetSlice] request={:s}'.format(grpc_message_to_json_string(request))) + return set_entry( + self.database, 'slice[{:s}]'.format(str(request.slice_id.context_id.context_uuid.uuid)), + request.slice_id.slice_uuid.uuid, request).slice_id + + def RemoveSlice(self, request: SliceId, context : grpc.ServicerContext) -> Empty: + LOGGER.info('[RemoveSlice] request={:s}'.format(grpc_message_to_json_string(request))) + container_name = 'slice[{:s}]'.format(str(request.context_id.context_uuid.uuid)) + return del_entry(context, self.database, container_name, request.slice_uuid.uuid) + + def GetSliceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[SliceEvent]: + LOGGER.info('[GetSliceEvents] request={:s}'.format(grpc_message_to_json_string(request))) + + # ----- Service ---------------------------------------------------------------------------------------------------- def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList: LOGGER.info('[ListServiceIds] request={:s}'.format(grpc_message_to_json_string(request))) - services = get_entries(self.database, 'service[{:s}]'.format(str(request.context_id.context_uuid.uuid))) + services = get_entries(self.database, 'service[{:s}]'.format(str(request.context_uuid.uuid))) return ServiceIdList(service_ids=[service.service_id for service in services]) def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: LOGGER.info('[ListServices] request={:s}'.format(grpc_message_to_json_string(request))) - services = get_entries(self.database, 'service[{:s}]'.format(str(request.context_id.context_uuid.uuid))) + services = get_entries(self.database, 'service[{:s}]'.format(str(request.context_uuid.uuid))) return ServiceList(services=[service for service in services]) def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service: diff --git a/src/pathcomp/proto/pathcomp_pb2.py b/src/pathcomp/proto/pathcomp_pb2.py index 4a7e8461407a9639cff16a2a2b2c05b7440d05c5..bae04a93e1554f2db743c8a9d3c753fcb878fff6 100644 --- a/src/pathcomp/proto/pathcomp_pb2.py +++ b/src/pathcomp/proto/pathcomp_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0epathcomp.proto\x12\x08pathcomp\x1a\rcontext.proto\"5\n\x0fPathCompRequest\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"9\n\rPathCompReply\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection2R\n\x0fPathCompService\x12?\n\x07\x43ompute\x12\x19.pathcomp.PathCompRequest\x1a\x17.pathcomp.PathCompReply\"\x00\x62\x06proto3' + serialized_pb=b'\n\x0epathcomp.proto\x12\x08pathcomp\x1a\rcontext.proto\"5\n\x0fPathCompRequest\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"]\n\rPathCompReply\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\x12(\n\x0b\x63onnections\x18\x02 \x03(\x0b\x32\x13.context.Connection2R\n\x0fPathCompService\x12?\n\x07\x43ompute\x12\x19.pathcomp.PathCompRequest\x1a\x17.pathcomp.PathCompReply\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) @@ -68,12 +68,19 @@ _PATHCOMPREPLY = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='connections', full_name='pathcomp.PathCompReply.connections', index=0, + name='services', full_name='pathcomp.PathCompReply.services', index=0, number=1, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='connections', full_name='pathcomp.PathCompReply.connections', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), ], extensions=[ ], @@ -87,10 +94,11 @@ _PATHCOMPREPLY = _descriptor.Descriptor( oneofs=[ ], serialized_start=98, - serialized_end=155, + serialized_end=191, ) _PATHCOMPREQUEST.fields_by_name['services'].message_type = context__pb2._SERVICE +_PATHCOMPREPLY.fields_by_name['services'].message_type = context__pb2._SERVICE _PATHCOMPREPLY.fields_by_name['connections'].message_type = context__pb2._CONNECTION DESCRIPTOR.message_types_by_name['PathCompRequest'] = _PATHCOMPREQUEST DESCRIPTOR.message_types_by_name['PathCompReply'] = _PATHCOMPREPLY @@ -119,8 +127,8 @@ _PATHCOMPSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=157, - serialized_end=239, + serialized_start=193, + serialized_end=275, methods=[ _descriptor.MethodDescriptor( name='Compute', diff --git a/src/pathcomp/service/PathCompServiceServicerImpl.py b/src/pathcomp/service/PathCompServiceServicerImpl.py index 8fa012b40d05e61425dc801fc83062d7188eeeb6..1bb4470b1c2863a04e7be2816f250e5e42fcb500 100644 --- a/src/pathcomp/service/PathCompServiceServicerImpl.py +++ b/src/pathcomp/service/PathCompServiceServicerImpl.py @@ -12,10 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -import grpc, logging +from typing import List +import grpc, logging, uuid from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string from context.client.ContextClient import ContextClient +from context.proto.context_pb2 import Connection, Empty, EndPointId from pathcomp.proto.pathcomp_pb2 import PathCompReply, PathCompRequest from pathcomp.proto.pathcomp_pb2_grpc import PathCompServiceServicer @@ -28,10 +30,54 @@ METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) class PathCompServiceServicerImpl(PathCompServiceServicer): def __init__(self) -> None: LOGGER.debug('Creating Servicer...') - self.context_client = ContextClient() LOGGER.debug('Servicer Created') @safe_and_metered_rpc_method(METRICS, LOGGER) def Compute(self, request : PathCompRequest, context : grpc.ServicerContext) -> PathCompReply: LOGGER.info('[Compute] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) - return PathCompReply() + + context_client = ContextClient() + + grpc_contexts = context_client.ListContexts(Empty()) + grpc_devices = context_client.ListDevices(Empty()) + grpc_links = context_client.ListLinks(Empty()) + for grpc_context in grpc_contexts.contexts: + # TODO: add context to request + grpc_topologies = context_client.ListTopologies(grpc_context.context_id) + for grpc_topology in grpc_topologies.topologies: #pylint: disable=unused-variable + # TODO: add topology to request + pass + for grpc_device in grpc_devices.devices: #pylint: disable=unused-variable + # TODO: add device to request + pass + for grpc_link in grpc_links.links: #pylint: disable=unused-variable + # TODO: add link to request + pass + + reply = PathCompReply() + # TODO: issue path computation request + # TODO: compose reply populating reply.services and reply.connections + + for service in request.services: + # TODO: implement support for multi-point services + service_endpoint_ids = service.service_endpoint_ids + if len(service_endpoint_ids) != 2: raise NotImplementedError('Service must have 2 endpoints') + a_endpoint_id, z_endpoint_id = service_endpoint_ids[0], service_endpoint_ids[-1] + + connection_uuid = str(uuid.uuid4()) + connection_path_hops : List[EndPointId] = [] + connection_path_hops.extend([ + grpc_message_to_json(a_endpoint_id), + grpc_message_to_json(z_endpoint_id), + ]) + connection = Connection(**{ + 'connection_id': {'connection_uuid': {'uuid': connection_uuid}}, + 'service_id': grpc_message_to_json(service.service_id), + 'path_hops_endpoint_ids': connection_path_hops, + 'sub_service_ids': [], + }) + reply.connections.append(connection) #pylint: disable=no-member + reply.services.append(service) #pylint: disable=no-member + + LOGGER.info('[Compute] end ; reply = {:s}'.format(grpc_message_to_json_string(reply))) + return reply diff --git a/src/pathcomp/tests/MockService_Dependencies.py b/src/pathcomp/tests/MockService_Dependencies.py index 606f25acdc692a7cd9406258f493704f17d0c962..f390b26029309211629c7d8292838c105ad4bc05 100644 --- a/src/pathcomp/tests/MockService_Dependencies.py +++ b/src/pathcomp/tests/MockService_Dependencies.py @@ -17,15 +17,21 @@ from typing import Union from common.Constants import ServiceNameEnum from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name from common.tests.MockServicerImpl_Context import MockServicerImpl_Context +from common.tests.MockServicerImpl_Device import MockServicerImpl_Device +from common.tests.MockServicerImpl_Service import MockServicerImpl_Service from common.tools.service.GenericGrpcService import GenericGrpcService from context.proto.context_pb2_grpc import add_ContextServiceServicer_to_server +from device.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server +from service.proto.service_pb2_grpc import add_ServiceServiceServicer_to_server LOCAL_HOST = '127.0.0.1' SERVICE_CONTEXT = ServiceNameEnum.CONTEXT +SERVICE_DEVICE = ServiceNameEnum.DEVICE +SERVICE_SERVICE = ServiceNameEnum.SERVICE class MockService_Dependencies(GenericGrpcService): - # Mock Service implementing Context to simplify unitary tests of PathComp + # Mock Service implementing Context, Device, and Service to simplify unitary tests of PathComp def __init__(self, bind_port: Union[str, int]) -> None: super().__init__(bind_port, LOCAL_HOST, enable_health_servicer=False, cls_name='MockService') @@ -35,6 +41,18 @@ class MockService_Dependencies(GenericGrpcService): self.context_servicer = MockServicerImpl_Context() add_ContextServiceServicer_to_server(self.context_servicer, self.server) + self.device_servicer = MockServicerImpl_Device() + add_DeviceServiceServicer_to_server(self.device_servicer, self.server) + + self.service_servicer = MockServicerImpl_Service() + add_ServiceServiceServicer_to_server(self.service_servicer, self.server) + def configure_env_vars(self): os.environ[get_env_var_name(SERVICE_CONTEXT, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address) os.environ[get_env_var_name(SERVICE_CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port) + + os.environ[get_env_var_name(SERVICE_DEVICE, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address) + os.environ[get_env_var_name(SERVICE_DEVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port) + + os.environ[get_env_var_name(SERVICE_SERVICE, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address) + os.environ[get_env_var_name(SERVICE_SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port) diff --git a/src/pathcomp/tests/Objects.py b/src/pathcomp/tests/Objects.py index f335c2f9bdb1f316895fb2c9833c1665e9a4cb7f..a3cf10b789d0fa7cb6fd3fdf3e2802dcb6024ec8 100644 --- a/src/pathcomp/tests/Objects.py +++ b/src/pathcomp/tests/Objects.py @@ -23,20 +23,20 @@ from common.tools.object_factory.Topology import json_topology, json_topology_id def compose_device(device_uuid, endpoint_uuids): device_id = json_device_id(device_uuid) - endpoints = [(endpoint_uuid, 'copper') for endpoint_uuid in endpoint_uuids] + endpoints = [(endpoint_uuid, 'copper', []) for endpoint_uuid in endpoint_uuids] endpoints = json_endpoints(device_id, endpoints, topology_id=TOPOLOGY_A_ID) device = json_device_emulated_packet_router_disabled(device_uuid, endpoints=endpoints) return device_id, endpoints, device -def compose_link(endpoint_a_id, endpoint_z_id): - link_uuid = get_link_uuid(endpoint_a_id, endpoint_z_id) +def compose_link(endpoint_a, endpoint_z): + link_uuid = get_link_uuid(endpoint_a['endpoint_id'], endpoint_z['endpoint_id']) link_id = json_link_id(link_uuid) - link = json_link(link_uuid, [endpoint_a_id, endpoint_z_id]) + link = json_link(link_uuid, [endpoint_a['endpoint_id'], endpoint_z['endpoint_id']]) return link_id, link -def compose_service(endpoint_a_id, endpoint_z_id, constraints=[]): - service_uuid = get_service_uuid(endpoint_a_id, endpoint_z_id) - endpoint_ids = [endpoint_a_id, endpoint_z_id] +def compose_service(endpoint_a, endpoint_z, constraints=[]): + service_uuid = get_service_uuid(endpoint_a['endpoint_id'], endpoint_z['endpoint_id']) + endpoint_ids = [endpoint_a['endpoint_id'], endpoint_z['endpoint_id']] service = json_service_l3nm_planned(service_uuid, endpoint_ids=endpoint_ids, constraints=constraints) return service @@ -98,13 +98,13 @@ SERVICE_A1_B1 = compose_service(DEVICE_A1_ENDPOINTS[2], DEVICE_B1_ENDPOINTS[2], ]) # ----- Containers ----------------------------------------------------------------------------------------------------- -CONTEXTS = [CONTEXT], -TOPOLOGIES = [TOPOLOGY_ADMIN, TOPOLOGY_A, TOPOLOGY_B, TOPOLOGY_C], +CONTEXTS = [CONTEXT] +TOPOLOGIES = [TOPOLOGY_ADMIN, TOPOLOGY_A, TOPOLOGY_B, TOPOLOGY_C] DEVICES = [ DEVICE_A1, DEVICE_A2, DEVICE_A3, DEVICE_B1, DEVICE_B2, DEVICE_B3, - DEVICE_C1, DEVICE_C2, DEVICE_C3, ], + DEVICE_C1, DEVICE_C2, DEVICE_C3, ] LINKS = [ LINK_A2_C3, LINK_C1_B2, LINK_A1_A2, LINK_A1_A3, LINK_A2_A3, LINK_B1_B2, LINK_B1_B3, LINK_B2_B3, - LINK_C1_C2, LINK_C1_C3, LINK_C2_C3, ], + LINK_C1_C2, LINK_C1_C3, LINK_C2_C3, ] SERVICES = [SERVICE_A1_B1] diff --git a/src/pathcomp/tests/PrepareTestScenario.py b/src/pathcomp/tests/PrepareTestScenario.py index bcf3cd156ab04e932e440837dc8ca0df645dc0cc..a4efcbdbfc0d311dfb120ab8124a9d2268660daf 100644 --- a/src/pathcomp/tests/PrepareTestScenario.py +++ b/src/pathcomp/tests/PrepareTestScenario.py @@ -18,17 +18,15 @@ from common.Settings import ( ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_service_port_grpc) from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient -from service.client.ServiceClient import ServiceClient -from service.service.ServiceService import ServiceService -from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory -from service.service.service_handlers import SERVICE_HANDLERS -from service.tests.MockService_Dependencies import MockService_Dependencies +from pathcomp.client.PathCompClient import PathCompClient +from pathcomp.service.PathCompService import PathCompService +from pathcomp.tests.MockService_Dependencies import MockService_Dependencies LOCAL_HOST = '127.0.0.1' MOCKSERVICE_PORT = 10000 -SERVICE_SERVICE_PORT = MOCKSERVICE_PORT + get_service_port_grpc(ServiceNameEnum.SERVICE) # avoid privileged ports -os.environ[get_env_var_name(ServiceNameEnum.SERVICE, ENVVAR_SUFIX_SERVICE_HOST )] = str(LOCAL_HOST) -os.environ[get_env_var_name(ServiceNameEnum.SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(SERVICE_SERVICE_PORT) +PATHCOMP_SERVICE_PORT = MOCKSERVICE_PORT + get_service_port_grpc(ServiceNameEnum.PATHCOMP) # avoid privileged ports +os.environ[get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_HOST )] = str(LOCAL_HOST) +os.environ[get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(PATHCOMP_SERVICE_PORT) @pytest.fixture(scope='session') def mock_service(): @@ -51,18 +49,17 @@ def device_client(mock_service : MockService_Dependencies): # pylint: disable=re _client.close() @pytest.fixture(scope='session') -def service_service( +def pathcomp_service( context_client : ContextClient, # pylint: disable=redefined-outer-name device_client : DeviceClient): # pylint: disable=redefined-outer-name - _service_handler_factory = ServiceHandlerFactory(SERVICE_HANDLERS) - _service = ServiceService(_service_handler_factory) + _service = PathCompService() _service.start() yield _service _service.stop() @pytest.fixture(scope='session') -def service_client(service_service : ServiceService): # pylint: disable=redefined-outer-name - _client = ServiceClient() +def pathcomp_client(pathcomp_service : PathCompService): # pylint: disable=redefined-outer-name + _client = PathCompClient() yield _client _client.close() diff --git a/src/pathcomp/tests/test_unitary.py b/src/pathcomp/tests/test_unitary.py index 60fd17371771c6d5764b18255595f8b4520e8447..9e2dd8bbcc639e006a00ba96cd405570c7f66d16 100644 --- a/src/pathcomp/tests/test_unitary.py +++ b/src/pathcomp/tests/test_unitary.py @@ -12,174 +12,84 @@ # See the License for the specific language governing permissions and # limitations under the License. -import copy, grpc, logging, pytest -from common.tests.PytestGenerateTests import pytest_generate_tests # (required) pylint: disable=unused-import -from common.tools.grpc.Tools import grpc_message_to_json_string +import logging +from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string from context.client.ContextClient import ContextClient from context.proto.context_pb2 import Context, ContextId, DeviceId, Link, LinkId, Topology, Device, TopologyId from device.client.DeviceClient import DeviceClient -from service.client.ServiceClient import ServiceClient -from service.proto.context_pb2 import Service, ServiceId +from pathcomp.client.PathCompClient import PathCompClient +from pathcomp.proto.pathcomp_pb2 import PathCompRequest from .PrepareTestScenario import ( # pylint: disable=unused-import # be careful, order of symbols is important here! - mock_service, service_service, context_client, device_client, service_client) + mock_service, pathcomp_service, context_client, device_client, pathcomp_client) +from .Objects import CONTEXTS, DEVICES, LINKS, SERVICES, TOPOLOGIES LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) -try: - from .ServiceHandlersToTest import SERVICE_HANDLERS_TO_TEST -except ImportError: - LOGGER.exception('Unable to load service handlers, nothing will be tested.') - SERVICE_HANDLERS_TO_TEST = [] -class TestServiceHandlers: - scenarios = SERVICE_HANDLERS_TO_TEST - - def test_prepare_environment( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - for context in contexts: context_client.SetContext(Context(**context)) - for topology in topologies: context_client.SetTopology(Topology(**topology)) - for device in devices: device_client.AddDevice(Device(**device)) - for link in links: context_client.SetLink(Link(**link)) - - - def test_service_create_error_cases( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - with pytest.raises(grpc.RpcError) as e: - service_with_endpoints = copy.deepcopy(service_descriptor) - service_with_endpoints['service_endpoint_ids'].extend(service_endpoint_ids) - service_client.CreateService(Service(**service_with_endpoints)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg_head = 'service.service_endpoint_ids([' - msg_tail = ']) is invalid; RPC method CreateService does not accept Endpoints. '\ - 'Endpoints should be configured after creating the service.' - except_msg = str(e.value.details()) - assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) - - with pytest.raises(grpc.RpcError) as e: - service_with_config_rules = copy.deepcopy(service_descriptor) - service_with_config_rules['service_config']['config_rules'].extend(service_config_rules) - service_client.CreateService(Service(**service_with_config_rules)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg_head = 'service.service_config.config_rules([' - msg_tail = ']) is invalid; RPC method CreateService does not accept Config Rules. '\ - 'Config Rules should be configured after creating the service.' - except_msg = str(e.value.details()) - assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) - - with pytest.raises(grpc.RpcError) as e: - service_with_constraints = copy.deepcopy(service_descriptor) - service_with_constraints['service_constraints'].extend(service_constraints) - service_client.CreateService(Service(**service_with_constraints)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg_head = 'service.service_constraints([' - msg_tail = ']) is invalid; RPC method CreateService does not accept Constraints. '\ - 'Constraints should be configured after creating the service.' - except_msg = str(e.value.details()) - assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) - - - def test_service_create_correct( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - service_client.CreateService(Service(**service_descriptor)) - - - def test_service_get_created( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - service_data = context_client.GetService(ServiceId(**service_id)) - LOGGER.info('service_data = {:s}'.format(grpc_message_to_json_string(service_data))) - - - def test_service_update_configure( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - service_with_settings = copy.deepcopy(service_descriptor) - service_with_settings['service_endpoint_ids'].extend(service_endpoint_ids) - service_with_settings['service_config']['config_rules'].extend(service_config_rules) - service_with_settings['service_constraints'].extend(service_constraints) - service_client.UpdateService(Service(**service_with_settings)) - - for endpoint_id in service_endpoint_ids: - device_id = endpoint_id['device_id'] - device_data = context_client.GetDevice(DeviceId(**device_id)) - for i,config_rule in enumerate(device_data.device_config.config_rules): - LOGGER.info('device_data[{:s}][#{:d}] => {:s}'.format( - str(device_id), i, grpc_message_to_json_string(config_rule))) - - - def test_service_update_deconfigure( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - service_with_settings = copy.deepcopy(service_descriptor) - service_with_settings['service_endpoint_ids'].extend([]) # remove endpoints - service_client.UpdateService(Service(**service_with_settings)) - - for endpoint_id in service_endpoint_ids: - device_id = endpoint_id['device_id'] - device_data = context_client.GetDevice(DeviceId(**device_id)) - for i,config_rule in enumerate(device_data.device_config.config_rules): - LOGGER.info('device_data[{:s}][#{:d}] => {:s}'.format( - str(device_id), i, grpc_message_to_json_string(config_rule))) - - - def test_service_get_updated( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - service_data = context_client.GetService(ServiceId(**service_id)) - LOGGER.info('service_data = {:s}'.format(grpc_message_to_json_string(service_data))) - - - def test_service_delete( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - service_client.DeleteService(ServiceId(**service_id)) - - - def test_cleanup_environment( - self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, - contexts, topologies, devices, links, - context_client : ContextClient, # pylint: disable=redefined-outer-name - device_client : DeviceClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient): # pylint: disable=redefined-outer-name - - for link in links: context_client.RemoveLink(LinkId(**link['link_id'])) - for device in devices: device_client.DeleteDevice(DeviceId(**device['device_id'])) - for topology in topologies: context_client.RemoveTopology(TopologyId(**topology['topology_id'])) - for context in contexts: context_client.RemoveContext(ContextId(**context['context_id'])) +def test_prepare_environment( + context_client : ContextClient, # pylint: disable=redefined-outer-name + device_client : DeviceClient): # pylint: disable=redefined-outer-name + + for context in CONTEXTS : context_client.SetContext (Context (**context )) + for topology in TOPOLOGIES: context_client.SetTopology(Topology(**topology)) + for device in DEVICES : device_client .AddDevice (Device (**device )) + for link in LINKS : context_client.SetLink (Link (**link )) + + +def test_request_service( + pathcomp_client : PathCompClient): # pylint: disable=redefined-outer-name + + request_services = SERVICES + pathcomp_request = PathCompRequest(services=request_services) + pathcomp_reply = pathcomp_client.Compute(pathcomp_request) + pathcomp_reply = grpc_message_to_json(pathcomp_reply) + reply_services = pathcomp_reply['services'] + reply_connections = pathcomp_reply['connections'] + assert len(request_services) <= len(reply_services) + request_service_ids = { + '{:s}/{:s}'.format( + svc['service_id']['context_id']['context_uuid']['uuid'], + svc['service_id']['service_uuid']['uuid'] + ) + for svc in request_services + } + reply_service_ids = { + '{:s}/{:s}'.format( + svc['service_id']['context_id']['context_uuid']['uuid'], + svc['service_id']['service_uuid']['uuid'] + ) + for svc in reply_services + } + # Assert all requested services have a reply + # It permits having other services not requested (i.e., sub-services) + assert len(request_service_ids.difference(reply_service_ids)) == 0 + + reply_connection_service_ids = { + '{:s}/{:s}'.format( + conn['service_id']['context_id']['context_uuid']['uuid'], + conn['service_id']['service_uuid']['uuid'] + ) + for conn in reply_connections + } + # Assert all requested services have a connection associated + # It permits having other connections not requested (i.e., connections for sub-services) + assert len(request_service_ids.difference(reply_connection_service_ids)) == 0 + + # TODO: implement other checks. examples: + # - request service and reply service endpoints match + # - request service and reply connection endpoints match + # - reply sub-service and reply sub-connection endpoints match + # - others? + #for json_service,json_connection in zip(json_services, json_connections): + + +def test_cleanup_environment( + context_client : ContextClient, # pylint: disable=redefined-outer-name + device_client : DeviceClient): # pylint: disable=redefined-outer-name + + for link in LINKS : context_client.RemoveLink (LinkId (**link ['link_id' ])) + for device in DEVICES : device_client .DeleteDevice (DeviceId (**device ['device_id' ])) + for topology in TOPOLOGIES: context_client.RemoveTopology(TopologyId(**topology['topology_id'])) + for context in CONTEXTS : context_client.RemoveContext (ContextId (**context ['context_id' ]))