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