Loading src/pathcomp/frontend/service/PathCompServiceServicerImpl.py +61 −24 Original line number Diff line number Diff line Loading @@ -13,15 +13,16 @@ # limitations under the License. import grpc, json, logging, requests, uuid from typing import List from common.proto.context_pb2 import Connection, Empty, EndPointId from typing import Dict, Tuple from common.proto.context_pb2 import Empty, EndPointId, Service from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest from common.proto.pathcomp_pb2_grpc import PathCompServiceServicer from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method 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 pathcomp.frontend.Config import BACKEND_HOST, BACKEND_PORT, BACKEND_URL from pathcomp.frontend.service.tools.ComposeRequest import compose_device, compose_link, compose_service #from pathcomp.frontend.service.tools.Constants import CapacityUnit LOGGER = logging.getLogger(__name__) Loading Loading @@ -55,6 +56,12 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): compose_service(grpc_service, algorithm) for grpc_service in request.services ] get_service_key = lambda service_id: (service_id['contextId'], service_id['service_uuid']) service_dict : Dict[Tuple[str, str], Tuple[Dict, Service]] = { get_service_key(json_service['serviceId']): (json_service, grpc_service) for json_service,grpc_service in zip(service_list, request.services) } #LOGGER.info('service_dict = {:s}'.format(str(service_dict))) # TODO: consider filtering resources Loading @@ -71,6 +78,14 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): compose_device(grpc_device) for grpc_device in grpc_devices.devices ] endpoint_dict : Dict[str, Dict[str, Tuple[Dict, EndPointId]]] = { json_device['device_Id']: { json_endpoint['endpoint_id']['endpoint_uuid']: (json_endpoint['endpoint_id'], grpc_endpoint.endpoint_id) for json_endpoint,grpc_endpoint in zip(json_device['device_endpoints'], grpc_device.device_endpoints) } for json_device,grpc_device in zip(device_list, grpc_devices.devices) } #LOGGER.info('endpoint_dict = {:s}'.format(str(endpoint_dict))) grpc_links = context_client.ListLinks(Empty()) link_list = [ Loading @@ -95,31 +110,53 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): LOGGER.info('status_code={:s} reply={:s}'.format( str(reply.status_code), str(reply.content.decode('UTF-8')))) json_reply = reply.json() response_list = json_reply.get('response-list', []) reply = PathCompReply() # TODO: compose reply populating reply.services and reply.connections for response in response_list: service_key = get_service_key(response['serviceId']) tuple_service = service_dict.get(service_key) if tuple_service is None: raise Exception('ServiceKey({:s}) not found'.format(str(service_key))) json_service, grpc_service = tuple_service for service in request.services: # TODO: implement support for multi-point services service_endpoint_ids = service.service_endpoint_ids service_endpoint_ids = grpc_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 service = reply.services.add() service.CopyFrom(grpc_service) connection = reply.connections.add() connection.connection_id.connection_uuid.uuid = str(uuid.uuid4()) connection.service_id.CopyFrom(service.service_id) no_path_issue = response.get('noPath', {}).get('issue') if no_path_issue is not None: # no path found: leave connection with no endpoints # no_path_issue == 1 => no path due to a constraint continue service_paths = response['path'] for service_path in service_paths: # ... "path-capacity": {"total-size": {"value": 200, "unit": 0}}, # ... "path-latency": {"fixed-latency-characteristic": "10.000000"}, # ... "path-cost": {"cost-name": "", "cost-value": "5.000000", "cost-algorithm": "0.000000"}, #path_capacity = service_path['path-capacity']['total-size'] #path_capacity_value = path_capacity['value'] #path_capacity_unit = CapacityUnit(path_capacity['unit']) #path_latency = service_path['path-latency']['fixed-latency-characteristic'] #path_cost = service_path['path-cost'] #path_cost_name = path_cost['cost-name'] #path_cost_value = path_cost['cost-value'] #path_cost_algorithm = path_cost['cost-algorithm'] path_endpoints = service_path['devices'] for endpoint in path_endpoints: device_uuid = endpoint['device_id'] endpoint_uuid = endpoint['endpoint_uuid'] endpoint_id = connection.path_hops_endpoint_ids.add() endpoint_id.CopyFrom(endpoint_dict[device_uuid][endpoint_uuid][1]) LOGGER.info('[Compute] end ; reply = {:s}'.format(grpc_message_to_json_string(reply))) return reply src/pathcomp/frontend/service/tools/ComposeRequest.py +2 −40 Original line number Diff line number Diff line Loading @@ -12,48 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. from enum import IntEnum from typing import Dict from common.proto.context_pb2 import Constraint, Device, EndPointId, Link, Service, ServiceId, TopologyId from common.tools.grpc.Tools import grpc_message_to_json_string class CapacityUnit(IntEnum): TB = 0 TBPS = 1 GB = 2 GBPS = 3 MB = 4 MBPS = 5 KB = 6 KBPS = 7 GHZ = 8 MHZ = 9 class LinkPortDirection(IntEnum): BIDIRECTIONAL = 0 INPUT = 1 OUTPUT = 2 UNKNOWN = 3 class TerminationDirection(IntEnum): BIDIRECTIONAL = 0 SINK = 1 SOURCE = 2 UNKNOWN = 3 class TerminationState(IntEnum): CAN_NEVER_TERMINATE = 0 NOT_TERMINATED = 1 TERMINATED_SERVER_TO_CLIENT = 2 TERMINATED_CLIENT_TO_SERVER = 3 TERMINATED_BIDIRECTIONAL = 4 PERMENANTLY_TERMINATED = 5 TERMINATION_STATE_UNKNOWN = 6 class LinkForwardingDirection(IntEnum): BIDIRECTIONAL = 0 UNIDIRECTIONAL = 1 UNKNOWN = 2 from .Constants import CapacityUnit, LinkForwardingDirection, LinkPortDirection, TerminationDirection, TerminationState def compose_topology_id(topology_id : TopologyId) -> Dict: context_uuid = topology_id.context_id.context_uuid.uuid Loading Loading @@ -127,7 +89,7 @@ def compose_link(grpc_link : Link) -> Dict: for link_endpoint_id in grpc_link.link_endpoint_ids ] forwarding_direction = LinkForwardingDirection.UNIDIRECTIONAL.value forwarding_direction = LinkForwardingDirection.BIDIRECTIONAL.value total_potential_capacity = compose_capacity(200, CapacityUnit.MBPS.value) available_capacity = compose_capacity(200, CapacityUnit.MBPS.value) cost_characteristics = compose_cost_characteristics('linkcost', '1', '0') Loading src/pathcomp/frontend/service/tools/Constants.py 0 → 100644 +66 −0 Original line number Diff line number Diff line # Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from enum import IntEnum class CapacityUnit(IntEnum): TB = 0 TBPS = 1 GB = 2 GBPS = 3 MB = 4 MBPS = 5 KB = 6 KBPS = 7 GHZ = 8 MHZ = 9 CAPACITY_MULTIPLIER = { CapacityUnit.TB : 1.e12, CapacityUnit.TBPS : 1.e12, CapacityUnit.GB : 1.e9, CapacityUnit.GBPS : 1.e9, CapacityUnit.MB : 1.e6, CapacityUnit.MBPS : 1.e6, CapacityUnit.KB : 1.e3, CapacityUnit.KBPS : 1.e3, CapacityUnit.GHZ : 1.e9, CapacityUnit.MHZ : 1.e6, } class LinkPortDirection(IntEnum): BIDIRECTIONAL = 0 INPUT = 1 OUTPUT = 2 UNKNOWN = 3 class TerminationDirection(IntEnum): BIDIRECTIONAL = 0 SINK = 1 SOURCE = 2 UNKNOWN = 3 class TerminationState(IntEnum): CAN_NEVER_TERMINATE = 0 NOT_TERMINATED = 1 TERMINATED_SERVER_TO_CLIENT = 2 TERMINATED_CLIENT_TO_SERVER = 3 TERMINATED_BIDIRECTIONAL = 4 PERMENANTLY_TERMINATED = 5 TERMINATION_STATE_UNKNOWN = 6 class LinkForwardingDirection(IntEnum): BIDIRECTIONAL = 0 UNIDIRECTIONAL = 1 UNKNOWN = 2 src/pathcomp/frontend/tests/Objects.py +51 −23 Original line number Diff line number Diff line Loading @@ -21,10 +21,10 @@ from common.tools.object_factory.Link import get_link_uuid, json_link, json_link from common.tools.object_factory.Service import get_service_uuid, json_service_l3nm_planned 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, topology_id=None): device_id = json_device_id(device_uuid) 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_id) device = json_device_emulated_packet_router_disabled(device_uuid, endpoints=endpoints) return device_id, endpoints, device Loading @@ -45,32 +45,36 @@ CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID) CONTEXT = json_context(DEFAULT_CONTEXT_UUID) # ----- Domains -------------------------------------------------------------------------------------------------------- TOPOLOGY_ADMIN_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID) TOPOLOGY_ADMIN = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID) TOPOLOGY_ADMIN_UUID = DEFAULT_TOPOLOGY_UUID TOPOLOGY_ADMIN_ID = json_topology_id(TOPOLOGY_ADMIN_UUID, context_id=CONTEXT_ID) TOPOLOGY_ADMIN = json_topology(TOPOLOGY_ADMIN_UUID, context_id=CONTEXT_ID) TOPOLOGY_A_ID = json_topology_id('A', context_id=CONTEXT_ID) TOPOLOGY_A = json_topology('A', context_id=CONTEXT_ID) TOPOLOGY_A_UUID = 'A' TOPOLOGY_A_ID = json_topology_id(TOPOLOGY_A_UUID, context_id=CONTEXT_ID) TOPOLOGY_A = json_topology(TOPOLOGY_A_UUID, context_id=CONTEXT_ID) TOPOLOGY_B_ID = json_topology_id('B', context_id=CONTEXT_ID) TOPOLOGY_B = json_topology('B', context_id=CONTEXT_ID) TOPOLOGY_B_UUID = 'B' TOPOLOGY_B_ID = json_topology_id(TOPOLOGY_B_UUID, context_id=CONTEXT_ID) TOPOLOGY_B = json_topology(TOPOLOGY_B_UUID, context_id=CONTEXT_ID) TOPOLOGY_C_ID = json_topology_id('C', context_id=CONTEXT_ID) TOPOLOGY_C = json_topology('C', context_id=CONTEXT_ID) TOPOLOGY_C_UUID = 'C' TOPOLOGY_C_ID = json_topology_id(TOPOLOGY_C_UUID, context_id=CONTEXT_ID) TOPOLOGY_C = json_topology(TOPOLOGY_C_UUID, context_id=CONTEXT_ID) # ----- Devices Domain A ----------------------------------------------------------------------------------------------- DEVICE_A1_ID, DEVICE_A1_ENDPOINTS, DEVICE_A1 = compose_device('A1', ['1', '2', '2000']) DEVICE_A2_ID, DEVICE_A2_ENDPOINTS, DEVICE_A2 = compose_device('A2', ['1', '2', '1001']) DEVICE_A3_ID, DEVICE_A3_ENDPOINTS, DEVICE_A3 = compose_device('A3', ['1', '2']) DEVICE_A1_ID, DEVICE_A1_ENDPOINTS, DEVICE_A1 = compose_device('A1', ['1', '2', '2000'], topology_id=TOPOLOGY_A_ID) DEVICE_A2_ID, DEVICE_A2_ENDPOINTS, DEVICE_A2 = compose_device('A2', ['1', '2', '1001'], topology_id=TOPOLOGY_A_ID) DEVICE_A3_ID, DEVICE_A3_ENDPOINTS, DEVICE_A3 = compose_device('A3', ['1', '2' ], topology_id=TOPOLOGY_A_ID) # ----- Devices Domain B ----------------------------------------------------------------------------------------------- DEVICE_B1_ID, DEVICE_B1_ENDPOINTS, DEVICE_B1 = compose_device('B1', ['1', '2', '2000']) DEVICE_B2_ID, DEVICE_B2_ENDPOINTS, DEVICE_B2 = compose_device('B2', ['1', '2', '1002']) DEVICE_B3_ID, DEVICE_B3_ENDPOINTS, DEVICE_B3 = compose_device('B3', ['1', '2']) DEVICE_B1_ID, DEVICE_B1_ENDPOINTS, DEVICE_B1 = compose_device('B1', ['1', '2', '2000'], topology_id=TOPOLOGY_B_ID) DEVICE_B2_ID, DEVICE_B2_ENDPOINTS, DEVICE_B2 = compose_device('B2', ['1', '2', '1002'], topology_id=TOPOLOGY_B_ID) DEVICE_B3_ID, DEVICE_B3_ENDPOINTS, DEVICE_B3 = compose_device('B3', ['1', '2' ], topology_id=TOPOLOGY_B_ID) # ----- Devices Domain C ----------------------------------------------------------------------------------------------- DEVICE_C1_ID, DEVICE_C1_ENDPOINTS, DEVICE_C1 = compose_device('C1', ['1', '2', '1001']) DEVICE_C2_ID, DEVICE_C2_ENDPOINTS, DEVICE_C2 = compose_device('C2', ['1', '2']) DEVICE_C3_ID, DEVICE_C3_ENDPOINTS, DEVICE_C3 = compose_device('C3', ['1', '2', '1002']) DEVICE_C1_ID, DEVICE_C1_ENDPOINTS, DEVICE_C1 = compose_device('C1', ['1', '2', '1001'], topology_id=TOPOLOGY_C_ID) DEVICE_C2_ID, DEVICE_C2_ENDPOINTS, DEVICE_C2 = compose_device('C2', ['1', '2' ], topology_id=TOPOLOGY_C_ID) DEVICE_C3_ID, DEVICE_C3_ENDPOINTS, DEVICE_C3 = compose_device('C3', ['1', '2', '1002'], topology_id=TOPOLOGY_C_ID) # ----- InterDomain Links ---------------------------------------------------------------------------------------------- LINK_A2_C3_ID, LINK_A2_C3 = compose_link(DEVICE_A2_ENDPOINTS[2], DEVICE_C3_ENDPOINTS[2]) Loading @@ -94,7 +98,7 @@ LINK_C2_C3_ID, LINK_C2_C3 = compose_link(DEVICE_C2_ENDPOINTS[1], DEVICE_C3_ENDPO # ----- Service -------------------------------------------------------------------------------------------------------- SERVICE_A1_B1 = compose_service(DEVICE_A1_ENDPOINTS[2], DEVICE_B1_ENDPOINTS[2], constraints=[ json_constraint('bandwidth[gbps]', 10.0), json_constraint('latency[ms]', 5.0), json_constraint('latency[ms]', 12.0), ]) # ----- Containers ----------------------------------------------------------------------------------------------------- Loading @@ -108,3 +112,27 @@ LINKS = [ LINK_A2_C3, LINK_C1_B2, LINK_B1_B2, LINK_B1_B3, LINK_B2_B3, LINK_C1_C2, LINK_C1_C3, LINK_C2_C3, ] SERVICES = [ SERVICE_A1_B1] OBJECTS_PER_TOPOLOGY = [ (TOPOLOGY_ADMIN_ID, [ DEVICE_A1_ID, DEVICE_A2_ID, DEVICE_A3_ID, DEVICE_B1_ID, DEVICE_B2_ID, DEVICE_B3_ID, DEVICE_C1_ID, DEVICE_C2_ID, DEVICE_C3_ID, ], [ LINK_A2_C3_ID, LINK_C1_B2_ID, LINK_A1_A2_ID, LINK_A1_A3_ID, LINK_A2_A3_ID, LINK_B1_B2_ID, LINK_B1_B3_ID, LINK_B2_B3_ID, LINK_C1_C2_ID, LINK_C1_C3_ID, LINK_C2_C3_ID, ], ), (TOPOLOGY_A_ID, [ DEVICE_A1_ID, DEVICE_A2_ID, DEVICE_A3_ID, ], [ LINK_A1_A2_ID, LINK_A1_A3_ID, LINK_A2_A3_ID, ], ), (TOPOLOGY_B_ID, [ DEVICE_B1_ID, DEVICE_B2_ID, DEVICE_B3_ID, ], [ LINK_B1_B2_ID, LINK_B1_B3_ID, LINK_B2_B3_ID, ], ), (TOPOLOGY_C_ID, [ DEVICE_C1_ID, DEVICE_C2_ID, DEVICE_C3_ID, ], [ LINK_C1_C2_ID, LINK_C1_C3_ID, LINK_C2_C3_ID, ], ), ] src/pathcomp/frontend/tests/test_unitary.py +18 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ from common.tools.grpc.Tools import grpc_message_to_json from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from pathcomp.frontend.client.PathCompClient import PathCompClient from .Objects import CONTEXTS, DEVICES, LINKS, SERVICES, TOPOLOGIES from .Objects import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES from .PrepareTestScenario import ( # pylint: disable=unused-import # be careful, order of symbols is important here! mock_service, pathcomp_service, context_client, device_client, pathcomp_client) Loading @@ -37,6 +37,23 @@ def test_prepare_environment( for device in DEVICES : device_client .AddDevice (Device (**device )) for link in LINKS : context_client.SetLink (Link (**link )) for topology_id, device_ids, link_ids in OBJECTS_PER_TOPOLOGY: topology = Topology() topology.CopyFrom(context_client.GetTopology(TopologyId(**topology_id))) device_ids_in_topology = {device_id.device_uuid.uuid for device_id in topology.device_ids} func_device_id_not_added = lambda device_id: device_id['device_uuid']['uuid'] not in device_ids_in_topology func_device_id_json_to_grpc = lambda device_id: DeviceId(**device_id) device_ids_to_add = list(map(func_device_id_json_to_grpc, filter(func_device_id_not_added, device_ids))) topology.device_ids.extend(device_ids_to_add) link_ids_in_topology = {link_id.link_uuid.uuid for link_id in topology.link_ids} func_link_id_not_added = lambda link_id: link_id['link_uuid']['uuid'] not in link_ids_in_topology func_link_id_json_to_grpc = lambda link_id: LinkId(**link_id) link_ids_to_add = list(map(func_link_id_json_to_grpc, filter(func_link_id_not_added, link_ids))) topology.link_ids.extend(link_ids_to_add) context_client.SetTopology(topology) def test_request_service( pathcomp_client : PathCompClient): # pylint: disable=redefined-outer-name Loading Loading
src/pathcomp/frontend/service/PathCompServiceServicerImpl.py +61 −24 Original line number Diff line number Diff line Loading @@ -13,15 +13,16 @@ # limitations under the License. import grpc, json, logging, requests, uuid from typing import List from common.proto.context_pb2 import Connection, Empty, EndPointId from typing import Dict, Tuple from common.proto.context_pb2 import Empty, EndPointId, Service from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest from common.proto.pathcomp_pb2_grpc import PathCompServiceServicer from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method 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 pathcomp.frontend.Config import BACKEND_HOST, BACKEND_PORT, BACKEND_URL from pathcomp.frontend.service.tools.ComposeRequest import compose_device, compose_link, compose_service #from pathcomp.frontend.service.tools.Constants import CapacityUnit LOGGER = logging.getLogger(__name__) Loading Loading @@ -55,6 +56,12 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): compose_service(grpc_service, algorithm) for grpc_service in request.services ] get_service_key = lambda service_id: (service_id['contextId'], service_id['service_uuid']) service_dict : Dict[Tuple[str, str], Tuple[Dict, Service]] = { get_service_key(json_service['serviceId']): (json_service, grpc_service) for json_service,grpc_service in zip(service_list, request.services) } #LOGGER.info('service_dict = {:s}'.format(str(service_dict))) # TODO: consider filtering resources Loading @@ -71,6 +78,14 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): compose_device(grpc_device) for grpc_device in grpc_devices.devices ] endpoint_dict : Dict[str, Dict[str, Tuple[Dict, EndPointId]]] = { json_device['device_Id']: { json_endpoint['endpoint_id']['endpoint_uuid']: (json_endpoint['endpoint_id'], grpc_endpoint.endpoint_id) for json_endpoint,grpc_endpoint in zip(json_device['device_endpoints'], grpc_device.device_endpoints) } for json_device,grpc_device in zip(device_list, grpc_devices.devices) } #LOGGER.info('endpoint_dict = {:s}'.format(str(endpoint_dict))) grpc_links = context_client.ListLinks(Empty()) link_list = [ Loading @@ -95,31 +110,53 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): LOGGER.info('status_code={:s} reply={:s}'.format( str(reply.status_code), str(reply.content.decode('UTF-8')))) json_reply = reply.json() response_list = json_reply.get('response-list', []) reply = PathCompReply() # TODO: compose reply populating reply.services and reply.connections for response in response_list: service_key = get_service_key(response['serviceId']) tuple_service = service_dict.get(service_key) if tuple_service is None: raise Exception('ServiceKey({:s}) not found'.format(str(service_key))) json_service, grpc_service = tuple_service for service in request.services: # TODO: implement support for multi-point services service_endpoint_ids = service.service_endpoint_ids service_endpoint_ids = grpc_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 service = reply.services.add() service.CopyFrom(grpc_service) connection = reply.connections.add() connection.connection_id.connection_uuid.uuid = str(uuid.uuid4()) connection.service_id.CopyFrom(service.service_id) no_path_issue = response.get('noPath', {}).get('issue') if no_path_issue is not None: # no path found: leave connection with no endpoints # no_path_issue == 1 => no path due to a constraint continue service_paths = response['path'] for service_path in service_paths: # ... "path-capacity": {"total-size": {"value": 200, "unit": 0}}, # ... "path-latency": {"fixed-latency-characteristic": "10.000000"}, # ... "path-cost": {"cost-name": "", "cost-value": "5.000000", "cost-algorithm": "0.000000"}, #path_capacity = service_path['path-capacity']['total-size'] #path_capacity_value = path_capacity['value'] #path_capacity_unit = CapacityUnit(path_capacity['unit']) #path_latency = service_path['path-latency']['fixed-latency-characteristic'] #path_cost = service_path['path-cost'] #path_cost_name = path_cost['cost-name'] #path_cost_value = path_cost['cost-value'] #path_cost_algorithm = path_cost['cost-algorithm'] path_endpoints = service_path['devices'] for endpoint in path_endpoints: device_uuid = endpoint['device_id'] endpoint_uuid = endpoint['endpoint_uuid'] endpoint_id = connection.path_hops_endpoint_ids.add() endpoint_id.CopyFrom(endpoint_dict[device_uuid][endpoint_uuid][1]) LOGGER.info('[Compute] end ; reply = {:s}'.format(grpc_message_to_json_string(reply))) return reply
src/pathcomp/frontend/service/tools/ComposeRequest.py +2 −40 Original line number Diff line number Diff line Loading @@ -12,48 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. from enum import IntEnum from typing import Dict from common.proto.context_pb2 import Constraint, Device, EndPointId, Link, Service, ServiceId, TopologyId from common.tools.grpc.Tools import grpc_message_to_json_string class CapacityUnit(IntEnum): TB = 0 TBPS = 1 GB = 2 GBPS = 3 MB = 4 MBPS = 5 KB = 6 KBPS = 7 GHZ = 8 MHZ = 9 class LinkPortDirection(IntEnum): BIDIRECTIONAL = 0 INPUT = 1 OUTPUT = 2 UNKNOWN = 3 class TerminationDirection(IntEnum): BIDIRECTIONAL = 0 SINK = 1 SOURCE = 2 UNKNOWN = 3 class TerminationState(IntEnum): CAN_NEVER_TERMINATE = 0 NOT_TERMINATED = 1 TERMINATED_SERVER_TO_CLIENT = 2 TERMINATED_CLIENT_TO_SERVER = 3 TERMINATED_BIDIRECTIONAL = 4 PERMENANTLY_TERMINATED = 5 TERMINATION_STATE_UNKNOWN = 6 class LinkForwardingDirection(IntEnum): BIDIRECTIONAL = 0 UNIDIRECTIONAL = 1 UNKNOWN = 2 from .Constants import CapacityUnit, LinkForwardingDirection, LinkPortDirection, TerminationDirection, TerminationState def compose_topology_id(topology_id : TopologyId) -> Dict: context_uuid = topology_id.context_id.context_uuid.uuid Loading Loading @@ -127,7 +89,7 @@ def compose_link(grpc_link : Link) -> Dict: for link_endpoint_id in grpc_link.link_endpoint_ids ] forwarding_direction = LinkForwardingDirection.UNIDIRECTIONAL.value forwarding_direction = LinkForwardingDirection.BIDIRECTIONAL.value total_potential_capacity = compose_capacity(200, CapacityUnit.MBPS.value) available_capacity = compose_capacity(200, CapacityUnit.MBPS.value) cost_characteristics = compose_cost_characteristics('linkcost', '1', '0') Loading
src/pathcomp/frontend/service/tools/Constants.py 0 → 100644 +66 −0 Original line number Diff line number Diff line # Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from enum import IntEnum class CapacityUnit(IntEnum): TB = 0 TBPS = 1 GB = 2 GBPS = 3 MB = 4 MBPS = 5 KB = 6 KBPS = 7 GHZ = 8 MHZ = 9 CAPACITY_MULTIPLIER = { CapacityUnit.TB : 1.e12, CapacityUnit.TBPS : 1.e12, CapacityUnit.GB : 1.e9, CapacityUnit.GBPS : 1.e9, CapacityUnit.MB : 1.e6, CapacityUnit.MBPS : 1.e6, CapacityUnit.KB : 1.e3, CapacityUnit.KBPS : 1.e3, CapacityUnit.GHZ : 1.e9, CapacityUnit.MHZ : 1.e6, } class LinkPortDirection(IntEnum): BIDIRECTIONAL = 0 INPUT = 1 OUTPUT = 2 UNKNOWN = 3 class TerminationDirection(IntEnum): BIDIRECTIONAL = 0 SINK = 1 SOURCE = 2 UNKNOWN = 3 class TerminationState(IntEnum): CAN_NEVER_TERMINATE = 0 NOT_TERMINATED = 1 TERMINATED_SERVER_TO_CLIENT = 2 TERMINATED_CLIENT_TO_SERVER = 3 TERMINATED_BIDIRECTIONAL = 4 PERMENANTLY_TERMINATED = 5 TERMINATION_STATE_UNKNOWN = 6 class LinkForwardingDirection(IntEnum): BIDIRECTIONAL = 0 UNIDIRECTIONAL = 1 UNKNOWN = 2
src/pathcomp/frontend/tests/Objects.py +51 −23 Original line number Diff line number Diff line Loading @@ -21,10 +21,10 @@ from common.tools.object_factory.Link import get_link_uuid, json_link, json_link from common.tools.object_factory.Service import get_service_uuid, json_service_l3nm_planned 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, topology_id=None): device_id = json_device_id(device_uuid) 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_id) device = json_device_emulated_packet_router_disabled(device_uuid, endpoints=endpoints) return device_id, endpoints, device Loading @@ -45,32 +45,36 @@ CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID) CONTEXT = json_context(DEFAULT_CONTEXT_UUID) # ----- Domains -------------------------------------------------------------------------------------------------------- TOPOLOGY_ADMIN_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID) TOPOLOGY_ADMIN = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=CONTEXT_ID) TOPOLOGY_ADMIN_UUID = DEFAULT_TOPOLOGY_UUID TOPOLOGY_ADMIN_ID = json_topology_id(TOPOLOGY_ADMIN_UUID, context_id=CONTEXT_ID) TOPOLOGY_ADMIN = json_topology(TOPOLOGY_ADMIN_UUID, context_id=CONTEXT_ID) TOPOLOGY_A_ID = json_topology_id('A', context_id=CONTEXT_ID) TOPOLOGY_A = json_topology('A', context_id=CONTEXT_ID) TOPOLOGY_A_UUID = 'A' TOPOLOGY_A_ID = json_topology_id(TOPOLOGY_A_UUID, context_id=CONTEXT_ID) TOPOLOGY_A = json_topology(TOPOLOGY_A_UUID, context_id=CONTEXT_ID) TOPOLOGY_B_ID = json_topology_id('B', context_id=CONTEXT_ID) TOPOLOGY_B = json_topology('B', context_id=CONTEXT_ID) TOPOLOGY_B_UUID = 'B' TOPOLOGY_B_ID = json_topology_id(TOPOLOGY_B_UUID, context_id=CONTEXT_ID) TOPOLOGY_B = json_topology(TOPOLOGY_B_UUID, context_id=CONTEXT_ID) TOPOLOGY_C_ID = json_topology_id('C', context_id=CONTEXT_ID) TOPOLOGY_C = json_topology('C', context_id=CONTEXT_ID) TOPOLOGY_C_UUID = 'C' TOPOLOGY_C_ID = json_topology_id(TOPOLOGY_C_UUID, context_id=CONTEXT_ID) TOPOLOGY_C = json_topology(TOPOLOGY_C_UUID, context_id=CONTEXT_ID) # ----- Devices Domain A ----------------------------------------------------------------------------------------------- DEVICE_A1_ID, DEVICE_A1_ENDPOINTS, DEVICE_A1 = compose_device('A1', ['1', '2', '2000']) DEVICE_A2_ID, DEVICE_A2_ENDPOINTS, DEVICE_A2 = compose_device('A2', ['1', '2', '1001']) DEVICE_A3_ID, DEVICE_A3_ENDPOINTS, DEVICE_A3 = compose_device('A3', ['1', '2']) DEVICE_A1_ID, DEVICE_A1_ENDPOINTS, DEVICE_A1 = compose_device('A1', ['1', '2', '2000'], topology_id=TOPOLOGY_A_ID) DEVICE_A2_ID, DEVICE_A2_ENDPOINTS, DEVICE_A2 = compose_device('A2', ['1', '2', '1001'], topology_id=TOPOLOGY_A_ID) DEVICE_A3_ID, DEVICE_A3_ENDPOINTS, DEVICE_A3 = compose_device('A3', ['1', '2' ], topology_id=TOPOLOGY_A_ID) # ----- Devices Domain B ----------------------------------------------------------------------------------------------- DEVICE_B1_ID, DEVICE_B1_ENDPOINTS, DEVICE_B1 = compose_device('B1', ['1', '2', '2000']) DEVICE_B2_ID, DEVICE_B2_ENDPOINTS, DEVICE_B2 = compose_device('B2', ['1', '2', '1002']) DEVICE_B3_ID, DEVICE_B3_ENDPOINTS, DEVICE_B3 = compose_device('B3', ['1', '2']) DEVICE_B1_ID, DEVICE_B1_ENDPOINTS, DEVICE_B1 = compose_device('B1', ['1', '2', '2000'], topology_id=TOPOLOGY_B_ID) DEVICE_B2_ID, DEVICE_B2_ENDPOINTS, DEVICE_B2 = compose_device('B2', ['1', '2', '1002'], topology_id=TOPOLOGY_B_ID) DEVICE_B3_ID, DEVICE_B3_ENDPOINTS, DEVICE_B3 = compose_device('B3', ['1', '2' ], topology_id=TOPOLOGY_B_ID) # ----- Devices Domain C ----------------------------------------------------------------------------------------------- DEVICE_C1_ID, DEVICE_C1_ENDPOINTS, DEVICE_C1 = compose_device('C1', ['1', '2', '1001']) DEVICE_C2_ID, DEVICE_C2_ENDPOINTS, DEVICE_C2 = compose_device('C2', ['1', '2']) DEVICE_C3_ID, DEVICE_C3_ENDPOINTS, DEVICE_C3 = compose_device('C3', ['1', '2', '1002']) DEVICE_C1_ID, DEVICE_C1_ENDPOINTS, DEVICE_C1 = compose_device('C1', ['1', '2', '1001'], topology_id=TOPOLOGY_C_ID) DEVICE_C2_ID, DEVICE_C2_ENDPOINTS, DEVICE_C2 = compose_device('C2', ['1', '2' ], topology_id=TOPOLOGY_C_ID) DEVICE_C3_ID, DEVICE_C3_ENDPOINTS, DEVICE_C3 = compose_device('C3', ['1', '2', '1002'], topology_id=TOPOLOGY_C_ID) # ----- InterDomain Links ---------------------------------------------------------------------------------------------- LINK_A2_C3_ID, LINK_A2_C3 = compose_link(DEVICE_A2_ENDPOINTS[2], DEVICE_C3_ENDPOINTS[2]) Loading @@ -94,7 +98,7 @@ LINK_C2_C3_ID, LINK_C2_C3 = compose_link(DEVICE_C2_ENDPOINTS[1], DEVICE_C3_ENDPO # ----- Service -------------------------------------------------------------------------------------------------------- SERVICE_A1_B1 = compose_service(DEVICE_A1_ENDPOINTS[2], DEVICE_B1_ENDPOINTS[2], constraints=[ json_constraint('bandwidth[gbps]', 10.0), json_constraint('latency[ms]', 5.0), json_constraint('latency[ms]', 12.0), ]) # ----- Containers ----------------------------------------------------------------------------------------------------- Loading @@ -108,3 +112,27 @@ LINKS = [ LINK_A2_C3, LINK_C1_B2, LINK_B1_B2, LINK_B1_B3, LINK_B2_B3, LINK_C1_C2, LINK_C1_C3, LINK_C2_C3, ] SERVICES = [ SERVICE_A1_B1] OBJECTS_PER_TOPOLOGY = [ (TOPOLOGY_ADMIN_ID, [ DEVICE_A1_ID, DEVICE_A2_ID, DEVICE_A3_ID, DEVICE_B1_ID, DEVICE_B2_ID, DEVICE_B3_ID, DEVICE_C1_ID, DEVICE_C2_ID, DEVICE_C3_ID, ], [ LINK_A2_C3_ID, LINK_C1_B2_ID, LINK_A1_A2_ID, LINK_A1_A3_ID, LINK_A2_A3_ID, LINK_B1_B2_ID, LINK_B1_B3_ID, LINK_B2_B3_ID, LINK_C1_C2_ID, LINK_C1_C3_ID, LINK_C2_C3_ID, ], ), (TOPOLOGY_A_ID, [ DEVICE_A1_ID, DEVICE_A2_ID, DEVICE_A3_ID, ], [ LINK_A1_A2_ID, LINK_A1_A3_ID, LINK_A2_A3_ID, ], ), (TOPOLOGY_B_ID, [ DEVICE_B1_ID, DEVICE_B2_ID, DEVICE_B3_ID, ], [ LINK_B1_B2_ID, LINK_B1_B3_ID, LINK_B2_B3_ID, ], ), (TOPOLOGY_C_ID, [ DEVICE_C1_ID, DEVICE_C2_ID, DEVICE_C3_ID, ], [ LINK_C1_C2_ID, LINK_C1_C3_ID, LINK_C2_C3_ID, ], ), ]
src/pathcomp/frontend/tests/test_unitary.py +18 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ from common.tools.grpc.Tools import grpc_message_to_json from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from pathcomp.frontend.client.PathCompClient import PathCompClient from .Objects import CONTEXTS, DEVICES, LINKS, SERVICES, TOPOLOGIES from .Objects import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES from .PrepareTestScenario import ( # pylint: disable=unused-import # be careful, order of symbols is important here! mock_service, pathcomp_service, context_client, device_client, pathcomp_client) Loading @@ -37,6 +37,23 @@ def test_prepare_environment( for device in DEVICES : device_client .AddDevice (Device (**device )) for link in LINKS : context_client.SetLink (Link (**link )) for topology_id, device_ids, link_ids in OBJECTS_PER_TOPOLOGY: topology = Topology() topology.CopyFrom(context_client.GetTopology(TopologyId(**topology_id))) device_ids_in_topology = {device_id.device_uuid.uuid for device_id in topology.device_ids} func_device_id_not_added = lambda device_id: device_id['device_uuid']['uuid'] not in device_ids_in_topology func_device_id_json_to_grpc = lambda device_id: DeviceId(**device_id) device_ids_to_add = list(map(func_device_id_json_to_grpc, filter(func_device_id_not_added, device_ids))) topology.device_ids.extend(device_ids_to_add) link_ids_in_topology = {link_id.link_uuid.uuid for link_id in topology.link_ids} func_link_id_not_added = lambda link_id: link_id['link_uuid']['uuid'] not in link_ids_in_topology func_link_id_json_to_grpc = lambda link_id: LinkId(**link_id) link_ids_to_add = list(map(func_link_id_json_to_grpc, filter(func_link_id_not_added, link_ids))) topology.link_ids.extend(link_ids_to_add) context_client.SetTopology(topology) def test_request_service( pathcomp_client : PathCompClient): # pylint: disable=redefined-outer-name Loading