diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py
index 50f645e4234b667b07858f1a9d50c88f653725fb..87eda2678a8c14c2236809d0524e700a3480aa4a 100644
--- a/src/service/service/ServiceServiceServicerImpl.py
+++ b/src/service/service/ServiceServiceServicerImpl.py
@@ -95,13 +95,17 @@ class ServiceServiceServicerImpl(ServiceServiceServicer):
         service.CopyFrom(request if _service is None else _service)
         service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED     # pylint: disable=no-member
 
-        del service.service_endpoint_ids[:]                                     # pylint: disable=no-member
-        for service_endpoint_id in request.service_endpoint_ids:
-            service.service_endpoint_ids.add().CopyFrom(service_endpoint_id)    # pylint: disable=no-member
+        del service.service_endpoint_ids[:] # pylint: disable=no-member
+        for endpoint_id in request.service_endpoint_ids:
+            service.service_endpoint_ids.add().CopyFrom(endpoint_id)    # pylint: disable=no-member
 
-        del service.service_constraints[:]                                      # pylint: disable=no-member
-        for service_constraint in request.service_constraints:
-            service.service_constraints.add().CopyFrom(service_constraint)      # pylint: disable=no-member
+        del service.service_constraints[:]  # pylint: disable=no-member
+        for constraint in request.service_constraints:
+            service.service_constraints.add().CopyFrom(constraint)  # pylint: disable=no-member
+
+        del service.service_config.config_rules[:]  # pylint: disable=no-member
+        for config_rule in request.service_config.config_rules:
+            service.service_config.config_rules.add().CopyFrom(config_rule) # pylint: disable=no-member
 
         service_id_with_uuids = context_client.SetService(service)
         service_with_uuids = context_client.GetService(service_id_with_uuids)
diff --git a/src/service/service/service_handler_api/SettingsHandler.py b/src/service/service/service_handler_api/SettingsHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..4df24cee0e5a865dcc36473d3e118864c3421881
--- /dev/null
+++ b/src/service/service/service_handler_api/SettingsHandler.py
@@ -0,0 +1,88 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import anytree, json, logging
+from typing import Any, List, Optional, Tuple, Union
+from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, Device, EndPoint, ServiceConfig
+from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string
+from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
+
+LOGGER = logging.getLogger(__name__)
+
+class SettingsHandler:
+    def __init__(self, service_config : ServiceConfig, **settings) -> None:
+        self.__resolver = anytree.Resolver(pathattr='name')
+        self.__config = TreeNode('.')
+        for config_rule in service_config.config_rules:
+            self.update_config_rule(config_rule)
+
+    @staticmethod
+    def _config_rule_to_raw(config_rule : ConfigRule) -> Optional[Tuple[int, str, Any]]:
+        action = config_rule.action
+        kind = config_rule.WhichOneof('config_rule')
+        if kind == 'custom':
+            key_or_path = config_rule.custom.resource_key
+            value = config_rule.custom.resource_value
+            try:
+                value = json.loads(value)
+            except: # pylint: disable=bare-except
+                pass
+        elif kind == 'acl':
+            device_uuid = config_rule.acl.endpoint_id.device_id.device_uuid.uuid
+            endpoint_uuid = config_rule.acl.endpoint_id.endpoint_uuid.uuid
+            acl_ruleset_name = config_rule.acl.rule_set.name
+            ACL_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/acl_ruleset[{:s}]'
+            key_or_path = ACL_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, acl_ruleset_name)
+            value = grpc_message_to_json(config_rule.acl)
+        else:
+            MSG = 'Unsupported Kind({:s}) in ConfigRule({:s})'
+            LOGGER.warning(MSG.format(str(kind), grpc_message_to_json_string(config_rule)))
+            return None
+
+        return action, key_or_path, value
+
+    def get(self, key_or_path : Union[str, List[str]], default : Optional[Any] = None) -> Optional[TreeNode]:
+        return get_subnode(self.__resolver, self.__config, key_or_path, default=default)
+
+    def get_endpoint_settings(self, device : Device, endpoint : EndPoint) -> Optional[TreeNode]:
+        device_keys   = device.device_id.device_uuid.uuid,       device.name
+        endpoint_keys = endpoint.endpoint_id.endpoint_uuid.uuid, endpoint.name
+
+        for device_key in device_keys:
+            for endpoint_key in endpoint_keys:
+                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_key, endpoint_key)
+                endpoint_settings = self.get(endpoint_settings_uri)
+                if endpoint_settings is not None: return endpoint_settings
+
+        return None
+
+    def set(self, key_or_path : Union[str, List[str]], value : Any) -> None:
+        set_subnode_value(self.__resolver, self.__config, key_or_path, value)
+
+    def delete(self, key_or_path : Union[str, List[str]]) -> None:
+        delete_subnode(self.__resolver, self.__config, key_or_path)
+
+    def update_config_rule(self, config_rule : ConfigRule) -> None:
+        raw_data = SettingsHandler._config_rule_to_raw(config_rule)
+        if raw_data is None: return
+        action, key_or_path, value = raw_data
+
+        if action == ConfigActionEnum.CONFIGACTION_SET:
+            self.set(key_or_path, value)
+        elif action == ConfigActionEnum.CONFIGACTION_DELETE:
+            self.delete(key_or_path)
+        else:
+            MSG = 'Unsupported Action({:s}) in ConfigRule({:s})'
+            LOGGER.warning(MSG.format(str(action), grpc_message_to_json_string(config_rule)))
+            return
diff --git a/src/service/service/service_handler_api/Tools.py b/src/service/service/service_handler_api/Tools.py
index 61ad7976132d2175319fffdeb5199f459c16d14f..ebd16a532c4ef4e74a61fd075afe9298755e26fb 100644
--- a/src/service/service/service_handler_api/Tools.py
+++ b/src/service/service/service_handler_api/Tools.py
@@ -14,6 +14,8 @@
 
 import functools
 from typing import Any, List, Union
+from common.method_wrappers.ServiceExceptions import NotFoundException
+from common.proto.context_pb2 import Device, EndPoint
 
 ACTION_MSG_SET_ENDPOINT      = 'Set EndPoint(device_uuid={:s}, endpoint_uuid={:s}, topology_uuid={:s})'
 ACTION_MSG_DELETE_ENDPOINT   = 'Delete EndPoint(device_uuid={:s}, endpoint_uuid={:s}, topology_uuid={:s})'
@@ -40,3 +42,12 @@ check_errors_setconstraint    = functools.partial(_check_errors, ACTION_MSG_SET_
 check_errors_deleteconstraint = functools.partial(_check_errors, ACTION_MSG_DELETE_CONSTRAINT)
 check_errors_setconfig        = functools.partial(_check_errors, ACTION_MSG_SET_CONFIG       )
 check_errors_deleteconfig     = functools.partial(_check_errors, ACTION_MSG_DELETE_CONFIG    )
+
+def get_endpoint_matching(device : Device, endpoint_uuid_or_name : str) -> EndPoint:
+    for endpoint in device.device_endpoints:
+        choices = {endpoint.endpoint_id.endpoint_uuid.uuid, endpoint.name}
+        if endpoint_uuid_or_name in choices: return endpoint
+
+    device_uuid = device.device_id.device_uuid.uuid
+    extra_details = 'Device({:s})'.format(str(device_uuid))
+    raise NotFoundException('Endpoint', endpoint_uuid_or_name, extra_details=extra_details)
diff --git a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py
index bc628c160eaaa9ac282c81bd4c0e02536e88a80c..66259d1f636c712bc41f282b4a5d947c57e01fc4 100644
--- a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py
+++ b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py
@@ -12,14 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import anytree, json, logging
+import json, logging
 from typing import Any, List, Optional, Tuple, Union
 from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF
-from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
 from common.tools.object_factory.Device import json_device_id
 from common.type_checkers.Checkers import chk_length, chk_type
+from service.service.service_handler_api.Tools import get_endpoint_matching
 from service.service.service_handler_api._ServiceHandler import _ServiceHandler
-from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
 from service.service.task_scheduler.TaskExecutor import TaskExecutor
 from .ConfigRules import setup_config_rules, teardown_config_rules
 
@@ -47,22 +48,8 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
         self, service : Service, task_executor : TaskExecutor, **settings
     ) -> None:
         self.__service = service
-        self.__task_executor = task_executor # pylint: disable=unused-private-member
-        self.__resolver = anytree.Resolver(pathattr='name')
-        self.__config = TreeNode('.')
-        for config_rule in service.service_config.config_rules:
-            action = config_rule.action
-            if config_rule.WhichOneof('config_rule') != 'custom': continue
-            resource_key = config_rule.custom.resource_key
-            resource_value = config_rule.custom.resource_value
-            if action == ConfigActionEnum.CONFIGACTION_SET:
-                try:
-                    resource_value = json.loads(resource_value)
-                except: # pylint: disable=bare-except
-                    pass
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
-            elif action == ConfigActionEnum.CONFIGACTION_DELETE:
-                delete_subnode(self.__resolver, self.__config, resource_key)
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
 
     @metered_subclass_method(METRICS_POOL)
     def SetEndpoint(
@@ -72,7 +59,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        settings = self.__settings_handler.get('/settings')
 
         results = []
         for endpoint in endpoints:
@@ -81,17 +68,17 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
                 chk_length('endpoint', endpoint, min_length=2, max_length=3)
                 device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
 
-                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
-                endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
 
                 json_config_rules = setup_config_rules(
                     service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings)
 
-                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-                del device.device_config.config_rules[:]
+                del device_obj.device_config.config_rules[:]
                 for json_config_rule in json_config_rules:
-                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-                self.__task_executor.configure_device(device)
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
@@ -107,7 +94,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        settings = self.__settings_handler.get('/settings')
 
         results = []
         for endpoint in endpoints:
@@ -116,17 +103,17 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
                 chk_length('endpoint', endpoint, min_length=2, max_length=3)
                 device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
 
-                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
-                endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
 
                 json_config_rules = teardown_config_rules(
                     service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings)
 
-                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-                del device.device_config.config_rules[:]
+                del device_obj.device_config.config_rules[:]
                 for json_config_rule in json_config_rules:
-                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-                self.__task_executor.configure_device(device)
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
@@ -160,9 +147,8 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, resource_value = resource
-                resource_value = json.loads(resource_value)
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
+                resource_value = json.loads(resource[1])
+                self.__settings_handler.set(resource[0], resource_value)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
@@ -178,8 +164,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, _ = resource
-                delete_subnode(self.__resolver, self.__config, resource_key)
+                self.__settings_handler.delete(resource[0])
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
                 results.append(e)
diff --git a/src/service/service/service_handlers/l2nm_openconfig/L2NMOpenConfigServiceHandler.py b/src/service/service/service_handlers/l2nm_openconfig/L2NMOpenConfigServiceHandler.py
index 23df44413c17e66a631988eb6256316badf0d554..63442a6b46d3301ff15ee0d4416468f01d2f61a5 100644
--- a/src/service/service/service_handlers/l2nm_openconfig/L2NMOpenConfigServiceHandler.py
+++ b/src/service/service/service_handlers/l2nm_openconfig/L2NMOpenConfigServiceHandler.py
@@ -12,14 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import anytree, json, logging
+import json, logging
 from typing import Any, List, Optional, Tuple, Union
 from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF
-from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
 from common.tools.object_factory.Device import json_device_id
 from common.type_checkers.Checkers import chk_length, chk_type
+from service.service.service_handler_api.Tools import get_endpoint_matching
 from service.service.service_handler_api._ServiceHandler import _ServiceHandler
-from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
 from service.service.task_scheduler.TaskExecutor import TaskExecutor
 from .ConfigRules import setup_config_rules, teardown_config_rules
 
@@ -47,22 +48,8 @@ class L2NMOpenConfigServiceHandler(_ServiceHandler):
         self, service : Service, task_executor : TaskExecutor, **settings
     ) -> None:
         self.__service = service
-        self.__task_executor = task_executor # pylint: disable=unused-private-member
-        self.__resolver = anytree.Resolver(pathattr='name')
-        self.__config = TreeNode('.')
-        for config_rule in service.service_config.config_rules:
-            action = config_rule.action
-            if config_rule.WhichOneof('config_rule') != 'custom': continue
-            resource_key = config_rule.custom.resource_key
-            resource_value = config_rule.custom.resource_value
-            if action == ConfigActionEnum.CONFIGACTION_SET:
-                try:
-                    resource_value = json.loads(resource_value)
-                except: # pylint: disable=bare-except
-                    pass
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
-            elif action == ConfigActionEnum.CONFIGACTION_DELETE:
-                delete_subnode(self.__resolver, self.__config, resource_key)
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
 
     @metered_subclass_method(METRICS_POOL)
     def SetEndpoint(
@@ -72,7 +59,7 @@ class L2NMOpenConfigServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        settings = self.__settings_handler.get('/settings')
 
         results = []
         for endpoint in endpoints:
@@ -81,17 +68,17 @@ class L2NMOpenConfigServiceHandler(_ServiceHandler):
                 chk_length('endpoint', endpoint, min_length=2, max_length=3)
                 device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
 
-                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
-                endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
 
                 json_config_rules = setup_config_rules(
                     service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings)
 
-                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-                del device.device_config.config_rules[:]
+                del device_obj.device_config.config_rules[:]
                 for json_config_rule in json_config_rules:
-                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-                self.__task_executor.configure_device(device)
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
@@ -107,7 +94,7 @@ class L2NMOpenConfigServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        settings = self.__settings_handler.get('/settings')
 
         results = []
         for endpoint in endpoints:
@@ -116,17 +103,17 @@ class L2NMOpenConfigServiceHandler(_ServiceHandler):
                 chk_length('endpoint', endpoint, min_length=2, max_length=3)
                 device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
 
-                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
-                endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
 
                 json_config_rules = teardown_config_rules(
                     service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings)
 
-                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-                del device.device_config.config_rules[:]
+                del device_obj.device_config.config_rules[:]
                 for json_config_rule in json_config_rules:
-                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-                self.__task_executor.configure_device(device)
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
@@ -160,9 +147,8 @@ class L2NMOpenConfigServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, resource_value = resource
-                resource_value = json.loads(resource_value)
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
+                resource_value = json.loads(resource[1])
+                self.__settings_handler.set(resource[0], resource_value)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
@@ -178,8 +164,7 @@ class L2NMOpenConfigServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, _ = resource
-                delete_subnode(self.__resolver, self.__config, resource_key)
+                self.__settings_handler.delete(resource[0])
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
                 results.append(e)
diff --git a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
index f161225192dfe7f9eb0804b9d9bff4e5acba9e21..8a39ed47463d70bf2c2c42cbb9308ba5e072caf4 100644
--- a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
+++ b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py
@@ -12,14 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import anytree, json, logging
+import json, logging
 from typing import Any, List, Optional, Tuple, Union
 from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF
-from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
 from common.tools.object_factory.Device import json_device_id
 from common.type_checkers.Checkers import chk_length, chk_type
+from service.service.service_handler_api.Tools import get_endpoint_matching
 from service.service.service_handler_api._ServiceHandler import _ServiceHandler
-from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
 from service.service.task_scheduler.TaskExecutor import TaskExecutor
 from .ConfigRules import setup_config_rules, teardown_config_rules
 
@@ -47,67 +48,64 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
         self, service : Service, task_executor : TaskExecutor, **settings
     ) -> None:
         self.__service = service
-        self.__task_executor = task_executor # pylint: disable=unused-private-member
-        self.__resolver = anytree.Resolver(pathattr='name')
-        self.__config = TreeNode('.')
-        for config_rule in service.service_config.config_rules:
-            action = config_rule.action
-            if config_rule.WhichOneof('config_rule') != 'custom': continue
-            resource_key = config_rule.custom.resource_key
-            resource_value = config_rule.custom.resource_value
-            if action == ConfigActionEnum.CONFIGACTION_SET:
-                try:
-                    resource_value = json.loads(resource_value)
-                except: # pylint: disable=bare-except
-                    pass
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
-            elif action == ConfigActionEnum.CONFIGACTION_DELETE:
-                delete_subnode(self.__resolver, self.__config, resource_key)
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
 
     @metered_subclass_method(METRICS_POOL)
     def SetEndpoint(
         self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
     ) -> List[Union[bool, Exception]]:
+        LOGGER.info('[SetEndpoint] endpoints={:s}'.format(str(endpoints)))
+        LOGGER.info('[SetEndpoint] connection_uuid={:s}'.format(str(connection_uuid)))
+
         chk_type('endpoints', endpoints, list)
         if len(endpoints) == 0: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        settings = self.__settings_handler.get('/settings')
+        LOGGER.info('[SetEndpoint] settings={:s}'.format(str(settings)))
 
         results = []
         for endpoint in endpoints:
+            LOGGER.info('[SetEndpoint] endpoint={:s}'.format(str(endpoint)))
             try:
                 chk_type('endpoint', endpoint, (tuple, list))
                 chk_length('endpoint', endpoint, min_length=2, max_length=3)
                 device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
 
-                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
-                endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
+                LOGGER.info('[SetEndpoint] endpoint_settings={:s}'.format(str(endpoint_settings)))
 
                 json_config_rules = setup_config_rules(
                     service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings)
+                LOGGER.info('[SetEndpoint] json_config_rules={:s}'.format(str(json_config_rules)))
 
-                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-                del device.device_config.config_rules[:]
+                del device_obj.device_config.config_rules[:]
                 for json_config_rule in json_config_rules:
-                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-                self.__task_executor.configure_device(device)
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
                 results.append(e)
 
+        LOGGER.info('[SetEndpoint] results={:s}'.format(str(results)))
         return results
 
     @metered_subclass_method(METRICS_POOL)
     def DeleteEndpoint(
         self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
     ) -> List[Union[bool, Exception]]:
+        LOGGER.info('[DeleteEndpoint] endpoints={:s}'.format(str(endpoints)))
+        LOGGER.info('[DeleteEndpoint] connection_uuid={:s}'.format(str(connection_uuid)))
+
         chk_type('endpoints', endpoints, list)
         if len(endpoints) == 0: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        settings = self.__settings_handler.get('/settings')
 
         results = []
         for endpoint in endpoints:
@@ -116,17 +114,17 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
                 chk_length('endpoint', endpoint, min_length=2, max_length=3)
                 device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
 
-                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
-                endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
 
                 json_config_rules = teardown_config_rules(
                     service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings)
 
-                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-                del device.device_config.config_rules[:]
+                del device_obj.device_config.config_rules[:]
                 for json_config_rule in json_config_rules:
-                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-                self.__task_executor.configure_device(device)
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
@@ -160,9 +158,8 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, resource_value = resource
-                resource_value = json.loads(resource_value)
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
+                resource_value = json.loads(resource[1])
+                self.__settings_handler.set(resource[0], resource_value)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
@@ -178,8 +175,7 @@ class L3NMEmulatedServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, _ = resource
-                delete_subnode(self.__resolver, self.__config, resource_key)
+                self.__settings_handler.delete(resource[0])
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
                 results.append(e)
diff --git a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py
index 0f5cb6c558c1515b81d011074ecda7e167c47e90..3dc98f71b3f64557782b700220d1d3ab84314b4b 100644
--- a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py
+++ b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py
@@ -12,14 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import anytree, json, logging
+import json, logging
 from typing import Any, List, Optional, Tuple, Union
 from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF
-from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
 from common.tools.object_factory.Device import json_device_id
 from common.type_checkers.Checkers import chk_length, chk_type
+from service.service.service_handler_api.Tools import get_endpoint_matching
 from service.service.service_handler_api._ServiceHandler import _ServiceHandler
-from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
 from service.service.task_scheduler.TaskExecutor import TaskExecutor
 from .ConfigRules import setup_config_rules, teardown_config_rules
 
@@ -47,22 +48,8 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
         self, service : Service, task_executor : TaskExecutor, **settings
     ) -> None:
         self.__service = service
-        self.__task_executor = task_executor # pylint: disable=unused-private-member
-        self.__resolver = anytree.Resolver(pathattr='name')
-        self.__config = TreeNode('.')
-        for config_rule in service.service_config.config_rules:
-            action = config_rule.action
-            if config_rule.WhichOneof('config_rule') != 'custom': continue
-            resource_key = config_rule.custom.resource_key
-            resource_value = config_rule.custom.resource_value
-            if action == ConfigActionEnum.CONFIGACTION_SET:
-                try:
-                    resource_value = json.loads(resource_value)
-                except: # pylint: disable=bare-except
-                    pass
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
-            elif action == ConfigActionEnum.CONFIGACTION_DELETE:
-                delete_subnode(self.__resolver, self.__config, resource_key)
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
 
     @metered_subclass_method(METRICS_POOL)
     def SetEndpoint(
@@ -72,7 +59,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        settings = self.__settings_handler.get('/settings')
 
         results = []
         for endpoint in endpoints:
@@ -81,17 +68,17 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
                 chk_length('endpoint', endpoint, min_length=2, max_length=3)
                 device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
 
-                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
-                endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
 
                 json_config_rules = setup_config_rules(
                     service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings)
 
-                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-                del device.device_config.config_rules[:]
+                del device_obj.device_config.config_rules[:]
                 for json_config_rule in json_config_rules:
-                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-                self.__task_executor.configure_device(device)
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
@@ -107,7 +94,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
         if len(endpoints) == 0: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
+        settings = self.__settings_handler.get('/settings')
 
         results = []
         for endpoint in endpoints:
@@ -116,17 +103,17 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
                 chk_length('endpoint', endpoint, min_length=2, max_length=3)
                 device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
 
-                endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
-                endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None)
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
 
                 json_config_rules = teardown_config_rules(
                     service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings)
 
-                device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-                del device.device_config.config_rules[:]
+                del device_obj.device_config.config_rules[:]
                 for json_config_rule in json_config_rules:
-                    device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-                self.__task_executor.configure_device(device)
+                    device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                self.__task_executor.configure_device(device_obj)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
@@ -160,9 +147,8 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, resource_value = resource
-                resource_value = json.loads(resource_value)
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
+                resource_value = json.loads(resource[1])
+                self.__settings_handler.set(resource[0], resource_value)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
@@ -178,8 +164,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, _ = resource
-                delete_subnode(self.__resolver, self.__config, resource_key)
+                self.__settings_handler.delete(resource[0])
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
                 results.append(e)
diff --git a/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py b/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py
index fb54a1bc1db3071e88fd26e935c7779c7c2f19ee..a16f8cdfad5524a45c36502610d615f3b5dbbba4 100644
--- a/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py
+++ b/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py
@@ -12,15 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import anytree, json, logging
+import json, logging
 from typing import Any, Dict, List, Optional, Tuple, Union
 from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
-from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
 from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
 from common.tools.object_factory.Device import json_device_id
 from common.type_checkers.Checkers import chk_type
 from service.service.service_handler_api._ServiceHandler import _ServiceHandler
-from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
 from service.service.task_scheduler.TaskExecutor import TaskExecutor
 
 LOGGER = logging.getLogger(__name__)
@@ -38,22 +38,8 @@ class MicrowaveServiceHandler(_ServiceHandler):
         self, service : Service, task_executor : TaskExecutor, **settings
     ) -> None:
         self.__service = service
-        self.__task_executor = task_executor # pylint: disable=unused-private-member
-        self.__resolver = anytree.Resolver(pathattr='name')
-        self.__config = TreeNode('.')
-        for config_rule in service.service_config.config_rules:
-            action = config_rule.action
-            if config_rule.WhichOneof('config_rule') != 'custom': continue
-            resource_key = config_rule.custom.resource_key
-            resource_value = config_rule.custom.resource_value
-            if action == ConfigActionEnum.CONFIGACTION_SET:
-                try:
-                    resource_value = json.loads(resource_value)
-                except: # pylint: disable=bare-except
-                    pass
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
-            elif action == ConfigActionEnum.CONFIGACTION_DELETE:
-                delete_subnode(self.__resolver, self.__config, resource_key)
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
 
     @metered_subclass_method(METRICS_POOL)
     def SetEndpoint(
@@ -61,26 +47,22 @@ class MicrowaveServiceHandler(_ServiceHandler):
     ) -> List[Union[bool, Exception]]:
         LOGGER.info('[SetEndpoint] endpoints={:s}'.format(str(endpoints)))
         LOGGER.info('[SetEndpoint] connection_uuid={:s}'.format(str(connection_uuid)))
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) != 2: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
+        settings = self.__settings_handler.get('/settings')
+        json_settings : Dict = {} if settings is None else settings.value
+        vlan_id = json_settings.get('vlan_id', 121)
 
         results = []
         try:
-            chk_type('endpoints', endpoints, list)
-            if len(endpoints) != 2: raise Exception('len(endpoints) != 2')
-
-            settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
-            if settings is None:
-                raise Exception('Unable to retrieve settings for Service({:s})'.format(str(service_uuid)))
-
-            json_settings : Dict = settings.value
-            vlan_id = json_settings.get('vlan_id', 121)
             # endpoints are retrieved in the following format --> '/endpoints/endpoint[172.26.60.243:9]'
             node_id_src, tp_id_src = check_endpoint(endpoints[0][1], service_uuid)
             node_id_dst, tp_id_dst = check_endpoint(endpoints[1][1], service_uuid)
         
             device_uuid = endpoints[0][0]
-            device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+            device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
             json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), {
                 'uuid'       : service_uuid,
                 'node_id_src': node_id_src,
@@ -89,9 +71,9 @@ class MicrowaveServiceHandler(_ServiceHandler):
                 'tp_id_dst'  : tp_id_dst,
                 'vlan_id'    : vlan_id,
             })
-            del device.device_config.config_rules[:]
-            device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-            self.__task_executor.configure_device(device)
+            del device_obj.device_config.config_rules[:]
+            device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+            self.__task_executor.configure_device(device_obj)
             results.append(True)
         except Exception as e: # pylint: disable=broad-except
             LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid)))
@@ -106,21 +88,21 @@ class MicrowaveServiceHandler(_ServiceHandler):
         LOGGER.info('[DeleteEndpoint] endpoints={:s}'.format(str(endpoints)))
         LOGGER.info('[DeleteEndpoint] connection_uuid={:s}'.format(str(connection_uuid)))
 
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) != 2: return []
+
         service_uuid = self.__service.service_id.service_uuid.uuid
 
         results = []
         try:
-            chk_type('endpoints', endpoints, list)
-            if len(endpoints) < 1: raise Exception('len(endpoints) < 1')
-
             device_uuid = endpoints[0][0]
-            device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+            device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
             json_config_rule = json_config_rule_delete('/services/service[{:s}]'.format(service_uuid), {
                 'uuid': service_uuid
             })
-            del device.device_config.config_rules[:]
-            device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-            self.__task_executor.configure_device(device)
+            del device_obj.device_config.config_rules[:]
+            device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+            self.__task_executor.configure_device(device_obj)
             results.append(True)
         except Exception as e: # pylint: disable=broad-except
             LOGGER.exception('Unable to DeleteEndpoint for Service({:s})'.format(str(service_uuid)))
@@ -154,9 +136,8 @@ class MicrowaveServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, resource_value = resource
-                resource_value = json.loads(resource_value)
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
+                resource_value = json.loads(resource[1])
+                self.__settings_handler.set(resource[0], resource_value)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
@@ -172,8 +153,7 @@ class MicrowaveServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, _ = resource
-                delete_subnode(self.__resolver, self.__config, resource_key)
+                self.__settings_handler.delete(resource[0])
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
                 results.append(e)
diff --git a/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py
index 24371203ad599d7ad9a7f66e5ad96874471be00b..d8a4668bbf102fa5b5f8c9e9f542f34b063bc819 100644
--- a/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py
+++ b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py
@@ -12,15 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import anytree, json, logging
+import json, logging
 from typing import Any, Dict, List, Optional, Tuple, Union
 from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
-from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
 from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
 from common.tools.object_factory.Device import json_device_id
 from common.type_checkers.Checkers import chk_type
 from service.service.service_handler_api._ServiceHandler import _ServiceHandler
-from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
 from service.service.task_scheduler.TaskExecutor import TaskExecutor
 
 LOGGER = logging.getLogger(__name__)
@@ -32,22 +32,8 @@ class TapiServiceHandler(_ServiceHandler):
         self, service : Service, task_executor : TaskExecutor, **settings
     ) -> None:
         self.__service = service
-        self.__task_executor = task_executor # pylint: disable=unused-private-member
-        self.__resolver = anytree.Resolver(pathattr='name')
-        self.__config = TreeNode('.')
-        for config_rule in service.service_config.config_rules:
-            action = config_rule.action
-            if config_rule.WhichOneof('config_rule') != 'custom': continue
-            resource_key = config_rule.custom.resource_key
-            resource_value = config_rule.custom.resource_value
-            if action == ConfigActionEnum.CONFIGACTION_SET:
-                try:
-                    resource_value = json.loads(resource_value)
-                except: # pylint: disable=bare-except
-                    pass
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
-            elif action == ConfigActionEnum.CONFIGACTION_DELETE:
-                delete_subnode(self.__resolver, self.__config, resource_key)
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
 
     @metered_subclass_method(METRICS_POOL)
     def SetEndpoint(
@@ -59,10 +45,8 @@ class TapiServiceHandler(_ServiceHandler):
         if len(endpoints) != 2: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
-        settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None)
-        if settings is None: raise Exception('Unable to retrieve settings for Service({:s})'.format(str(service_uuid)))
-
-        json_settings : Dict = settings.value
+        settings = self.__settings_handler.get('/settings')
+        json_settings : Dict = {} if settings is None else settings.value
         capacity_value   = json_settings.get('capacity_value',   50.0)
         capacity_unit    = json_settings.get('capacity_unit',    'GHz')
         layer_proto_name = json_settings.get('layer_proto_name', 'PHOTONIC_MEDIA')
@@ -72,7 +56,7 @@ class TapiServiceHandler(_ServiceHandler):
         results = []
         try:
             device_uuid = endpoints[0][0]
-            device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+            device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
             json_config_rule = json_config_rule_set('/service[{:s}]'.format(service_uuid), {
                 'uuid'                    : service_uuid,
                 'input_sip'               : endpoints[0][1],
@@ -83,12 +67,12 @@ class TapiServiceHandler(_ServiceHandler):
                 'layer_protocol_qualifier': layer_proto_qual,
                 'direction'               : direction,
             })
-            del device.device_config.config_rules[:]
-            device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-            self.__task_executor.configure_device(device)
+            del device_obj.device_config.config_rules[:]
+            device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+            self.__task_executor.configure_device(device_obj)
             results.append(True)
         except Exception as e: # pylint: disable=broad-except
-            LOGGER.exception('Unable to configure Service({:s})'.format(str(service_uuid)))
+            LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid)))
             results.append(e)
 
         return results
@@ -104,14 +88,17 @@ class TapiServiceHandler(_ServiceHandler):
         if len(endpoints) != 2: return []
 
         service_uuid = self.__service.service_id.service_uuid.uuid
+
         results = []
         try:
             device_uuid = endpoints[0][0]
-            device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
-            json_config_rule = json_config_rule_delete('/service[{:s}]'.format(service_uuid), {'uuid': service_uuid})
-            del device.device_config.config_rules[:]
-            device.device_config.config_rules.append(ConfigRule(**json_config_rule))
-            self.__task_executor.configure_device(device)
+            device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+            json_config_rule = json_config_rule_delete('/service[{:s}]'.format(service_uuid), {
+                'uuid': service_uuid
+            })
+            del device_obj.device_config.config_rules[:]
+            device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+            self.__task_executor.configure_device(device_obj)
             results.append(True)
         except Exception as e: # pylint: disable=broad-except
             LOGGER.exception('Unable to DeleteEndpoint for Service({:s})'.format(str(service_uuid)))
@@ -145,9 +132,8 @@ class TapiServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, resource_value = resource
-                resource_value = json.loads(resource_value)
-                set_subnode_value(self.__resolver, self.__config, resource_key, resource_value)
+                resource_value = json.loads(resource[1])
+                self.__settings_handler.set(resource[0], resource_value)
                 results.append(True)
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
@@ -163,8 +149,7 @@ class TapiServiceHandler(_ServiceHandler):
         results = []
         for resource in resources:
             try:
-                resource_key, _ = resource
-                delete_subnode(self.__resolver, self.__config, resource_key)
+                self.__settings_handler.delete(resource[0])
             except Exception as e: # pylint: disable=broad-except
                 LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
                 results.append(e)