diff --git a/scripts/run_tests_locally-telemetry-gnmi.sh b/scripts/run_tests_locally-telemetry-gnmi.sh
index 3c7630edae762c5279a1c4b41cdd14a10748803c..bff0317f63573e4a5596815b460b80a79b7af5af 100755
--- a/scripts/run_tests_locally-telemetry-gnmi.sh
+++ b/scripts/run_tests_locally-telemetry-gnmi.sh
@@ -24,6 +24,6 @@ cd $PROJECTDIR/src
 # export CRDB_URI="cockroachdb://tfs:tfs123@${CRDB_SQL_ADDRESS}:26257/tfs_telemetry?sslmode=require"
 RCFILE=$PROJECTDIR/coverage/.coveragerc
 
-python3 -m pytest --log-level=debug --log-cli-level=info --verbose \
-    telemetry/backend/tests/test_gnmi_collector.py
+python3 -m pytest --log-level=info --log-cli-level=info --verbose \
+    telemetry/backend/tests/gnmi_openconfig/test_gnmi_openconfig_collector.py
 
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/__init__.py b/src/telemetry/backend/collectors/gnmi_openconfig/__init__.py
deleted file mode 100644
index 023830645e0fcb60e3f8583674a954810af222f2..0000000000000000000000000000000000000000
--- a/src/telemetry/backend/collectors/gnmi_openconfig/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
-#
-# 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.
diff --git a/src/telemetry/backend/service/collector_api/AnyTreeTools.py b/src/telemetry/backend/service/collector_api/AnyTreeTools.py
new file mode 100644
index 0000000000000000000000000000000000000000..9d9228d9b7da119f032cdfd9f8e0244e3af37279
--- /dev/null
+++ b/src/telemetry/backend/service/collector_api/AnyTreeTools.py
@@ -0,0 +1,83 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# 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
+from typing import Any, List, Optional, Union
+from apscheduler.job import Job
+
+class TreeNode(anytree.node.Node):
+    def __init__(self, name, parent=None, children=None, **kwargs) -> None:
+        super().__init__(name, parent=parent, children=children, **kwargs)
+        self.value : Optional[Any] = None
+
+    def get_full_path(self):
+        return self.separator.join([''] + [str(node.name) for node in self.path])
+
+class RawStyle(anytree.render.AbstractStyle):
+    def __init__(self):
+        """
+        Raw style.
+
+        >>> from anytree import Node, RenderTree
+        >>> root = Node("root")
+        >>> s0 = Node("sub0", parent=root)
+        >>> s0b = Node("sub0B", parent=s0)
+        >>> s0a = Node("sub0A", parent=s0)
+        >>> s1 = Node("sub1", parent=root)
+        >>> print(RenderTree(root, style=RawStyle()))
+        Node('/root')
+        Node('/root/sub0')
+        Node('/root/sub0/sub0B')
+        Node('/root/sub0/sub0A')
+        Node('/root/sub1')
+        """
+        super(RawStyle, self).__init__('', '', '')
+
+def get_subnode(
+    resolver : anytree.Resolver, root : TreeNode, key_or_path : Union[str, List[str]], default : Optional[Any] = None):
+
+    if isinstance(key_or_path, str): key_or_path = key_or_path.split('/')
+    node = root
+    for path_item in key_or_path:
+        try:
+            node = resolver.get(node, path_item)
+        except anytree.ChildResolverError:
+            return default
+    return node
+
+def set_subnode_value(resolver : anytree.Resolver, root : TreeNode, key_or_path : Union[str, List[str]], value : Any):
+    if isinstance(key_or_path, str): key_or_path = key_or_path.split('/')
+    node = root
+    for path_item in key_or_path:
+        try:
+            node = resolver.get(node, path_item)
+        except anytree.ChildResolverError:
+            node = TreeNode(path_item, parent=node)
+    if isinstance(node.value, dict) and isinstance(value, dict):
+        node.value.update(value)
+    else:
+        node.value = value
+
+def dump_subtree(root : TreeNode):
+    if not isinstance(root, TreeNode): raise Exception('root must be a TreeNode')
+    results = []
+    for row in anytree.RenderTree(root, style=RawStyle()):
+        node : TreeNode = row.node
+        path = node.get_full_path()[2:] # get full path except the heading root placeholder "/."
+        if len(path) == 0: continue
+        value = node.value
+        if value is None: continue
+        if isinstance(value, Job): value = str(value)
+        results.append((path, value))
+    return results
diff --git a/src/telemetry/backend/service/collector_api/DriverFactory.py b/src/telemetry/backend/service/collector_api/DriverFactory.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd11ca125fd4198ed3cf2643b42e44fac5fea0aa
--- /dev/null
+++ b/src/telemetry/backend/service/collector_api/DriverFactory.py
@@ -0,0 +1,88 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# 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 logging, operator
+from enum import Enum
+from typing import Any, Dict, Iterable, List, Set, Tuple
+from ._Collector import _Collector
+from .Exceptions import (
+    UnsatisfiedFilterException, UnsupportedDriverClassException, UnsupportedFilterFieldException,
+    UnsupportedFilterFieldValueException)
+from .FilterFields import FILTER_FIELD_ALLOWED_VALUES, FilterFieldEnum
+
+LOGGER = logging.getLogger(__name__)
+
+class DriverFactory:
+    def __init__(self, drivers : List[Tuple[type, List[Dict[FilterFieldEnum, Any]]]]) -> None:
+        self.__indices : Dict[str, Dict[str, Set[_Collector]]] = {} # Dict{field_name => Dict{field_value => Set{Driver}}}
+
+        for driver_class,filter_field_sets in drivers:
+            for filter_fields in filter_field_sets:
+                filter_fields = {k.value:v for k,v in filter_fields.items()}
+                self.register_driver_class(driver_class, **filter_fields)
+
+    def register_driver_class(self, driver_class, **filter_fields):
+        if not issubclass(driver_class, _Collector): raise UnsupportedDriverClassException(str(driver_class))
+
+        driver_name = driver_class.__name__
+        supported_filter_fields = set(FILTER_FIELD_ALLOWED_VALUES.keys())
+        unsupported_filter_fields = set(filter_fields.keys()).difference(supported_filter_fields)
+        if len(unsupported_filter_fields) > 0:
+            raise UnsupportedFilterFieldException(unsupported_filter_fields, driver_class_name=driver_name)
+
+        for field_name, field_values in filter_fields.items():
+            field_indice = self.__indices.setdefault(field_name, dict())
+            field_enum_values = FILTER_FIELD_ALLOWED_VALUES.get(field_name)
+            if not isinstance(field_values, Iterable) or isinstance(field_values, str):
+                field_values = [field_values]
+            for field_value in field_values:
+                if isinstance(field_value, Enum): field_value = field_value.value
+                if field_enum_values is not None and field_value not in field_enum_values:
+                    raise UnsupportedFilterFieldValueException(
+                        field_name, field_value, field_enum_values, driver_class_name=driver_name)
+                field_indice_drivers = field_indice.setdefault(field_value, set())
+                field_indice_drivers.add(driver_class)
+
+    def get_driver_class(self, **filter_fields) -> _Collector:
+        supported_filter_fields = set(FILTER_FIELD_ALLOWED_VALUES.keys())
+        unsupported_filter_fields = set(filter_fields.keys()).difference(supported_filter_fields)
+        if len(unsupported_filter_fields) > 0: raise UnsupportedFilterFieldException(unsupported_filter_fields)
+
+        candidate_driver_classes : Dict[_Collector, int] = None # number of filter hits per driver
+        for field_name, field_values in filter_fields.items():
+            field_indice = self.__indices.get(field_name)
+            if field_indice is None: continue
+            field_enum_values = FILTER_FIELD_ALLOWED_VALUES.get(field_name)
+            if not isinstance(field_values, Iterable) or isinstance(field_values, str):
+                field_values = [field_values]
+
+            field_candidate_driver_classes = set()
+            for field_value in field_values:
+                if field_enum_values is not None and field_value not in field_enum_values:
+                    raise UnsupportedFilterFieldValueException(field_name, field_value, field_enum_values)
+                field_indice_drivers = field_indice.get(field_value)
+                if field_indice_drivers is None: continue
+                field_candidate_driver_classes = field_candidate_driver_classes.union(field_indice_drivers)
+
+            if candidate_driver_classes is None:
+                if len(field_candidate_driver_classes) == 0: continue
+                candidate_driver_classes = {k:1 for k in field_candidate_driver_classes}
+            else:
+                for candidate_driver_class in candidate_driver_classes:
+                    if candidate_driver_class not in field_candidate_driver_classes: continue
+                    candidate_driver_classes[candidate_driver_class] += 1
+
+        if len(candidate_driver_classes) == 0: raise UnsatisfiedFilterException(filter_fields)
+        candidate_driver_classes = sorted(candidate_driver_classes.items(), key=operator.itemgetter(1), reverse=True)
+        return candidate_driver_classes[0][0]
diff --git a/src/telemetry/backend/service/collector_api/DriverInstanceCache.py b/src/telemetry/backend/service/collector_api/DriverInstanceCache.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c9becc4aeeeb403ea286780cd7aa3a1bd47d0aa
--- /dev/null
+++ b/src/telemetry/backend/service/collector_api/DriverInstanceCache.py
@@ -0,0 +1,115 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# 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 json, logging, threading
+from typing import Any, Dict, Optional
+from common.method_wrappers.ServiceExceptions import InvalidArgumentException
+from common.proto.context_pb2 import Device, Empty
+from context.client.ContextClient import ContextClient
+from telemetry.backend.Tools import get_connect_rules
+
+from ._Collector import _Collector
+from .DriverFactory import DriverFactory
+from .Exceptions import DriverInstanceCacheTerminatedException
+from .FilterFields import FilterFieldEnum, get_device_driver_filter_fields
+
+LOGGER = logging.getLogger(__name__)
+
+class DriverInstanceCache:
+    def __init__(self, driver_factory : DriverFactory) -> None:
+        self._lock = threading.Lock()
+        self._terminate = threading.Event()
+        self._device_uuid__to__driver_instance : Dict[str, _Collector] = {}
+        self._driver_factory = driver_factory
+
+    def get(
+        self, device_uuid : str, filter_fields : Dict[FilterFieldEnum, Any] = {}, address : Optional[str] = None,
+        port : Optional[int] = None, settings : Dict[str, Any] = {}
+    ) -> _Collector:
+
+        if self._terminate.is_set():
+            raise DriverInstanceCacheTerminatedException()
+
+        filter_fields = {k.value:v for k,v in filter_fields.items()}
+
+        with self._lock:
+            driver_instance = self._device_uuid__to__driver_instance.get(device_uuid)
+            if driver_instance is not None: return driver_instance
+
+            if len(filter_fields) == 0: return None
+            MSG = 'Selecting driver for device({:s}) with filter_fields({:s})...'
+            LOGGER.info(MSG.format(str(device_uuid), str(filter_fields)))
+            driver_class = self._driver_factory.get_driver_class(**filter_fields)
+            MSG = 'Driver({:s}) selected for device({:s}) with filter_fields({:s})...'
+            LOGGER.info(MSG.format(str(driver_class.__name__), str(device_uuid), str(filter_fields)))
+
+            if driver_class.__name__ == "OCDriver":
+                driver_instance : _Collector = driver_class(address, port, device_uuid=device_uuid, **settings)
+            else:
+                driver_instance : _Collector = driver_class(address, port, **settings)
+
+            self._device_uuid__to__driver_instance[device_uuid] = driver_instance
+            return driver_instance
+
+    def delete(self, device_uuid : str) -> None:
+        with self._lock:
+            device_driver = self._device_uuid__to__driver_instance.pop(device_uuid, None)
+            if device_driver is None: return
+            device_driver.Disconnect()
+
+    def terminate(self) -> None:
+        self._terminate.set()
+        with self._lock:
+            while len(self._device_uuid__to__driver_instance) > 0:
+                device_uuid,device_driver = self._device_uuid__to__driver_instance.popitem()
+                try:
+                    device_driver.Disconnect()
+                except: # pylint: disable=bare-except
+                    msg = 'Error disconnecting Driver({:s}) from device. Will retry later...'
+                    LOGGER.exception(msg.format(device_uuid))
+                    # re-adding to retry disconnect
+                    self._device_uuid__to__driver_instance[device_uuid] = device_driver
+
+def get_driver(driver_instance_cache : DriverInstanceCache, device : Device) -> _Collector:
+    device_uuid = device.device_id.device_uuid.uuid
+
+    driver : _Collector = driver_instance_cache.get(device_uuid)
+    if driver is not None: return driver
+
+    driver_filter_fields = get_device_driver_filter_fields(device)
+    connect_rules = get_connect_rules(device.device_config)
+
+    #LOGGER.info('[get_driver] connect_rules = {:s}'.format(str(connect_rules)))
+    address  = connect_rules.get('address',  '127.0.0.1')
+    port     = connect_rules.get('port',     '0')
+    settings = connect_rules.get('settings', '{}')
+
+    try:
+        settings = json.loads(settings)
+    except ValueError as e:
+        raise InvalidArgumentException(
+            'device.device_config.config_rules[settings]', settings,
+            extra_details='_connect/settings Config Rules provided cannot be decoded as JSON dictionary.'
+        ) from e
+
+    driver : _Collector = driver_instance_cache.get(
+        device_uuid, filter_fields=driver_filter_fields, address=address, port=port, settings=settings)
+    driver.Connect()
+
+    return driver
+
+def preload_drivers(driver_instance_cache : DriverInstanceCache) -> None:
+    context_client = ContextClient()
+    devices = context_client.ListDevices(Empty())
+    for device in devices.devices: get_driver(driver_instance_cache, device)
diff --git a/src/telemetry/backend/service/collector_api/Exceptions.py b/src/telemetry/backend/service/collector_api/Exceptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..8bc607d955c1b60576f28ad743e47a754eba3acd
--- /dev/null
+++ b/src/telemetry/backend/service/collector_api/Exceptions.py
@@ -0,0 +1,68 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# 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.
+
+class UnsatisfiedFilterException(Exception):
+    def __init__(self, filter_fields):
+        msg = 'No Driver satisfies FilterFields({:s})'
+        super().__init__(msg.format(str(filter_fields)))
+
+class UnsupportedDriverClassException(Exception):
+    def __init__(self, driver_class_name):
+        msg = 'Class({:s}) is not a subclass of _Driver'
+        super().__init__(msg.format(str(driver_class_name)))
+
+class UnsupportedFilterFieldException(Exception):
+    def __init__(self, unsupported_filter_fields, driver_class_name=None):
+        if driver_class_name:
+            msg = 'FilterFields({:s}) specified by Driver({:s}) are not supported'
+            msg = msg.format(str(unsupported_filter_fields), str(driver_class_name))
+        else:
+            msg = 'FilterFields({:s}) specified in Filter are not supported'
+            msg = msg.format(str(unsupported_filter_fields))
+        super().__init__(msg)
+
+class UnsupportedFilterFieldValueException(Exception):
+    def __init__(self, filter_field_name, filter_field_value, allowed_filter_field_values, driver_class_name=None):
+        if driver_class_name:
+            msg = 'FilterField({:s}={:s}) specified by Driver({:s}) is not supported. Allowed values are {:s}'
+            msg = msg.format(
+                str(filter_field_name), str(filter_field_value), str(driver_class_name),
+                str(allowed_filter_field_values))
+        else:
+            msg = 'FilterField({:s}={:s}) specified in Filter is not supported. Allowed values are {:s}'
+            msg = msg.format(str(filter_field_name), str(filter_field_value), str(allowed_filter_field_values))
+        super().__init__(msg)
+
+class DriverInstanceCacheTerminatedException(Exception):
+    def __init__(self):
+        msg = 'DriverInstanceCache is terminated. No new instances can be processed.'
+        super().__init__(msg)
+
+class UnsupportedResourceKeyException(Exception):
+    def __init__(self, resource_key):
+        msg = 'ResourceKey({:s}) not supported'
+        msg = msg.format(str(resource_key))
+        super().__init__(msg)
+
+class ConfigFieldNotFoundException(Exception):
+    def __init__(self, config_field_name):
+        msg = 'ConfigField({:s}) not specified in resource'
+        msg = msg.format(str(config_field_name))
+        super().__init__(msg)
+
+class ConfigFieldsNotSupportedException(Exception):
+    def __init__(self, config_fields):
+        msg = 'ConfigFields({:s}) not supported in resource'
+        msg = msg.format(str(config_fields))
+        super().__init__(msg)
diff --git a/src/telemetry/backend/service/collector_api/FilterFields.py b/src/telemetry/backend/service/collector_api/FilterFields.py
new file mode 100644
index 0000000000000000000000000000000000000000..b1d2cb3144f85f9e8db35c39808accc0d3c386a5
--- /dev/null
+++ b/src/telemetry/backend/service/collector_api/FilterFields.py
@@ -0,0 +1,41 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# 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.
+
+from enum import Enum
+from typing import Any, Dict, Optional
+from common.DeviceTypes import DeviceTypeEnum
+from common.proto.context_pb2 import Device, DeviceDriverEnum
+
+class FilterFieldEnum(Enum):
+    DEVICE_TYPE   = 'device_type'
+    DRIVER        = 'driver'
+    VENDOR        = 'vendor'
+    MODEL         = 'model'
+    SERIAL_NUMBER = 'serial_number'
+
+# Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified
+FILTER_FIELD_ALLOWED_VALUES = {
+    FilterFieldEnum.DEVICE_TYPE.value   : {i.value for i in DeviceTypeEnum},
+    FilterFieldEnum.DRIVER.value        : set(DeviceDriverEnum.values()),
+    FilterFieldEnum.VENDOR.value        : None,
+    FilterFieldEnum.MODEL.value         : None,
+    FilterFieldEnum.SERIAL_NUMBER.value : None,
+}
+
+def get_device_driver_filter_fields(device : Optional[Device]) -> Dict[FilterFieldEnum, Any]:
+    if device is None: return {}
+    return {
+        FilterFieldEnum.DEVICE_TYPE: device.device_type,
+        FilterFieldEnum.DRIVER     : [driver for driver in device.device_drivers],
+    }
diff --git a/src/telemetry/backend/collector_api/_Collector.py b/src/telemetry/backend/service/collector_api/_Collector.py
similarity index 97%
rename from src/telemetry/backend/collector_api/_Collector.py
rename to src/telemetry/backend/service/collector_api/_Collector.py
index e56644dd2826bc40087d775dcd1c28d89719af3a..4a73e516f4f41ef7212ce80c6a13bd8873aa5aff 100644
--- a/src/telemetry/backend/collector_api/_Collector.py
+++ b/src/telemetry/backend/service/collector_api/_Collector.py
@@ -18,14 +18,14 @@ from typing import Any, Iterator, List, Optional, Tuple, Union
 # Special resource names to request to the collector to retrieve the specified
 # configuration/structural resources.
 # These resource names should be used with GetConfig() method.
-RESOURCE_ENDPOINTS = '__endpoints__'
-RESOURCE_INTERFACES = '__interfaces__'
+RESOURCE_ENDPOINTS         = '__endpoints__'
+RESOURCE_INTERFACES        = '__interfaces__'
 RESOURCE_NETWORK_INSTANCES = '__network_instances__'
-RESOURCE_ROUTING_POLICIES = '__routing_policies__'
-RESOURCE_SERVICES = '__services__'
-RESOURCE_ACL = '__acl__'
-RESOURCE_INVENTORY = '__inventory__'
-
+RESOURCE_ROUTING_POLICIES  = '__routing_policies__'
+RESOURCE_SERVICES          = '__services__'
+RESOURCE_ACL               = '__acl__'
+RESOURCE_INVENTORY         = '__inventory__'
+RESOURCE_RULES             = "__rules__"
 
 class _Collector:
     def __init__(self, name : str, address: str, port: int, **settings) -> None:
diff --git a/src/telemetry/backend/collector_api/__init__.py b/src/telemetry/backend/service/collector_api/__init__.py
similarity index 100%
rename from src/telemetry/backend/collector_api/__init__.py
rename to src/telemetry/backend/service/collector_api/__init__.py
diff --git a/src/telemetry/backend/service/collectors/__init__.py b/src/telemetry/backend/service/collectors/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab707f5cad9730bff151efbb2b5f6a66feea932c
--- /dev/null
+++ b/src/telemetry/backend/service/collectors/__init__.py
@@ -0,0 +1,58 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# 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.
+
+from common.DeviceTypes           import DeviceTypeEnum
+from common.proto.context_pb2     import DeviceDriverEnum
+from telemetry.backend.Config     import LOAD_ALL_DEVICE_DRIVERS
+from ..collector_api.FilterFields import FilterFieldEnum
+
+DRIVERS = []
+
+from .emulated.EmulatedCollector import EmulatedCollector # pylint: disable=wrong-import-position
+DRIVERS.append(
+    (EmulatedCollector, [
+        # TODO: multi-filter is not working
+        {
+            FilterFieldEnum.DEVICE_TYPE: [
+                DeviceTypeEnum.EMULATED_P4_SWITCH,
+                DeviceTypeEnum.EMULATED_PACKET_ROUTER,
+                DeviceTypeEnum.EMULATED_PACKET_SWITCH,
+            ],
+            FilterFieldEnum.DRIVER: [
+                DeviceDriverEnum.DEVICEDRIVER_UNDEFINED,
+            ],
+        },
+    ]))
+
+if LOAD_ALL_DEVICE_DRIVERS:
+    from .gnmi_openconfig.GnmiOpenConfigCollector import GnmiOpenConfigCollector # pylint: disable=wrong-import-position
+    DRIVERS.append(
+        (GnmiOpenConfigCollector, [
+            {
+                # Real Packet Router, specifying OpenConfig Driver => use OpenConfigDriver
+                FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.PACKET_ROUTER,
+                FilterFieldEnum.DRIVER     : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG,
+            }
+        ]))
+
+if LOAD_ALL_DEVICE_DRIVERS:
+    from .p4.p4_collector import P4Collector # pylint: disable=wrong-import-position
+    DRIVERS.append(
+        (P4Collector, [
+            {
+                # Real P4 Switch, specifying P4 Collector => use P4Collector
+                FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.P4_SWITCH,
+                FilterFieldEnum.DRIVER     : DeviceDriverEnum.DEVICEDRIVER_P4,
+            }
+        ]))
diff --git a/src/telemetry/backend/collectors/emulated/EmulatedCollector.py b/src/telemetry/backend/service/collectors/emulated/EmulatedCollector.py
similarity index 98%
rename from src/telemetry/backend/collectors/emulated/EmulatedCollector.py
rename to src/telemetry/backend/service/collectors/emulated/EmulatedCollector.py
index 48102a943f54eead9b0119b2839faaa123e1cb51..1a9478b08cb6cb891e17bccecd569491ebcdc1bb 100644
--- a/src/telemetry/backend/collectors/emulated/EmulatedCollector.py
+++ b/src/telemetry/backend/service/collectors/emulated/EmulatedCollector.py
@@ -21,7 +21,7 @@ from apscheduler.jobstores.memory import MemoryJobStore
 from apscheduler.executors.pool import ThreadPoolExecutor
 from datetime import datetime, timedelta
 from typing import Any, Iterator, List, Tuple, Union, Optional
-from telemetry.backend.collector_api._Collector import _Collector
+from telemetry.backend.service.collector_api._Collector import _Collector
 from .EmulatedHelper import EmulatedCollectorHelper
 from .SyntheticMetricsGenerator import SyntheticMetricsGenerator
 
diff --git a/src/telemetry/backend/collectors/emulated/EmulatedHelper.py b/src/telemetry/backend/service/collectors/emulated/EmulatedHelper.py
similarity index 100%
rename from src/telemetry/backend/collectors/emulated/EmulatedHelper.py
rename to src/telemetry/backend/service/collectors/emulated/EmulatedHelper.py
diff --git a/src/telemetry/backend/collectors/emulated/SyntheticMetricsGenerator.py b/src/telemetry/backend/service/collectors/emulated/SyntheticMetricsGenerator.py
similarity index 100%
rename from src/telemetry/backend/collectors/emulated/SyntheticMetricsGenerator.py
rename to src/telemetry/backend/service/collectors/emulated/SyntheticMetricsGenerator.py
diff --git a/src/telemetry/backend/collectors/__init__.py b/src/telemetry/backend/service/collectors/emulated/__init__.py
similarity index 100%
rename from src/telemetry/backend/collectors/__init__.py
rename to src/telemetry/backend/service/collectors/emulated/__init__.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/Acknowledgement.txt b/src/telemetry/backend/service/collectors/gnmi_openconfig/Acknowledgement.txt
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/Acknowledgement.txt
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/Acknowledgement.txt
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/GnmiOpenConfigCollector.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/GnmiOpenConfigCollector.py
similarity index 98%
rename from src/telemetry/backend/collectors/gnmi_openconfig/GnmiOpenConfigCollector.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/GnmiOpenConfigCollector.py
index b318ae1d870c4b6ca7abc7c12a651423f71d3f8d..9d854824eae0aa957db5da735ca859fd2a3c82b8 100644
--- a/src/telemetry/backend/collectors/gnmi_openconfig/GnmiOpenConfigCollector.py
+++ b/src/telemetry/backend/service/collectors/gnmi_openconfig/GnmiOpenConfigCollector.py
@@ -16,7 +16,7 @@ import logging, queue, threading
 from typing import Any, Iterator, List, Optional, Tuple, Union
 from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
 from common.type_checkers.Checkers import chk_type
-from telemetry.backend.collector_api._Collector import _Collector
+from telemetry.backend.service.collector_api._Collector import _Collector
 from .GnmiSessionHandler import GnmiSessionHandler
 
 COLLECTOR_NAME = 'gnmi_openconfig'
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/GnmiSessionHandler.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/GnmiSessionHandler.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/GnmiSessionHandler.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/GnmiSessionHandler.py
diff --git a/src/telemetry/backend/collectors/emulated/__init__.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/__init__.py
similarity index 100%
rename from src/telemetry/backend/collectors/emulated/__init__.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/__init__.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/clone-yang-models.sh b/src/telemetry/backend/service/collectors/gnmi_openconfig/clone-yang-models.sh
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/clone-yang-models.sh
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/clone-yang-models.sh
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/gnmi/Acknowledgement.txt b/src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/Acknowledgement.txt
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/gnmi/Acknowledgement.txt
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/Acknowledgement.txt
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/gnmi/__init__.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/__init__.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/gnmi/__init__.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/__init__.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi.proto b/src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi.proto
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi.proto
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi.proto
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_ext.proto b/src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_ext.proto
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_ext.proto
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_ext.proto
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_pb2.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_pb2.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_pb2.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_pb2.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_pb2.py.old b/src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_pb2.py.old
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_pb2.py.old
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_pb2.py.old
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_pb2.pyi b/src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_pb2.pyi
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_pb2.pyi
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_pb2.pyi
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_pb2_grpc.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_pb2_grpc.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/gnmi/gnmi_pb2_grpc.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/gnmi/gnmi_pb2_grpc.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/Component.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/Component.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/Component.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/Component.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/Interface.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/Interface.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/Interface.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/Interface.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/InterfaceCounter.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/InterfaceCounter.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/InterfaceCounter.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/InterfaceCounter.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/NetworkInstance.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/NetworkInstance.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/NetworkInstance.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/NetworkInstance.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/NetworkInstanceInterface.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/NetworkInstanceInterface.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/NetworkInstanceInterface.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/NetworkInstanceInterface.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/NetworkInstanceProtocol.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/NetworkInstanceProtocol.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/NetworkInstanceProtocol.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/NetworkInstanceProtocol.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/NetworkInstanceStaticRoute.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/NetworkInstanceStaticRoute.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/NetworkInstanceStaticRoute.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/NetworkInstanceStaticRoute.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/Tools.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/Tools.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/Tools.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/Tools.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/YangHandler.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/YangHandler.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/YangHandler.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/YangHandler.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/_Handler.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/_Handler.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/_Handler.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/_Handler.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/__init__.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/__init__.py
similarity index 97%
rename from src/telemetry/backend/collectors/gnmi_openconfig/handlers/__init__.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/__init__.py
index 6af371cebffb4870004cb5d9e249f0bca3aaf035..0c803bb161d1303399cf9a3235e0b4874435922b 100644
--- a/src/telemetry/backend/collectors/gnmi_openconfig/handlers/__init__.py
+++ b/src/telemetry/backend/service/collectors/gnmi_openconfig/handlers/__init__.py
@@ -14,7 +14,7 @@
 
 import logging
 from typing import Any, Dict, List, Optional, Tuple, Union
-from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
+from telemetry.backend.service.collector_api._Collector import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
 from ._Handler import _Handler
 from .Component import ComponentHandler
 from .Interface import InterfaceHandler
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/tools/Capabilities.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Capabilities.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/tools/Capabilities.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Capabilities.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/tools/Channel.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Channel.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/tools/Channel.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Channel.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/tools/Path.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Path.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/tools/Path.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Path.py
diff --git a/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/ResourceKeyMapper.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/ResourceKeyMapper.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f4a2e00c0841b96ef38d8ae766031122ecc4ab5
--- /dev/null
+++ b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/ResourceKeyMapper.py
@@ -0,0 +1,49 @@
+# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# 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 copy
+import threading
+from typing import Dict, Optional, Tuple
+from common.proto.kpi_sample_types_pb2 import KpiSampleType
+
+class ResourceKeyMapper:
+    def __init__(self) -> None:
+        self._lock_device_endpoint = threading.Lock()
+        self._device_endpoint_sampletype__to__resource_key : Dict[Tuple[str, str, int], str] = dict()
+
+    def add_resource_key(
+        self, device_uuid : str, endpoint_uuid : str, kpi_sample_type : KpiSampleType, resource_key : str
+    ) -> None:
+        with self._lock_device_endpoint:
+            key = (device_uuid, endpoint_uuid, kpi_sample_type)
+            self._device_endpoint_sampletype__to__resource_key[key] = resource_key
+
+    def get_resource_key(
+        self, device_uuid : str, endpoint_uuid : str, kpi_sample_type : KpiSampleType
+    ) -> Optional[str]:
+        with self._lock_device_endpoint:
+            key = (device_uuid, endpoint_uuid, kpi_sample_type)
+            return self._device_endpoint_sampletype__to__resource_key.get(key)
+
+    def get_all_resource_keys(self) -> Dict[Tuple[str, str, int], str]:
+        with self._lock_device_endpoint:
+            return copy.deepcopy(self._device_endpoint_sampletype__to__resource_key)
+
+    def remove_resource_key(
+        self, device_uuid : str, endpoint_uuid : str, kpi_sample_type : KpiSampleType
+    ) -> None:
+        with self._lock_device_endpoint:
+            key = (device_uuid, endpoint_uuid, kpi_sample_type)
+            self._device_endpoint_sampletype__to__resource_key.pop(key, None)
+
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/tools/Subscriptions.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Subscriptions.py
similarity index 94%
rename from src/telemetry/backend/collectors/gnmi_openconfig/tools/Subscriptions.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Subscriptions.py
index 21c8edd61af727eeedff7e8ef23411cf1a509706..c6d62f569b44c4de6a3dfd5bcd9e4f7177881a90 100644
--- a/src/telemetry/backend/collectors/gnmi_openconfig/tools/Subscriptions.py
+++ b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Subscriptions.py
@@ -18,7 +18,7 @@
 
 import anytree
 from typing import Any, List
-from device.service.driver_api.AnyTreeTools import TreeNode, get_subnode, set_subnode_value
+from telemetry.backend.service.collector_api.AnyTreeTools import TreeNode, get_subnode, set_subnode_value
 
 class Subscriptions:
     def __init__(self) -> None:
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/tools/Value.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Value.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/tools/Value.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/tools/Value.py
diff --git a/src/telemetry/backend/collectors/gnmi_openconfig/tools/__init__.py b/src/telemetry/backend/service/collectors/gnmi_openconfig/tools/__init__.py
similarity index 100%
rename from src/telemetry/backend/collectors/gnmi_openconfig/tools/__init__.py
rename to src/telemetry/backend/service/collectors/gnmi_openconfig/tools/__init__.py