From c271546860af1fe04a49b91f8c60e1720ee97b2e Mon Sep 17 00:00:00 2001
From: hajipour <shajipour@cttc.es>
Date: Fri, 27 Dec 2024 22:04:46 +0100
Subject: [PATCH] integration: several integrations to match the requirements
 of L3VPN service handler

---
 .../l3slice_ietfslice/ConfigRules.py          |  49 +-
 .../L3SliceIETFSliceServiceHandler.py         | 984 ++++++++++--------
 2 files changed, 611 insertions(+), 422 deletions(-)

diff --git a/src/service/service/service_handlers/l3slice_ietfslice/ConfigRules.py b/src/service/service/service_handlers/l3slice_ietfslice/ConfigRules.py
index 23f3e8159..cd6072e85 100644
--- a/src/service/service/service_handlers/l3slice_ietfslice/ConfigRules.py
+++ b/src/service/service/service_handlers/l3slice_ietfslice/ConfigRules.py
@@ -20,7 +20,6 @@ from common.tools.object_factory.ConfigRule import (
     json_config_rule_set,
 )
 from context.client.ContextClient import ContextClient
-from service.service.service_handler_api.AnyTreeTools import TreeNode
 
 
 def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]:
@@ -30,11 +29,19 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]:
     src_ac_node_id: str = json_settings["src_ac_node_id"]
     src_ac_ep_id: str = json_settings["src_ac_ep_id"]
     src_vlan: str = json_settings["src_vlan"]
+    src_source_ip_prefix: str = json_settings["src_source_ip_prefix"]
+    src_source_tcp_port: str = json_settings["src_source_tcp_port"]
+    src_destination_ip_prefix: str = json_settings["src_destination_ip_prefix"]
+    src_destination_tcp_port: str = json_settings["src_destination_tcp_port"]
     dst_node_id: str = json_settings["dst_node_id"]
     dst_mgmt_ip_address: str = json_settings["dst_mgmt_ip_address"]
     dst_ac_node_id: str = json_settings["dst_ac_node_id"]
     dst_ac_ep_id: str = json_settings["dst_ac_ep_id"]
     dst_vlan: str = json_settings["dst_vlan"]
+    dst_source_ip_prefix: str = json_settings["dst_source_ip_prefix"]
+    dst_source_tcp_port: str = json_settings["dst_source_tcp_port"]
+    dst_destination_ip_prefix: str = json_settings["dst_destination_ip_prefix"]
+    dst_destination_tcp_port: str = json_settings["dst_destination_tcp_port"]
     slice_id: str = json_settings["slice_id"]
     delay: str = json_settings["delay"]
     bandwidth: str = json_settings["bandwidth"]
@@ -54,6 +61,22 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]:
                                 "type": "ietf-network-slice-service:vlan",
                                 "value": [src_vlan],
                             },
+                            {
+                                "type": "ietf-network-slice-service:source-ip-prefix",
+                                "value": [src_source_ip_prefix],
+                            },
+                            {
+                                "type": "ietf-network-slice-service:source-tcp-port",
+                                "value": [src_source_tcp_port],
+                            },
+                            {
+                                "type": "ietf-network-slice-service:destination-ip-prefix",
+                                "value": [src_destination_ip_prefix],
+                            },
+                            {
+                                "type": "ietf-network-slice-service:destination-tcp-port",
+                                "value": [src_destination_tcp_port],
+                            },
                         ],
                         "target-connection-group-id": "line1",
                     }
@@ -83,6 +106,22 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]:
                                 "type": "ietf-network-slice-service:vlan",
                                 "value": [dst_vlan],
                             },
+                            {
+                                "type": "ietf-network-slice-service:source-ip-prefix",
+                                "value": [dst_source_ip_prefix],
+                            },
+                            {
+                                "type": "ietf-network-slice-service:source-tcp-port",
+                                "value": [dst_source_tcp_port],
+                            },
+                            {
+                                "type": "ietf-network-slice-service:destination-ip-prefix",
+                                "value": [dst_destination_ip_prefix],
+                            },
+                            {
+                                "type": "ietf-network-slice-service:destination-tcp-port",
+                                "value": [dst_destination_tcp_port],
+                            },
                         ],
                         "target-connection-group-id": "line1",
                     }
@@ -108,8 +147,8 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]:
             "connectivity-construct": [
                 {
                     "id": 1,
-                    "p2mp-sender-sdp": "1",
-                    "p2mp-receiver-sdp": ["2"],
+                    "p2p-sender-sdp": "1",
+                    "p2p-receiver-sdp": "2",
                     "service-slo-sle-policy": {
                         "slo-policy": {
                             "metric-bound": [
@@ -134,8 +173,8 @@ def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]:
                 },
                 {
                     "id": 2,
-                    "p2mp-sender-sdp": "2",
-                    "p2mp-receiver-sdp": ["1"],
+                    "p2p-sender-sdp": "2",
+                    "p2p-receiver-sdp": "1",
                     "service-slo-sle-policy": {
                         "slo-policy": {
                             "metric-bound": [
diff --git a/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py b/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py
index e584283a4..fa4ebd35d 100644
--- a/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py
+++ b/src/service/service/service_handlers/l3slice_ietfslice/L3SliceIETFSliceServiceHandler.py
@@ -15,7 +15,7 @@
 import json
 import logging
 import re
-from typing import Any, List, Optional, Tuple, Union
+from typing import Any, Dict, List, Optional, Tuple, TypedDict, Union
 
 from deepdiff import DeepDiff
 
@@ -37,6 +37,21 @@ from .ConfigRules import (
     teardown_config_rules,
 )
 
+
+class Ipv4Info(TypedDict):
+    src_ip: str
+    dst_ip: str
+    src_port: str
+    dst_port: str
+    vlan: str
+
+
+class DeviceEpInfo(TypedDict):
+    ipv4_info: Ipv4Info
+    node_name: str
+    endpoint_name: str
+
+
 RUNNING_RESOURCE_KEY = "running_ietf_slice"
 CANDIDATE_RESOURCE_KEY = "candidate_ietf_slice"
 
@@ -62,6 +77,53 @@ METRICS_POOL = MetricsPool(
 RAISE_IF_DIFFERS = True
 
 
+def extract_source_destination_device_endpoint_info(
+    device_ep_pairs: list, connection_group: Dict
+) -> Tuple[DeviceEpInfo, DeviceEpInfo]:
+    connectivity_construct = connection_group["connectivity-construct"][0]
+    sender_sdp = connectivity_construct["p2p-sender-sdp"]
+    if sender_sdp == device_ep_pairs[0][4]:
+        ...
+    elif sender_sdp == device_ep_pairs[1][4]:
+        device_ep_pairs = device_ep_pairs[::-1]
+    else:
+        raise Exception("Sender SDP not found in device_ep_pairs")
+    source_device_ep_info = DeviceEpInfo(
+        ipv4_info=device_ep_pairs[0][5],
+        node_name=device_ep_pairs[0][2],
+        endpoint_name=device_ep_pairs[0][3],
+    )
+    destination_device_ep_info = DeviceEpInfo(
+        ipv4_info=device_ep_pairs[1][5],
+        node_name=device_ep_pairs[1][2],
+        endpoint_name=device_ep_pairs[1][3],
+    )
+    return source_device_ep_info, destination_device_ep_info
+
+
+def extract_match_criterion_ipv4_info(
+    match_criterion: Dict,
+) -> Ipv4Info:
+    for type_value in match_criterion["match-type"]:
+        if type_value["type"] == "ietf-network-slice-service:source-ip-prefix":
+            src_ip = type_value["value"][0].split("/")[0]
+        elif type_value["type"] == "ietf-network-slice-service:destination-ip-prefix":
+            dst_ip = type_value["value"][0].split("/")[0]
+        elif type_value["type"] == "ietf-network-slice-service:source-tcp-port":
+            src_port = type_value["value"][0]
+        elif type_value["type"] == "ietf-network-slice-service:destination-tcp-port":
+            dst_port = type_value["value"][0]
+        elif type_value["type"] == "ietf-network-slice-service:vlan":
+            vlan = type_value["value"][0]
+    return Ipv4Info(
+        src_ip=src_ip,
+        dst_ip=dst_ip,
+        src_port=src_port,
+        dst_port=dst_port,
+        vlan=vlan,
+    )
+
+
 def get_custom_config_rule(
     service_config: ServiceConfig, resource_key: str
 ) -> Optional[ConfigRule]:
@@ -73,7 +135,7 @@ def get_custom_config_rule(
             return cr
 
 
-def get_running_candidate_ietf_slice_data_diff(service_config: ServiceConfig) -> dict:
+def get_running_candidate_ietf_slice_data_diff(service_config: ServiceConfig) -> Dict:
     running_ietf_slice_cr = get_custom_config_rule(service_config, RUNNING_RESOURCE_KEY)
     running_resource_value_dict = json.loads(
         running_ietf_slice_cr.custom.resource_value
@@ -84,16 +146,9 @@ def get_running_candidate_ietf_slice_data_diff(service_config: ServiceConfig) ->
     candidate_resource_value_dict = json.loads(
         candidate_ietf_slice_cr.custom.resource_value
     )
-    return (
-        DeepDiff(
-            running_resource_value_dict,
-            candidate_resource_value_dict,
-        ),
-        DeepDiff(
-            running_resource_value_dict,
-            candidate_resource_value_dict,
-            ignore_order=True,
-        ),
+    return DeepDiff(
+        running_resource_value_dict,
+        candidate_resource_value_dict,
     )
 
 
@@ -114,64 +169,107 @@ class L3NMSliceIETFSliceServiceHandler(_ServiceHandler):
         chk_type("endpoints", endpoints, list)
         if len(endpoints) == 0:
             return []
-        service_uuid = self.__service.service_id.service_uuid.uuid
-        service_config = self.__service.service_config
-        src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0])
-        src_device_obj = self.__task_executor.get_device(
-            DeviceId(**json_device_id(src_device_uuid))
-        )
-        src_device_name = src_device_obj.name
-        src_controller = self.__task_executor.get_device_controller(src_device_obj)
-        dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[-1])
-        dst_device_obj = self.__task_executor.get_device(
-            DeviceId(**json_device_id(dst_device_uuid))
-        )
-        dst_device_name = dst_device_obj.name
-        dst_controller = self.__task_executor.get_device_controller(dst_device_obj)
-        if (
-            src_controller.device_id.device_uuid.uuid
-            != dst_controller.device_id.device_uuid.uuid
-        ):
-            raise Exception("Different Src-Dst devices not supported by now")
-        controller = src_controller
-        context_client = ContextClient()
-        edge_device_names = [src_device_name, dst_device_name]
-        link_list = context_client.ListLinks(Empty())
-        links = link_list.links
-        max_delay = 1e9
-        packet_loss = 1.0
-        bandwidth = 0.0
-        device_ep_pairs = []
-        sdp_ids = []
-        running_candidate_diff, running_candidate_diff_no_order = (
-            get_running_candidate_ietf_slice_data_diff(service_config)
-        )
-        candidate_ietf_slice_cr = get_custom_config_rule(
-            service_config, CANDIDATE_RESOURCE_KEY
-        )
-        candidate_resource_value_dict = json.loads(
-            candidate_ietf_slice_cr.custom.resource_value
-        )
-        running_ietf_slice_cr = get_custom_config_rule(
-            service_config, RUNNING_RESOURCE_KEY
-        )
-        running_resource_value_dict = json.loads(
-            running_ietf_slice_cr.custom.resource_value
-        )
-        LOGGER.debug(f"P46: {candidate_resource_value_dict}")
-        LOGGER.debug(f"P47: {running_resource_value_dict}")
-        LOGGER.debug(f"P41: {running_candidate_diff}")
-        LOGGER.debug(f"P45: {running_candidate_diff_no_order}")
-        if not running_candidate_diff:  # Slice Creation
-            slice_services = candidate_resource_value_dict["network-slice-services"][
-                "slice-service"
-            ]
-            slice_service = slice_services[0]
-            sdps = slice_service["sdps"]["sdp"]
-            operation_type = "create"
-            sdp_ids = [sdp["node-id"] for sdp in sdps]
-            for sdp in sdps:
-                node_id = sdp["node-id"]
+
+        results = []
+        try:
+            service_config = self.__service.service_config
+            src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0])
+            src_device_obj = self.__task_executor.get_device(
+                DeviceId(**json_device_id(src_device_uuid))
+            )
+            src_device_name = src_device_obj.name
+            src_controller = self.__task_executor.get_device_controller(src_device_obj)
+            dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(
+                endpoints[-1]
+            )
+            dst_device_obj = self.__task_executor.get_device(
+                DeviceId(**json_device_id(dst_device_uuid))
+            )
+            dst_device_name = dst_device_obj.name
+            dst_controller = self.__task_executor.get_device_controller(dst_device_obj)
+            if (
+                src_controller.device_id.device_uuid.uuid
+                != dst_controller.device_id.device_uuid.uuid
+            ):
+                raise Exception("Different Src-Dst devices not supported by now")
+            controller = src_controller
+            context_client = ContextClient()
+            edge_device_names = [src_device_name, dst_device_name]
+            link_list = context_client.ListLinks(Empty())
+            links = link_list.links
+            max_delay = 1e9
+            packet_loss = 1.0
+            bandwidth = 0.0
+            device_ep_pairs = []
+            sdp_ids = []
+            running_candidate_diff = get_running_candidate_ietf_slice_data_diff(
+                service_config
+            )
+            candidate_ietf_slice_cr = get_custom_config_rule(
+                service_config, CANDIDATE_RESOURCE_KEY
+            )
+            candidate_resource_value_dict = json.loads(
+                candidate_ietf_slice_cr.custom.resource_value
+            )
+            running_ietf_slice_cr = get_custom_config_rule(
+                service_config, RUNNING_RESOURCE_KEY
+            )
+            running_resource_value_dict = json.loads(
+                running_ietf_slice_cr.custom.resource_value
+            )
+            slice_name = running_resource_value_dict["network-slice-services"]['slice-service'][0]['id']
+            if not running_candidate_diff:  # Slice Creation
+                slice_services = candidate_resource_value_dict[
+                    "network-slice-services"
+                ]["slice-service"]
+                slice_service = slice_services[0]
+                sdps = slice_service["sdps"]["sdp"]
+                operation_type = "create"
+                sdp_ids = [sdp["node-id"] for sdp in sdps]
+                for sdp in sdps:
+                    node_id = sdp["node-id"]
+                    for link in links:
+                        (
+                            device_obj_name_1,
+                            ep_name_1,
+                            device_obj_1,
+                            device_obj_name_2,
+                            ep_name_2,
+                            device_obj_2,
+                        ) = get_link_ep_device_names(link, context_client)
+                        if (
+                            device_obj_name_1 == node_id
+                            and device_obj_name_2 in edge_device_names
+                        ):
+                            del edge_device_names[
+                                edge_device_names.index(device_obj_name_2)
+                            ]
+                            match_criteria = sdp["service-match-criteria"][
+                                "match-criterion"
+                            ]
+                            if len(match_criteria) != 1:
+                                raise Exception(
+                                    "Only one match criteria allowed for initial slice creation"
+                                )
+                            match_criterion = match_criteria[0]
+                            ipv4_info = extract_match_criterion_ipv4_info(
+                                match_criterion
+                            )
+                            device_ep_pairs.append(
+                                (
+                                    node_id,
+                                    ep_name_1,
+                                    device_obj_name_2,
+                                    ep_name_2,
+                                    sdp["id"],
+                                    ipv4_info,
+                                )
+                            )
+                            target_connection_group_id = match_criterion[
+                                "target-connection-group-id"
+                            ]
+                            del sdp_ids[sdp_ids.index(node_id)]
+                            break
                 for link in links:
                     (
                         device_obj_name_1,
@@ -182,359 +280,411 @@ class L3NMSliceIETFSliceServiceHandler(_ServiceHandler):
                         device_obj_2,
                     ) = get_link_ep_device_names(link, context_client)
                     if (
-                        device_obj_name_1 == node_id
-                        and device_obj_name_2 in edge_device_names
+                        device_obj_name_1 == edge_device_names[0]
+                        and device_obj_2.controller_id != device_obj_1.controller_id
                     ):
-                        device_ep_pairs.append(
-                            (node_id, ep_name_1, device_obj_name_2, ep_name_2)
-                        )
-                        del edge_device_names[
-                            edge_device_names.index(device_obj_name_2)
-                        ]
-                        match_criteria = sdp["service-match-criteria"][
-                            "match-criterion"
-                        ]
-                        vlan_id = set()
-                        for match in match_criteria:
-                            for type_value in match["match-type"]:
-                                if (
-                                    type_value["type"]
-                                    == "ietf-network-slice-service:vlan"
-                                ):
-                                    vlan_id.add(type_value["value"][0])
-                        if len(vlan_id) != 1:
-                            raise Exception("one vlan id found in SDP match criteria")
-                        src_vlan = list(vlan_id)[0]
-                        del sdp_ids[sdp_ids.index(node_id)]
+                        for sdp in sdps:
+                            if sdp["node-id"] != sdp_ids[0]:
+                                continue
+                            match_criteria = sdp["service-match-criteria"][
+                                "match-criterion"
+                            ]
+                            if len(match_criteria) != 1:
+                                raise Exception(
+                                    "Only one match criteria allowed for initial slice creation"
+                                )
+                            match_criterion = match_criteria[0]
+                            ipv4_info = extract_match_criterion_ipv4_info(
+                                match_criterion
+                            )
+                            device_ep_pairs.append(
+                                (
+                                    device_obj_name_2,
+                                    ep_name_2,
+                                    device_obj_name_1,
+                                    ep_name_1,
+                                    sdp["id"],
+                                    ipv4_info,
+                                )
+                            )
                         break
-            for link in links:
-                (
-                    device_obj_name_1,
-                    ep_name_1,
-                    device_obj_1,
-                    device_obj_name_2,
-                    ep_name_2,
-                    device_obj_2,
-                ) = get_link_ep_device_names(link, context_client)
-                if (
-                    device_obj_name_1 == edge_device_names[0]
-                    and device_obj_2.controller_id != device_obj_1.controller_id
-                ):
-                    device_ep_pairs.append(
-                        (device_obj_name_2, ep_name_2, device_obj_name_1, ep_name_1)
-                    )
-                    for sdp in sdps:
-                        if sdp["node-id"] != sdp_ids[0]:
-                            continue
-                        match_criteria = sdp["service-match-criteria"][
-                            "match-criterion"
-                        ]
-                        vlan_id = set()
-                        for match in match_criteria:
-                            for type_value in match["match-type"]:
-                                if (
-                                    type_value["type"]
-                                    == "ietf-network-slice-service:vlan"
-                                ):
-                                    vlan_id.add(type_value["value"][0])
-                        if len(vlan_id) != 1:
-                            raise Exception("one vlan id found in SDP match criteria")
-                        dst_vlan = list(vlan_id)[0]
-                    break
-            else:
-                raise Exception("sdp between the domains not found")
-        elif "iterable_item_added" in running_candidate_diff:  # new SDP added
-            slice_services = candidate_resource_value_dict["network-slice-services"][
-                "slice-service"
-            ]
-            slice_service = slice_services[0]
-            sdps = slice_service["sdps"]["sdp"]
-            operation_type = "update"
-            added_items = {
-                "sdp": {"sdp_idx": None, "value": {}},
-                "connection_group": {"connection_group_idx": None, "value": {}},
-                "match_criterion": {
-                    "sdp_idx": None,
-                    "match_criterion_idx": None,
-                    "value": {},
-                },
-            }
-            for added_key, added_value in running_candidate_diff[
-                "iterable_item_added"
-            ].items():
-                sdp_match = SDP_DIFF_RE.match(added_key)
-                connection_group_match = CONNECTION_GROUP_DIFF_RE.match(added_key)
-                match_criterion_match = MATCH_CRITERION_DIFF_RE.match(added_key)
-                if sdp_match:
-                    added_items["sdp"] = {
-                        "sdp_idx": int(sdp_match.groups()[0]),
-                        "value": added_value,
-                    }
-                elif connection_group_match:
-                    added_items["connection_group"] = {
-                        "connection_group_idx": int(connection_group_match.groups()[0]),
-                        "value": added_value,
-                    }
-                elif match_criterion_match:
-                    added_items["match_criterion"] = {
-                        "sdp_idx": int(match_criterion_match.groups()[0]),
-                        "match_criterion_idx": int(match_criterion_match.groups()[1]),
-                        "value": added_value,
-                    }
-            new_sdp = sdps[added_items["sdp"]["sdp_idx"]]
-            src_sdp_name = new_sdp["node-id"]
-            dst_sdp_idx = sdps[added_items["match_criterion"]["sdp_idx"]]["id"]
-            dst_sdp_name = sdps[added_items["match_criterion"]["sdp_idx"]]["node-id"]
-            for link in links:
-                (
-                    device_obj_name_1,
-                    ep_name_1,
-                    device_obj_1,
-                    device_obj_name_2,
-                    ep_name_2,
-                    device_obj_2,
-                ) = get_link_ep_device_names(link, context_client)
-                if (
-                    device_obj_name_1 == src_sdp_name
-                    and device_obj_2.controller_id != device_obj_1.controller_id
-                ):
-                    device_ep_pairs.append(
-                        (device_obj_name_2, ep_name_2, device_obj_name_1, ep_name_1)
-                    )
-                    for sdp in sdps:
-                        if sdp["node-id"] != src_sdp_name:
-                            continue
-                        match_criteria = sdp["service-match-criteria"][
-                            "match-criterion"
-                        ]
-                        vlan_id = set()
-                        for match in match_criteria:
-                            for type_value in match["match-type"]:
-                                if (
-                                    type_value["type"]
-                                    == "ietf-network-slice-service:vlan"
-                                ):
-                                    vlan_id.add(type_value["value"][0])
-                        if len(vlan_id) != 1:
-                            raise Exception("one vlan id found in SDP match criteria")
-                        src_vlan = list(vlan_id)[0]
-                    break
-            else:
-                raise Exception("sdp between the domains not found")
-            for link in links:
-                (
-                    device_obj_name_1,
-                    ep_name_1,
-                    device_obj_1,
-                    device_obj_name_2,
-                    ep_name_2,
-                    device_obj_2,
-                ) = get_link_ep_device_names(link, context_client)
-                if (
-                    device_obj_name_1 == dst_sdp_name
-                    and device_obj_2.controller_id != device_obj_1.controller_id
-                ):
-                    device_ep_pairs.append(
-                        (device_obj_name_2, ep_name_2, device_obj_name_1, ep_name_1)
-                    )
-                    for sdp in sdps:
-                        if sdp["node-id"] != dst_sdp_name:
-                            continue
-                        match_criteria = sdp["service-match-criteria"][
-                            "match-criterion"
-                        ]
-                        vlan_id = set()
-                        for match in match_criteria:
-                            for type_value in match["match-type"]:
-                                if (
-                                    type_value["type"]
-                                    == "ietf-network-slice-service:vlan"
-                                ):
-                                    vlan_id.add(type_value["value"][0])
-                        if len(vlan_id) != 1:
-                            raise Exception("one vlan id found in SDP match criteria")
-                        dst_vlan = list(vlan_id)[0]
-                    break
-            else:
-                raise Exception("sdp between the domains not found")
-        elif "iterable_item_removed" in running_candidate_diff:  # an SDP removed
-            slice_services = running_resource_value_dict["network-slice-services"][
-                "slice-service"
-            ]
-            slice_service = slice_services[0]
-            sdps = slice_service["sdps"]["sdp"]
-            operation_type = "update"
-            added_items = {
-                "sdp": {"sdp_idx": None, "value": {}},
-                "connection_group": {"connection_group_idx": None, "value": {}},
-                "match_criterion": {
-                    "sdp_idx": None,
-                    "match_criterion_idx": None,
-                    "value": {},
-                },
-            }
-            for added_key, added_value in running_candidate_diff[
-                "iterable_item_removed"
-            ].items():
-                sdp_match = SDP_DIFF_RE.match(added_key)
-                connection_group_match = CONNECTION_GROUP_DIFF_RE.match(added_key)
-                match_criterion_match = MATCH_CRITERION_DIFF_RE.match(added_key)
-                LOGGER.debug(
-                    f"P40: {sdp_match} *{connection_group_match}* {match_criterion_match}"
-                )
-                if sdp_match:
-                    added_items["sdp"] = {
-                        "sdp_idx": int(sdp_match.groups()[0]),
-                        "value": added_value,
-                    }
-                elif connection_group_match:
-                    added_items["connection_group"] = {
-                        "connection_group_idx": int(connection_group_match.groups()[0]),
-                        "value": added_value,
-                    }
-                elif match_criterion_match:
-                    added_items["match_criterion"] = {
-                        "sdp_idx": int(match_criterion_match.groups()[0]),
-                        "match_criterion_idx": int(match_criterion_match.groups()[1]),
-                        "value": added_value,
-                    }
-            new_sdp = sdps[added_items["sdp"]["sdp_idx"]]
-            src_sdp_name = new_sdp["node-id"]
-            dst_sdp_idx = sdps[added_items["match_criterion"]["sdp_idx"]]["id"]
-            dst_sdp_name = sdps[added_items["match_criterion"]["sdp_idx"]]["node-id"]
-            for link in links:
-                (
-                    device_obj_name_1,
-                    ep_name_1,
-                    device_obj_1,
-                    device_obj_name_2,
-                    ep_name_2,
-                    device_obj_2,
-                ) = get_link_ep_device_names(link, context_client)
-                if (
-                    device_obj_name_1 == src_sdp_name
-                    and device_obj_2.controller_id != device_obj_1.controller_id
-                ):
-                    device_ep_pairs.append(
-                        (device_obj_name_2, ep_name_2, device_obj_name_1, ep_name_1)
-                    )
-                    for sdp in sdps:
-                        if sdp["node-id"] != src_sdp_name:
-                            continue
-                        match_criteria = sdp["service-match-criteria"][
-                            "match-criterion"
-                        ]
-                        vlan_id = set()
-                        LOGGER.debug(f"P81: {match_criteria}")
-                        for match in match_criteria:
-                            for type_value in match["match-type"]:
-                                if (
-                                    type_value["type"]
-                                    == "ietf-network-slice-service:vlan"
-                                ):
-                                    vlan_id.add(type_value["value"][0])
-                        LOGGER.debug(f"P82: {vlan_id}")
-                        if len(vlan_id) != 1:
-                            raise Exception("one vlan id found in SDP match criteria")
-                        src_vlan = list(vlan_id)[0]
-                    break
-            else:
-                raise Exception("sdp between the domains not found")
-            for link in links:
-                (
-                    device_obj_name_1,
-                    ep_name_1,
-                    device_obj_1,
-                    device_obj_name_2,
-                    ep_name_2,
-                    device_obj_2,
-                ) = get_link_ep_device_names(link, context_client)
-                if (
-                    device_obj_name_1 == dst_sdp_name
-                    and device_obj_2.controller_id != device_obj_1.controller_id
-                ):
-                    device_ep_pairs.append(
-                        (device_obj_name_2, ep_name_2, device_obj_name_1, ep_name_1)
-                    )
-                    for sdp in sdps:
-                        if sdp["node-id"] != dst_sdp_name:
-                            continue
-                        match_criteria = sdp["service-match-criteria"][
-                            "match-criterion"
-                        ]
-                        LOGGER.debug(f"P83: {match_criteria}")
-                        vlan_id = set()
-                        for match in match_criteria:
-                            for type_value in match["match-type"]:
-                                if (
-                                    type_value["type"]
-                                    == "ietf-network-slice-service:vlan"
-                                ):
-                                    vlan_id.add(type_value["value"][0])
-
-                        LOGGER.debug(f"P84: {vlan_id}")
-                        if len(vlan_id) != 1:
-                            raise Exception("one vlan id found in SDP match criteria")
-                        dst_vlan = list(vlan_id)[0]
-                    break
-            else:
-                raise Exception("sdp between the domains not found")
-        connection_groups = slice_service["connection-groups"]["connection-group"]
-        for cg in connection_groups:
-            for cc in cg["connectivity-construct"]:
-                for metric_bound in cc["service-slo-sle-policy"]["slo-policy"][
-                    "metric-bound"
-                ]:
+                else:
+                    raise Exception("sdp between the domains not found")
+            elif "iterable_item_added" in running_candidate_diff:  # new SDP added
+                slice_services = candidate_resource_value_dict[
+                    "network-slice-services"
+                ]["slice-service"]
+                slice_service = slice_services[0]
+                sdps = slice_service["sdps"]["sdp"]
+                operation_type = "update"
+                added_items = {
+                    "sdp": {"sdp_idx": None, "value": {}},
+                    "connection_group": {"connection_group_idx": None, "value": {}},
+                    "match_criterion": {
+                        "sdp_idx": None,
+                        "match_criterion_idx": None,
+                        "value": {},
+                    },
+                }
+                for added_key, added_value in running_candidate_diff[
+                    "iterable_item_added"
+                ].items():
+                    sdp_match = SDP_DIFF_RE.match(added_key)
+                    connection_group_match = CONNECTION_GROUP_DIFF_RE.match(added_key)
+                    match_criterion_match = MATCH_CRITERION_DIFF_RE.match(added_key)
+                    if sdp_match:
+                        added_items["sdp"] = {
+                            "sdp_idx": int(sdp_match.groups()[0]),
+                            "value": added_value,
+                        }
+                    elif connection_group_match:
+                        added_items["connection_group"] = {
+                            "connection_group_idx": int(
+                                connection_group_match.groups()[0]
+                            ),
+                            "value": added_value,
+                        }
+                    elif match_criterion_match:
+                        added_items["match_criterion"] = {
+                            "sdp_idx": int(match_criterion_match.groups()[0]),
+                            "match_criterion_idx": int(
+                                match_criterion_match.groups()[1]
+                            ),
+                            "value": added_value,
+                        }
+                new_sdp = sdps[added_items["sdp"]["sdp_idx"]]
+                src_sdp_name = new_sdp["node-id"]
+                dst_sdp_idx = sdps[added_items["match_criterion"]["sdp_idx"]]["id"]
+                dst_sdp_name = sdps[added_items["match_criterion"]["sdp_idx"]][
+                    "node-id"
+                ]
+                for link in links:
+                    (
+                        device_obj_name_1,
+                        ep_name_1,
+                        device_obj_1,
+                        device_obj_name_2,
+                        ep_name_2,
+                        device_obj_2,
+                    ) = get_link_ep_device_names(link, context_client)
                     if (
-                        metric_bound["metric-type"]
-                        == "ietf-network-slice-service:one-way-delay-maximum"
-                        and metric_bound["metric-unit"] == "milliseconds"
+                        device_obj_name_1 == src_sdp_name
+                        and device_obj_2.controller_id != device_obj_1.controller_id
                     ):
-                        metric_value = int(metric_bound["bound"])
-                        if metric_value < max_delay:
-                            max_delay = metric_value
-                    elif (
-                        metric_bound["metric-type"]
-                        == "ietf-network-slice-service:two-way-packet-loss"
-                        and metric_bound["metric-unit"] == "percentage"
+                        for sdp in sdps:
+                            if sdp["node-id"] != src_sdp_name:
+                                continue
+                            match_criteria = sdp["service-match-criteria"][
+                                "match-criterion"
+                            ]
+                            if len(match_criteria) != 1:
+                                raise Exception(
+                                    "Only one match criteria allowed for initial slice creation"
+                                )
+                            match_criterion = match_criteria[0]
+                            ipv4_info = extract_match_criterion_ipv4_info(
+                                match_criterion
+                            )
+                            device_ep_pairs.append(
+                                (
+                                    device_obj_name_2,
+                                    ep_name_2,
+                                    device_obj_name_1,
+                                    ep_name_1,
+                                    sdp["id"],
+                                    ipv4_info,
+                                )
+                            )
+                            target_connection_group_id = match_criterion[
+                                "target-connection-group-id"
+                            ]
+                        break
+                else:
+                    raise Exception("sdp between the domains not found")
+                for link in links:
+                    (
+                        device_obj_name_1,
+                        ep_name_1,
+                        device_obj_1,
+                        device_obj_name_2,
+                        ep_name_2,
+                        device_obj_2,
+                    ) = get_link_ep_device_names(link, context_client)
+                    if (
+                        device_obj_name_1 == dst_sdp_name
+                        and device_obj_2.controller_id != device_obj_1.controller_id
                     ):
-                        metric_value = float(metric_bound["percentile-value"])
-                        if metric_value < packet_loss:
-                            packet_loss = metric_value
-                    elif (
-                        metric_bound["metric-type"]
-                        == "ietf-network-slice-service:one-way-bandwidth"
-                        and metric_bound["metric-unit"] == "Mbps"
+                        for sdp in sdps:
+                            if sdp["node-id"] != dst_sdp_name:
+                                continue
+                            match_criteria = sdp["service-match-criteria"][
+                                "match-criterion"
+                            ]
+                            vlan_id = set()
+                            for match in match_criteria:
+                                for type_value in match["match-type"]:
+                                    if (
+                                        type_value["type"]
+                                        == "ietf-network-slice-service:vlan"
+                                    ):
+                                        vlan_id.add(type_value["value"][0])
+                            if len(vlan_id) != 1:
+                                raise Exception(
+                                    "one vlan id found in SDP match criteria"
+                                )
+                            match_criterion = match_criteria[
+                                added_items["match_criterion"]["match_criterion_idx"]
+                            ]
+                            ipv4_info = extract_match_criterion_ipv4_info(
+                                match_criterion
+                            )
+                            device_ep_pairs.append(
+                                (
+                                    device_obj_name_2,
+                                    ep_name_2,
+                                    device_obj_name_1,
+                                    ep_name_1,
+                                    sdp["id"],
+                                    ipv4_info,
+                                )
+                            )
+                        break
+                else:
+                    raise Exception("sdp between the domains not found")
+            elif "iterable_item_removed" in running_candidate_diff:  # an SDP removed
+                slice_services = running_resource_value_dict["network-slice-services"][
+                    "slice-service"
+                ]
+                slice_service = slice_services[0]
+                sdps = slice_service["sdps"]["sdp"]
+                operation_type = "update"
+                removed_items = {
+                    "sdp": {"sdp_idx": None, "value": {}},
+                    "connection_group": {"connection_group_idx": None, "value": {}},
+                    "match_criterion": {
+                        "sdp_idx": None,
+                        "match_criterion_idx": None,
+                        "value": {},
+                    },
+                }
+                for added_key, added_value in running_candidate_diff[
+                    "iterable_item_removed"
+                ].items():
+                    sdp_match = SDP_DIFF_RE.match(added_key)
+                    connection_group_match = CONNECTION_GROUP_DIFF_RE.match(added_key)
+                    match_criterion_match = MATCH_CRITERION_DIFF_RE.match(added_key)
+                    if sdp_match:
+                        removed_items["sdp"] = {
+                            "sdp_idx": int(sdp_match.groups()[0]),
+                            "value": added_value,
+                        }
+                    elif connection_group_match:
+                        removed_items["connection_group"] = {
+                            "connection_group_idx": int(
+                                connection_group_match.groups()[0]
+                            ),
+                            "value": added_value,
+                        }
+                    elif match_criterion_match:
+                        removed_items["match_criterion"] = {
+                            "sdp_idx": int(match_criterion_match.groups()[0]),
+                            "match_criterion_idx": int(
+                                match_criterion_match.groups()[1]
+                            ),
+                            "value": added_value,
+                        }
+                new_sdp = sdps[removed_items["sdp"]["sdp_idx"]]
+                src_sdp_name = new_sdp["node-id"]
+                dst_sdp_idx = sdps[removed_items["match_criterion"]["sdp_idx"]]["id"]
+                dst_sdp_name = sdps[removed_items["match_criterion"]["sdp_idx"]][
+                    "node-id"
+                ]
+                for link in links:
+                    (
+                        device_obj_name_1,
+                        ep_name_1,
+                        device_obj_1,
+                        device_obj_name_2,
+                        ep_name_2,
+                        device_obj_2,
+                    ) = get_link_ep_device_names(link, context_client)
+                    if (
+                        device_obj_name_1 == src_sdp_name
+                        and device_obj_2.controller_id != device_obj_1.controller_id
                     ):
-                        metric_value = float(metric_bound["bound"])
-                        bandwidth += metric_value
-        results = []
-        resource_value_dict = {
-            "uuid": service_uuid,
-            "operation_type": operation_type,
-            "src_node_id": device_ep_pairs[0][2],
-            "src_mgmt_ip_address": device_ep_pairs[0][2],
-            "src_ac_node_id": device_ep_pairs[0][2],
-            "src_ac_ep_id": device_ep_pairs[0][3],
-            "src_vlan": src_vlan,
-            "dst_node_id": device_ep_pairs[1][2],
-            "dst_mgmt_ip_address": device_ep_pairs[1][2],
-            "dst_ac_node_id": device_ep_pairs[1][2],
-            "dst_ac_ep_id": device_ep_pairs[1][3],
-            "dst_vlan": dst_vlan,
-            "slice_id": service_uuid,
-            "delay": max_delay,
-            "bandwidth": bandwidth,
-            "packet_loss": packet_loss,
-        }
-
-        json_config_rules = setup_config_rules(service_uuid, resource_value_dict)
-        del controller.device_config.config_rules[:]
-        for jcr in json_config_rules:
-            controller.device_config.config_rules.append(ConfigRule(**jcr))
-        self.__task_executor.configure_device(controller)
-        # except Exception as e:  # pylint: disable=broad-except
-        #     results.append(e)
+                        for sdp in sdps:
+                            if sdp["node-id"] != src_sdp_name:
+                                continue
+                            match_criteria = sdp["service-match-criteria"][
+                                "match-criterion"
+                            ]
+                            if len(match_criteria) != 1:
+                                raise Exception(
+                                    "Only one match criteria allowed for new SDP addition"
+                                )
+                            match_criterion = match_criteria[0]
+                            ipv4_info = extract_match_criterion_ipv4_info(
+                                match_criterion
+                            )
+                            device_ep_pairs.append(
+                                (
+                                    device_obj_name_2,
+                                    ep_name_2,
+                                    device_obj_name_1,
+                                    ep_name_1,
+                                    sdp["id"],
+                                    ipv4_info,
+                                )
+                            )
+                            target_connection_group_id = match_criterion[
+                                "target-connection-group-id"
+                            ]
+                        break
+                else:
+                    raise Exception("sdp between the domains not found")
+                for link in links:
+                    (
+                        device_obj_name_1,
+                        ep_name_1,
+                        device_obj_1,
+                        device_obj_name_2,
+                        ep_name_2,
+                        device_obj_2,
+                    ) = get_link_ep_device_names(link, context_client)
+                    if (
+                        device_obj_name_1 == dst_sdp_name
+                        and device_obj_2.controller_id != device_obj_1.controller_id
+                    ):
+                        for sdp in sdps:
+                            if sdp["node-id"] != dst_sdp_name:
+                                continue
+                            match_criteria = sdp["service-match-criteria"][
+                                "match-criterion"
+                            ]
+                            vlan_id = set()
+                            for match in match_criteria:
+                                for type_value in match["match-type"]:
+                                    if (
+                                        type_value["type"]
+                                        == "ietf-network-slice-service:vlan"
+                                    ):
+                                        vlan_id.add(type_value["value"][0])
+                            if len(vlan_id) != 1:
+                                raise Exception(
+                                    "one vlan id found in SDP match criteria"
+                                )
+                            match_criterion = match_criteria[
+                                removed_items["match_criterion"]["match_criterion_idx"]
+                            ]
+                            ipv4_info = extract_match_criterion_ipv4_info(
+                                match_criterion
+                            )
+                            device_ep_pairs.append(
+                                (
+                                    device_obj_name_2,
+                                    ep_name_2,
+                                    device_obj_name_1,
+                                    ep_name_1,
+                                    sdp["id"],
+                                    ipv4_info,
+                                )
+                            )
+                        break
+                else:
+                    raise Exception("sdp between the domains not found")
+            connection_groups = slice_service["connection-groups"]["connection-group"]
+            for cg in connection_groups:
+                for cc in cg["connectivity-construct"]:
+                    for metric_bound in cc["service-slo-sle-policy"]["slo-policy"][
+                        "metric-bound"
+                    ]:
+                        if (
+                            metric_bound["metric-type"]
+                            == "ietf-network-slice-service:one-way-delay-maximum"
+                            and metric_bound["metric-unit"] == "milliseconds"
+                        ):
+                            metric_value = int(metric_bound["bound"])
+                            if metric_value < max_delay:
+                                max_delay = metric_value
+                        elif (
+                            metric_bound["metric-type"]
+                            == "ietf-network-slice-service:two-way-packet-loss"
+                            and metric_bound["metric-unit"] == "percentage"
+                        ):
+                            metric_value = float(metric_bound["percentile-value"])
+                            if metric_value < packet_loss:
+                                packet_loss = metric_value
+                        elif (
+                            metric_bound["metric-type"]
+                            == "ietf-network-slice-service:one-way-bandwidth"
+                            and metric_bound["metric-unit"] == "Mbps"
+                        ):
+                            metric_value = float(metric_bound["bound"])
+                            bandwidth += metric_value
+            if (
+                len(
+                    candidate_resource_value_dict["network-slice-services"][
+                        "slice-service"
+                    ][0]["connection-groups"]["connection-group"]
+                )
+                == 0
+            ):
+                operation_type = "delete"
+            target_connection_group = next(
+                cg for cg in connection_groups if cg["id"] == target_connection_group_id
+            )
+            source_device_ep_info, destination_device_ep_info = (
+                extract_source_destination_device_endpoint_info(
+                    device_ep_pairs, target_connection_group
+                )
+            )
+            resource_value_dict = {
+                "uuid": slice_name,
+                "operation_type": operation_type,
+                "src_node_id": source_device_ep_info["node_name"],
+                "src_mgmt_ip_address": source_device_ep_info["node_name"],
+                "src_ac_node_id": source_device_ep_info["node_name"],
+                "src_ac_ep_id": source_device_ep_info["endpoint_name"],
+                "src_vlan": source_device_ep_info["ipv4_info"]["vlan"],
+                "src_source_ip_prefix": source_device_ep_info["ipv4_info"]["src_ip"],
+                "src_source_tcp_port": source_device_ep_info["ipv4_info"]["src_port"],
+                "src_destination_ip_prefix": source_device_ep_info["ipv4_info"][
+                    "dst_ip"
+                ],
+                "src_destination_tcp_port": source_device_ep_info["ipv4_info"][
+                    "dst_port"
+                ],
+                "dst_node_id": destination_device_ep_info["node_name"],
+                "dst_mgmt_ip_address": destination_device_ep_info["node_name"],
+                "dst_ac_node_id": destination_device_ep_info["node_name"],
+                "dst_ac_ep_id": destination_device_ep_info["endpoint_name"],
+                "dst_vlan": destination_device_ep_info["ipv4_info"]["vlan"],
+                "dst_source_ip_prefix": destination_device_ep_info["ipv4_info"][
+                    "src_ip"
+                ],
+                "dst_source_tcp_port": destination_device_ep_info["ipv4_info"][
+                    "src_port"
+                ],
+                "dst_destination_ip_prefix": destination_device_ep_info["ipv4_info"][
+                    "dst_ip"
+                ],
+                "dst_destination_tcp_port": destination_device_ep_info["ipv4_info"][
+                    "dst_port"
+                ],
+                "slice_id": slice_name,
+                "delay": max_delay,
+                "bandwidth": bandwidth,
+                "packet_loss": packet_loss,
+            }
+
+            json_config_rules = setup_config_rules(slice_name, resource_value_dict)
+            del controller.device_config.config_rules[:]
+            for jcr in json_config_rules:
+                controller.device_config.config_rules.append(ConfigRule(**jcr))
+            self.__task_executor.configure_device(controller)
+        except Exception as e:  # pylint: disable=broad-except
+            raise e
+            results.append(e)
         return results
 
     @metered_subclass_method(METRICS_POOL)
-- 
GitLab