Skip to content
Snippets Groups Projects
Commit f4cff5db authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

PathComp component:

- extended proto file to support retrieval of both services and connections, e.g., services requested + sub-services, and the connections and sub-connections supporting the services and sub-services.
- minor bug fixings in MockServicerImpl_Context and implementation of support for Slices
- implemented and pre-tested unitary tests for PathComp
- implemented in PathComp service the logic to retrieve context data, and the skeleton to retrieve the replies to compute requests.
parent b51eac16
No related branches found
No related tags found
1 merge request!54Release 2.0.0
...@@ -26,5 +26,12 @@ message PathCompRequest { ...@@ -26,5 +26,12 @@ message PathCompRequest {
} }
message PathCompReply { 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;
} }
...@@ -18,8 +18,8 @@ from common.tools.grpc.Tools import grpc_message_to_json_string ...@@ -18,8 +18,8 @@ from common.tools.grpc.Tools import grpc_message_to_json_string
from context.proto.context_pb2 import ( from context.proto.context_pb2 import (
Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId, Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId,
ContextIdList, ContextList, Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList, Empty, Link, LinkEvent, ContextIdList, ContextList, Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList, Empty, Link, LinkEvent,
LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Topology, LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent,
TopologyEvent, TopologyId, TopologyIdList, TopologyList) SliceId, SliceIdList, SliceList, Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList)
from context.proto.context_pb2_grpc import ContextServiceServicer from context.proto.context_pb2_grpc import ContextServiceServicer
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
...@@ -92,12 +92,12 @@ class MockServicerImpl_Context(ContextServiceServicer): ...@@ -92,12 +92,12 @@ class MockServicerImpl_Context(ContextServiceServicer):
def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList:
LOGGER.info('[ListTopologyIds] request={:s}'.format(grpc_message_to_json_string(request))) 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]) return TopologyIdList(topology_ids=[topology.topology_id for topology in topologies])
def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList:
LOGGER.info('[ListTopologies] request={:s}'.format(grpc_message_to_json_string(request))) 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]) return TopologyList(topologies=[topology for topology in topologies])
def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology:
...@@ -171,16 +171,48 @@ class MockServicerImpl_Context(ContextServiceServicer): ...@@ -171,16 +171,48 @@ class MockServicerImpl_Context(ContextServiceServicer):
LOGGER.info('[GetLinkEvents] request={:s}'.format(grpc_message_to_json_string(request))) 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 ---------------------------------------------------------------------------------------------------- # ----- Service ----------------------------------------------------------------------------------------------------
def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList: def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList:
LOGGER.info('[ListServiceIds] request={:s}'.format(grpc_message_to_json_string(request))) 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]) return ServiceIdList(service_ids=[service.service_id for service in services])
def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList:
LOGGER.info('[ListServices] request={:s}'.format(grpc_message_to_json_string(request))) 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]) return ServiceList(services=[service for service in services])
def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service: def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service:
......
...@@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( ...@@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
syntax='proto3', syntax='proto3',
serialized_options=None, serialized_options=None,
create_key=_descriptor._internal_create_key, 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,]) dependencies=[context__pb2.DESCRIPTOR,])
...@@ -68,12 +68,19 @@ _PATHCOMPREPLY = _descriptor.Descriptor( ...@@ -68,12 +68,19 @@ _PATHCOMPREPLY = _descriptor.Descriptor(
create_key=_descriptor._internal_create_key, create_key=_descriptor._internal_create_key,
fields=[ fields=[
_descriptor.FieldDescriptor( _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, number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[], has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 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=[ extensions=[
], ],
...@@ -87,10 +94,11 @@ _PATHCOMPREPLY = _descriptor.Descriptor( ...@@ -87,10 +94,11 @@ _PATHCOMPREPLY = _descriptor.Descriptor(
oneofs=[ oneofs=[
], ],
serialized_start=98, serialized_start=98,
serialized_end=155, serialized_end=191,
) )
_PATHCOMPREQUEST.fields_by_name['services'].message_type = context__pb2._SERVICE _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 _PATHCOMPREPLY.fields_by_name['connections'].message_type = context__pb2._CONNECTION
DESCRIPTOR.message_types_by_name['PathCompRequest'] = _PATHCOMPREQUEST DESCRIPTOR.message_types_by_name['PathCompRequest'] = _PATHCOMPREQUEST
DESCRIPTOR.message_types_by_name['PathCompReply'] = _PATHCOMPREPLY DESCRIPTOR.message_types_by_name['PathCompReply'] = _PATHCOMPREPLY
...@@ -119,8 +127,8 @@ _PATHCOMPSERVICE = _descriptor.ServiceDescriptor( ...@@ -119,8 +127,8 @@ _PATHCOMPSERVICE = _descriptor.ServiceDescriptor(
index=0, index=0,
serialized_options=None, serialized_options=None,
create_key=_descriptor._internal_create_key, create_key=_descriptor._internal_create_key,
serialized_start=157, serialized_start=193,
serialized_end=239, serialized_end=275,
methods=[ methods=[
_descriptor.MethodDescriptor( _descriptor.MethodDescriptor(
name='Compute', name='Compute',
......
...@@ -12,10 +12,12 @@ ...@@ -12,10 +12,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # 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.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.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 import PathCompReply, PathCompRequest
from pathcomp.proto.pathcomp_pb2_grpc import PathCompServiceServicer from pathcomp.proto.pathcomp_pb2_grpc import PathCompServiceServicer
...@@ -28,10 +30,54 @@ METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) ...@@ -28,10 +30,54 @@ METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES)
class PathCompServiceServicerImpl(PathCompServiceServicer): class PathCompServiceServicerImpl(PathCompServiceServicer):
def __init__(self) -> None: def __init__(self) -> None:
LOGGER.debug('Creating Servicer...') LOGGER.debug('Creating Servicer...')
self.context_client = ContextClient()
LOGGER.debug('Servicer Created') LOGGER.debug('Servicer Created')
@safe_and_metered_rpc_method(METRICS, LOGGER) @safe_and_metered_rpc_method(METRICS, LOGGER)
def Compute(self, request : PathCompRequest, context : grpc.ServicerContext) -> PathCompReply: def Compute(self, request : PathCompRequest, context : grpc.ServicerContext) -> PathCompReply:
LOGGER.info('[Compute] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) 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
...@@ -17,15 +17,21 @@ from typing import Union ...@@ -17,15 +17,21 @@ from typing import Union
from common.Constants import ServiceNameEnum from common.Constants import ServiceNameEnum
from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name 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_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 common.tools.service.GenericGrpcService import GenericGrpcService
from context.proto.context_pb2_grpc import add_ContextServiceServicer_to_server 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' LOCAL_HOST = '127.0.0.1'
SERVICE_CONTEXT = ServiceNameEnum.CONTEXT SERVICE_CONTEXT = ServiceNameEnum.CONTEXT
SERVICE_DEVICE = ServiceNameEnum.DEVICE
SERVICE_SERVICE = ServiceNameEnum.SERVICE
class MockService_Dependencies(GenericGrpcService): 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: def __init__(self, bind_port: Union[str, int]) -> None:
super().__init__(bind_port, LOCAL_HOST, enable_health_servicer=False, cls_name='MockService') super().__init__(bind_port, LOCAL_HOST, enable_health_servicer=False, cls_name='MockService')
...@@ -35,6 +41,18 @@ class MockService_Dependencies(GenericGrpcService): ...@@ -35,6 +41,18 @@ class MockService_Dependencies(GenericGrpcService):
self.context_servicer = MockServicerImpl_Context() self.context_servicer = MockServicerImpl_Context()
add_ContextServiceServicer_to_server(self.context_servicer, self.server) 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): 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_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_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)
...@@ -23,20 +23,20 @@ from common.tools.object_factory.Topology import json_topology, json_topology_id ...@@ -23,20 +23,20 @@ from common.tools.object_factory.Topology import json_topology, json_topology_id
def compose_device(device_uuid, endpoint_uuids): def compose_device(device_uuid, endpoint_uuids):
device_id = json_device_id(device_uuid) 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) endpoints = json_endpoints(device_id, endpoints, topology_id=TOPOLOGY_A_ID)
device = json_device_emulated_packet_router_disabled(device_uuid, endpoints=endpoints) device = json_device_emulated_packet_router_disabled(device_uuid, endpoints=endpoints)
return device_id, endpoints, device return device_id, endpoints, device
def compose_link(endpoint_a_id, endpoint_z_id): def compose_link(endpoint_a, endpoint_z):
link_uuid = get_link_uuid(endpoint_a_id, endpoint_z_id) link_uuid = get_link_uuid(endpoint_a['endpoint_id'], endpoint_z['endpoint_id'])
link_id = json_link_id(link_uuid) 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 return link_id, link
def compose_service(endpoint_a_id, endpoint_z_id, constraints=[]): def compose_service(endpoint_a, endpoint_z, constraints=[]):
service_uuid = get_service_uuid(endpoint_a_id, endpoint_z_id) service_uuid = get_service_uuid(endpoint_a['endpoint_id'], endpoint_z['endpoint_id'])
endpoint_ids = [endpoint_a_id, endpoint_z_id] endpoint_ids = [endpoint_a['endpoint_id'], endpoint_z['endpoint_id']]
service = json_service_l3nm_planned(service_uuid, endpoint_ids=endpoint_ids, constraints=constraints) service = json_service_l3nm_planned(service_uuid, endpoint_ids=endpoint_ids, constraints=constraints)
return service return service
...@@ -98,13 +98,13 @@ SERVICE_A1_B1 = compose_service(DEVICE_A1_ENDPOINTS[2], DEVICE_B1_ENDPOINTS[2], ...@@ -98,13 +98,13 @@ SERVICE_A1_B1 = compose_service(DEVICE_A1_ENDPOINTS[2], DEVICE_B1_ENDPOINTS[2],
]) ])
# ----- Containers ----------------------------------------------------------------------------------------------------- # ----- Containers -----------------------------------------------------------------------------------------------------
CONTEXTS = [CONTEXT], CONTEXTS = [CONTEXT]
TOPOLOGIES = [TOPOLOGY_ADMIN, TOPOLOGY_A, TOPOLOGY_B, TOPOLOGY_C], TOPOLOGIES = [TOPOLOGY_ADMIN, TOPOLOGY_A, TOPOLOGY_B, TOPOLOGY_C]
DEVICES = [ DEVICE_A1, DEVICE_A2, DEVICE_A3, DEVICES = [ DEVICE_A1, DEVICE_A2, DEVICE_A3,
DEVICE_B1, DEVICE_B2, DEVICE_B3, 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, LINKS = [ LINK_A2_C3, LINK_C1_B2,
LINK_A1_A2, LINK_A1_A3, LINK_A2_A3, LINK_A1_A2, LINK_A1_A3, LINK_A2_A3,
LINK_B1_B2, LINK_B1_B3, LINK_B2_B3, 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] SERVICES = [SERVICE_A1_B1]
...@@ -18,17 +18,15 @@ from common.Settings import ( ...@@ -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) ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_service_port_grpc)
from context.client.ContextClient import ContextClient from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient from device.client.DeviceClient import DeviceClient
from service.client.ServiceClient import ServiceClient from pathcomp.client.PathCompClient import PathCompClient
from service.service.ServiceService import ServiceService from pathcomp.service.PathCompService import PathCompService
from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory from pathcomp.tests.MockService_Dependencies import MockService_Dependencies
from service.service.service_handlers import SERVICE_HANDLERS
from service.tests.MockService_Dependencies import MockService_Dependencies
LOCAL_HOST = '127.0.0.1' LOCAL_HOST = '127.0.0.1'
MOCKSERVICE_PORT = 10000 MOCKSERVICE_PORT = 10000
SERVICE_SERVICE_PORT = MOCKSERVICE_PORT + get_service_port_grpc(ServiceNameEnum.SERVICE) # avoid privileged ports PATHCOMP_SERVICE_PORT = MOCKSERVICE_PORT + get_service_port_grpc(ServiceNameEnum.PATHCOMP) # 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.PATHCOMP, ENVVAR_SUFIX_SERVICE_HOST )] = str(LOCAL_HOST)
os.environ[get_env_var_name(ServiceNameEnum.SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(SERVICE_SERVICE_PORT) os.environ[get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(PATHCOMP_SERVICE_PORT)
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def mock_service(): def mock_service():
...@@ -51,18 +49,17 @@ def device_client(mock_service : MockService_Dependencies): # pylint: disable=re ...@@ -51,18 +49,17 @@ def device_client(mock_service : MockService_Dependencies): # pylint: disable=re
_client.close() _client.close()
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def service_service( def pathcomp_service(
context_client : ContextClient, # pylint: disable=redefined-outer-name context_client : ContextClient, # pylint: disable=redefined-outer-name
device_client : DeviceClient): # pylint: disable=redefined-outer-name device_client : DeviceClient): # pylint: disable=redefined-outer-name
_service_handler_factory = ServiceHandlerFactory(SERVICE_HANDLERS) _service = PathCompService()
_service = ServiceService(_service_handler_factory)
_service.start() _service.start()
yield _service yield _service
_service.stop() _service.stop()
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def service_client(service_service : ServiceService): # pylint: disable=redefined-outer-name def pathcomp_client(pathcomp_service : PathCompService): # pylint: disable=redefined-outer-name
_client = ServiceClient() _client = PathCompClient()
yield _client yield _client
_client.close() _client.close()
...@@ -12,174 +12,84 @@ ...@@ -12,174 +12,84 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import copy, grpc, logging, pytest import logging
from common.tests.PytestGenerateTests import pytest_generate_tests # (required) pylint: disable=unused-import from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string
from common.tools.grpc.Tools import grpc_message_to_json_string
from context.client.ContextClient import ContextClient from context.client.ContextClient import ContextClient
from context.proto.context_pb2 import Context, ContextId, DeviceId, Link, LinkId, Topology, Device, TopologyId from context.proto.context_pb2 import Context, ContextId, DeviceId, Link, LinkId, Topology, Device, TopologyId
from device.client.DeviceClient import DeviceClient from device.client.DeviceClient import DeviceClient
from service.client.ServiceClient import ServiceClient from pathcomp.client.PathCompClient import PathCompClient
from service.proto.context_pb2 import Service, ServiceId from pathcomp.proto.pathcomp_pb2 import PathCompRequest
from .PrepareTestScenario import ( # pylint: disable=unused-import from .PrepareTestScenario import ( # pylint: disable=unused-import
# be careful, order of symbols is important here! # 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 = logging.getLogger(__name__)
LOGGER.setLevel(logging.DEBUG) 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: def test_prepare_environment(
scenarios = SERVICE_HANDLERS_TO_TEST context_client : ContextClient, # pylint: disable=redefined-outer-name
device_client : DeviceClient): # pylint: disable=redefined-outer-name
def test_prepare_environment(
self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, for context in CONTEXTS : context_client.SetContext (Context (**context ))
contexts, topologies, devices, links, for topology in TOPOLOGIES: context_client.SetTopology(Topology(**topology))
context_client : ContextClient, # pylint: disable=redefined-outer-name for device in DEVICES : device_client .AddDevice (Device (**device ))
device_client : DeviceClient, # pylint: disable=redefined-outer-name for link in LINKS : context_client.SetLink (Link (**link ))
service_client : ServiceClient): # pylint: disable=redefined-outer-name
for context in contexts: context_client.SetContext(Context(**context)) def test_request_service(
for topology in topologies: context_client.SetTopology(Topology(**topology)) pathcomp_client : PathCompClient): # pylint: disable=redefined-outer-name
for device in devices: device_client.AddDevice(Device(**device))
for link in links: context_client.SetLink(Link(**link)) request_services = SERVICES
pathcomp_request = PathCompRequest(services=request_services)
pathcomp_reply = pathcomp_client.Compute(pathcomp_request)
def test_service_create_error_cases( pathcomp_reply = grpc_message_to_json(pathcomp_reply)
self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, reply_services = pathcomp_reply['services']
contexts, topologies, devices, links, reply_connections = pathcomp_reply['connections']
context_client : ContextClient, # pylint: disable=redefined-outer-name assert len(request_services) <= len(reply_services)
device_client : DeviceClient, # pylint: disable=redefined-outer-name request_service_ids = {
service_client : ServiceClient): # pylint: disable=redefined-outer-name '{:s}/{:s}'.format(
svc['service_id']['context_id']['context_uuid']['uuid'],
with pytest.raises(grpc.RpcError) as e: svc['service_id']['service_uuid']['uuid']
service_with_endpoints = copy.deepcopy(service_descriptor) )
service_with_endpoints['service_endpoint_ids'].extend(service_endpoint_ids) for svc in request_services
service_client.CreateService(Service(**service_with_endpoints)) }
assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT reply_service_ids = {
msg_head = 'service.service_endpoint_ids([' '{:s}/{:s}'.format(
msg_tail = ']) is invalid; RPC method CreateService does not accept Endpoints. '\ svc['service_id']['context_id']['context_uuid']['uuid'],
'Endpoints should be configured after creating the service.' svc['service_id']['service_uuid']['uuid']
except_msg = str(e.value.details()) )
assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) for svc in reply_services
}
with pytest.raises(grpc.RpcError) as e: # Assert all requested services have a reply
service_with_config_rules = copy.deepcopy(service_descriptor) # It permits having other services not requested (i.e., sub-services)
service_with_config_rules['service_config']['config_rules'].extend(service_config_rules) assert len(request_service_ids.difference(reply_service_ids)) == 0
service_client.CreateService(Service(**service_with_config_rules))
assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT reply_connection_service_ids = {
msg_head = 'service.service_config.config_rules([' '{:s}/{:s}'.format(
msg_tail = ']) is invalid; RPC method CreateService does not accept Config Rules. '\ conn['service_id']['context_id']['context_uuid']['uuid'],
'Config Rules should be configured after creating the service.' conn['service_id']['service_uuid']['uuid']
except_msg = str(e.value.details()) )
assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) for conn in reply_connections
}
with pytest.raises(grpc.RpcError) as e: # Assert all requested services have a connection associated
service_with_constraints = copy.deepcopy(service_descriptor) # It permits having other connections not requested (i.e., connections for sub-services)
service_with_constraints['service_constraints'].extend(service_constraints) assert len(request_service_ids.difference(reply_connection_service_ids)) == 0
service_client.CreateService(Service(**service_with_constraints))
assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT # TODO: implement other checks. examples:
msg_head = 'service.service_constraints([' # - request service and reply service endpoints match
msg_tail = ']) is invalid; RPC method CreateService does not accept Constraints. '\ # - request service and reply connection endpoints match
'Constraints should be configured after creating the service.' # - reply sub-service and reply sub-connection endpoints match
except_msg = str(e.value.details()) # - others?
assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) #for json_service,json_connection in zip(json_services, json_connections):
def test_service_create_correct( def test_cleanup_environment(
self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, context_client : ContextClient, # pylint: disable=redefined-outer-name
contexts, topologies, devices, links, device_client : DeviceClient): # pylint: disable=redefined-outer-name
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' ]))
service_client : ServiceClient): # pylint: disable=redefined-outer-name for device in DEVICES : device_client .DeleteDevice (DeviceId (**device ['device_id' ]))
for topology in TOPOLOGIES: context_client.RemoveTopology(TopologyId(**topology['topology_id']))
service_client.CreateService(Service(**service_descriptor)) for context in CONTEXTS : context_client.RemoveContext (ContextId (**context ['context_id' ]))
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']))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment