diff --git a/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py b/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py index 3c37036499e693b1a25760adde54ba12404da1d2..205306d0ec2d156a2050d1f95c5c1e990796e018 100644 --- a/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py +++ b/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py @@ -20,12 +20,11 @@ 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.context_queries.Device import get_devices_in_topology from common.tools.context_queries.Link import get_links_in_topology -from common.tools.context_queries.InterDomain import is_multi_domain +from common.tools.context_queries.InterDomain import is_inter_domain from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Context import json_context_id from context.client.ContextClient import ContextClient from pathcomp.frontend.service.algorithms.Factory import get_algorithm -from pathcomp.frontend.service.algorithms.ShortestPathAlgorithm import ShortestPathAlgorithm LOGGER = logging.getLogger(__name__) @@ -46,7 +45,7 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): context_client = ContextClient() - if (len(request.services) == 1) and is_multi_domain(context_client, request.services[0].service_endpoint_ids): + if (len(request.services) == 1) and is_inter_domain(context_client, request.services[0].service_endpoint_ids): devices = get_devices_in_topology(context_client, ADMIN_CONTEXT_ID, INTERDOMAIN_TOPOLOGY_UUID) links = get_links_in_topology(context_client, ADMIN_CONTEXT_ID, INTERDOMAIN_TOPOLOGY_UUID) else: diff --git a/src/pathcomp/frontend/service/algorithms/ShortestPathAlgorithm.py b/src/pathcomp/frontend/service/algorithms/ShortestPathAlgorithm.py index d5f937fd207807ba650669ea9fb2395b2e21b164..e0a2441823627843f1e14bde905da4f82ed7a593 100644 --- a/src/pathcomp/frontend/service/algorithms/ShortestPathAlgorithm.py +++ b/src/pathcomp/frontend/service/algorithms/ShortestPathAlgorithm.py @@ -12,15 +12,42 @@ # See the License for the specific language governing permissions and # limitations under the License. -from common.proto.pathcomp_pb2 import Algorithm_ShortestPath +from typing import Dict, Optional +from common.proto.pathcomp_pb2 import Algorithm_ShortestPath, PathCompRequest from ._Algorithm import _Algorithm class ShortestPathAlgorithm(_Algorithm): def __init__(self, algorithm : Algorithm_ShortestPath, class_name=__name__) -> None: super().__init__('SP', False, class_name=class_name) - def add_service_requests(self, requested_services) -> None: - super().add_service_requests(requested_services) + def add_service_requests(self, request : PathCompRequest) -> None: + super().add_service_requests(request) for service_request in self.service_list: service_request['algId' ] = self.algorithm_id service_request['syncPaths'] = self.sync_paths + + def _single_device_request(self) -> Optional[Dict]: + if len(self.service_list) != 1: return None + service = self.service_list[0] + endpoint_ids = service['service_endpoints_ids'] + if len(endpoint_ids) != 2: return None + if endpoint_ids[0]['device_id'] != endpoint_ids[-1]['device_id']: return None + return {'response-list': [{ + 'serviceId': service['serviceId'], + 'service_endpoints_ids': [endpoint_ids[0], endpoint_ids[-1]], + 'path': [{ + # not used by now + #'path-capacity': {'total-size': {'value': 200, 'unit': 0}}, + #'path-latency': {'fixed-latency-characteristic': '2.000000'}, + #'path-cost': {'cost-name': '', 'cost-value': '1.000000', 'cost-algorithm': '0.000000'}, + 'devices': [endpoint_ids[0], endpoint_ids[-1]] + }] + }]} + + def execute(self, dump_request_filename : Optional[str] = None, dump_reply_filename : Optional[str] = None) -> None: + # if request is composed of a single service with single device (not supported by backend), + # produce synthetic reply directly + self.json_reply = self._single_device_request() + if self.json_reply is None: + # otherwise, follow normal logic through the backend + return super().execute(dump_request_filename, dump_reply_filename) diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py index dfd657467e19bacdf3d6c8023a60c9a87cfacdc6..b0db01749902dff1d65a2f1d06c51f2fd9a05d5a 100644 --- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py +++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py @@ -23,7 +23,8 @@ from pathcomp.frontend.Config import BACKEND_URL from pathcomp.frontend.service.algorithms.tools.ConstantsMappings import DEVICE_LAYER_TO_SERVICE_TYPE, DeviceLayerEnum from .tools.EroPathToHops import eropath_to_hops from .tools.ComposeRequest import compose_device, compose_link, compose_service -from .tools.ComputeSubServices import convert_explicit_path_hops_to_connections +from .tools.ComputeSubServices import ( + convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection) class _Algorithm: def __init__(self, algorithm_id : str, sync_paths : bool, class_name=__name__) -> None: @@ -204,7 +205,12 @@ class _Algorithm: for service_path_ero in response['path']: path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict) - connections = convert_explicit_path_hops_to_connections(path_hops, self.device_dict, service_uuid) + try: + connections = convert_explicit_path_hops_to_connections(path_hops, self.device_dict, service_uuid) + except: # pylint: disable=bare-except + # if not able to extrapolate sub-services and sub-connections, + # assume single service and single connection + connections = convert_explicit_path_hops_to_plain_connection(path_hops, service_uuid) for connection in connections: connection_uuid,device_layer,path_hops,_ = connection diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py index c1977cedb9b341fbb767a5fb8c829cd5f633884c..17a7e74ef573e4926d53045ab8888c71a3dd73d7 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py @@ -34,9 +34,11 @@ def compose_topology_id(topology_id : TopologyId) -> Dict: return {'contextId': context_uuid, 'topology_uuid': topology_uuid} def compose_service_id(service_id : ServiceId) -> Dict: - context_uuid = service_id.context_id.context_uuid.uuid - - if len(context_uuid) == 0: context_uuid = DEFAULT_CONTEXT_UUID + # force context_uuid to be always DEFAULT_CONTEXT_UUID for simplicity + # for interdomain contexts are managed in a particular way + #context_uuid = service_id.context_id.context_uuid.uuid + #if len(context_uuid) == 0: context_uuid = DEFAULT_CONTEXT_UUID + context_uuid = DEFAULT_CONTEXT_UUID service_uuid = service_id.service_uuid.uuid return {'contextId': context_uuid, 'service_uuid': service_uuid} diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py index f2c66cb24ca3c15c71f22dbe4eeca634e18d985a..7c7b62e2d039d2e6bad979b3601e09ca1c54ea51 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py @@ -94,3 +94,19 @@ def convert_explicit_path_hops_to_connections( connections.append(connection_stack.get()) assert connection_stack.empty() return connections + +def convert_explicit_path_hops_to_plain_connection( + path_hops : List[Dict], main_connection_uuid : str +) -> List[Tuple[str, DeviceLayerEnum, List[str], List[str]]]: + + connection : Tuple[str, DeviceLayerEnum, List[str], List[str]] = \ + (main_connection_uuid, DeviceLayerEnum.PACKET_DEVICE, [], []) + + last_device_uuid = None + for path_hop in path_hops: + device_uuid = path_hop['device'] + if last_device_uuid == device_uuid: continue + connection[2].append(path_hop) + last_device_uuid = device_uuid + + return [connection] diff --git a/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py b/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py index 021940937c23a7cb461a603aa32a15f16626eb1d..a885ddb29c3fa70d6bccea18f43fef5b038aae68 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py +++ b/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py @@ -42,35 +42,43 @@ # ] # +import logging from typing import Dict, List +LOGGER = logging.getLogger(__name__) + def eropath_to_hops(ero_path : List[Dict], endpoint_to_link_dict : Dict) -> List[Dict]: - path_hops = [] - for endpoint in ero_path: - device_uuid = endpoint['device_id'] - endpoint_uuid = endpoint['endpoint_uuid'] + try: + path_hops = [] + for endpoint in ero_path: + device_uuid = endpoint['device_id'] + endpoint_uuid = endpoint['endpoint_uuid'] - if len(path_hops) == 0: - path_hops.append({'device': device_uuid, 'ingress_ep': endpoint_uuid}) - continue + if len(path_hops) == 0: + path_hops.append({'device': device_uuid, 'ingress_ep': endpoint_uuid}) + continue - last_hop = path_hops[-1] - if (last_hop['device'] == device_uuid): - if ('ingress_ep' not in last_hop) or ('egress_ep' in last_hop): continue - last_hop['egress_ep'] = endpoint_uuid - continue + last_hop = path_hops[-1] + if (last_hop['device'] == device_uuid): + if ('ingress_ep' not in last_hop) or ('egress_ep' in last_hop): continue + last_hop['egress_ep'] = endpoint_uuid + continue - endpoint_key = (last_hop['device'], last_hop['egress_ep']) - link_tuple = endpoint_to_link_dict.get(endpoint_key) - ingress = next(iter([ - ep_id for ep_id in link_tuple[0]['link_endpoint_ids'] - if (ep_id['endpoint_id']['device_id'] == device_uuid) and\ - (ep_id['endpoint_id']['endpoint_uuid'] != endpoint_uuid) - ]), None) - if ingress['endpoint_id']['device_id'] != device_uuid: raise Exception('Malformed path') - path_hops.append({ - 'device': ingress['endpoint_id']['device_id'], - 'ingress_ep': ingress['endpoint_id']['endpoint_uuid'], - 'egress_ep': endpoint_uuid, - }) - return path_hops + endpoint_key = (last_hop['device'], last_hop['egress_ep']) + link_tuple = endpoint_to_link_dict.get(endpoint_key) + ingress = next(iter([ + ep_id for ep_id in link_tuple[0]['link_endpoint_ids'] + if (ep_id['endpoint_id']['device_id'] == device_uuid) and\ + (ep_id['endpoint_id']['endpoint_uuid'] != endpoint_uuid) + ]), None) + if ingress['endpoint_id']['device_id'] != device_uuid: raise Exception('Malformed path') + path_hops.append({ + 'device': ingress['endpoint_id']['device_id'], + 'ingress_ep': ingress['endpoint_id']['endpoint_uuid'], + 'egress_ep': endpoint_uuid, + }) + return path_hops + except: + LOGGER.exception('Unhandled exception: ero_path={:s} endpoint_to_link_dict={:s}'.format( + str(ero_path), str(endpoint_to_link_dict))) + raise