From 6161b92a0b6d9bee9bcc7fc8183c0d1be15c4d34 Mon Sep 17 00:00:00 2001 From: hajipour <shajipour@cttc.es> Date: Mon, 17 Mar 2025 15:04:49 +0100 Subject: [PATCH 1/3] debug/refactor: - sub service creation code changed to avoid new sub service creation when multi-domain services are updated - sub service creation code refactored with dataclass addition --- .../algorithms/tools/ComputeSubServices.py | 76 +++++++++++-------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py index dfcd1156b..728bb58c8 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py @@ -45,24 +45,39 @@ # ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) # ] -import logging, queue, uuid +import logging, queue +from dataclasses import dataclass, field from typing import Dict, List, Optional, Tuple from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import Device, ServiceTypeEnum +from common.tools.context_queries.Slice import get_uuid_from_string from .ResourceGroups import IGNORED_DEVICE_TYPES, REMOTEDOMAIN_DEVICE_TYPES, get_resource_classification from .ServiceTypes import get_service_type LOGGER = logging.getLogger(__name__) +@dataclass +class ConnectionEntry: + uuid: str = '' + service_type : ServiceTypeEnum = ServiceTypeEnum.SERVICETYPE_UNKNOWN + path_hops : List[Dict] = field(default_factory=list) + dependencies : List[str] = field(default_factory=list) + + def __post_init__(self) -> None: + if self.uuid != '': + return + composed_string = '-'.join(f'{path_hop["device"]}/{path_hop["ingress_ep"]}/{path_hop["egress_ep"]}' for path_hop in self.path_hops) + self.sub_service_uuid = get_uuid_from_string(composed_string) + def convert_explicit_path_hops_to_connections( path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], main_service_uuid : str, main_service_type : ServiceTypeEnum -) -> List[Tuple[str, int, List[str], List[str]]]: +) -> List[Tuple[str, int, List[Dict], List[str]]]: LOGGER.debug('path_hops={:s}'.format(str(path_hops))) connection_stack = queue.LifoQueue() - connections : List[Tuple[str, int, List[str], List[str]]] = list() + connections : List[ConnectionEntry] = list() prv_device_uuid = None prv_res_class : Tuple[Optional[int], Optional[DeviceTypeEnum], Optional[str]] = None, None, None @@ -85,82 +100,83 @@ def convert_explicit_path_hops_to_connections( LOGGER.debug(' create and terminate underlying connection') # create underlying connection - sub_service_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] + prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) - connection_stack.put((sub_service_uuid, service_type, [path_hop], [])) + connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_stack.put(connection_entry) # underlying connection ended - connection = connection_stack.get() + connection: ConnectionEntry = connection_stack.get() connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) + connection_stack.queue[-1].dependencies.append(connection.uuid) #connection_stack.queue[-1][2].append(path_hop) elif prv_res_class[2] is None and res_class[2] is not None: # entering domain of a device controller, create underlying connection LOGGER.debug(' entering domain of a device controller, create underlying connection') - sub_service_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] + prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) - connection_stack.put((sub_service_uuid, service_type, [path_hop], [])) + connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_stack.put(connection_entry) elif prv_res_class[2] is not None and res_class[2] is None: # leaving domain of a device controller, terminate underlying connection LOGGER.debug(' leaving domain of a device controller, terminate underlying connection') connection = connection_stack.get() connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) - connection_stack.queue[-1][2].append(path_hop) + connection_stack.queue[-1].dependencies.append(connection.uuid) + connection_stack.queue[-1].path_hops.append(path_hop) elif prv_res_class[2] is not None and res_class[2] is not None: if prv_res_class[2] == res_class[2]: # stay in domain of a device controller, connection continues LOGGER.debug(' stay in domain of a device controller, connection continues') - connection_stack.queue[-1][2].append(path_hop) + connection_stack.queue[-1].path_hops.append(path_hop) else: # switching to different device controller, chain connections LOGGER.debug(' switching to different device controller, chain connections') connection = connection_stack.get() connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) + connection_stack.queue[-1].dependencies.append(connection.uuid) - sub_service_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] + prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) - connection_stack.put((sub_service_uuid, service_type, [path_hop], [])) + connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_stack.put(connection_entry) elif prv_res_class[0] is None: # path ingress LOGGER.debug(' path ingress') - connection_stack.put((main_service_uuid, main_service_type, [path_hop], [])) + connection_entry = ConnectionEntry(uuid=main_service_uuid, service_type=main_service_type, path_hops=[path_hop]) + connection_stack.put(connection_entry) elif prv_res_class[0] > res_class[0]: # create underlying connection LOGGER.debug(' create underlying connection') - sub_service_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] + prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) - connection_stack.put((sub_service_uuid, service_type, [path_hop], [])) + connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_stack.put(connection_entry) elif prv_res_class[0] == res_class[0]: # same resource group kind LOGGER.debug(' same resource group kind') if prv_res_class[1] == res_class[1] and prv_res_class[2] == res_class[2]: # same device type and device controller: connection continues LOGGER.debug(' connection continues') - connection_stack.queue[-1][2].append(path_hop) + connection_stack.queue[-1].path_hops.append(path_hop) else: # different device type or device controller: chain connections LOGGER.debug(' chain connections') connection = connection_stack.get() connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) + connection_stack.queue[-1].dependencies.append(connection.uuid) - sub_service_uuid = str(uuid.uuid4()) - prv_service_type = connection_stack.queue[-1][1] + prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) - connection_stack.put((sub_service_uuid, service_type, [path_hop], [])) + connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_stack.put(connection_entry) elif prv_res_class[0] < res_class[0]: # underlying connection ended LOGGER.debug(' underlying connection ended') connection = connection_stack.get() connections.append(connection) - connection_stack.queue[-1][3].append(connection[0]) - connection_stack.queue[-1][2].append(path_hop) + connection_stack.queue[-1].dependencies.append(connection.uuid) + connection_stack.queue[-1].path_hops.append(path_hop) else: raise Exception('Uncontrolled condition') @@ -172,7 +188,7 @@ def convert_explicit_path_hops_to_connections( connections.append(connection_stack.get()) LOGGER.debug('connections={:s}'.format(str(connections))) assert connection_stack.empty() - return connections + return [(c.uuid, c.service_type, c.path_hops, c.dependencies) for c in connections] def convert_explicit_path_hops_to_plain_connection( path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum -- GitLab From 9f1b13b1cd7a40fab36332ef6a3b5837aa61c563 Mon Sep 17 00:00:00 2001 From: hajipour <shajipour@cttc.es> Date: Mon, 17 Mar 2025 18:33:40 +0100 Subject: [PATCH 2/3] debug: main service uuid added to the prefix of the hash function of the subservices, __post_init__ changed to a method tha is called in the code --- .../algorithms/tools/ComputeSubServices.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py index 728bb58c8..053e6d542 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py @@ -63,11 +63,11 @@ class ConnectionEntry: path_hops : List[Dict] = field(default_factory=list) dependencies : List[str] = field(default_factory=list) - def __post_init__(self) -> None: - if self.uuid != '': - return - composed_string = '-'.join(f'{path_hop["device"]}/{path_hop["ingress_ep"]}/{path_hop["egress_ep"]}' for path_hop in self.path_hops) - self.sub_service_uuid = get_uuid_from_string(composed_string) + def calculate_subservice_uuid(self, main_service_uuid: str) -> None: + composed_string = main_service_uuid + '-'.join( + f'{path_hop["device"]}/{path_hop["ingress_ep"]}/{path_hop["egress_ep"]}' for path_hop in self.path_hops + ) + self.uuid = get_uuid_from_string(composed_string) def convert_explicit_path_hops_to_connections( path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], @@ -103,6 +103,7 @@ def convert_explicit_path_hops_to_connections( prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_entry.calculate_subservice_uuid(main_service_uuid) connection_stack.put(connection_entry) # underlying connection ended @@ -116,6 +117,7 @@ def convert_explicit_path_hops_to_connections( prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_entry.calculate_subservice_uuid(main_service_uuid) connection_stack.put(connection_entry) elif prv_res_class[2] is not None and res_class[2] is None: # leaving domain of a device controller, terminate underlying connection @@ -139,6 +141,7 @@ def convert_explicit_path_hops_to_connections( prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_entry.calculate_subservice_uuid (main_service_uuid) connection_stack.put(connection_entry) elif prv_res_class[0] is None: # path ingress @@ -151,6 +154,7 @@ def convert_explicit_path_hops_to_connections( prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_entry.calculate_subservice_uuid(main_service_uuid) connection_stack.put(connection_entry) elif prv_res_class[0] == res_class[0]: # same resource group kind @@ -169,6 +173,7 @@ def convert_explicit_path_hops_to_connections( prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) + connection_entry.calculate_subservice_uuid(main_service_uuid) connection_stack.put(connection_entry) elif prv_res_class[0] < res_class[0]: # underlying connection ended -- GitLab From 681e6276696c7ecb543d65acfcfac3c6696bbdb8 Mon Sep 17 00:00:00 2001 From: hajipour <shajipour@cttc.es> Date: Tue, 18 Mar 2025 17:41:56 +0100 Subject: [PATCH 3/3] debug & refactor: ConnectionEntry added as a dependency of the ConnectionEntry --- .../algorithms/tools/ComputeSubServices.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py index 053e6d542..7cd419c6b 100644 --- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py +++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py @@ -61,9 +61,11 @@ class ConnectionEntry: uuid: str = '' service_type : ServiceTypeEnum = ServiceTypeEnum.SERVICETYPE_UNKNOWN path_hops : List[Dict] = field(default_factory=list) - dependencies : List[str] = field(default_factory=list) + dependencies : List['ConnectionEntry'] = field(default_factory=list) def calculate_subservice_uuid(self, main_service_uuid: str) -> None: + if self.uuid: + return composed_string = main_service_uuid + '-'.join( f'{path_hop["device"]}/{path_hop["ingress_ep"]}/{path_hop["egress_ep"]}' for path_hop in self.path_hops ) @@ -103,28 +105,25 @@ def convert_explicit_path_hops_to_connections( prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) - connection_entry.calculate_subservice_uuid(main_service_uuid) connection_stack.put(connection_entry) # underlying connection ended connection: ConnectionEntry = connection_stack.get() connections.append(connection) - connection_stack.queue[-1].dependencies.append(connection.uuid) - #connection_stack.queue[-1][2].append(path_hop) + connection_stack.queue[-1].dependencies.append(connection) elif prv_res_class[2] is None and res_class[2] is not None: # entering domain of a device controller, create underlying connection LOGGER.debug(' entering domain of a device controller, create underlying connection') prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) - connection_entry.calculate_subservice_uuid(main_service_uuid) connection_stack.put(connection_entry) elif prv_res_class[2] is not None and res_class[2] is None: # leaving domain of a device controller, terminate underlying connection LOGGER.debug(' leaving domain of a device controller, terminate underlying connection') connection = connection_stack.get() connections.append(connection) - connection_stack.queue[-1].dependencies.append(connection.uuid) + connection_stack.queue[-1].dependencies.append(connection) connection_stack.queue[-1].path_hops.append(path_hop) elif prv_res_class[2] is not None and res_class[2] is not None: if prv_res_class[2] == res_class[2]: @@ -136,12 +135,11 @@ def convert_explicit_path_hops_to_connections( LOGGER.debug(' switching to different device controller, chain connections') connection = connection_stack.get() connections.append(connection) - connection_stack.queue[-1].dependencies.append(connection.uuid) + connection_stack.queue[-1].dependencies.append(connection) prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) - connection_entry.calculate_subservice_uuid (main_service_uuid) connection_stack.put(connection_entry) elif prv_res_class[0] is None: # path ingress @@ -154,7 +152,6 @@ def convert_explicit_path_hops_to_connections( prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) - connection_entry.calculate_subservice_uuid(main_service_uuid) connection_stack.put(connection_entry) elif prv_res_class[0] == res_class[0]: # same resource group kind @@ -168,19 +165,18 @@ def convert_explicit_path_hops_to_connections( LOGGER.debug(' chain connections') connection = connection_stack.get() connections.append(connection) - connection_stack.queue[-1].dependencies.append(connection.uuid) + connection_stack.queue[-1].dependencies.append(connection) prv_service_type = connection_stack.queue[-1].service_type service_type = get_service_type(res_class[1], prv_service_type) connection_entry = ConnectionEntry(service_type=service_type, path_hops=[path_hop]) - connection_entry.calculate_subservice_uuid(main_service_uuid) connection_stack.put(connection_entry) elif prv_res_class[0] < res_class[0]: # underlying connection ended LOGGER.debug(' underlying connection ended') connection = connection_stack.get() connections.append(connection) - connection_stack.queue[-1].dependencies.append(connection.uuid) + connection_stack.queue[-1].dependencies.append(connection) connection_stack.queue[-1].path_hops.append(path_hop) else: raise Exception('Uncontrolled condition') @@ -193,7 +189,9 @@ def convert_explicit_path_hops_to_connections( connections.append(connection_stack.get()) LOGGER.debug('connections={:s}'.format(str(connections))) assert connection_stack.empty() - return [(c.uuid, c.service_type, c.path_hops, c.dependencies) for c in connections] + for c in connections: + c.calculate_subservice_uuid(main_service_uuid) + return [(c.uuid, c.service_type, c.path_hops, [cd.uuid for cd in c.dependencies]) for c in connections] def convert_explicit_path_hops_to_plain_connection( path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum -- GitLab