From ef25a3f8250298ed39f5647b0278ff02ec802cf5 Mon Sep 17 00:00:00 2001
From: camposl <leandro.camposhernaez.practicas@telefonica.com>
Date: Wed, 12 Mar 2025 12:31:58 +0100
Subject: [PATCH] Service

---
 src/device/service/Tools.py                   |  19 +++
 .../frontend/service/algorithms/_Algorithm.py |   6 +-
 .../algorithms/tools/ComposeConfigRules.py    | 121 ++++++++++++------
 .../service/algorithms/tools/ServiceTypes.py  |  11 +-
 .../java/org/etsi/tfs/policy/Serializer.java  |   4 +-
 .../policy/context/model/ServiceTypeEnum.java |   3 +-
 .../grpc/context/ContextOuterClass.java       |   1 +
 7 files changed, 114 insertions(+), 51 deletions(-)

diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py
index ee6838536..9164d050a 100644
--- a/src/device/service/Tools.py
+++ b/src/device/service/Tools.py
@@ -313,6 +313,15 @@ def compute_rules_to_add_delete(
             ACL_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/acl_ruleset[{:s}]'
             key_or_path = ACL_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, acl_ruleset_name)            
             context_config_rules[key_or_path] = grpc_message_to_json(config_rule.acl)    # get the resource value of the acl
+
+            
+        elif config_rule_kind == 'pon_access':
+            device_uuid = config_rule.pon_access.endpoint_id.device_id.device_uuid.uuid # get the device name
+            endpoint_uuid = config_rule.pon_access.endpoint_id.endpoint_uuid.uuid       # get the endpoint name    request_config_rules = []
+            pon_access_ruleset_name = config_rule.pon_access.rule_set.name                     # get the pon_access name
+            PON_ACCESS_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/pon_access_ruleset[{:s}]'
+            key_or_path = PON_ACCESS_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, pon_access_ruleset_name)            
+            context_config_rules[key_or_path] = grpc_message_to_json(config_rule.pon_access)    # get the resource value of the pon_access
  
     request_config_rules = []
     for config_rule in request.device_config.config_rules:
@@ -331,6 +340,16 @@ def compute_rules_to_add_delete(
                 config_rule.action, key_or_path, grpc_message_to_json(config_rule.acl)
             ))
 
+        
+        elif config_rule_kind == 'pon_access':  # resource management of "pon_access" rule  
+            device_uuid = config_rule.pon_access.endpoint_id.device_id.device_uuid.uuid
+            endpoint_uuid = config_rule.pon_access.endpoint_id.endpoint_uuid.uuid
+            PON_ACCESS_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/pon_access_ruleset'
+            key_or_path = PON_ACCESS_KEY_TEMPLATE.format(device_uuid, endpoint_uuid) 
+            request_config_rules.append((
+                config_rule.action, key_or_path, grpc_message_to_json(config_rule.pon_access)
+            ))
+
     resources_to_set    : List[Tuple[str, Any]] = [] # key, value
     resources_to_delete : List[Tuple[str, Any]] = [] # key, value
 
diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py
index ea54f7797..b5f5a9300 100644
--- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py
+++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py
@@ -24,7 +24,7 @@ from pathcomp.frontend.Config import BACKEND_URL
 from .tools.EroPathToHops import eropath_to_hops
 from .tools.ComposeConfigRules import (
     compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules,
-    generate_neighbor_endpoint_config_rules
+    generate_neighbor_endpoint_config_rules, compose_pon_access_config_rules
 )
 from .tools.ComposeRequest import compose_device, compose_link, compose_service
 from .tools.ComputeSubServices import (
@@ -183,6 +183,10 @@ class _Algorithm:
             compose_l3nm_config_rules(config_rules, service.service_config.config_rules)
         elif service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE:
             compose_tapi_config_rules(config_rules, service.service_config.config_rules)
+        elif service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE:
+            compose_tapi_config_rules(config_rules, service.service_config.config_rules)
+        elif service_type == ServiceTypeEnum.SERVICETYPE_PON_ACCESS:
+            compose_pon_access_config_rules(config_rules, service.service_config.config_rules)
         else:
             MSG = 'Unhandled generic Config Rules for service {:s} {:s}'
             self.logger.warning(MSG.format(str(service_uuid), str(ServiceTypeEnum.Name(service_type))))
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
index 9eac4d353..961613505 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
@@ -108,6 +108,12 @@ def compose_tapi_config_rules(main_service_config_rules : List, subservice_confi
     for rule_name, defaults in CONFIG_RULES:
         compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults)
 
+
+def compose_pon_access_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None:
+    CONFIG_RULES: List[Tuple[str, dict]] = [(SETTINGS_RULE_NAME)]
+    for rule_name, defaults in CONFIG_RULES:
+        compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults)
+
 def compose_device_config_rules(
     config_rules : List, subservice_config_rules : List, path_hops : List,
     device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str]
@@ -157,6 +163,30 @@ def compose_device_config_rules(
             LOGGER.debug('[compose_device_config_rules]   adding acl config rule')
             subservice_config_rules.append(config_rule)
 
+        elif config_rule.WhichOneof('config_rule') == 'pon_access':
+            LOGGER.debug('[compose_device_config_rules]   is pon_access')
+            endpoint_id = config_rule.pon_access.endpoint_id
+            device_uuid_or_name = endpoint_id.device_id.device_uuid.uuid
+            LOGGER.debug('[compose_device_config_rules]   device_uuid_or_name={:s}'.format(str(device_uuid_or_name)))
+            device_name_or_uuid = device_name_mapping.get(device_uuid_or_name, device_uuid_or_name)
+            LOGGER.debug('[compose_device_config_rules]   device_name_or_uuid={:s}'.format(str(device_name_or_uuid)))
+            device_keys = {device_uuid_or_name, device_name_or_uuid}
+            if len(device_keys.intersection(devices_traversed)) == 0: continue
+
+            endpoint_uuid = endpoint_id.endpoint_uuid.uuid
+            LOGGER.debug('[compose_device_config_rules]   endpoint_uuid={:s}'.format(str(endpoint_uuid)))
+            endpoint_uuid_or_name = (endpoint_uuid[::-1].split('.', maxsplit=1)[-1])[::-1]
+            LOGGER.debug('[compose_device_config_rules]   endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name)))
+            endpoint_name_or_uuid_1 = endpoint_name_mapping[(device_uuid_or_name, endpoint_uuid_or_name)]
+            endpoint_name_or_uuid_2 = endpoint_name_mapping[(device_name_or_uuid, endpoint_uuid_or_name)]
+            endpoint_keys = {endpoint_uuid_or_name, endpoint_name_or_uuid_1, endpoint_name_or_uuid_2}
+
+            device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys))
+            if len(device_endpoint_keys.intersection(endpoints_traversed)) == 0: continue
+
+            LOGGER.debug('[compose_device_config_rules]   adding pon_access config rule')
+            subservice_config_rules.append(config_rule)
+
         elif config_rule.WhichOneof('config_rule') == 'custom':
             LOGGER.debug('[compose_device_config_rules]   is custom')
 
@@ -292,49 +322,56 @@ def generate_neighbor_endpoint_config_rules(
 
         for config_rule in config_rules:
             # Only applicable, by now, to Custom Config Rules for endpoint settings
-            if 'custom' not in config_rule: continue
-            match = RE_ENDPOINT_SETTINGS.match(config_rule['custom']['resource_key'])
-            if match is None:
-                match = RE_ENDPOINT_VLAN_SETTINGS.match(config_rule['custom']['resource_key'])
-            if match is None: continue
-
-            resource_key_values = match.groups()
-            if resource_key_values[0:2] in device_endpoint_keys_a:
-                resource_key_values = list(resource_key_values)
-                resource_key_values[0] = link_endpoint_b['device']
-                resource_key_values[1] = link_endpoint_b['ingress_ep']
-            elif resource_key_values[0:2] in device_endpoint_keys_b:
-                resource_key_values = list(resource_key_values)
-                resource_key_values[0] = link_endpoint_a['device']
-                resource_key_values[1] = link_endpoint_a['egress_ep']
+            if 'custom' not in config_rule or 'pon_access' not in config_rule: continue
+            if 'custom'  in config_rule: 
+                match = RE_ENDPOINT_SETTINGS.match(config_rule['custom']['resource_key'])
+                if match is None:
+                    match = RE_ENDPOINT_VLAN_SETTINGS.match(config_rule['custom']['resource_key'])
+                if match is None: continue
+                resource_key_values = match.groups()
+                if resource_key_values[0:2] in device_endpoint_keys_a:
+                    resource_key_values = list(resource_key_values)
+                    resource_key_values[0] = link_endpoint_b['device']
+                    resource_key_values[1] = link_endpoint_b['ingress_ep']
+                elif resource_key_values[0:2] in device_endpoint_keys_b:
+                    resource_key_values = list(resource_key_values)
+                    resource_key_values[0] = link_endpoint_a['device']
+                    resource_key_values[1] = link_endpoint_a['egress_ep']
+                else:
+                    continue
+
+                device_keys = compute_device_keys(resource_key_values[0], device_name_mapping)
+                device_names = {device_key for device_key in device_keys if RE_UUID.match(device_key) is None}
+                if len(device_names) != 1:
+                    MSG = 'Unable to identify name for Device({:s}): device_keys({:s})'
+                    raise Exception(MSG.format(str(resource_key_values[0]), str(device_keys)))
+                resource_key_values[0] = device_names.pop()
+
+                endpoint_keys = compute_endpoint_keys(device_keys, resource_key_values[1], endpoint_name_mapping)
+                endpoint_names = {endpoint_key for endpoint_key in endpoint_keys if RE_UUID.match(endpoint_key) is None}
+                if len(endpoint_names) != 1:
+                    MSG = 'Unable to identify name for Endpoint({:s}): endpoint_keys({:s})'
+                    raise Exception(MSG.format(str(resource_key_values[1]), str(endpoint_keys)))
+                resource_key_values[1] = endpoint_names.pop()
+
+                resource_value : Dict = json.loads(config_rule['custom']['resource_value'])
+                if 'neighbor_address' not in resource_value: continue
+                resource_value['ip_address'] = resource_value.pop('neighbor_address')
+
+                # remove neighbor_address also from original rule as it is already consumed
+
+                resource_key_template = TMPL_ENDPOINT_VLAN_SETTINGS if len(match.groups()) == 3 else TMPL_ENDPOINT_SETTINGS
+                generated_config_rule = copy.deepcopy(config_rule)
+                generated_config_rule['custom']['resource_key'] = resource_key_template.format(*resource_key_values)
+                generated_config_rule['custom']['resource_value'] = json.dumps(resource_value)
+                generated_config_rules.append(generated_config_rule)
             else:
-                continue
-
-            device_keys = compute_device_keys(resource_key_values[0], device_name_mapping)
-            device_names = {device_key for device_key in device_keys if RE_UUID.match(device_key) is None}
-            if len(device_names) != 1:
-                MSG = 'Unable to identify name for Device({:s}): device_keys({:s})'
-                raise Exception(MSG.format(str(resource_key_values[0]), str(device_keys)))
-            resource_key_values[0] = device_names.pop()
-
-            endpoint_keys = compute_endpoint_keys(device_keys, resource_key_values[1], endpoint_name_mapping)
-            endpoint_names = {endpoint_key for endpoint_key in endpoint_keys if RE_UUID.match(endpoint_key) is None}
-            if len(endpoint_names) != 1:
-                MSG = 'Unable to identify name for Endpoint({:s}): endpoint_keys({:s})'
-                raise Exception(MSG.format(str(resource_key_values[1]), str(endpoint_keys)))
-            resource_key_values[1] = endpoint_names.pop()
-
-            resource_value : Dict = json.loads(config_rule['custom']['resource_value'])
-            if 'neighbor_address' not in resource_value: continue
-            resource_value['ip_address'] = resource_value.pop('neighbor_address')
-
-            # remove neighbor_address also from original rule as it is already consumed
-
-            resource_key_template = TMPL_ENDPOINT_VLAN_SETTINGS if len(match.groups()) == 3 else TMPL_ENDPOINT_SETTINGS
-            generated_config_rule = copy.deepcopy(config_rule)
-            generated_config_rule['custom']['resource_key'] = resource_key_template.format(*resource_key_values)
-            generated_config_rule['custom']['resource_value'] = json.dumps(resource_value)
-            generated_config_rules.append(generated_config_rule)
+
+                LOGGER.debug('[generate_neighbor_endpoint_config_rules] PON_ACCESS: {:s}'.format(str(config_rule)))
+                resource_value : Dict = config_rule['pon_access']
+                generated_config_rule = copy.deepcopy(config_rule)
+                generated_config_rule['pon_access'] = resource_value
+                generated_config_rules.append(generated_config_rule)
 
     LOGGER.debug('[generate_neighbor_endpoint_config_rules] generated_config_rules={:s}'.format(str(generated_config_rules)))
     LOGGER.debug('[generate_neighbor_endpoint_config_rules] end')
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
index ae567d9d6..0e298c7e9 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
@@ -42,13 +42,14 @@ OPTICAL_DEVICE_TYPES = {
     DeviceTypeEnum.OPTICAL_TRANSPONDER, DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER,
 }
 
-SERVICE_TYPE_L2NM = {ServiceTypeEnum.SERVICETYPE_L2NM}
-SERVICE_TYPE_L3NM = {ServiceTypeEnum.SERVICETYPE_L3NM}
-SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM}
-SERVICE_TYPE_TAPI = {ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE}
+SERVICE_TYPE_L2NM   = {ServiceTypeEnum.SERVICETYPE_L2NM}
+SERVICE_TYPE_L3NM   = {ServiceTypeEnum.SERVICETYPE_L3NM}
+SERVICE_TYPE_LXNM   = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM}
+SERVICE_TYPE_TAPI   = {ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE}
+SERVICE_TYPE_PON_ACCESS = {ServiceTypeEnum.SERVICETYPE_PON_ACCESS}
 
 def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum:
-    if device_type in PACKET_DEVICE_TYPES and prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type
+    if device_type in PACKET_DEVICE_TYPES and (prv_service_type in SERVICE_TYPE_LXNM or prv_service_type in SERVICE_TYPE_PON_ACCESS): return prv_service_type
     if device_type in L2_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_L2NM
     if device_type in OPTICAL_DEVICE_TYPES: return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE
     if device_type in NETWORK_DEVICE_TYPES: return prv_service_type
diff --git a/src/policy/src/main/java/org/etsi/tfs/policy/Serializer.java b/src/policy/src/main/java/org/etsi/tfs/policy/Serializer.java
index 6c6c820ec..eab1c4994 100644
--- a/src/policy/src/main/java/org/etsi/tfs/policy/Serializer.java
+++ b/src/policy/src/main/java/org/etsi/tfs/policy/Serializer.java
@@ -1177,8 +1177,8 @@ public class Serializer {
                 return ContextOuterClass.ServiceTypeEnum.SERVICETYPE_L3NM;
             case TAPI_CONNECTIVITY_SERVICE:
                 return ContextOuterClass.ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE;
-            case SERVICETYPE_IPLINK:
-                return ContextOuterClass.ServiceTypeEnum.SERVICETYPE_IPLINK;
+            case PON_ACCESS:
+                return ContextOuterClass.ServiceTypeEnum.SERVICETYPE_PON_ACCESS;
             case UNKNOWN:
                 return ContextOuterClass.ServiceTypeEnum.SERVICETYPE_UNKNOWN;
             default:
diff --git a/src/policy/src/main/java/org/etsi/tfs/policy/context/model/ServiceTypeEnum.java b/src/policy/src/main/java/org/etsi/tfs/policy/context/model/ServiceTypeEnum.java
index d09c6da06..d0c5c1e43 100644
--- a/src/policy/src/main/java/org/etsi/tfs/policy/context/model/ServiceTypeEnum.java
+++ b/src/policy/src/main/java/org/etsi/tfs/policy/context/model/ServiceTypeEnum.java
@@ -20,5 +20,6 @@ public enum ServiceTypeEnum {
     UNKNOWN,
     L3NM,
     L2NM,
-    TAPI_CONNECTIVITY_SERVICE
+    TAPI_CONNECTIVITY_SERVICE,
+    PON_ACCESS
 }
diff --git a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
index d41b80f1d..555efb8e1 100644
--- a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
+++ b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
@@ -519,6 +519,7 @@ public final class ContextOuterClass {
          * <code>SERVICETYPE_QKD = 7;</code>
          */
         SERVICETYPE_QKD(7),
+ 
         UNRECOGNIZED(-1);
 
         /**
-- 
GitLab