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
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -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;
}
+38 −6
Original line number Diff line number Diff line
@@ -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:
+13 −5
Original line number Diff line number Diff line
@@ -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',
+50 −4
Original line number Diff line number Diff line
@@ -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
+19 −1
Original line number Diff line number Diff line
@@ -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)
Loading