From 654eb4328bdb90c9b267cad11683385e02d191f6 Mon Sep 17 00:00:00 2001
From: carcel <jose.carcel@atos.net>
Date: Wed, 20 Mar 2024 10:14:21 +0000
Subject: [PATCH] Morpheus Client Extension

---
 manifests/nginx_ingress_http.yaml             |   7 +
 proto/context.proto                           |   1 +
 src/common/DeviceTypes.py                     |   1 +
 src/common/tools/object_factory/Device.py     |  11 +
 src/common/type_checkers/Assertions.py        |   1 +
 .../data/sql_hash_join_full_scan_tests.sql    |   2 +-
 .../database/models/enums/DeviceDriver.py     |   1 +
 src/device/service/drivers/__init__.py        |  12 +
 .../drivers/ietf_l2vpn/TfsDebugApiClient.py   |   2 +
 .../drivers/smartnic/SmartnicDriver.py        | 122 ++++
 src/device/service/drivers/smartnic/Tools.py  | 174 +++++
 .../service/drivers/smartnic/__init__.py      |  20 +
 src/nbi/service/__main__.py                   |   2 +
 .../nbi_plugins/agent_probes/Resources.py     | 236 +++++++
 .../nbi_plugins/agent_probes/Tools.py         | 118 ++++
 .../nbi_plugins/agent_probes/__init__.py      |  69 ++
 .../data/agent_probes_configuration_rule.json |  17 +
 src/nbi/tests/data/agent_probes_device.json   |  27 +
 src/policy/Dockerfile                         |  67 +-
 .../java/org/etsi/tfs/policy/Serializer.java  |   4 +
 .../context/model/DeviceDriverEnum.java       |   3 +-
 src/policy/src/main/proto/acl.proto           |  70 +-
 src/policy/src/main/proto/context.proto       | 616 +++++++++++++++++-
 .../src/main/proto/context_policy.proto       |  29 +-
 src/policy/src/main/proto/device.proto        |  35 +-
 .../src/main/proto/kpi_sample_types.proto     |  43 +-
 src/policy/src/main/proto/monitoring.proto    | 175 ++++-
 src/policy/src/main/proto/policy.proto        | 114 +++-
 src/policy/src/main/proto/policy_action.proto |  43 +-
 .../src/main/proto/policy_condition.proto     |  44 +-
 src/policy/src/main/proto/service.proto       |  26 +-
 .../service_handler_api/FilterFields.py       |   1 +
 .../service/service_handlers/__init__.py      |   6 +
 src/webui/service/device/forms.py             |   1 +
 src/webui/service/device/routes.py            |   2 +
 src/webui/service/templates/device/add.html   |   1 +
 src/ztp/Dockerfile                            |  68 +-
 src/ztp/src/main/proto/acl.proto              |  70 +-
 src/ztp/src/main/proto/context.proto          | 616 +++++++++++++++++-
 src/ztp/src/main/proto/device.proto           |  35 +-
 src/ztp/src/main/proto/kpi_sample_types.proto |  43 +-
 src/ztp/src/main/proto/monitoring.proto       | 175 ++++-
 src/ztp/src/main/proto/ztp.proto              |  70 +-
 43 files changed, 3160 insertions(+), 20 deletions(-)
 create mode 100644 src/device/service/drivers/smartnic/SmartnicDriver.py
 create mode 100644 src/device/service/drivers/smartnic/Tools.py
 create mode 100644 src/device/service/drivers/smartnic/__init__.py
 create mode 100644 src/nbi/service/rest_server/nbi_plugins/agent_probes/Resources.py
 create mode 100644 src/nbi/service/rest_server/nbi_plugins/agent_probes/Tools.py
 create mode 100644 src/nbi/service/rest_server/nbi_plugins/agent_probes/__init__.py
 create mode 100644 src/nbi/tests/data/agent_probes_configuration_rule.json
 create mode 100644 src/nbi/tests/data/agent_probes_device.json
 mode change 120000 => 100644 src/policy/Dockerfile
 mode change 120000 => 100644 src/policy/src/main/proto/acl.proto
 mode change 120000 => 100644 src/policy/src/main/proto/context.proto
 mode change 120000 => 100644 src/policy/src/main/proto/context_policy.proto
 mode change 120000 => 100644 src/policy/src/main/proto/device.proto
 mode change 120000 => 100644 src/policy/src/main/proto/kpi_sample_types.proto
 mode change 120000 => 100644 src/policy/src/main/proto/monitoring.proto
 mode change 120000 => 100644 src/policy/src/main/proto/policy.proto
 mode change 120000 => 100644 src/policy/src/main/proto/policy_action.proto
 mode change 120000 => 100644 src/policy/src/main/proto/policy_condition.proto
 mode change 120000 => 100644 src/policy/src/main/proto/service.proto
 mode change 120000 => 100644 src/ztp/Dockerfile
 mode change 120000 => 100644 src/ztp/src/main/proto/acl.proto
 mode change 120000 => 100644 src/ztp/src/main/proto/context.proto
 mode change 120000 => 100644 src/ztp/src/main/proto/device.proto
 mode change 120000 => 100644 src/ztp/src/main/proto/kpi_sample_types.proto
 mode change 120000 => 100644 src/ztp/src/main/proto/monitoring.proto
 mode change 120000 => 100644 src/ztp/src/main/proto/ztp.proto

diff --git a/manifests/nginx_ingress_http.yaml b/manifests/nginx_ingress_http.yaml
index e8e8a80e4..336f164b4 100644
--- a/manifests/nginx_ingress_http.yaml
+++ b/manifests/nginx_ingress_http.yaml
@@ -50,6 +50,13 @@ spec:
               name: nbiservice
               port:
                 number: 8080
+        - path: /()(agent-probes/.*)
+          pathType: Prefix
+          backend:
+            service:
+              name: nbiservice
+              port:
+                number: 8080
         - path: /()(bmw/.*)
           pathType: Prefix
           backend:
diff --git a/proto/context.proto b/proto/context.proto
index 5085cad33..856caa4f9 100644
--- a/proto/context.proto
+++ b/proto/context.proto
@@ -204,6 +204,7 @@ enum DeviceDriverEnum {
   DEVICEDRIVER_GNMI_OPENCONFIG = 8;
   DEVICEDRIVER_FLEXSCALE = 9;
   DEVICEDRIVER_IETF_ACTN = 10;
+  DEVICEDRIVER_SMARTNIC = 12;
 }
 
 enum DeviceOperationalStatusEnum {
diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py
index 72b3e21fd..5ed4b6681 100644
--- a/src/common/DeviceTypes.py
+++ b/src/common/DeviceTypes.py
@@ -47,6 +47,7 @@ class DeviceTypeEnum(Enum):
     PACKET_ROUTER                   = 'packet-router'
     PACKET_SWITCH                   = 'packet-switch'
     XR_CONSTELLATION                = 'xr-constellation'
+    SMARTNIC                        = 'smartnic'
 
     # ETSI TeraFlowSDN controller
     TERAFLOWSDN_CONTROLLER          = 'teraflowsdn'
diff --git a/src/common/tools/object_factory/Device.py b/src/common/tools/object_factory/Device.py
index 76959232a..b3182e302 100644
--- a/src/common/tools/object_factory/Device.py
+++ b/src/common/tools/object_factory/Device.py
@@ -49,6 +49,9 @@ DEVICE_TFS_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN]
 DEVICE_IETF_ACTN_TYPE    = DeviceTypeEnum.OPEN_LINE_SYSTEM.value
 DEVICE_IETF_ACTN_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN]
 
+DEVICE_SMARTNIC_TYPE = DeviceTypeEnum.SMARTNIC.value
+DEVICE_SMARTNIC_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_SMARTNIC]
+
 
 def json_device_id(device_uuid : str):
     return {'device_uuid': {'uuid': device_uuid}}
@@ -148,6 +151,14 @@ def json_device_ietf_actn_disabled(
         device_uuid, DEVICE_IETF_ACTN_TYPE, DEVICE_DISABLED, name=name, endpoints=endpoints, config_rules=config_rules,
         drivers=drivers)
 
+def json_device_smartnic_disabled(
+        device_uuid : str, name : Optional[str] = None, endpoints : List[Dict] = [], config_rules : List[Dict] = [],
+        drivers : List[Dict] = DEVICE_SMARTNIC_DRIVERS
+    ):
+    return json_device(
+        device_uuid, DEVICE_SMARTNIC_TYPE, DEVICE_DISABLED, name=name, endpoints=endpoints, config_rules=config_rules,
+        drivers=drivers)
+
 def json_device_connect_rules(address : str, port : int, settings : Dict = {}) -> List[Dict]:
     return [
         json_config_rule_set('_connect/address',  address),
diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py
index 87d8e54ee..06bcd4482 100644
--- a/src/common/type_checkers/Assertions.py
+++ b/src/common/type_checkers/Assertions.py
@@ -48,6 +48,7 @@ def validate_device_driver_enum(message):
         'DEVICEDRIVER_GNMI_OPENCONFIG',
         'DEVICEDRIVER_FLEXSCALE',
         'DEVICEDRIVER_IETF_ACTN',
+        'DEVICEDRIVER_SMARTNIC'
     ]
 
 def validate_device_operational_status_enum(message):
diff --git a/src/context/data/sql_hash_join_full_scan_tests.sql b/src/context/data/sql_hash_join_full_scan_tests.sql
index ebead1be6..34330f916 100644
--- a/src/context/data/sql_hash_join_full_scan_tests.sql
+++ b/src/context/data/sql_hash_join_full_scan_tests.sql
@@ -9,7 +9,7 @@ CREATE DATABASE tests;
 USE tests;
 
 CREATE TYPE public.orm_deviceoperationalstatusenum AS ENUM ('UNDEFINED', 'DISABLED', 'ENABLED');
-CREATE TYPE public.orm_devicedriverenum AS ENUM ('UNDEFINED', 'OPENCONFIG', 'TRANSPORT_API', 'P4', 'IETF_NETWORK_TOPOLOGY', 'ONF_TR_352', 'XR', 'IETF_L2VPN');
+CREATE TYPE public.orm_devicedriverenum AS ENUM ('UNDEFINED', 'OPENCONFIG', 'TRANSPORT_API', 'P4', 'IETF_NETWORK_TOPOLOGY', 'ONF_TR_352', 'XR', 'IETF_L2VPN', 'SMARTNIC');
 CREATE TYPE public.configrulekindenum AS ENUM ('CUSTOM', 'ACL');
 CREATE TYPE public.orm_configactionenum AS ENUM ('UNDEFINED', 'SET', 'DELETE');
 
diff --git a/src/context/service/database/models/enums/DeviceDriver.py b/src/context/service/database/models/enums/DeviceDriver.py
index 8e15bf058..559291965 100644
--- a/src/context/service/database/models/enums/DeviceDriver.py
+++ b/src/context/service/database/models/enums/DeviceDriver.py
@@ -33,6 +33,7 @@ class ORM_DeviceDriverEnum(enum.Enum):
     GNMI_OPENCONFIG       = DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG
     FLEXSCALE             = DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE
     IETF_ACTN             = DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN
+    SMARTNIC              = DeviceDriverEnum.DEVICEDRIVER_SMARTNIC
 
 grpc_to_enum__device_driver = functools.partial(
     grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum)
diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py
index 27c61f89f..1534655db 100644
--- a/src/device/service/drivers/__init__.py
+++ b/src/device/service/drivers/__init__.py
@@ -136,6 +136,18 @@ if LOAD_ALL_DEVICE_DRIVERS:
                 FilterFieldEnum.DRIVER     : DeviceDriverEnum.DEVICEDRIVER_P4,
             }
         ]))
+    
+if LOAD_ALL_DEVICE_DRIVERS:
+    from .smartnic.SmartnicDriver import SmartnicDriver # pylint: disable=wrong-import-position
+    DRIVERS.append(
+        (SmartnicDriver, [
+            {
+                # Real P4 Switch, specifying P4 Driver => use P4Driver
+                FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.SMARTNIC,
+                FilterFieldEnum.DRIVER     : DeviceDriverEnum.DEVICEDRIVER_SMARTNIC,
+            }
+        ]))
+
 
 if LOAD_ALL_DEVICE_DRIVERS:
     from .microwave.IETFApiDriver import IETFApiDriver # pylint: disable=wrong-import-position
diff --git a/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py b/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py
index 06c55c5dc..ee2d1ab6e 100644
--- a/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py
+++ b/src/device/service/drivers/ietf_l2vpn/TfsDebugApiClient.py
@@ -45,6 +45,8 @@ MAPPING_DRIVER = {
     'DEVICEDRIVER_IETF_L2VPN'           : 7,
     'DEVICEDRIVER_GNMI_OPENCONFIG'      : 8,
     'DEVICEDRIVER_FLEXSCALE'            : 9,
+    'DEVICEDRIVER_IETF_ACTN'            : 10,
+    'DEVICEDRIVER_SMARTNIC'             : 12
 }
 
 MSG_ERROR = 'Could not retrieve devices in remote TeraFlowSDN instance({:s}). status_code={:s} reply={:s}'
diff --git a/src/device/service/drivers/smartnic/SmartnicDriver.py b/src/device/service/drivers/smartnic/SmartnicDriver.py
new file mode 100644
index 000000000..4bad52db4
--- /dev/null
+++ b/src/device/service/drivers/smartnic/SmartnicDriver.py
@@ -0,0 +1,122 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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, requests, threading
+from requests.auth import HTTPBasicAuth
+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_string, chk_type
+from device.service.driver_api._Driver import _Driver
+from . import ALL_RESOURCE_KEYS
+from .Tools import create_connectivity_service, find_key, config_getter, delete_connectivity_service
+
+LOGGER = logging.getLogger(__name__)
+
+DRIVER_NAME = 'smartnic'
+METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME})
+
+class SmartnicDriver(_Driver):
+    def __init__(self, address: str, port: int, **settings) -> None:
+        super().__init__(DRIVER_NAME, address, port, **settings)
+        self.__lock = threading.Lock()
+        self.__started = threading.Event()
+        self.__terminate = threading.Event()
+        username = self.settings.get('username')
+        password = self.settings.get('password')
+        #self.__auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None
+        scheme = self.settings.get('scheme', 'http')
+        self.__tapi_root = '{:s}://{:s}:{:d}'.format(scheme, self.address, int(self.port))
+        self.__timeout = int(self.settings.get('timeout', 120))
+
+    def Connect(self) -> bool:
+        url = self.__tapi_root
+        with self.__lock:
+            if self.__started.is_set(): return True
+            try:
+                requests.get(url, timeout=self.__timeout, verify=False)
+                #requests.get(url, timeout=self.__timeout, verify=False, auth=self.__auth)
+            except requests.exceptions.Timeout:
+                LOGGER.exception('Timeout connecting {:s}'.format(str(self.__tapi_root)))
+                return False
+            except Exception:  # pylint: disable=broad-except
+                LOGGER.exception('Exception connecting {:s}'.format(str(self.__tapi_root)))
+                return False
+            else:
+                self.__started.set()
+                return True
+
+    def Disconnect(self) -> bool:
+        with self.__lock:
+            self.__terminate.set()
+            return True
+
+    @metered_subclass_method(METRICS_POOL)
+    def GetInitialConfig(self) -> List[Tuple[str, Any]]:
+        with self.__lock:
+            return []
+
+    @metered_subclass_method(METRICS_POOL)
+    def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]:
+        chk_type('resources', resource_keys, list)
+        results = []
+        with self.__lock:
+            if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS
+            for i, resource_key in enumerate(resource_keys):
+                str_resource_name = 'resource_key[#{:d}]'.format(i)
+                chk_string(str_resource_name, resource_key, allow_empty=False)
+                results.extend(config_getter(
+                    self.__tapi_root, resource_key, timeout=self.__timeout))
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        results = []
+        if len(resources) == 0:
+            return results
+        with self.__lock:
+            for resource in resources:
+                LOGGER.info('resource = {:s}'.format(str(resource)))
+                config_rules = find_key(resource, 'config_rules')
+                data = create_connectivity_service(
+                    self.__tapi_root, config_rules, timeout=self.__timeout)
+                results.extend(data)
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        results = []
+        if len(resources) == 0: return results
+        with self.__lock:
+            for resource in resources:
+                LOGGER.info('resource = {:s}'.format(str(resource)))
+                config_rules = find_key(resource, 'config_rules')
+                results.extend(delete_connectivity_service(
+                    self.__tapi_root, config_rules, timeout=self.__timeout))
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        # TODO: TAPI does not support monitoring by now
+        return [False for _ in subscriptions]
+
+    @metered_subclass_method(METRICS_POOL)
+    def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        # TODO: TAPI does not support monitoring by now
+        return [False for _ in subscriptions]
+
+    def GetState(
+        self, blocking=False, terminate : Optional[threading.Event] = None
+    ) -> Iterator[Tuple[float, str, Any]]:
+        # TODO: TAPI does not support monitoring by now
+        return []
diff --git a/src/device/service/drivers/smartnic/Tools.py b/src/device/service/drivers/smartnic/Tools.py
new file mode 100644
index 000000000..54961bbdd
--- /dev/null
+++ b/src/device/service/drivers/smartnic/Tools.py
@@ -0,0 +1,174 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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, operator, requests
+from requests.auth import HTTPBasicAuth
+from typing import Optional
+#from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_SERVICES, RESOURCE_INTERFACES
+
+
+import logging
+from typing import Any, Dict, Optional, Tuple
+from common.proto.kpi_sample_types_pb2 import KpiSampleType
+from common.type_checkers.Checkers import chk_attribute, chk_string, chk_type
+from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES
+
+LOGGER = logging.getLogger(__name__)
+
+
+SPECIAL_RESOURCE_MAPPINGS = {
+    RESOURCE_ENDPOINTS        : '/endpoints',
+    RESOURCE_INTERFACES       : '/interfaces'
+}
+
+HTTP_OK_CODES = {
+    200,    # OK
+    201,    # Created
+    202,    # Accepted
+    204,    # No Content
+}
+
+def find_key(resource, key):
+    return json.loads(resource[1])[key]
+
+
+def config_getter(
+    root_url : str, resource_key : str, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None
+):
+    url = '{:s}/manage-probe/ports'.format(root_url)
+    result = []
+    try:
+        response = requests.get(url, timeout=timeout, verify=False)
+    except requests.exceptions.Timeout:
+        LOGGER.exception('Timeout connecting {:s}'.format(url))
+        return result
+    except Exception as e:  # pylint: disable=broad-except
+        LOGGER.exception('Exception retrieving {:s}'.format(resource_key))
+        result.append((resource_key, e))
+        return result
+    return response
+
+    # try:
+    #     context = json.loads(response.content)
+    # except Exception as e:  # pylint: disable=broad-except
+    #     LOGGER.warning('Unable to decode reply: {:s}'.format(str(response.content)))
+    #     result.append((resource_key, e))
+    #     return result
+
+
+
+def create_connectivity_service(
+    root_url, config_rules, timeout : Optional[int] = None, auth : Optional[HTTPBasicAuth] = None 
+):
+
+    url = '{:s}/configure'.format(root_url)
+    headers = {'content-type': 'application/json'}
+    results = []
+    try:
+        LOGGER.info('Configuring Smartnic rules')
+        response = requests.post(
+            url=url, data=json.dumps(config_rules), timeout=timeout, headers=headers, verify=False)
+        LOGGER.info('SmartNIC Probes response: {:s}'.format(str(response)))
+    except Exception as e:  # pylint: disable=broad-except
+        LOGGER.exception('Exception creating ConfigRule')
+        results.append(e)
+    else:
+        if response.status_code not in HTTP_OK_CODES:
+            msg = 'Could not create ConfigRule status_code={:s} reply={:s}'
+            LOGGER.error(msg.format(str(response.status_code), str(response)))
+        results.append(response.status_code in HTTP_OK_CODES)
+    return results
+
+def delete_connectivity_service(root_url, config_rules, timeout : Optional[int] = None, auth : Optional[HTTPBasicAuth] = None 
+):
+    url = '{:s}/configure'.format(root_url)
+    results = []
+    try:
+        response = requests.delete(url=url, timeout=timeout, verify=False)
+    except Exception as e:  # pylint: disable=broad-except
+        LOGGER.exception('Exception deleting ConfigRule')
+        results.append(e)
+    else:
+        if response.status_code not in HTTP_OK_CODES:
+            msg = 'Could not delete ConfigRule status_code={:s} reply={:s}'
+            LOGGER.error(msg.format(str(response.status_code), str(response)))
+        results.append(response.status_code in HTTP_OK_CODES)
+    return results
+
+def process_optional_string_field(
+    endpoint_data : Dict[str, Any], field_name : str, endpoint_resource_value : Dict[str, Any]
+) -> None:
+    field_value = chk_attribute(field_name, endpoint_data, 'endpoint_data', default=None)
+    if field_value is None: return
+    chk_string('endpoint_data.{:s}'.format(field_name), field_value)
+    if len(field_value) > 0: endpoint_resource_value[field_name] = field_value
+
+def compose_resource_endpoint(endpoint_data : Dict[str, Any]) -> Optional[Tuple[str, Dict]]:
+    try:
+        # Check type of endpoint_data
+        chk_type('endpoint_data', endpoint_data, dict)
+
+        # Check endpoint UUID (mandatory)
+        endpoint_uuid = chk_attribute('uuid', endpoint_data, 'endpoint_data')
+        chk_string('endpoint_data.uuid', endpoint_uuid, min_length=1)
+        endpoint_resource_path = SPECIAL_RESOURCE_MAPPINGS.get(RESOURCE_ENDPOINTS)
+        endpoint_resource_key = '{:s}/endpoint[{:s}]'.format(endpoint_resource_path, endpoint_uuid)
+        endpoint_resource_value = {'uuid': endpoint_uuid}
+
+        # Check endpoint optional string fields
+        process_optional_string_field(endpoint_data, 'name', endpoint_resource_value)
+        process_optional_string_field(endpoint_data, 'type', endpoint_resource_value)
+        process_optional_string_field(endpoint_data, 'context_uuid', endpoint_resource_value)
+        process_optional_string_field(endpoint_data, 'topology_uuid', endpoint_resource_value)
+
+        # Check endpoint sample types (optional)
+        endpoint_sample_types = chk_attribute('sample_types', endpoint_data, 'endpoint_data', default=[])
+        chk_type('endpoint_data.sample_types', endpoint_sample_types, list)
+        sample_types = {}
+        sample_type_errors = []
+        for i,endpoint_sample_type in enumerate(endpoint_sample_types):
+            field_name = 'endpoint_data.sample_types[{:d}]'.format(i)
+            try:
+                chk_type(field_name, endpoint_sample_type, (int, str))
+                if isinstance(endpoint_sample_type, int):
+                    metric_name = KpiSampleType.Name(endpoint_sample_type)
+                    metric_id = endpoint_sample_type
+                elif isinstance(endpoint_sample_type, str):
+                    metric_id = KpiSampleType.Value(endpoint_sample_type)
+                    metric_name = endpoint_sample_type
+                else:
+                    str_type = str(type(endpoint_sample_type))
+                    raise Exception('Bad format: {:s}'.format(str_type)) # pylint: disable=broad-exception-raised
+            except Exception as e: # pylint: disable=broad-exception-caught
+                MSG = 'Unsupported {:s}({:s}) : {:s}'
+                sample_type_errors.append(MSG.format(field_name, str(endpoint_sample_type), str(e)))
+
+            metric_name = metric_name.lower().replace('kpisampletype_', '')
+            monitoring_resource_key = '{:s}/state/{:s}'.format(endpoint_resource_key, metric_name)
+            sample_types[metric_id] = monitoring_resource_key
+
+        if len(sample_type_errors) > 0:
+            # pylint: disable=broad-exception-raised
+            raise Exception('Malformed Sample Types:\n{:s}'.format('\n'.join(sample_type_errors)))
+
+        if len(sample_types) > 0:
+            endpoint_resource_value['sample_types'] = sample_types
+    
+        if 'location' in endpoint_data:
+            endpoint_resource_value['location'] = endpoint_data['location']
+            
+        return endpoint_resource_key, endpoint_resource_value
+    except: # pylint: disable=bare-except
+        LOGGER.exception('Problem composing endpoint({:s})'.format(str(endpoint_data)))
+        return None
diff --git a/src/device/service/drivers/smartnic/__init__.py b/src/device/service/drivers/smartnic/__init__.py
new file mode 100644
index 000000000..bc88d00fa
--- /dev/null
+++ b/src/device/service/drivers/smartnic/__init__.py
@@ -0,0 +1,20 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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 device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_SERVICES, RESOURCE_INTERFACES
+
+ALL_RESOURCE_KEYS = [
+    RESOURCE_ENDPOINTS,
+    RESOURCE_SERVICES,
+    RESOURCE_INTERFACES
+]
diff --git a/src/nbi/service/__main__.py b/src/nbi/service/__main__.py
index 8834e45a2..0b6fb18e3 100644
--- a/src/nbi/service/__main__.py
+++ b/src/nbi/service/__main__.py
@@ -20,6 +20,7 @@ from common.Settings import (
     wait_for_environment_variables)
 from .NbiService import NbiService
 from .rest_server.RestServer import RestServer
+from .rest_server.nbi_plugins.agent_probes import register_agent_probes
 from .rest_server.nbi_plugins.debug_api import register_debug_api
 from .rest_server.nbi_plugins.etsi_bwm import register_etsi_bwm_api
 from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn
@@ -62,6 +63,7 @@ def main():
     grpc_service.start()
 
     rest_server = RestServer()
+    register_agent_probes(rest_server)
     register_debug_api(rest_server)
     register_etsi_bwm_api(rest_server)
     register_ietf_l2vpn(rest_server)  # Registering L2VPN entrypoint
diff --git a/src/nbi/service/rest_server/nbi_plugins/agent_probes/Resources.py b/src/nbi/service/rest_server/nbi_plugins/agent_probes/Resources.py
new file mode 100644
index 000000000..2b0a537cc
--- /dev/null
+++ b/src/nbi/service/rest_server/nbi_plugins/agent_probes/Resources.py
@@ -0,0 +1,236 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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
+from flask.json import jsonify
+from flask_restful import Resource, request
+from common.proto.context_pb2 import Empty
+from common.tools.grpc.Tools import grpc_message_to_json
+from context.client.ContextClient import ContextClient
+from device.client.DeviceClient import DeviceClient
+from service.client.ServiceClient import ServiceClient
+from .Tools import (
+    format_grpc_to_json, grpc_connection_id, grpc_context_id, grpc_device, grpc_device_id, grpc_link_id, grpc_policy_rule_id,
+    grpc_service_id, grpc_service, grpc_slice_id, grpc_topology_id)
+
+class _Resource(Resource):
+    def __init__(self) -> None:
+        super().__init__()
+        self.client = ContextClient()
+        self.device_client = DeviceClient()
+        self.service_client = ServiceClient()
+
+class ContextIds(_Resource):
+    def get(self):
+        return format_grpc_to_json(self.client.ListContextIds(Empty()))
+
+class Contexts(_Resource):
+    def get(self):
+        return format_grpc_to_json(self.client.ListContexts(Empty()))
+
+class DummyContexts(_Resource):
+    def get(self):
+        contexts = grpc_message_to_json(self.client.ListContexts(Empty()), use_integers_for_enums=True)['contexts']
+        devices = grpc_message_to_json(self.client.ListDevices(Empty()), use_integers_for_enums=True)['devices']
+        links = grpc_message_to_json(self.client.ListLinks(Empty()), use_integers_for_enums=True)['links']
+
+        topologies  = list()
+        slices      = list()
+        services    = list()
+        connections = list()
+
+        for context in contexts:
+            context_uuid = context['context_id']['context_uuid']['uuid']
+            context_id = grpc_context_id(context_uuid)
+
+            topologies.extend(grpc_message_to_json(
+                self.client.ListTopologies(context_id),
+                use_integers_for_enums=True
+            )['topologies'])
+
+            slices.extend(grpc_message_to_json(
+                self.client.ListSlices(context_id),
+                use_integers_for_enums=True
+            )['slices'])
+
+            context_services = grpc_message_to_json(
+                self.client.ListServices(context_id),
+                use_integers_for_enums=True
+            )['services']
+            services.extend(context_services)
+
+            for service in context_services:
+                service_uuid = service['service_id']['service_uuid']['uuid']
+                service_id = grpc_service_id(context_uuid, service_uuid)
+                connections.extend(grpc_message_to_json(
+                    self.client.ListConnections(service_id),
+                    use_integers_for_enums=True
+                )['connections'])
+
+        for device in devices:
+            for config_rule in device['device_config']['config_rules']:
+                if 'custom' not in config_rule: continue
+                resource_value = config_rule['custom']['resource_value']
+                if not isinstance(resource_value, str): continue
+                try:
+                    resource_value = json.loads(resource_value)
+                except: # pylint: disable=bare-except
+                    pass
+                config_rule['custom']['resource_value'] = resource_value
+
+        dummy_context = {'dummy_mode': True}
+        if len(contexts   ) > 0: dummy_context['contexts'   ] = contexts
+        if len(topologies ) > 0: dummy_context['topologies' ] = topologies
+        if len(devices    ) > 0: dummy_context['devices'    ] = devices
+        if len(links      ) > 0: dummy_context['links'      ] = links
+        if len(slices     ) > 0: dummy_context['slices'     ] = slices
+        if len(services   ) > 0: dummy_context['services'   ] = services
+        if len(connections) > 0: dummy_context['connections'] = connections
+        return jsonify(dummy_context)
+
+class Context(_Resource):
+    def get(self, context_uuid : str):
+        return format_grpc_to_json(self.client.GetContext(grpc_context_id(context_uuid)))
+
+class TopologyIds(_Resource):
+    def get(self, context_uuid : str):
+        return format_grpc_to_json(self.client.ListTopologyIds(grpc_context_id(context_uuid)))
+
+class Topologies(_Resource):
+    def get(self, context_uuid : str):
+        return format_grpc_to_json(self.client.ListTopologies(grpc_context_id(context_uuid)))
+
+class Topology(_Resource):
+    def get(self, context_uuid : str, topology_uuid : str):
+        return format_grpc_to_json(self.client.GetTopology(grpc_topology_id(context_uuid, topology_uuid)))
+
+class ServiceIds(_Resource):
+    def get(self, context_uuid : str):
+        return format_grpc_to_json(self.client.ListServiceIds(grpc_context_id(context_uuid)))
+
+class Services(_Resource):
+    def get(self, context_uuid : str):
+        return format_grpc_to_json(self.client.ListServices(grpc_context_id(context_uuid)))
+
+class Service(_Resource):
+    def get(self, context_uuid : str, service_uuid : str):
+        return format_grpc_to_json(self.client.GetService(grpc_service_id(context_uuid, service_uuid)))
+
+    def post(self, context_uuid : str, service_uuid : str): # pylint: disable=unused-argument
+        service = request.get_json()['services'][0]
+        return format_grpc_to_json(self.service_client.CreateService(grpc_service(
+            service_uuid = service['service_id']['service_uuid']['uuid'],
+            service_type = service['service_type'],
+            context_uuid = service['service_id']['context_id']['context_uuid']['uuid'],
+        )))
+
+    def put(self, context_uuid : str, service_uuid : str):  # pylint: disable=unused-argument
+        service = request.get_json()['services'][0]
+        return format_grpc_to_json(self.service_client.UpdateService(grpc_service(
+            service_uuid = service['service_id']['service_uuid']['uuid'],
+            service_type = service['service_type'],
+            context_uuid = service['service_id']['context_id']['context_uuid']['uuid'],
+            status       = service['service_status']['service_status'],
+            endpoint_ids = service['service_endpoint_ids'],
+            constraints  = service['service_constraints'],
+            config_rules = service['service_config']['config_rules']
+        )))
+
+    def delete(self, context_uuid : str, service_uuid : str):
+        return format_grpc_to_json(self.service_client.DeleteService(grpc_service_id(
+            context_uuid, service_uuid,
+        )))
+
+class SliceIds(_Resource):
+    def get(self, context_uuid : str):
+        return format_grpc_to_json(self.client.ListSliceIds(grpc_context_id(context_uuid)))
+
+class Slices(_Resource):
+    def get(self, context_uuid : str):
+        return format_grpc_to_json(self.client.ListSlices(grpc_context_id(context_uuid)))
+
+class Slice(_Resource):
+    def get(self, context_uuid : str, slice_uuid : str):
+        return format_grpc_to_json(self.client.GetSlice(grpc_slice_id(context_uuid, slice_uuid)))
+
+class DeviceIds(_Resource):
+    def get(self):
+        return format_grpc_to_json(self.client.ListDeviceIds(Empty()))
+    
+class Devices(_Resource):
+    def get(self):
+        return format_grpc_to_json(self.client.ListDevices(Empty()))
+
+class Device(_Resource):
+    def get(self, device_uuid : str):
+        return format_grpc_to_json(self.client.GetDevice(grpc_device_id(device_uuid)))
+
+    def post(self, device_uuid : str): # pylint: disable=unused-argument
+        device = request.get_json()['devices'][0]
+        return format_grpc_to_json(self.device_client.AddDevice(grpc_device(
+            device_uuid = device['device_id']['device_uuid']['uuid'],
+            device_type = device['device_type'],
+            config_rules = device['device_config']['config_rules'],
+            status = device['device_operational_status'],
+            drivers = device['device_drivers'],
+            endpoints = device['device_endpoints']            
+        )))
+
+    def put(self, device_uuid : str):  # pylint: disable=unused-argument
+        device = request.get_json()['devices'][0]
+        return format_grpc_to_json(self.device_client.ConfigureDevice(grpc_device(
+            device_uuid = device['device_id']['device_uuid']['uuid'],
+            device_type = device['device_type'],
+            device_config = device['device_config']['config_rules'],
+            device_operational_status = device['device_operational_status'],
+            device_drivers = device['device_drivers'],
+            device_endpoints = device['device_endpoints']            
+        )))
+
+
+class LinkIds(_Resource):
+    def get(self):
+        return format_grpc_to_json(self.client.ListLinkIds(Empty()))
+
+class Links(_Resource):
+    def get(self):
+        return format_grpc_to_json(self.client.ListLinks(Empty()))
+
+class Link(_Resource):
+    def get(self, link_uuid : str):
+        return format_grpc_to_json(self.client.GetLink(grpc_link_id(link_uuid)))
+
+class ConnectionIds(_Resource):
+    def get(self, context_uuid : str, service_uuid : str):
+        return format_grpc_to_json(self.client.ListConnectionIds(grpc_service_id(context_uuid, service_uuid)))
+
+class Connections(_Resource):
+    def get(self, context_uuid : str, service_uuid : str):
+        return format_grpc_to_json(self.client.ListConnections(grpc_service_id(context_uuid, service_uuid)))
+
+class Connection(_Resource):
+    def get(self, connection_uuid : str):
+        return format_grpc_to_json(self.client.GetConnection(grpc_connection_id(connection_uuid)))
+
+class PolicyRuleIds(_Resource):
+    def get(self):
+        return format_grpc_to_json(self.client.ListPolicyRuleIds(Empty()))
+
+class PolicyRules(_Resource):
+    def get(self):
+        return format_grpc_to_json(self.client.ListPolicyRules(Empty()))
+
+class PolicyRule(_Resource):
+    def get(self, policy_rule_uuid : str):
+        return format_grpc_to_json(self.client.GetPolicyRule(grpc_policy_rule_id(policy_rule_uuid)))
diff --git a/src/nbi/service/rest_server/nbi_plugins/agent_probes/Tools.py b/src/nbi/service/rest_server/nbi_plugins/agent_probes/Tools.py
new file mode 100644
index 000000000..17b6dcdfd
--- /dev/null
+++ b/src/nbi/service/rest_server/nbi_plugins/agent_probes/Tools.py
@@ -0,0 +1,118 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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 flask.json import jsonify
+from common.proto.context_pb2 import (
+    ConnectionId, ContextId, DeviceDriverEnum, Device, DeviceId, DeviceOperationalStatusEnum, LinkId, ServiceId, SliceId, TopologyId, Service, ServiceStatusEnum
+)
+from common.proto.policy_pb2 import PolicyRuleId
+from common.tools.grpc.Tools import grpc_message_to_json
+from common.tools.object_factory.Connection import json_connection_id
+from common.tools.object_factory.Context import json_context_id
+from common.tools.object_factory.ConfigRule import json_config_rule
+from common.tools.object_factory.Constraint import json_constraint_custom
+from common.tools.object_factory.EndPoint import json_endpoint_id, json_endpoint
+from common.tools.object_factory.Device import json_device_id, json_device
+from common.tools.object_factory.Link import json_link_id
+from common.tools.object_factory.PolicyRule import json_policyrule_id
+from common.tools.object_factory.Service import json_service_id, json_service
+from common.tools.object_factory.Slice import json_slice_id
+from common.tools.object_factory.Topology import json_topology_id
+
+
+def format_grpc_to_json(grpc_reply):
+    return jsonify(grpc_message_to_json(grpc_reply))
+
+def grpc_connection_id(connection_uuid):
+    return ConnectionId(**json_connection_id(connection_uuid))
+
+def grpc_context_id(context_uuid):
+    return ContextId(**json_context_id(context_uuid))
+
+def grpc_device_id(device_uuid):
+    return DeviceId(**json_device_id(device_uuid))
+
+def grpc_device(
+    device_uuid, device_type, config_rules=None, status=None, drivers=None, endpoints=None
+):
+    json_config_rules = [
+        json_config_rule(
+            config_rule['action'],
+            config_rule['custom']['resource_key'],
+            config_rule['custom']['resource_value']
+        )
+        for config_rule in config_rules
+    ] if config_rules else []
+    json_status = status if status else DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED
+    json_drivers = drivers if drivers else DeviceDriverEnum.DEVICEDRIVER_UNDEFINED
+    json_endpoints = [
+        json_endpoint(
+            endpoint['device_id']['device_uuid']['uuid'],
+            endpoint['endpoint_uuid']['uuid'],
+            endpoint['endpoint_type'],
+            endpoint['topology_id'],
+            endpoint['kpi_sample_types'],
+            endpoint['location']['region']
+        )
+        for endpoint in endpoints
+    ] if endpoints else []
+    return Device(**json_device(
+        device_uuid, device_type, json_config_rules, json_status,
+        json_drivers, json_endpoints))
+
+def grpc_link_id(link_uuid):
+    return LinkId(**json_link_id(link_uuid))
+
+def grpc_service_id(context_uuid, service_uuid):
+    return ServiceId(**json_service_id(service_uuid, context_id=json_context_id(context_uuid)))
+
+def grpc_service(
+    service_uuid, service_type, context_uuid, status=None, endpoint_ids=None, constraints=None, config_rules=None
+):
+    json_context = json_context_id(context_uuid)
+    json_status = status if status else ServiceStatusEnum.SERVICESTATUS_PLANNED
+    json_endpoints_ids = [
+        json_endpoint_id(
+            json_device_id(endpoint_id['device_id']['device_uuid']['uuid']),
+            endpoint_id['endpoint_uuid']['uuid']
+        )
+        for endpoint_id in endpoint_ids
+    ] if endpoint_ids else []
+    json_constraints = [
+        json_constraint_custom(
+            constraint['custom']['constraint_type'],
+            constraint['custom']['constraint_value']
+        )
+        for constraint in constraints
+    ] if constraints else []
+    json_config_rules = [
+        json_config_rule(
+            config_rule['action'],
+            config_rule['custom']['resource_key'],
+            config_rule['custom']['resource_value']
+        )
+        for config_rule in config_rules
+    ] if config_rules else []
+    return Service(**json_service(
+        service_uuid, service_type, json_context, json_status,
+        json_endpoints_ids, json_constraints, json_config_rules))
+
+def grpc_slice_id(context_uuid, slice_uuid):
+    return SliceId(**json_slice_id(slice_uuid, context_id=json_context_id(context_uuid)))
+    
+def grpc_topology_id(context_uuid, topology_uuid):
+    return TopologyId(**json_topology_id(topology_uuid, context_id=json_context_id(context_uuid)))
+
+def grpc_policy_rule_id(policy_rule_uuid):
+    return PolicyRuleId(**json_policyrule_id(policy_rule_uuid))
diff --git a/src/nbi/service/rest_server/nbi_plugins/agent_probes/__init__.py b/src/nbi/service/rest_server/nbi_plugins/agent_probes/__init__.py
new file mode 100644
index 000000000..0b85500fc
--- /dev/null
+++ b/src/nbi/service/rest_server/nbi_plugins/agent_probes/__init__.py
@@ -0,0 +1,69 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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 nbi.service.rest_server.RestServer import RestServer
+from .Resources import (
+    Connection, ConnectionIds, Connections,
+    Context, ContextIds, Contexts,
+    Device, DeviceIds, Devices,
+    DummyContexts,
+    Link, LinkIds, Links,
+    PolicyRule, PolicyRuleIds, PolicyRules,
+    Service, ServiceIds, Services,
+    Slice, SliceIds, Slices,
+    Topologies, Topology, TopologyIds
+)
+
+URL_PREFIX = '/agent-probes'
+
+# Use 'path' type since some identifiers might contain char '/' and Flask is unable to recognize them in 'string' type.
+RESOURCES = [
+    # (endpoint_name, resource_class, resource_url)
+    # ('api.context_ids',    ContextIds,    '/context_ids'),
+    # ('api.contexts',       Contexts,      '/contexts'),
+    # ('api.dummy_contexts', DummyContexts, '/dummy_contexts'),
+    # ('api.context',        Context,       '/context/<path:context_uuid>'),
+
+    # ('api.topology_ids',   TopologyIds,   '/context/<path:context_uuid>/topology_ids'),
+    # ('api.topologies',     Topologies,    '/context/<path:context_uuid>/topologies'),
+    # ('api.topology',       Topology,      '/context/<path:context_uuid>/topology/<path:topology_uuid>'),
+
+    # ('api.service_ids',    ServiceIds,    '/context/<path:context_uuid>/service_ids'),
+    # ('api.services',       Services,      '/context/<path:context_uuid>/services'),
+    # ('api.service',        Service,       '/context/<path:context_uuid>/service/<path:service_uuid>'),
+
+    # ('api.slice_ids',      SliceIds,      '/context/<path:context_uuid>/slice_ids'),
+    # ('api.slices',         Slices,        '/context/<path:context_uuid>/slices'),
+    # ('api.slice',          Slice,         '/context/<path:context_uuid>/slice/<path:slice_uuid>'),
+
+    ('api.smartnic.device_ids',     DeviceIds,     '/device_ids'),
+    ('api.smartnic.devices',        Devices,       '/devices'),
+    ('api.smartnic.device',         Device,        '/device/<path:device_uuid>'),
+
+    # ('api.link_ids',       LinkIds,       '/link_ids'),
+    # ('api.links',          Links,         '/links'),
+    # ('api.link',           Link,          '/link/<path:link_uuid>'),
+
+    # ('api.connection_ids', ConnectionIds, '/context/<path:context_uuid>/service/<path:service_uuid>/connection_ids'),
+    # ('api.connections',    Connections,   '/context/<path:context_uuid>/service/<path:service_uuid>/connections'),
+    # ('api.connection',     Connection,    '/connection/<path:connection_uuid>'),
+
+    # ('api.policyrule_ids', PolicyRuleIds, '/policyrule_ids'),
+    # ('api.policyrules',    PolicyRules,   '/policyrules'),
+    # ('api.policyrule',     PolicyRule,    '/policyrule/<path:policyrule_uuid>'),
+]
+
+def register_agent_probes(rest_server : RestServer):
+    for endpoint_name, resource_class, resource_url in RESOURCES:
+        rest_server.add_resource(resource_class, URL_PREFIX + resource_url, endpoint=endpoint_name)
diff --git a/src/nbi/tests/data/agent_probes_configuration_rule.json b/src/nbi/tests/data/agent_probes_configuration_rule.json
new file mode 100644
index 000000000..dbbf953ad
--- /dev/null
+++ b/src/nbi/tests/data/agent_probes_configuration_rule.json
@@ -0,0 +1,17 @@
+{ 
+    "devices": [
+        {
+        "device_id": {"device_uuid":{"uuid":"smp-01"}},
+        "device_type": "smartnic",
+        "device_config": {"config_rules": [
+            {"action": 1, "custom": {
+                "resource_key": "config_rules",
+                "resource_value": {"pipeline_name":"pipeline_example","num_threads":2,"pipeline_batch_size":2048,"model_max_batch_size":4096,"input_file":"inputfile1.csv","output_file":"outputfile1.csv","nic_addr": "10.10.2.25", "gpu_addr":"11.6.15.2", "model_fea_length":8,"model_name":"modelname1","iterative":false,"server_url":"servertest.com","file_type":"csv","stages":{"FileSourceStage":{"stage_name":"FileSourceStage","FileSourceStage":{"fs_configuration":{"FsConf":{"mode":"FsConf"}},"filename":"file1.csv","file_type":"csv"}},"DeserializeStage":{"stage_name":"DeserializeStage","DeserializeStage":{"ds_configuration":{"DeserializeConf":{"mode":"DeserializeConf"}}}},"AbpPcapPreprocessingStage":{"stage_name":"AbpPcapPreprocessingStage","AbpPcapPreprocessingStage":{"apps_configuration":{"AppsConf":{"mode":"AppsConf"}}}},"MonitorStage":{"stage_name":"MonitorStage","MonitorStage":{"ms_configuration":{"MonitoringConf":{"mode":"MonitoringConf"}},"descriptions":"MetricMonitoring","unit":"kbps"}},"TritonInferenceStage":{"stage_name":"TritonInferenceStage","TritonInferenceStage":{"tis_configuration":{"TritonConf":{"mode":"TritonConf"}},"model_name":"Modeltest1","server_url":"servertest.com","force_convert_inputs":false}},"AddClassificationsStage":{"stage_name":"AddClassificationsStage","AddClassificationsStage":{"acs_configuration":{"ClassificationConf":{"mode":"ClassificationConf"}}}},"SerializeStage":{"stage_name":"SerializeStage","SerializeStage":{"ss_configuration":{"SerializeConf":{"mode":"SerializeConf"}}}},"WriteToFileStage":{"stage_name":"WriteToFileStage","WriteToFileStage":{"wfs_configuration":{"WFSConf":{"mode":"WFSConf"}},"wfs_filename":"file2.txt","overwrite":false}},"CustomStage":{"stage_name":"CustomStage","custom":{"name":{"field_name":"name","field_value":"test"}}}}}
+            }}
+        ]},
+        "device_operational_status": 1,
+        "device_drivers": ["smartnic"],
+        "device_endpoints": []
+        }
+    ]
+}
diff --git a/src/nbi/tests/data/agent_probes_device.json b/src/nbi/tests/data/agent_probes_device.json
new file mode 100644
index 000000000..e9d931528
--- /dev/null
+++ b/src/nbi/tests/data/agent_probes_device.json
@@ -0,0 +1,27 @@
+{
+
+    "contexts": [
+        {"context_id": {"context_uuid": {"uuid": "admin"}}}
+    ],
+    "topologies": [
+        {"topology_id": {"topology_uuid": {"uuid": "admin"}, "context_id": {"context_uuid": {"uuid": "admin"}}}}
+    ],
+    "devices": [
+        {
+            "device_id": {"device_uuid": {"uuid": "smp-01"}},
+            "device_type": "smartnic",
+            "device_config": {"config_rules": [
+                {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "172.17.0.3"}},
+                {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8000"}},
+                {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+                    "username": "admin", "password": "admin", "base_url": "/manage-probe", "timeout" : 120
+                }}}
+            ]},
+            "device_operational_status": 1,
+            "device_drivers": [12],
+            "device_endpoints": []
+        }
+        
+    ]
+}
+ 
\ No newline at end of file
diff --git a/src/policy/Dockerfile b/src/policy/Dockerfile
deleted file mode 120000
index eec732273..000000000
--- a/src/policy/Dockerfile
+++ /dev/null
@@ -1 +0,0 @@
-src/main/docker/Dockerfile.multistage.jvm
\ No newline at end of file
diff --git a/src/policy/Dockerfile b/src/policy/Dockerfile
new file mode 100644
index 000000000..2c6412d07
--- /dev/null
+++ b/src/policy/Dockerfile
@@ -0,0 +1,66 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+# Multi-stage Docker image build
+
+# Stage 1
+FROM maven:3-jdk-11 AS builder
+
+# Define working directory
+WORKDIR /app
+
+# Copy every file in working directory, as defined in .dockerignore file
+COPY ./pom.xml pom.xml
+COPY ./src src/
+COPY ./target/generated-sources/ target/generated-sources/
+RUN mvn --errors --batch-mode package -Dmaven.test.skip=true
+
+# Stage 2
+FROM builder AS unit-test
+
+RUN mvn --errors --batch-mode -Pgenerate-consolidated-coverage verify
+
+# Stage 3
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 AS release
+
+ARG JAVA_PACKAGE=java-11-openjdk-headless
+ARG RUN_JAVA_VERSION=1.3.8
+ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
+# Install java and the run-java script
+# Also set up permissions for user `1001`
+RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
+    && microdnf update \
+    && microdnf clean all \
+    && mkdir /deployments \
+    && chown 1001 /deployments \
+    && chmod "g+rwX" /deployments \
+    && chown 1001:root /deployments \
+    && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
+    && chown 1001 /deployments/run-java.sh \
+    && chmod 540 /deployments/run-java.sh \
+    && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security
+
+# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
+ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+# We make four distinct layers so if there are application changes the library layers can be re-used
+COPY --from=builder --chown=1001 /app/target/quarkus-app/lib/ /deployments/lib/
+COPY --from=builder --chown=1001 /app/target/quarkus-app/*.jar /deployments/
+COPY --from=builder --chown=1001 /app/target/quarkus-app/app/ /deployments/app/
+COPY --from=builder --chown=1001 /app/target/quarkus-app/quarkus/ /deployments/quarkus/
+
+EXPOSE 8080
+EXPOSE 6060
+USER 1001
+
+ENTRYPOINT [ "/deployments/run-java.sh" ]
diff --git a/src/policy/src/main/java/org/etsi/tfs/policy/Serializer.java b/src/policy/src/main/java/org/etsi/tfs/policy/Serializer.java
index 570a7fb9e..018a08bdf 100644
--- a/src/policy/src/main/java/org/etsi/tfs/policy/Serializer.java
+++ b/src/policy/src/main/java/org/etsi/tfs/policy/Serializer.java
@@ -2304,6 +2304,8 @@ public class Serializer {
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE;
             case IETF_ACTN:
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN;
+            case SMARTNIC:
+                return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_SMARTNIC;
             case UNDEFINED:
             default:
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED;
@@ -2333,6 +2335,8 @@ public class Serializer {
                 return DeviceDriverEnum.FLEXSCALE;
             case DEVICEDRIVER_IETF_ACTN:
                 return DeviceDriverEnum.IETF_ACTN;
+            case DEVICEDRIVER_SMARTNIC:
+                return DeviceDriverEnum.SMARTNIC;
             case DEVICEDRIVER_UNDEFINED:
             case UNRECOGNIZED:
             default:
diff --git a/src/policy/src/main/java/org/etsi/tfs/policy/context/model/DeviceDriverEnum.java b/src/policy/src/main/java/org/etsi/tfs/policy/context/model/DeviceDriverEnum.java
index 72a1d7136..937752dc8 100644
--- a/src/policy/src/main/java/org/etsi/tfs/policy/context/model/DeviceDriverEnum.java
+++ b/src/policy/src/main/java/org/etsi/tfs/policy/context/model/DeviceDriverEnum.java
@@ -27,5 +27,6 @@ public enum DeviceDriverEnum {
     IETF_L2VPN,
     GNMI_OPENCONFIG,
     FLEXSCALE,
-    IETF_ACTN
+    IETF_ACTN,
+    SMARTNIC
 }
diff --git a/src/policy/src/main/proto/acl.proto b/src/policy/src/main/proto/acl.proto
deleted file mode 120000
index 158ae78eb..000000000
--- a/src/policy/src/main/proto/acl.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/acl.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/acl.proto b/src/policy/src/main/proto/acl.proto
new file mode 100644
index 000000000..3dba735dc
--- /dev/null
+++ b/src/policy/src/main/proto/acl.proto
@@ -0,0 +1,69 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package acl;
+
+enum AclRuleTypeEnum {
+  ACLRULETYPE_UNDEFINED = 0;
+  ACLRULETYPE_IPV4      = 1;
+  ACLRULETYPE_IPV6      = 2;
+  ACLRULETYPE_L2        = 3;
+  ACLRULETYPE_MPLS      = 4;
+  ACLRULETYPE_MIXED     = 5;
+}
+
+enum AclForwardActionEnum {
+  ACLFORWARDINGACTION_UNDEFINED = 0;
+  ACLFORWARDINGACTION_DROP      = 1;
+  ACLFORWARDINGACTION_ACCEPT    = 2;
+  ACLFORWARDINGACTION_REJECT    = 3;
+}
+
+enum AclLogActionEnum {
+  ACLLOGACTION_UNDEFINED = 0;
+  ACLLOGACTION_NOLOG     = 1;
+  ACLLOGACTION_SYSLOG    = 2;
+}
+
+message AclMatch {
+  uint32 dscp             = 1;
+  uint32 protocol         = 2;
+  string src_address      = 3;
+  string dst_address      = 4;
+  uint32 src_port         = 5;
+  uint32 dst_port         = 6;
+  uint32 start_mpls_label = 7;
+  uint32 end_mpls_label   = 8;
+}
+
+message AclAction {
+  AclForwardActionEnum forward_action = 1;
+  AclLogActionEnum     log_action     = 2;
+}
+
+message AclEntry {
+  uint32    sequence_id = 1;
+  string    description = 2;
+  AclMatch  match       = 3;
+  AclAction action      = 4;
+}
+
+message AclRuleSet {
+  string             name        = 1;
+  AclRuleTypeEnum    type        = 2;
+  string             description = 3;
+  string             user_id     = 4;
+  repeated AclEntry  entries     = 5;
+}
diff --git a/src/policy/src/main/proto/context.proto b/src/policy/src/main/proto/context.proto
deleted file mode 120000
index 7f33c4bc7..000000000
--- a/src/policy/src/main/proto/context.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/context.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/context.proto b/src/policy/src/main/proto/context.proto
new file mode 100644
index 000000000..fce1e71ad
--- /dev/null
+++ b/src/policy/src/main/proto/context.proto
@@ -0,0 +1,615 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package context;
+
+import "acl.proto";
+import "kpi_sample_types.proto";
+
+service ContextService {
+  rpc ListContextIds     (Empty         ) returns (       ContextIdList   ) {}
+  rpc ListContexts       (Empty         ) returns (       ContextList     ) {}
+  rpc GetContext         (ContextId     ) returns (       Context         ) {}
+  rpc SetContext         (Context       ) returns (       ContextId       ) {}
+  rpc RemoveContext      (ContextId     ) returns (       Empty           ) {}
+  rpc GetContextEvents   (Empty         ) returns (stream ContextEvent    ) {}
+
+  rpc ListTopologyIds    (ContextId     ) returns (       TopologyIdList  ) {}
+  rpc ListTopologies     (ContextId     ) returns (       TopologyList    ) {}
+  rpc GetTopology        (TopologyId    ) returns (       Topology        ) {}
+  rpc GetTopologyDetails (TopologyId    ) returns (       TopologyDetails ) {}
+  rpc SetTopology        (Topology      ) returns (       TopologyId      ) {}
+  rpc RemoveTopology     (TopologyId    ) returns (       Empty           ) {}
+  rpc GetTopologyEvents  (Empty         ) returns (stream TopologyEvent   ) {}
+
+  rpc ListDeviceIds      (Empty         ) returns (       DeviceIdList    ) {}
+  rpc ListDevices        (Empty         ) returns (       DeviceList      ) {}
+  rpc GetDevice          (DeviceId      ) returns (       Device          ) {}
+  rpc SetDevice          (Device        ) returns (       DeviceId        ) {}
+  rpc RemoveDevice       (DeviceId      ) returns (       Empty           ) {}
+  rpc GetDeviceEvents    (Empty         ) returns (stream DeviceEvent     ) {}
+  rpc SelectDevice       (DeviceFilter  ) returns (       DeviceList      ) {}
+  rpc ListEndPointNames  (EndPointIdList) returns (       EndPointNameList) {}
+
+  rpc ListLinkIds        (Empty         ) returns (       LinkIdList      ) {}
+  rpc ListLinks          (Empty         ) returns (       LinkList        ) {}
+  rpc GetLink            (LinkId        ) returns (       Link            ) {}
+  rpc SetLink            (Link          ) returns (       LinkId          ) {}
+  rpc RemoveLink         (LinkId        ) returns (       Empty           ) {}
+  rpc GetLinkEvents      (Empty         ) returns (stream LinkEvent       ) {}
+
+  rpc ListServiceIds     (ContextId     ) returns (       ServiceIdList   ) {}
+  rpc ListServices       (ContextId     ) returns (       ServiceList     ) {}
+  rpc GetService         (ServiceId     ) returns (       Service         ) {}
+  rpc SetService         (Service       ) returns (       ServiceId       ) {}
+  rpc UnsetService       (Service       ) returns (       ServiceId       ) {}
+  rpc RemoveService      (ServiceId     ) returns (       Empty           ) {}
+  rpc GetServiceEvents   (Empty         ) returns (stream ServiceEvent    ) {}
+  rpc SelectService      (ServiceFilter ) returns (       ServiceList     ) {}
+
+  rpc ListSliceIds       (ContextId     ) returns (       SliceIdList     ) {}
+  rpc ListSlices         (ContextId     ) returns (       SliceList       ) {}
+  rpc GetSlice           (SliceId       ) returns (       Slice           ) {}
+  rpc SetSlice           (Slice         ) returns (       SliceId         ) {}
+  rpc UnsetSlice         (Slice         ) returns (       SliceId         ) {}
+  rpc RemoveSlice        (SliceId       ) returns (       Empty           ) {}
+  rpc GetSliceEvents     (Empty         ) returns (stream SliceEvent      ) {}
+  rpc SelectSlice        (SliceFilter   ) returns (       SliceList       ) {}
+
+  rpc ListConnectionIds  (ServiceId     ) returns (       ConnectionIdList) {}
+  rpc ListConnections    (ServiceId     ) returns (       ConnectionList  ) {}
+  rpc GetConnection      (ConnectionId  ) returns (       Connection      ) {}
+  rpc SetConnection      (Connection    ) returns (       ConnectionId    ) {}
+  rpc RemoveConnection   (ConnectionId  ) returns (       Empty           ) {}
+  rpc GetConnectionEvents(Empty         ) returns (stream ConnectionEvent ) {}
+}
+
+// ----- Generic -------------------------------------------------------------------------------------------------------
+message Empty {}
+
+message Uuid {
+  string uuid = 1;
+}
+
+enum EventTypeEnum {
+  EVENTTYPE_UNDEFINED = 0;
+  EVENTTYPE_CREATE = 1;
+  EVENTTYPE_UPDATE = 2;
+  EVENTTYPE_REMOVE = 3;
+}
+
+message Timestamp {
+  double timestamp = 1;
+}
+
+message Event {
+  Timestamp timestamp = 1;
+  EventTypeEnum event_type = 2;
+}
+
+// ----- Context -------------------------------------------------------------------------------------------------------
+message ContextId {
+  Uuid context_uuid = 1;
+}
+
+message Context {
+  ContextId context_id = 1;
+  string name = 2;
+  repeated TopologyId topology_ids = 3;
+  repeated ServiceId service_ids = 4;
+  repeated SliceId slice_ids = 5;
+  TeraFlowController controller = 6;
+}
+
+message ContextIdList {
+  repeated ContextId context_ids = 1;
+}
+
+message ContextList {
+  repeated Context contexts = 1;
+}
+
+message ContextEvent {
+  Event event = 1;
+  ContextId context_id = 2;
+}
+
+
+// ----- Topology ------------------------------------------------------------------------------------------------------
+message TopologyId {
+  ContextId context_id = 1;
+  Uuid topology_uuid = 2;
+}
+
+message Topology {
+  TopologyId topology_id = 1;
+  string name = 2;
+  repeated DeviceId device_ids = 3;
+  repeated LinkId link_ids = 4;
+}
+
+message TopologyDetails {
+  TopologyId topology_id = 1;
+  string name = 2;
+  repeated Device devices = 3;
+  repeated Link links = 4;
+}
+
+message TopologyIdList {
+  repeated TopologyId topology_ids = 1;
+}
+
+message TopologyList {
+  repeated Topology topologies = 1;
+}
+
+message TopologyEvent {
+  Event event = 1;
+  TopologyId topology_id = 2;
+}
+
+
+// ----- Device --------------------------------------------------------------------------------------------------------
+message DeviceId {
+  Uuid device_uuid = 1;
+}
+
+message Device {
+  DeviceId device_id = 1;
+  string name = 2;
+  string device_type = 3;
+  DeviceConfig device_config = 4;
+  DeviceOperationalStatusEnum device_operational_status = 5;
+  repeated DeviceDriverEnum device_drivers = 6;
+  repeated EndPoint device_endpoints = 7;
+  repeated Component components = 8; // Used for inventory
+  DeviceId controller_id = 9; // Identifier of node controlling the actual device
+}
+
+message Component {                         //Defined previously to this section - Tested OK
+  Uuid component_uuid   = 1;
+  string name           = 2;
+  string type           = 3;
+  
+  map<string, string> attributes = 4; // dict[attr.name => json.dumps(attr.value)]
+  string parent         = 5;
+}
+
+message DeviceConfig {
+  repeated ConfigRule config_rules = 1;
+}
+
+enum DeviceDriverEnum {
+  DEVICEDRIVER_UNDEFINED = 0; // also used for emulated
+  DEVICEDRIVER_OPENCONFIG = 1;
+  DEVICEDRIVER_TRANSPORT_API = 2;
+  DEVICEDRIVER_P4 = 3;
+  DEVICEDRIVER_IETF_NETWORK_TOPOLOGY = 4;
+  DEVICEDRIVER_ONF_TR_532 = 5;
+  DEVICEDRIVER_XR = 6;
+  DEVICEDRIVER_IETF_L2VPN = 7;
+  DEVICEDRIVER_GNMI_OPENCONFIG = 8;
+  DEVICEDRIVER_FLEXSCALE = 9;
+  DEVICEDRIVER_IETF_ACTN = 10;
+  DEVICEDRIVER_SMARTNIC = 11;
+}
+
+enum DeviceOperationalStatusEnum {
+  DEVICEOPERATIONALSTATUS_UNDEFINED = 0;
+  DEVICEOPERATIONALSTATUS_DISABLED = 1;
+  DEVICEOPERATIONALSTATUS_ENABLED = 2;
+}
+
+message DeviceIdList {
+  repeated DeviceId device_ids = 1;
+}
+
+message DeviceList {
+  repeated Device devices = 1;
+}
+
+message DeviceFilter {
+  DeviceIdList device_ids = 1;
+  bool include_endpoints = 2;
+  bool include_config_rules = 3;
+  bool include_components = 4;
+}
+
+message DeviceEvent {
+  Event event = 1;
+  DeviceId device_id = 2;
+  DeviceConfig device_config = 3;
+}
+
+
+// ----- Link ----------------------------------------------------------------------------------------------------------
+message LinkId {
+  Uuid link_uuid = 1;
+}
+
+message LinkAttributes {
+  float total_capacity_gbps = 1;
+  float used_capacity_gbps  = 2;
+}
+
+message Link {
+  LinkId link_id = 1;
+  string name = 2;
+  repeated EndPointId link_endpoint_ids = 3;
+  LinkAttributes attributes = 4;
+}
+
+message LinkIdList {
+  repeated LinkId link_ids = 1;
+}
+
+message LinkList {
+  repeated Link links = 1;
+}
+
+message LinkEvent {
+  Event event = 1;
+  LinkId link_id = 2;
+}
+
+
+// ----- Service -------------------------------------------------------------------------------------------------------
+message ServiceId {
+  ContextId context_id = 1;
+  Uuid service_uuid = 2;
+}
+
+message Service {
+  ServiceId service_id = 1;
+  string name = 2;
+  ServiceTypeEnum service_type = 3;
+  repeated EndPointId service_endpoint_ids = 4;
+  repeated Constraint service_constraints = 5;
+  ServiceStatus service_status = 6;
+  ServiceConfig service_config = 7;
+  Timestamp timestamp = 8;
+}
+
+enum ServiceTypeEnum {
+  SERVICETYPE_UNKNOWN = 0;
+  SERVICETYPE_L3NM = 1;
+  SERVICETYPE_L2NM = 2;
+  SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3;
+  SERVICETYPE_TE = 4;
+  SERVICETYPE_E2E = 5;
+}
+
+enum ServiceStatusEnum {
+  SERVICESTATUS_UNDEFINED = 0;
+  SERVICESTATUS_PLANNED = 1;
+  SERVICESTATUS_ACTIVE = 2;
+  SERVICESTATUS_UPDATING = 3;
+  SERVICESTATUS_PENDING_REMOVAL = 4;
+  SERVICESTATUS_SLA_VIOLATED = 5;
+}
+
+message ServiceStatus {
+  ServiceStatusEnum service_status = 1;
+}
+
+message ServiceConfig {
+  repeated ConfigRule config_rules = 1;
+}
+
+message ServiceIdList {
+  repeated ServiceId service_ids = 1;
+}
+
+message ServiceList {
+  repeated Service services = 1;
+}
+
+message ServiceFilter {
+  ServiceIdList service_ids = 1;
+  bool include_endpoint_ids = 2;
+  bool include_constraints = 3;
+  bool include_config_rules = 4;
+}
+
+message ServiceEvent {
+  Event event = 1;
+  ServiceId service_id = 2;
+}
+
+// ----- Slice ---------------------------------------------------------------------------------------------------------
+message SliceId {
+  ContextId context_id = 1;
+  Uuid slice_uuid = 2;
+}
+
+message Slice {
+  SliceId slice_id = 1;
+  string name = 2;
+  repeated EndPointId slice_endpoint_ids = 3;
+  repeated Constraint slice_constraints = 4;
+  repeated ServiceId slice_service_ids = 5;
+  repeated SliceId slice_subslice_ids = 6;
+  SliceStatus slice_status = 7;
+  SliceConfig slice_config = 8;
+  SliceOwner slice_owner = 9;
+  Timestamp timestamp = 10;
+}
+
+message SliceOwner {
+  Uuid owner_uuid = 1;
+  string owner_string = 2;
+}
+
+enum SliceStatusEnum {
+  SLICESTATUS_UNDEFINED    = 0;
+  SLICESTATUS_PLANNED      = 1;
+  SLICESTATUS_INIT         = 2;
+  SLICESTATUS_ACTIVE       = 3;
+  SLICESTATUS_DEINIT       = 4;
+  SLICESTATUS_SLA_VIOLATED = 5;
+}
+
+message SliceStatus {
+  SliceStatusEnum slice_status = 1;
+}
+
+message SliceConfig {
+  repeated ConfigRule config_rules = 1;
+}
+
+message SliceIdList {
+  repeated SliceId slice_ids = 1;
+}
+
+message SliceList {
+  repeated Slice slices = 1;
+}
+
+message SliceFilter {
+  SliceIdList slice_ids = 1;
+  bool include_endpoint_ids = 2;
+  bool include_constraints = 3;
+  bool include_service_ids = 4;
+  bool include_subslice_ids = 5;
+  bool include_config_rules = 6;
+}
+
+message SliceEvent {
+  Event event = 1;
+  SliceId slice_id = 2;
+}
+
+// ----- Connection ----------------------------------------------------------------------------------------------------
+message ConnectionId {
+  Uuid connection_uuid = 1;
+}
+
+message ConnectionSettings_L0 {
+  string lsp_symbolic_name = 1;
+}
+
+message ConnectionSettings_L2 {
+  string src_mac_address = 1;
+  string dst_mac_address = 2;
+  uint32 ether_type = 3;
+  uint32 vlan_id = 4;
+  uint32 mpls_label = 5;
+  uint32 mpls_traffic_class = 6;
+}
+
+message ConnectionSettings_L3 {
+  string src_ip_address = 1;
+  string dst_ip_address = 2;
+  uint32 dscp = 3;
+  uint32 protocol = 4;
+  uint32 ttl = 5;
+}
+
+message ConnectionSettings_L4 {
+  uint32 src_port = 1;
+  uint32 dst_port = 2;
+  uint32 tcp_flags = 3;
+  uint32 ttl = 4;
+}
+
+message ConnectionSettings {
+  ConnectionSettings_L0 l0 = 1;
+  ConnectionSettings_L2 l2 = 2;
+  ConnectionSettings_L3 l3 = 3;
+  ConnectionSettings_L4 l4 = 4;
+}
+
+message Connection {
+  ConnectionId connection_id = 1;
+  ServiceId service_id = 2;
+  repeated EndPointId path_hops_endpoint_ids = 3;
+  repeated ServiceId sub_service_ids = 4;
+  ConnectionSettings settings = 5;
+}
+
+message ConnectionIdList {
+  repeated ConnectionId connection_ids = 1;
+}
+
+message ConnectionList {
+  repeated Connection connections = 1;
+}
+
+message ConnectionEvent {
+  Event event = 1;
+  ConnectionId connection_id = 2;
+}
+
+
+// ----- Endpoint ------------------------------------------------------------------------------------------------------
+message EndPointId {
+  TopologyId topology_id = 1;
+  DeviceId device_id = 2;
+  Uuid endpoint_uuid = 3;
+}
+
+message EndPoint {
+  EndPointId endpoint_id = 1;
+  string name = 2;
+  string endpoint_type = 3;
+  repeated kpi_sample_types.KpiSampleType kpi_sample_types = 4;
+  Location endpoint_location = 5;
+}
+
+message EndPointName {
+  EndPointId endpoint_id = 1;
+  string device_name = 2;
+  string endpoint_name = 3;
+  string endpoint_type = 4;
+}
+
+message EndPointIdList {
+  repeated EndPointId endpoint_ids = 1;
+}
+
+message EndPointNameList {
+  repeated EndPointName endpoint_names = 1;
+}
+
+
+// ----- Configuration -------------------------------------------------------------------------------------------------
+enum ConfigActionEnum {
+  CONFIGACTION_UNDEFINED = 0;
+  CONFIGACTION_SET       = 1;
+  CONFIGACTION_DELETE    = 2;
+}
+
+message ConfigRule_Custom {
+  string resource_key = 1;
+  string resource_value = 2;
+}
+
+message ConfigRule_ACL {
+  EndPointId endpoint_id = 1;
+  acl.AclRuleSet rule_set = 2;
+}
+
+message ConfigRule {
+  ConfigActionEnum action = 1;
+  oneof config_rule {
+    ConfigRule_Custom custom = 2;
+    ConfigRule_ACL acl = 3;
+  }
+}
+
+
+// ----- Constraint ----------------------------------------------------------------------------------------------------
+enum ConstraintActionEnum {
+  CONSTRAINTACTION_UNDEFINED = 0;
+  CONSTRAINTACTION_SET       = 1;
+  CONSTRAINTACTION_DELETE    = 2;
+}
+
+message Constraint_Custom {
+  string constraint_type = 1;
+  string constraint_value = 2;
+}
+
+message Constraint_Schedule {
+  float start_timestamp = 1;
+  float duration_days = 2;
+}
+
+message GPS_Position {
+  float latitude = 1;
+  float longitude = 2;
+}
+
+message Location {
+  oneof location {
+    string region = 1;
+    GPS_Position gps_position = 2;
+  }
+}
+
+message Constraint_EndPointLocation {
+  EndPointId endpoint_id = 1;
+  Location location = 2;
+}
+
+message Constraint_EndPointPriority {
+  EndPointId endpoint_id = 1;
+  uint32 priority = 2;
+}
+
+message Constraint_SLA_Latency {
+  float e2e_latency_ms = 1;
+}
+
+message Constraint_SLA_Capacity {
+  float capacity_gbps = 1;
+}
+
+message Constraint_SLA_Availability {
+  uint32 num_disjoint_paths = 1;
+  bool all_active = 2;
+  float availability = 3; // 0.0 .. 100.0 percentage of availability
+}
+
+enum IsolationLevelEnum {
+  NO_ISOLATION = 0;
+  PHYSICAL_ISOLATION = 1;
+  LOGICAL_ISOLATION = 2;
+  PROCESS_ISOLATION = 3;
+  PHYSICAL_MEMORY_ISOLATION = 4;
+  PHYSICAL_NETWORK_ISOLATION = 5;
+  VIRTUAL_RESOURCE_ISOLATION = 6;
+  NETWORK_FUNCTIONS_ISOLATION = 7;
+  SERVICE_ISOLATION = 8;
+}
+
+message Constraint_SLA_Isolation_level {
+  repeated IsolationLevelEnum isolation_level = 1;
+}
+
+message Constraint_Exclusions {
+  bool is_permanent = 1;
+  repeated DeviceId device_ids = 2;
+  repeated EndPointId endpoint_ids = 3;
+  repeated LinkId link_ids = 4;
+}
+
+message Constraint {
+  ConstraintActionEnum action = 1;
+  oneof constraint {
+    Constraint_Custom custom = 2;
+    Constraint_Schedule schedule = 3;
+    Constraint_EndPointLocation endpoint_location = 4;
+    Constraint_EndPointPriority endpoint_priority = 5;
+    Constraint_SLA_Capacity sla_capacity = 6;
+    Constraint_SLA_Latency sla_latency = 7;
+    Constraint_SLA_Availability sla_availability = 8;
+    Constraint_SLA_Isolation_level sla_isolation = 9;
+    Constraint_Exclusions exclusions = 10;
+  }
+}
+
+
+// ----- Miscellaneous -------------------------------------------------------------------------------------------------
+message TeraFlowController {
+  ContextId context_id = 1;
+  string ip_address = 2;
+  uint32 port = 3;
+}
+
+message AuthenticationResult {
+  ContextId context_id = 1;
+  bool authenticated = 2;
+}
diff --git a/src/policy/src/main/proto/context_policy.proto b/src/policy/src/main/proto/context_policy.proto
deleted file mode 120000
index d41593dde..000000000
--- a/src/policy/src/main/proto/context_policy.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/context_policy.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/context_policy.proto b/src/policy/src/main/proto/context_policy.proto
new file mode 100644
index 000000000..f6dae4830
--- /dev/null
+++ b/src/policy/src/main/proto/context_policy.proto
@@ -0,0 +1,28 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package context_policy;
+
+import "context.proto";
+import "policy.proto";
+
+// created as a separate service to prevent import-loops in context and policy
+service ContextPolicyService {
+  rpc ListPolicyRuleIds(context.Empty         ) returns (policy.PolicyRuleIdList) {}
+  rpc ListPolicyRules  (context.Empty         ) returns (policy.PolicyRuleList  ) {}
+  rpc GetPolicyRule    (policy.PolicyRuleId   ) returns (policy.PolicyRule      ) {}
+  rpc SetPolicyRule    (policy.PolicyRule     ) returns (policy.PolicyRuleId    ) {}
+  rpc RemovePolicyRule (policy.PolicyRuleId   ) returns (context.Empty          ) {}
+}
diff --git a/src/policy/src/main/proto/device.proto b/src/policy/src/main/proto/device.proto
deleted file mode 120000
index ad6e7c47e..000000000
--- a/src/policy/src/main/proto/device.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/device.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/device.proto b/src/policy/src/main/proto/device.proto
new file mode 100644
index 000000000..30e60079d
--- /dev/null
+++ b/src/policy/src/main/proto/device.proto
@@ -0,0 +1,34 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package device;
+
+import "context.proto";
+import "monitoring.proto";
+
+service DeviceService {
+  rpc AddDevice       (context.Device    ) returns (context.DeviceId    ) {}
+  rpc ConfigureDevice (context.Device    ) returns (context.DeviceId    ) {}
+  rpc DeleteDevice    (context.DeviceId  ) returns (context.Empty       ) {}
+  rpc GetInitialConfig(context.DeviceId  ) returns (context.DeviceConfig) {}
+  rpc MonitorDeviceKpi(MonitoringSettings) returns (context.Empty       ) {}
+}
+
+message MonitoringSettings {
+  monitoring.KpiId kpi_id = 1;
+  monitoring.KpiDescriptor kpi_descriptor = 2;
+  float sampling_duration_s = 3;
+  float sampling_interval_s = 4;
+}
diff --git a/src/policy/src/main/proto/kpi_sample_types.proto b/src/policy/src/main/proto/kpi_sample_types.proto
deleted file mode 120000
index 98e748bbf..000000000
--- a/src/policy/src/main/proto/kpi_sample_types.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/kpi_sample_types.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/kpi_sample_types.proto b/src/policy/src/main/proto/kpi_sample_types.proto
new file mode 100644
index 000000000..5b234a4e3
--- /dev/null
+++ b/src/policy/src/main/proto/kpi_sample_types.proto
@@ -0,0 +1,42 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package kpi_sample_types;
+
+enum KpiSampleType {
+    KPISAMPLETYPE_UNKNOWN                       = 0;
+
+    KPISAMPLETYPE_PACKETS_TRANSMITTED           = 101;
+    KPISAMPLETYPE_PACKETS_RECEIVED              = 102;
+    KPISAMPLETYPE_PACKETS_DROPPED               = 103;
+    KPISAMPLETYPE_BYTES_TRANSMITTED             = 201;
+    KPISAMPLETYPE_BYTES_RECEIVED                = 202;
+    KPISAMPLETYPE_BYTES_DROPPED                 = 203;
+
+    KPISAMPLETYPE_LINK_TOTAL_CAPACITY_GBPS      = 301;
+    KPISAMPLETYPE_LINK_USED_CAPACITY_GBPS       = 302;
+
+    KPISAMPLETYPE_ML_CONFIDENCE                 = 401;  //. can be used by both optical and L3 without any issue
+
+    KPISAMPLETYPE_OPTICAL_SECURITY_STATUS       = 501;  //. can be used by both optical and L3 without any issue
+
+    KPISAMPLETYPE_L3_UNIQUE_ATTACK_CONNS        = 601;
+    KPISAMPLETYPE_L3_TOTAL_DROPPED_PACKTS       = 602;
+    KPISAMPLETYPE_L3_UNIQUE_ATTACKERS           = 603;
+    KPISAMPLETYPE_L3_UNIQUE_COMPROMISED_CLIENTS = 604;
+    KPISAMPLETYPE_L3_SECURITY_STATUS_CRYPTO     = 605;
+
+    KPISAMPLETYPE_SERVICE_LATENCY_MS            = 701;
+}
diff --git a/src/policy/src/main/proto/monitoring.proto b/src/policy/src/main/proto/monitoring.proto
deleted file mode 120000
index aceaa7328..000000000
--- a/src/policy/src/main/proto/monitoring.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/monitoring.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/monitoring.proto b/src/policy/src/main/proto/monitoring.proto
new file mode 100644
index 000000000..45ba48b02
--- /dev/null
+++ b/src/policy/src/main/proto/monitoring.proto
@@ -0,0 +1,174 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package monitoring;
+
+import "context.proto";
+import "kpi_sample_types.proto";
+
+service MonitoringService {
+  rpc SetKpi                (KpiDescriptor      ) returns (KpiId               ) {} // Stable not final
+  rpc DeleteKpi             (KpiId              ) returns (context.Empty       ) {} // Stable and final
+  rpc GetKpiDescriptor      (KpiId              ) returns (KpiDescriptor       ) {} // Stable and final
+  rpc GetKpiDescriptorList  (context.Empty      ) returns (KpiDescriptorList   ) {} // Stable and final
+  rpc IncludeKpi            (Kpi                ) returns (context.Empty       ) {} // Stable and final
+  rpc MonitorKpi            (MonitorKpiRequest  ) returns (context.Empty       ) {} // Stable and final
+  rpc QueryKpiData          (KpiQuery           ) returns (RawKpiTable         ) {} // Not implemented
+  rpc SetKpiSubscription    (SubsDescriptor     ) returns (stream SubsResponse ) {} // Stable not final
+  rpc GetSubsDescriptor     (SubscriptionID     ) returns (SubsDescriptor      ) {} // Stable and final
+  rpc GetSubscriptions      (context.Empty      ) returns (SubsList            ) {} // Stable and final
+  rpc DeleteSubscription    (SubscriptionID     ) returns (context.Empty       ) {} // Stable and final
+  rpc SetKpiAlarm           (AlarmDescriptor    ) returns (AlarmID             ) {} // Stable not final
+  rpc GetAlarms             (context.Empty      ) returns (AlarmList           ) {} // Stable and final
+  rpc GetAlarmDescriptor    (AlarmID            ) returns (AlarmDescriptor     ) {} // Stable and final
+  rpc GetAlarmResponseStream(AlarmSubscription  ) returns (stream AlarmResponse) {} // Not Stable not final
+  rpc DeleteAlarm           (AlarmID            ) returns (context.Empty       ) {} // Stable and final
+  rpc GetStreamKpi          (KpiId              ) returns (stream Kpi          ) {} // Stable not final
+  rpc GetInstantKpi         (KpiId              ) returns (Kpi                 ) {} // Stable not final
+}
+
+message KpiDescriptor {
+  KpiId                          kpi_id          = 1;
+  string                         kpi_description = 2;
+  repeated KpiId                 kpi_id_list     = 3;
+  kpi_sample_types.KpiSampleType kpi_sample_type = 4;
+  context.DeviceId               device_id       = 5;
+  context.EndPointId             endpoint_id     = 6;
+  context.ServiceId              service_id      = 7;
+  context.SliceId                slice_id        = 8;
+  context.ConnectionId           connection_id   = 9;
+  context.LinkId                 link_id         = 10;
+}
+
+message MonitorKpiRequest {
+  KpiId kpi_id              = 1;
+  float monitoring_window_s = 2;
+  float sampling_rate_s     = 3;
+  // Pending add field to reflect Available Device Protocols
+}
+
+message KpiQuery {
+  repeated KpiId    kpi_ids             = 1;
+  float             monitoring_window_s = 2;
+  uint32            last_n_samples      = 3;  // used when you want something like "get the last N many samples
+  context.Timestamp start_timestamp     = 4;  // used when you want something like "get the samples since X date/time"
+  context.Timestamp end_timestamp       = 5;  // used when you want something like "get the samples until X date/time"
+}
+
+
+message RawKpi { // cell
+  context.Timestamp timestamp = 1;
+  KpiValue          kpi_value = 2;
+}
+
+message RawKpiList { // column
+  KpiId           kpi_id    = 1;
+  repeated RawKpi raw_kpis  = 2;
+}
+
+message RawKpiTable { // table
+  repeated RawKpiList raw_kpi_lists = 1;
+}
+
+message KpiId {
+  context.Uuid kpi_id = 1;
+}
+
+message Kpi {
+  KpiId             kpi_id    = 1;
+  context.Timestamp timestamp = 2;
+  KpiValue          kpi_value = 3;
+}
+
+message KpiValueRange {
+  KpiValue  kpiMinValue     = 1;
+  KpiValue  kpiMaxValue     = 2;
+  bool      inRange         = 3;  // by default True
+  bool      includeMinValue = 4;  // False is outside the interval
+  bool      includeMaxValue = 5;  // False is outside the interval
+}
+
+message KpiValue {
+  oneof value {
+    int32  int32Val  = 1;
+    uint32 uint32Val = 2;
+    int64  int64Val  = 3;
+    uint64 uint64Val = 4;
+    float  floatVal  = 5;
+    string stringVal = 6;
+    bool   boolVal   = 7;
+  }
+}
+
+
+message KpiList {
+  repeated Kpi kpi = 1;
+}
+
+message KpiDescriptorList {
+  repeated KpiDescriptor kpi_descriptor_list = 1;
+}
+
+message SubsDescriptor{
+  SubscriptionID    subs_id             = 1;
+  KpiId             kpi_id              = 2;
+  float             sampling_duration_s = 3;
+  float             sampling_interval_s = 4;
+  context.Timestamp start_timestamp     = 5;  // used when you want something like "get the samples since X date/time"
+  context.Timestamp end_timestamp       = 6;  // used when you want something like "get the samples until X date/time"
+  // Pending add field to reflect Available Device Protocols
+}
+
+message SubscriptionID {
+  context.Uuid subs_id = 1;
+}
+
+message SubsResponse {
+  SubscriptionID   subs_id  = 1;
+  KpiList          kpi_list = 2;
+}
+
+message SubsList {
+  repeated SubsDescriptor subs_descriptor = 1;
+}
+
+message AlarmDescriptor {
+  AlarmID                     alarm_id              = 1;
+  string                      alarm_description     = 2;
+  string                      name                  = 3;
+  KpiId                       kpi_id                = 4;
+  KpiValueRange               kpi_value_range       = 5;
+  context.Timestamp           timestamp             = 6;
+}
+
+message AlarmID{
+  context.Uuid alarm_id = 1;
+}
+
+message AlarmSubscription{
+  AlarmID alarm_id                  = 1;
+  float   subscription_timeout_s    = 2;
+  float   subscription_frequency_ms = 3;
+}
+
+message AlarmResponse {
+  AlarmID           alarm_id  = 1;
+  string            text      = 2;
+  KpiList           kpi_list  = 3;
+}
+
+message AlarmList {
+    repeated AlarmDescriptor alarm_descriptor = 1;
+}
diff --git a/src/policy/src/main/proto/policy.proto b/src/policy/src/main/proto/policy.proto
deleted file mode 120000
index df455f961..000000000
--- a/src/policy/src/main/proto/policy.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/policy.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/policy.proto b/src/policy/src/main/proto/policy.proto
new file mode 100644
index 000000000..a6f160150
--- /dev/null
+++ b/src/policy/src/main/proto/policy.proto
@@ -0,0 +1,113 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package policy;
+
+import "context.proto";
+import "policy_condition.proto";
+import "policy_action.proto";
+
+service PolicyService {
+  rpc PolicyAddService (PolicyRuleService) returns (PolicyRuleState) {}
+  rpc PolicyAddDevice (PolicyRuleDevice) returns (PolicyRuleState) {}
+  rpc PolicyUpdateService (PolicyRuleService) returns (PolicyRuleState) {}
+  rpc PolicyUpdateDevice (PolicyRuleDevice) returns (PolicyRuleState) {}
+  rpc PolicyDelete (PolicyRuleId) returns (PolicyRuleState) {}
+  rpc GetPolicyService (PolicyRuleId) returns (PolicyRuleService) {}
+  rpc GetPolicyDevice (PolicyRuleId) returns (PolicyRuleDevice) {}
+  rpc GetPolicyByServiceId (context.ServiceId) returns (PolicyRuleServiceList) {}
+}
+
+enum PolicyRuleStateEnum {
+  POLICY_UNDEFINED = 0;     // Undefined rule state
+  POLICY_FAILED = 1;        // Rule failed
+  POLICY_INSERTED = 2;      // Rule is just inserted
+  POLICY_VALIDATED = 3;     // Rule content is correct
+  POLICY_PROVISIONED = 4;   // Rule subscribed to Monitoring
+  POLICY_ACTIVE = 5;        // Rule is currently active (alarm is just thrown by Monitoring)
+  POLICY_ENFORCED = 6;      // Rule action is successfully enforced
+  POLICY_INEFFECTIVE = 7;   // The applied rule action did not work as expected
+  POLICY_EFFECTIVE = 8;     // The applied rule action did work as expected
+  POLICY_UPDATED = 9;       // Operator requires a policy to change
+  POLICY_REMOVED = 10;      // Operator requires to remove a policy
+}
+
+message PolicyRuleId {
+  context.Uuid uuid = 1;
+}
+
+message PolicyRuleState {
+  PolicyRuleStateEnum policyRuleState = 1;
+  string policyRuleStateMessage = 2;
+}
+
+// Basic policy rule attributes
+message PolicyRuleBasic {
+  PolicyRuleId policyRuleId = 1;
+  PolicyRuleState policyRuleState = 2; //policy.proto:58:12: Explicit 'optional' labels are disallowed in the Proto3 syntax. To define 'optional' fields in Proto3, simply remove the 'optional' label, as fields are 'optional' by default.
+  uint32 priority = 3;
+
+  // Event-Condition-Action (ECA) model
+  repeated PolicyRuleCondition conditionList = 4;  // When these policy conditions are met, an event is automatically thrown
+  BooleanOperator booleanOperator = 5;             // Evaluation operator to be used
+  repeated PolicyRuleAction actionList = 6;        // One or more actions should be applied
+}
+
+// Service-oriented policy rule
+message PolicyRuleService {
+  // Basic policy rule attributes
+  PolicyRuleBasic policyRuleBasic = 1;
+
+  // Affected service and (some of) its device(s)
+  context.ServiceId serviceId = 2;
+  repeated context.DeviceId deviceList = 3;  // List of devices this service is traversing (not exhaustive)
+}
+
+// Device-oriented policy rule
+message PolicyRuleDevice {
+  // Basic policy rule attributes
+  PolicyRuleBasic policyRuleBasic = 1;
+
+  // Affected device(s)
+  repeated context.DeviceId deviceList = 2;
+}
+
+// Wrapper policy rule object
+message PolicyRule {
+  oneof policy_rule {
+    PolicyRuleService service = 1;
+    PolicyRuleDevice device = 2;
+  }
+}
+
+// A list of policy rule IDs
+message PolicyRuleIdList {
+  repeated PolicyRuleId policyRuleIdList = 1;
+}
+
+// A list of service-oriented policy rules
+message PolicyRuleServiceList {
+  repeated PolicyRuleService policyRuleServiceList = 1;
+}
+
+// A list of device-oriented policy rules
+message PolicyRuleDeviceList {
+  repeated PolicyRuleDevice policyRuleDeviceList = 1;
+}
+
+// A list of policy rules
+message PolicyRuleList {
+  repeated PolicyRule policyRules = 1;
+}
diff --git a/src/policy/src/main/proto/policy_action.proto b/src/policy/src/main/proto/policy_action.proto
deleted file mode 120000
index 63dcef3d2..000000000
--- a/src/policy/src/main/proto/policy_action.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/policy_action.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/policy_action.proto b/src/policy/src/main/proto/policy_action.proto
new file mode 100644
index 000000000..d547e9779
--- /dev/null
+++ b/src/policy/src/main/proto/policy_action.proto
@@ -0,0 +1,42 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package policy;
+
+// Action
+message PolicyRuleAction {
+  PolicyRuleActionEnum action = 1;
+  repeated PolicyRuleActionConfig action_config = 2;
+}
+
+enum PolicyRuleActionEnum {
+  POLICYRULE_ACTION_NO_ACTION = 0;
+  POLICYRULE_ACTION_SET_DEVICE_STATUS = 1;
+  POLICYRULE_ACTION_ADD_SERVICE_CONFIGRULE = 2;
+  POLICYRULE_ACTION_ADD_SERVICE_CONSTRAINT = 3;
+  POLICY_RULE_ACTION_CALL_SERVICE_RPC = 4;
+  POLICY_RULE_ACTION_RECALCULATE_PATH = 5;
+}
+
+// Action configuration
+message PolicyRuleActionConfig {
+  string action_key = 1;
+  string action_value = 2;
+}
+
+// message PolicyRuleAction {
+//   PolicyRuleActionEnum action = 1;
+//   repeated string parameters = 2;
+// }
diff --git a/src/policy/src/main/proto/policy_condition.proto b/src/policy/src/main/proto/policy_condition.proto
deleted file mode 120000
index 31f7d9d10..000000000
--- a/src/policy/src/main/proto/policy_condition.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/policy_condition.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/policy_condition.proto b/src/policy/src/main/proto/policy_condition.proto
new file mode 100644
index 000000000..2037af93c
--- /dev/null
+++ b/src/policy/src/main/proto/policy_condition.proto
@@ -0,0 +1,43 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package policy;
+
+import "monitoring.proto";
+
+// Condition
+message PolicyRuleCondition {
+  monitoring.KpiId kpiId = 1;
+  NumericalOperator numericalOperator = 2;
+  monitoring.KpiValue kpiValue = 3;
+}
+
+// Operator to be used when comparing Kpis with condition values
+enum NumericalOperator {
+  POLICYRULE_CONDITION_NUMERICAL_UNDEFINED = 0;          // Kpi numerical operator undefined
+  POLICYRULE_CONDITION_NUMERICAL_EQUAL = 1;              // Kpi is equal with value
+  POLICYRULE_CONDITION_NUMERICAL_NOT_EQUAL = 2;          // Kpi is not equal with value
+  POLICYRULE_CONDITION_NUMERICAL_LESS_THAN = 3;          // Kpi is less than value
+  POLICYRULE_CONDITION_NUMERICAL_LESS_THAN_EQUAL = 4;    // Kpi is less than or equal with value
+  POLICYRULE_CONDITION_NUMERICAL_GREATER_THAN = 5;       // Kpi is greater than value
+  POLICYRULE_CONDITION_NUMERICAL_GREATER_THAN_EQUAL = 6; // Kpi is less than or equal with value
+}
+
+// Operator to be used when evaluating each condition
+enum BooleanOperator {
+  POLICYRULE_CONDITION_BOOLEAN_UNDEFINED = 0;  // Boolean operator undefined
+  POLICYRULE_CONDITION_BOOLEAN_AND = 1;        // Boolean AND operator
+  POLICYRULE_CONDITION_BOOLEAN_OR = 2;         // Boolean OR operator
+}
\ No newline at end of file
diff --git a/src/policy/src/main/proto/service.proto b/src/policy/src/main/proto/service.proto
deleted file mode 120000
index 5ca543da0..000000000
--- a/src/policy/src/main/proto/service.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/service.proto
\ No newline at end of file
diff --git a/src/policy/src/main/proto/service.proto b/src/policy/src/main/proto/service.proto
new file mode 100644
index 000000000..658859e3c
--- /dev/null
+++ b/src/policy/src/main/proto/service.proto
@@ -0,0 +1,25 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package service;
+
+import "context.proto";
+
+service ServiceService {
+  rpc CreateService       (context.Service  ) returns (context.ServiceId) {}
+  rpc UpdateService       (context.Service  ) returns (context.ServiceId) {}
+  rpc DeleteService       (context.ServiceId) returns (context.Empty    ) {}
+  rpc RecomputeConnections(context.Service  ) returns (context.Empty    ) {}
+}
diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py
index e771e24f1..e985fe292 100644
--- a/src/service/service/service_handler_api/FilterFields.py
+++ b/src/service/service/service_handler_api/FilterFields.py
@@ -40,6 +40,7 @@ DEVICE_DRIVER_VALUES = {
     DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG,
     DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE,
     DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN,
+    DeviceDriverEnum.DEVICEDRIVER_SMARTNIC
 }
 
 # Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified
diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py
index eaf8f715a..61376c0d2 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -94,6 +94,12 @@ SERVICE_HANDLERS = [
             FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN],
         }
     ]),
+    # (SMARTNIC_ServiceHandler, [
+    #     {
+    #         FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_L2NM,
+    #         FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_SMARTNIC],
+    #     }
+    # ]),
     (E2EOrchestratorServiceHandler, [
         {
             FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_E2E,
diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py
index 4c04bbfe1..af5ac4564 100644
--- a/src/webui/service/device/forms.py
+++ b/src/webui/service/device/forms.py
@@ -33,6 +33,7 @@ class AddDeviceForm(FlaskForm):
     device_drivers_gnmi_openconfig = BooleanField('GNMI OPENCONFIG')
     device_drivers_flexscale = BooleanField('FLEXSCALE')
     device_drivers_ietf_actn = BooleanField('IETF ACTN')
+    device_drivers_smartnic = BooleanField('SMARTNIC')
 
     device_config_address = StringField('connect/address',default='127.0.0.1',validators=[DataRequired(), Length(min=5)])
     device_config_port = StringField('connect/port',default='0',validators=[DataRequired(), Length(min=1)])
diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py
index 8b8bc236a..8aaaafccf 100644
--- a/src/webui/service/device/routes.py
+++ b/src/webui/service/device/routes.py
@@ -127,6 +127,8 @@ def add():
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG)
         if form.device_drivers_flexscale.data:
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE)
+        if form.device_drivers_smartnic.data:
+            device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_SMARTNIC)
         if form.device_drivers_ietf_actn.data:
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN)
         device_obj.device_drivers.extend(device_drivers) # pylint: disable=no-member
diff --git a/src/webui/service/templates/device/add.html b/src/webui/service/templates/device/add.html
index c4d7f1685..484bc30bf 100644
--- a/src/webui/service/templates/device/add.html
+++ b/src/webui/service/templates/device/add.html
@@ -95,6 +95,7 @@
                 <br />
                 {{ form.device_drivers_flexscale }} {{ form.device_drivers_flexscale.label(class="col-sm-3 col-form-label") }}
                 {{ form.device_drivers_ietf_actn }} {{ form.device_drivers_ietf_actn.label(class="col-sm-3 col-form-label") }}
+                {{ form.device_drivers_smartnic }} {{ form.device_drivers_smartnic_actn.label(class="col-sm-3 col-form-label") }}
                 {% endif %}
             </div>
         </div>
diff --git a/src/ztp/Dockerfile b/src/ztp/Dockerfile
deleted file mode 120000
index eec732273..000000000
--- a/src/ztp/Dockerfile
+++ /dev/null
@@ -1 +0,0 @@
-src/main/docker/Dockerfile.multistage.jvm
\ No newline at end of file
diff --git a/src/ztp/Dockerfile b/src/ztp/Dockerfile
new file mode 100644
index 000000000..43fef96b4
--- /dev/null
+++ b/src/ztp/Dockerfile
@@ -0,0 +1,67 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+# Multi-stage Docker image build
+
+# Stage 1
+FROM maven:3-jdk-11 AS builder
+
+# Define working directory
+WORKDIR /app
+
+# Copy every file in working directory, as defined in .dockerignore file
+COPY ./pom.xml pom.xml
+COPY ./src src/
+COPY ./target/generated-sources/ target/generated-sources/
+RUN mvn --errors --batch-mode package -Dmaven.test.skip=true
+
+# Stage 2
+FROM builder AS unit-test
+
+RUN mvn --errors --batch-mode -Pgenerate-consolidated-coverage verify
+
+# Stage 3
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 AS release
+
+ARG JAVA_PACKAGE=java-11-openjdk-headless
+ARG RUN_JAVA_VERSION=1.3.8
+ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
+# Install java and the run-java script
+# Also set up permissions for user `1001`
+RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
+    && microdnf update \
+    && microdnf clean all \
+    && mkdir /deployments \
+    && chown 1001 /deployments \
+    && chmod "g+rwX" /deployments \
+    && chown 1001:root /deployments \
+    && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
+    && chown 1001 /deployments/run-java.sh \
+    && chmod 540 /deployments/run-java.sh \
+    && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security
+
+# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
+ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+# We make four distinct layers so if there are application changes the library layers can be re-used
+COPY --from=builder --chown=1001 /app/target/quarkus-app/lib/ /deployments/lib/
+COPY --from=builder --chown=1001 /app/target/quarkus-app/*.jar /deployments/
+COPY --from=builder --chown=1001 /app/target/quarkus-app/app/ /deployments/app/
+COPY --from=builder --chown=1001 /app/target/quarkus-app/quarkus/ /deployments/quarkus/
+
+EXPOSE 8080
+EXPOSE 5050
+USER 1001
+
+ENTRYPOINT [ "/deployments/run-java.sh" ]
+
diff --git a/src/ztp/src/main/proto/acl.proto b/src/ztp/src/main/proto/acl.proto
deleted file mode 120000
index 158ae78eb..000000000
--- a/src/ztp/src/main/proto/acl.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/acl.proto
\ No newline at end of file
diff --git a/src/ztp/src/main/proto/acl.proto b/src/ztp/src/main/proto/acl.proto
new file mode 100644
index 000000000..3dba735dc
--- /dev/null
+++ b/src/ztp/src/main/proto/acl.proto
@@ -0,0 +1,69 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package acl;
+
+enum AclRuleTypeEnum {
+  ACLRULETYPE_UNDEFINED = 0;
+  ACLRULETYPE_IPV4      = 1;
+  ACLRULETYPE_IPV6      = 2;
+  ACLRULETYPE_L2        = 3;
+  ACLRULETYPE_MPLS      = 4;
+  ACLRULETYPE_MIXED     = 5;
+}
+
+enum AclForwardActionEnum {
+  ACLFORWARDINGACTION_UNDEFINED = 0;
+  ACLFORWARDINGACTION_DROP      = 1;
+  ACLFORWARDINGACTION_ACCEPT    = 2;
+  ACLFORWARDINGACTION_REJECT    = 3;
+}
+
+enum AclLogActionEnum {
+  ACLLOGACTION_UNDEFINED = 0;
+  ACLLOGACTION_NOLOG     = 1;
+  ACLLOGACTION_SYSLOG    = 2;
+}
+
+message AclMatch {
+  uint32 dscp             = 1;
+  uint32 protocol         = 2;
+  string src_address      = 3;
+  string dst_address      = 4;
+  uint32 src_port         = 5;
+  uint32 dst_port         = 6;
+  uint32 start_mpls_label = 7;
+  uint32 end_mpls_label   = 8;
+}
+
+message AclAction {
+  AclForwardActionEnum forward_action = 1;
+  AclLogActionEnum     log_action     = 2;
+}
+
+message AclEntry {
+  uint32    sequence_id = 1;
+  string    description = 2;
+  AclMatch  match       = 3;
+  AclAction action      = 4;
+}
+
+message AclRuleSet {
+  string             name        = 1;
+  AclRuleTypeEnum    type        = 2;
+  string             description = 3;
+  string             user_id     = 4;
+  repeated AclEntry  entries     = 5;
+}
diff --git a/src/ztp/src/main/proto/context.proto b/src/ztp/src/main/proto/context.proto
deleted file mode 120000
index 7f33c4bc7..000000000
--- a/src/ztp/src/main/proto/context.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/context.proto
\ No newline at end of file
diff --git a/src/ztp/src/main/proto/context.proto b/src/ztp/src/main/proto/context.proto
new file mode 100644
index 000000000..fce1e71ad
--- /dev/null
+++ b/src/ztp/src/main/proto/context.proto
@@ -0,0 +1,615 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package context;
+
+import "acl.proto";
+import "kpi_sample_types.proto";
+
+service ContextService {
+  rpc ListContextIds     (Empty         ) returns (       ContextIdList   ) {}
+  rpc ListContexts       (Empty         ) returns (       ContextList     ) {}
+  rpc GetContext         (ContextId     ) returns (       Context         ) {}
+  rpc SetContext         (Context       ) returns (       ContextId       ) {}
+  rpc RemoveContext      (ContextId     ) returns (       Empty           ) {}
+  rpc GetContextEvents   (Empty         ) returns (stream ContextEvent    ) {}
+
+  rpc ListTopologyIds    (ContextId     ) returns (       TopologyIdList  ) {}
+  rpc ListTopologies     (ContextId     ) returns (       TopologyList    ) {}
+  rpc GetTopology        (TopologyId    ) returns (       Topology        ) {}
+  rpc GetTopologyDetails (TopologyId    ) returns (       TopologyDetails ) {}
+  rpc SetTopology        (Topology      ) returns (       TopologyId      ) {}
+  rpc RemoveTopology     (TopologyId    ) returns (       Empty           ) {}
+  rpc GetTopologyEvents  (Empty         ) returns (stream TopologyEvent   ) {}
+
+  rpc ListDeviceIds      (Empty         ) returns (       DeviceIdList    ) {}
+  rpc ListDevices        (Empty         ) returns (       DeviceList      ) {}
+  rpc GetDevice          (DeviceId      ) returns (       Device          ) {}
+  rpc SetDevice          (Device        ) returns (       DeviceId        ) {}
+  rpc RemoveDevice       (DeviceId      ) returns (       Empty           ) {}
+  rpc GetDeviceEvents    (Empty         ) returns (stream DeviceEvent     ) {}
+  rpc SelectDevice       (DeviceFilter  ) returns (       DeviceList      ) {}
+  rpc ListEndPointNames  (EndPointIdList) returns (       EndPointNameList) {}
+
+  rpc ListLinkIds        (Empty         ) returns (       LinkIdList      ) {}
+  rpc ListLinks          (Empty         ) returns (       LinkList        ) {}
+  rpc GetLink            (LinkId        ) returns (       Link            ) {}
+  rpc SetLink            (Link          ) returns (       LinkId          ) {}
+  rpc RemoveLink         (LinkId        ) returns (       Empty           ) {}
+  rpc GetLinkEvents      (Empty         ) returns (stream LinkEvent       ) {}
+
+  rpc ListServiceIds     (ContextId     ) returns (       ServiceIdList   ) {}
+  rpc ListServices       (ContextId     ) returns (       ServiceList     ) {}
+  rpc GetService         (ServiceId     ) returns (       Service         ) {}
+  rpc SetService         (Service       ) returns (       ServiceId       ) {}
+  rpc UnsetService       (Service       ) returns (       ServiceId       ) {}
+  rpc RemoveService      (ServiceId     ) returns (       Empty           ) {}
+  rpc GetServiceEvents   (Empty         ) returns (stream ServiceEvent    ) {}
+  rpc SelectService      (ServiceFilter ) returns (       ServiceList     ) {}
+
+  rpc ListSliceIds       (ContextId     ) returns (       SliceIdList     ) {}
+  rpc ListSlices         (ContextId     ) returns (       SliceList       ) {}
+  rpc GetSlice           (SliceId       ) returns (       Slice           ) {}
+  rpc SetSlice           (Slice         ) returns (       SliceId         ) {}
+  rpc UnsetSlice         (Slice         ) returns (       SliceId         ) {}
+  rpc RemoveSlice        (SliceId       ) returns (       Empty           ) {}
+  rpc GetSliceEvents     (Empty         ) returns (stream SliceEvent      ) {}
+  rpc SelectSlice        (SliceFilter   ) returns (       SliceList       ) {}
+
+  rpc ListConnectionIds  (ServiceId     ) returns (       ConnectionIdList) {}
+  rpc ListConnections    (ServiceId     ) returns (       ConnectionList  ) {}
+  rpc GetConnection      (ConnectionId  ) returns (       Connection      ) {}
+  rpc SetConnection      (Connection    ) returns (       ConnectionId    ) {}
+  rpc RemoveConnection   (ConnectionId  ) returns (       Empty           ) {}
+  rpc GetConnectionEvents(Empty         ) returns (stream ConnectionEvent ) {}
+}
+
+// ----- Generic -------------------------------------------------------------------------------------------------------
+message Empty {}
+
+message Uuid {
+  string uuid = 1;
+}
+
+enum EventTypeEnum {
+  EVENTTYPE_UNDEFINED = 0;
+  EVENTTYPE_CREATE = 1;
+  EVENTTYPE_UPDATE = 2;
+  EVENTTYPE_REMOVE = 3;
+}
+
+message Timestamp {
+  double timestamp = 1;
+}
+
+message Event {
+  Timestamp timestamp = 1;
+  EventTypeEnum event_type = 2;
+}
+
+// ----- Context -------------------------------------------------------------------------------------------------------
+message ContextId {
+  Uuid context_uuid = 1;
+}
+
+message Context {
+  ContextId context_id = 1;
+  string name = 2;
+  repeated TopologyId topology_ids = 3;
+  repeated ServiceId service_ids = 4;
+  repeated SliceId slice_ids = 5;
+  TeraFlowController controller = 6;
+}
+
+message ContextIdList {
+  repeated ContextId context_ids = 1;
+}
+
+message ContextList {
+  repeated Context contexts = 1;
+}
+
+message ContextEvent {
+  Event event = 1;
+  ContextId context_id = 2;
+}
+
+
+// ----- Topology ------------------------------------------------------------------------------------------------------
+message TopologyId {
+  ContextId context_id = 1;
+  Uuid topology_uuid = 2;
+}
+
+message Topology {
+  TopologyId topology_id = 1;
+  string name = 2;
+  repeated DeviceId device_ids = 3;
+  repeated LinkId link_ids = 4;
+}
+
+message TopologyDetails {
+  TopologyId topology_id = 1;
+  string name = 2;
+  repeated Device devices = 3;
+  repeated Link links = 4;
+}
+
+message TopologyIdList {
+  repeated TopologyId topology_ids = 1;
+}
+
+message TopologyList {
+  repeated Topology topologies = 1;
+}
+
+message TopologyEvent {
+  Event event = 1;
+  TopologyId topology_id = 2;
+}
+
+
+// ----- Device --------------------------------------------------------------------------------------------------------
+message DeviceId {
+  Uuid device_uuid = 1;
+}
+
+message Device {
+  DeviceId device_id = 1;
+  string name = 2;
+  string device_type = 3;
+  DeviceConfig device_config = 4;
+  DeviceOperationalStatusEnum device_operational_status = 5;
+  repeated DeviceDriverEnum device_drivers = 6;
+  repeated EndPoint device_endpoints = 7;
+  repeated Component components = 8; // Used for inventory
+  DeviceId controller_id = 9; // Identifier of node controlling the actual device
+}
+
+message Component {                         //Defined previously to this section - Tested OK
+  Uuid component_uuid   = 1;
+  string name           = 2;
+  string type           = 3;
+  
+  map<string, string> attributes = 4; // dict[attr.name => json.dumps(attr.value)]
+  string parent         = 5;
+}
+
+message DeviceConfig {
+  repeated ConfigRule config_rules = 1;
+}
+
+enum DeviceDriverEnum {
+  DEVICEDRIVER_UNDEFINED = 0; // also used for emulated
+  DEVICEDRIVER_OPENCONFIG = 1;
+  DEVICEDRIVER_TRANSPORT_API = 2;
+  DEVICEDRIVER_P4 = 3;
+  DEVICEDRIVER_IETF_NETWORK_TOPOLOGY = 4;
+  DEVICEDRIVER_ONF_TR_532 = 5;
+  DEVICEDRIVER_XR = 6;
+  DEVICEDRIVER_IETF_L2VPN = 7;
+  DEVICEDRIVER_GNMI_OPENCONFIG = 8;
+  DEVICEDRIVER_FLEXSCALE = 9;
+  DEVICEDRIVER_IETF_ACTN = 10;
+  DEVICEDRIVER_SMARTNIC = 11;
+}
+
+enum DeviceOperationalStatusEnum {
+  DEVICEOPERATIONALSTATUS_UNDEFINED = 0;
+  DEVICEOPERATIONALSTATUS_DISABLED = 1;
+  DEVICEOPERATIONALSTATUS_ENABLED = 2;
+}
+
+message DeviceIdList {
+  repeated DeviceId device_ids = 1;
+}
+
+message DeviceList {
+  repeated Device devices = 1;
+}
+
+message DeviceFilter {
+  DeviceIdList device_ids = 1;
+  bool include_endpoints = 2;
+  bool include_config_rules = 3;
+  bool include_components = 4;
+}
+
+message DeviceEvent {
+  Event event = 1;
+  DeviceId device_id = 2;
+  DeviceConfig device_config = 3;
+}
+
+
+// ----- Link ----------------------------------------------------------------------------------------------------------
+message LinkId {
+  Uuid link_uuid = 1;
+}
+
+message LinkAttributes {
+  float total_capacity_gbps = 1;
+  float used_capacity_gbps  = 2;
+}
+
+message Link {
+  LinkId link_id = 1;
+  string name = 2;
+  repeated EndPointId link_endpoint_ids = 3;
+  LinkAttributes attributes = 4;
+}
+
+message LinkIdList {
+  repeated LinkId link_ids = 1;
+}
+
+message LinkList {
+  repeated Link links = 1;
+}
+
+message LinkEvent {
+  Event event = 1;
+  LinkId link_id = 2;
+}
+
+
+// ----- Service -------------------------------------------------------------------------------------------------------
+message ServiceId {
+  ContextId context_id = 1;
+  Uuid service_uuid = 2;
+}
+
+message Service {
+  ServiceId service_id = 1;
+  string name = 2;
+  ServiceTypeEnum service_type = 3;
+  repeated EndPointId service_endpoint_ids = 4;
+  repeated Constraint service_constraints = 5;
+  ServiceStatus service_status = 6;
+  ServiceConfig service_config = 7;
+  Timestamp timestamp = 8;
+}
+
+enum ServiceTypeEnum {
+  SERVICETYPE_UNKNOWN = 0;
+  SERVICETYPE_L3NM = 1;
+  SERVICETYPE_L2NM = 2;
+  SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3;
+  SERVICETYPE_TE = 4;
+  SERVICETYPE_E2E = 5;
+}
+
+enum ServiceStatusEnum {
+  SERVICESTATUS_UNDEFINED = 0;
+  SERVICESTATUS_PLANNED = 1;
+  SERVICESTATUS_ACTIVE = 2;
+  SERVICESTATUS_UPDATING = 3;
+  SERVICESTATUS_PENDING_REMOVAL = 4;
+  SERVICESTATUS_SLA_VIOLATED = 5;
+}
+
+message ServiceStatus {
+  ServiceStatusEnum service_status = 1;
+}
+
+message ServiceConfig {
+  repeated ConfigRule config_rules = 1;
+}
+
+message ServiceIdList {
+  repeated ServiceId service_ids = 1;
+}
+
+message ServiceList {
+  repeated Service services = 1;
+}
+
+message ServiceFilter {
+  ServiceIdList service_ids = 1;
+  bool include_endpoint_ids = 2;
+  bool include_constraints = 3;
+  bool include_config_rules = 4;
+}
+
+message ServiceEvent {
+  Event event = 1;
+  ServiceId service_id = 2;
+}
+
+// ----- Slice ---------------------------------------------------------------------------------------------------------
+message SliceId {
+  ContextId context_id = 1;
+  Uuid slice_uuid = 2;
+}
+
+message Slice {
+  SliceId slice_id = 1;
+  string name = 2;
+  repeated EndPointId slice_endpoint_ids = 3;
+  repeated Constraint slice_constraints = 4;
+  repeated ServiceId slice_service_ids = 5;
+  repeated SliceId slice_subslice_ids = 6;
+  SliceStatus slice_status = 7;
+  SliceConfig slice_config = 8;
+  SliceOwner slice_owner = 9;
+  Timestamp timestamp = 10;
+}
+
+message SliceOwner {
+  Uuid owner_uuid = 1;
+  string owner_string = 2;
+}
+
+enum SliceStatusEnum {
+  SLICESTATUS_UNDEFINED    = 0;
+  SLICESTATUS_PLANNED      = 1;
+  SLICESTATUS_INIT         = 2;
+  SLICESTATUS_ACTIVE       = 3;
+  SLICESTATUS_DEINIT       = 4;
+  SLICESTATUS_SLA_VIOLATED = 5;
+}
+
+message SliceStatus {
+  SliceStatusEnum slice_status = 1;
+}
+
+message SliceConfig {
+  repeated ConfigRule config_rules = 1;
+}
+
+message SliceIdList {
+  repeated SliceId slice_ids = 1;
+}
+
+message SliceList {
+  repeated Slice slices = 1;
+}
+
+message SliceFilter {
+  SliceIdList slice_ids = 1;
+  bool include_endpoint_ids = 2;
+  bool include_constraints = 3;
+  bool include_service_ids = 4;
+  bool include_subslice_ids = 5;
+  bool include_config_rules = 6;
+}
+
+message SliceEvent {
+  Event event = 1;
+  SliceId slice_id = 2;
+}
+
+// ----- Connection ----------------------------------------------------------------------------------------------------
+message ConnectionId {
+  Uuid connection_uuid = 1;
+}
+
+message ConnectionSettings_L0 {
+  string lsp_symbolic_name = 1;
+}
+
+message ConnectionSettings_L2 {
+  string src_mac_address = 1;
+  string dst_mac_address = 2;
+  uint32 ether_type = 3;
+  uint32 vlan_id = 4;
+  uint32 mpls_label = 5;
+  uint32 mpls_traffic_class = 6;
+}
+
+message ConnectionSettings_L3 {
+  string src_ip_address = 1;
+  string dst_ip_address = 2;
+  uint32 dscp = 3;
+  uint32 protocol = 4;
+  uint32 ttl = 5;
+}
+
+message ConnectionSettings_L4 {
+  uint32 src_port = 1;
+  uint32 dst_port = 2;
+  uint32 tcp_flags = 3;
+  uint32 ttl = 4;
+}
+
+message ConnectionSettings {
+  ConnectionSettings_L0 l0 = 1;
+  ConnectionSettings_L2 l2 = 2;
+  ConnectionSettings_L3 l3 = 3;
+  ConnectionSettings_L4 l4 = 4;
+}
+
+message Connection {
+  ConnectionId connection_id = 1;
+  ServiceId service_id = 2;
+  repeated EndPointId path_hops_endpoint_ids = 3;
+  repeated ServiceId sub_service_ids = 4;
+  ConnectionSettings settings = 5;
+}
+
+message ConnectionIdList {
+  repeated ConnectionId connection_ids = 1;
+}
+
+message ConnectionList {
+  repeated Connection connections = 1;
+}
+
+message ConnectionEvent {
+  Event event = 1;
+  ConnectionId connection_id = 2;
+}
+
+
+// ----- Endpoint ------------------------------------------------------------------------------------------------------
+message EndPointId {
+  TopologyId topology_id = 1;
+  DeviceId device_id = 2;
+  Uuid endpoint_uuid = 3;
+}
+
+message EndPoint {
+  EndPointId endpoint_id = 1;
+  string name = 2;
+  string endpoint_type = 3;
+  repeated kpi_sample_types.KpiSampleType kpi_sample_types = 4;
+  Location endpoint_location = 5;
+}
+
+message EndPointName {
+  EndPointId endpoint_id = 1;
+  string device_name = 2;
+  string endpoint_name = 3;
+  string endpoint_type = 4;
+}
+
+message EndPointIdList {
+  repeated EndPointId endpoint_ids = 1;
+}
+
+message EndPointNameList {
+  repeated EndPointName endpoint_names = 1;
+}
+
+
+// ----- Configuration -------------------------------------------------------------------------------------------------
+enum ConfigActionEnum {
+  CONFIGACTION_UNDEFINED = 0;
+  CONFIGACTION_SET       = 1;
+  CONFIGACTION_DELETE    = 2;
+}
+
+message ConfigRule_Custom {
+  string resource_key = 1;
+  string resource_value = 2;
+}
+
+message ConfigRule_ACL {
+  EndPointId endpoint_id = 1;
+  acl.AclRuleSet rule_set = 2;
+}
+
+message ConfigRule {
+  ConfigActionEnum action = 1;
+  oneof config_rule {
+    ConfigRule_Custom custom = 2;
+    ConfigRule_ACL acl = 3;
+  }
+}
+
+
+// ----- Constraint ----------------------------------------------------------------------------------------------------
+enum ConstraintActionEnum {
+  CONSTRAINTACTION_UNDEFINED = 0;
+  CONSTRAINTACTION_SET       = 1;
+  CONSTRAINTACTION_DELETE    = 2;
+}
+
+message Constraint_Custom {
+  string constraint_type = 1;
+  string constraint_value = 2;
+}
+
+message Constraint_Schedule {
+  float start_timestamp = 1;
+  float duration_days = 2;
+}
+
+message GPS_Position {
+  float latitude = 1;
+  float longitude = 2;
+}
+
+message Location {
+  oneof location {
+    string region = 1;
+    GPS_Position gps_position = 2;
+  }
+}
+
+message Constraint_EndPointLocation {
+  EndPointId endpoint_id = 1;
+  Location location = 2;
+}
+
+message Constraint_EndPointPriority {
+  EndPointId endpoint_id = 1;
+  uint32 priority = 2;
+}
+
+message Constraint_SLA_Latency {
+  float e2e_latency_ms = 1;
+}
+
+message Constraint_SLA_Capacity {
+  float capacity_gbps = 1;
+}
+
+message Constraint_SLA_Availability {
+  uint32 num_disjoint_paths = 1;
+  bool all_active = 2;
+  float availability = 3; // 0.0 .. 100.0 percentage of availability
+}
+
+enum IsolationLevelEnum {
+  NO_ISOLATION = 0;
+  PHYSICAL_ISOLATION = 1;
+  LOGICAL_ISOLATION = 2;
+  PROCESS_ISOLATION = 3;
+  PHYSICAL_MEMORY_ISOLATION = 4;
+  PHYSICAL_NETWORK_ISOLATION = 5;
+  VIRTUAL_RESOURCE_ISOLATION = 6;
+  NETWORK_FUNCTIONS_ISOLATION = 7;
+  SERVICE_ISOLATION = 8;
+}
+
+message Constraint_SLA_Isolation_level {
+  repeated IsolationLevelEnum isolation_level = 1;
+}
+
+message Constraint_Exclusions {
+  bool is_permanent = 1;
+  repeated DeviceId device_ids = 2;
+  repeated EndPointId endpoint_ids = 3;
+  repeated LinkId link_ids = 4;
+}
+
+message Constraint {
+  ConstraintActionEnum action = 1;
+  oneof constraint {
+    Constraint_Custom custom = 2;
+    Constraint_Schedule schedule = 3;
+    Constraint_EndPointLocation endpoint_location = 4;
+    Constraint_EndPointPriority endpoint_priority = 5;
+    Constraint_SLA_Capacity sla_capacity = 6;
+    Constraint_SLA_Latency sla_latency = 7;
+    Constraint_SLA_Availability sla_availability = 8;
+    Constraint_SLA_Isolation_level sla_isolation = 9;
+    Constraint_Exclusions exclusions = 10;
+  }
+}
+
+
+// ----- Miscellaneous -------------------------------------------------------------------------------------------------
+message TeraFlowController {
+  ContextId context_id = 1;
+  string ip_address = 2;
+  uint32 port = 3;
+}
+
+message AuthenticationResult {
+  ContextId context_id = 1;
+  bool authenticated = 2;
+}
diff --git a/src/ztp/src/main/proto/device.proto b/src/ztp/src/main/proto/device.proto
deleted file mode 120000
index ad6e7c47e..000000000
--- a/src/ztp/src/main/proto/device.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/device.proto
\ No newline at end of file
diff --git a/src/ztp/src/main/proto/device.proto b/src/ztp/src/main/proto/device.proto
new file mode 100644
index 000000000..30e60079d
--- /dev/null
+++ b/src/ztp/src/main/proto/device.proto
@@ -0,0 +1,34 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package device;
+
+import "context.proto";
+import "monitoring.proto";
+
+service DeviceService {
+  rpc AddDevice       (context.Device    ) returns (context.DeviceId    ) {}
+  rpc ConfigureDevice (context.Device    ) returns (context.DeviceId    ) {}
+  rpc DeleteDevice    (context.DeviceId  ) returns (context.Empty       ) {}
+  rpc GetInitialConfig(context.DeviceId  ) returns (context.DeviceConfig) {}
+  rpc MonitorDeviceKpi(MonitoringSettings) returns (context.Empty       ) {}
+}
+
+message MonitoringSettings {
+  monitoring.KpiId kpi_id = 1;
+  monitoring.KpiDescriptor kpi_descriptor = 2;
+  float sampling_duration_s = 3;
+  float sampling_interval_s = 4;
+}
diff --git a/src/ztp/src/main/proto/kpi_sample_types.proto b/src/ztp/src/main/proto/kpi_sample_types.proto
deleted file mode 120000
index 98e748bbf..000000000
--- a/src/ztp/src/main/proto/kpi_sample_types.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/kpi_sample_types.proto
\ No newline at end of file
diff --git a/src/ztp/src/main/proto/kpi_sample_types.proto b/src/ztp/src/main/proto/kpi_sample_types.proto
new file mode 100644
index 000000000..5b234a4e3
--- /dev/null
+++ b/src/ztp/src/main/proto/kpi_sample_types.proto
@@ -0,0 +1,42 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package kpi_sample_types;
+
+enum KpiSampleType {
+    KPISAMPLETYPE_UNKNOWN                       = 0;
+
+    KPISAMPLETYPE_PACKETS_TRANSMITTED           = 101;
+    KPISAMPLETYPE_PACKETS_RECEIVED              = 102;
+    KPISAMPLETYPE_PACKETS_DROPPED               = 103;
+    KPISAMPLETYPE_BYTES_TRANSMITTED             = 201;
+    KPISAMPLETYPE_BYTES_RECEIVED                = 202;
+    KPISAMPLETYPE_BYTES_DROPPED                 = 203;
+
+    KPISAMPLETYPE_LINK_TOTAL_CAPACITY_GBPS      = 301;
+    KPISAMPLETYPE_LINK_USED_CAPACITY_GBPS       = 302;
+
+    KPISAMPLETYPE_ML_CONFIDENCE                 = 401;  //. can be used by both optical and L3 without any issue
+
+    KPISAMPLETYPE_OPTICAL_SECURITY_STATUS       = 501;  //. can be used by both optical and L3 without any issue
+
+    KPISAMPLETYPE_L3_UNIQUE_ATTACK_CONNS        = 601;
+    KPISAMPLETYPE_L3_TOTAL_DROPPED_PACKTS       = 602;
+    KPISAMPLETYPE_L3_UNIQUE_ATTACKERS           = 603;
+    KPISAMPLETYPE_L3_UNIQUE_COMPROMISED_CLIENTS = 604;
+    KPISAMPLETYPE_L3_SECURITY_STATUS_CRYPTO     = 605;
+
+    KPISAMPLETYPE_SERVICE_LATENCY_MS            = 701;
+}
diff --git a/src/ztp/src/main/proto/monitoring.proto b/src/ztp/src/main/proto/monitoring.proto
deleted file mode 120000
index aceaa7328..000000000
--- a/src/ztp/src/main/proto/monitoring.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/monitoring.proto
\ No newline at end of file
diff --git a/src/ztp/src/main/proto/monitoring.proto b/src/ztp/src/main/proto/monitoring.proto
new file mode 100644
index 000000000..45ba48b02
--- /dev/null
+++ b/src/ztp/src/main/proto/monitoring.proto
@@ -0,0 +1,174 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package monitoring;
+
+import "context.proto";
+import "kpi_sample_types.proto";
+
+service MonitoringService {
+  rpc SetKpi                (KpiDescriptor      ) returns (KpiId               ) {} // Stable not final
+  rpc DeleteKpi             (KpiId              ) returns (context.Empty       ) {} // Stable and final
+  rpc GetKpiDescriptor      (KpiId              ) returns (KpiDescriptor       ) {} // Stable and final
+  rpc GetKpiDescriptorList  (context.Empty      ) returns (KpiDescriptorList   ) {} // Stable and final
+  rpc IncludeKpi            (Kpi                ) returns (context.Empty       ) {} // Stable and final
+  rpc MonitorKpi            (MonitorKpiRequest  ) returns (context.Empty       ) {} // Stable and final
+  rpc QueryKpiData          (KpiQuery           ) returns (RawKpiTable         ) {} // Not implemented
+  rpc SetKpiSubscription    (SubsDescriptor     ) returns (stream SubsResponse ) {} // Stable not final
+  rpc GetSubsDescriptor     (SubscriptionID     ) returns (SubsDescriptor      ) {} // Stable and final
+  rpc GetSubscriptions      (context.Empty      ) returns (SubsList            ) {} // Stable and final
+  rpc DeleteSubscription    (SubscriptionID     ) returns (context.Empty       ) {} // Stable and final
+  rpc SetKpiAlarm           (AlarmDescriptor    ) returns (AlarmID             ) {} // Stable not final
+  rpc GetAlarms             (context.Empty      ) returns (AlarmList           ) {} // Stable and final
+  rpc GetAlarmDescriptor    (AlarmID            ) returns (AlarmDescriptor     ) {} // Stable and final
+  rpc GetAlarmResponseStream(AlarmSubscription  ) returns (stream AlarmResponse) {} // Not Stable not final
+  rpc DeleteAlarm           (AlarmID            ) returns (context.Empty       ) {} // Stable and final
+  rpc GetStreamKpi          (KpiId              ) returns (stream Kpi          ) {} // Stable not final
+  rpc GetInstantKpi         (KpiId              ) returns (Kpi                 ) {} // Stable not final
+}
+
+message KpiDescriptor {
+  KpiId                          kpi_id          = 1;
+  string                         kpi_description = 2;
+  repeated KpiId                 kpi_id_list     = 3;
+  kpi_sample_types.KpiSampleType kpi_sample_type = 4;
+  context.DeviceId               device_id       = 5;
+  context.EndPointId             endpoint_id     = 6;
+  context.ServiceId              service_id      = 7;
+  context.SliceId                slice_id        = 8;
+  context.ConnectionId           connection_id   = 9;
+  context.LinkId                 link_id         = 10;
+}
+
+message MonitorKpiRequest {
+  KpiId kpi_id              = 1;
+  float monitoring_window_s = 2;
+  float sampling_rate_s     = 3;
+  // Pending add field to reflect Available Device Protocols
+}
+
+message KpiQuery {
+  repeated KpiId    kpi_ids             = 1;
+  float             monitoring_window_s = 2;
+  uint32            last_n_samples      = 3;  // used when you want something like "get the last N many samples
+  context.Timestamp start_timestamp     = 4;  // used when you want something like "get the samples since X date/time"
+  context.Timestamp end_timestamp       = 5;  // used when you want something like "get the samples until X date/time"
+}
+
+
+message RawKpi { // cell
+  context.Timestamp timestamp = 1;
+  KpiValue          kpi_value = 2;
+}
+
+message RawKpiList { // column
+  KpiId           kpi_id    = 1;
+  repeated RawKpi raw_kpis  = 2;
+}
+
+message RawKpiTable { // table
+  repeated RawKpiList raw_kpi_lists = 1;
+}
+
+message KpiId {
+  context.Uuid kpi_id = 1;
+}
+
+message Kpi {
+  KpiId             kpi_id    = 1;
+  context.Timestamp timestamp = 2;
+  KpiValue          kpi_value = 3;
+}
+
+message KpiValueRange {
+  KpiValue  kpiMinValue     = 1;
+  KpiValue  kpiMaxValue     = 2;
+  bool      inRange         = 3;  // by default True
+  bool      includeMinValue = 4;  // False is outside the interval
+  bool      includeMaxValue = 5;  // False is outside the interval
+}
+
+message KpiValue {
+  oneof value {
+    int32  int32Val  = 1;
+    uint32 uint32Val = 2;
+    int64  int64Val  = 3;
+    uint64 uint64Val = 4;
+    float  floatVal  = 5;
+    string stringVal = 6;
+    bool   boolVal   = 7;
+  }
+}
+
+
+message KpiList {
+  repeated Kpi kpi = 1;
+}
+
+message KpiDescriptorList {
+  repeated KpiDescriptor kpi_descriptor_list = 1;
+}
+
+message SubsDescriptor{
+  SubscriptionID    subs_id             = 1;
+  KpiId             kpi_id              = 2;
+  float             sampling_duration_s = 3;
+  float             sampling_interval_s = 4;
+  context.Timestamp start_timestamp     = 5;  // used when you want something like "get the samples since X date/time"
+  context.Timestamp end_timestamp       = 6;  // used when you want something like "get the samples until X date/time"
+  // Pending add field to reflect Available Device Protocols
+}
+
+message SubscriptionID {
+  context.Uuid subs_id = 1;
+}
+
+message SubsResponse {
+  SubscriptionID   subs_id  = 1;
+  KpiList          kpi_list = 2;
+}
+
+message SubsList {
+  repeated SubsDescriptor subs_descriptor = 1;
+}
+
+message AlarmDescriptor {
+  AlarmID                     alarm_id              = 1;
+  string                      alarm_description     = 2;
+  string                      name                  = 3;
+  KpiId                       kpi_id                = 4;
+  KpiValueRange               kpi_value_range       = 5;
+  context.Timestamp           timestamp             = 6;
+}
+
+message AlarmID{
+  context.Uuid alarm_id = 1;
+}
+
+message AlarmSubscription{
+  AlarmID alarm_id                  = 1;
+  float   subscription_timeout_s    = 2;
+  float   subscription_frequency_ms = 3;
+}
+
+message AlarmResponse {
+  AlarmID           alarm_id  = 1;
+  string            text      = 2;
+  KpiList           kpi_list  = 3;
+}
+
+message AlarmList {
+    repeated AlarmDescriptor alarm_descriptor = 1;
+}
diff --git a/src/ztp/src/main/proto/ztp.proto b/src/ztp/src/main/proto/ztp.proto
deleted file mode 120000
index 9183ce531..000000000
--- a/src/ztp/src/main/proto/ztp.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../proto/ztp.proto
\ No newline at end of file
diff --git a/src/ztp/src/main/proto/ztp.proto b/src/ztp/src/main/proto/ztp.proto
new file mode 100644
index 000000000..5c895900d
--- /dev/null
+++ b/src/ztp/src/main/proto/ztp.proto
@@ -0,0 +1,69 @@
+// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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.
+
+syntax = "proto3";
+package ztp;
+
+import "context.proto";
+
+service ZtpService {
+  rpc ZtpGetDeviceRole(DeviceRoleId) returns (DeviceRole) {}
+  rpc ZtpGetDeviceRolesByDeviceId(context.DeviceId) returns (DeviceRoleList) {}
+  rpc ZtpAdd(DeviceRole) returns (DeviceRoleState) {}
+  rpc ZtpUpdate(DeviceRoleConfig) returns (DeviceRoleState) {}
+  rpc ZtpDelete(DeviceRole) returns (DeviceRoleState) {}
+  rpc ZtpDeleteAll(context.Empty) returns (DeviceDeletionResult) {}
+}
+
+enum DeviceRoleType {
+  NONE = 0;
+  DEV_OPS = 1;
+  DEV_CONF = 2;
+  PIPELINE_CONF = 3;
+}
+
+message DeviceRoleId {
+  context.Uuid devRoleId = 1;
+  context.DeviceId devId = 2;
+}
+
+message DeviceRole {
+  DeviceRoleId devRoleId = 1;
+  DeviceRoleType devRoleType = 2;
+}
+
+message DeviceRoleConfig {
+  DeviceRole devRole = 1;
+  context.DeviceConfig devConfig = 2;
+}
+
+message DeviceRoleList {
+  repeated DeviceRole devRole = 1;
+}
+
+message DeviceRoleState {
+  DeviceRoleId devRoleId = 1;
+  ZtpDeviceState devRoleState = 2;
+}
+
+message DeviceDeletionResult {
+  repeated string deleted = 1;
+}
+
+enum ZtpDeviceState {
+  ZTP_DEV_STATE_UNDEFINED = 0;
+  ZTP_DEV_STATE_CREATED  = 1;
+  ZTP_DEV_STATE_UPDATED  = 2;
+  ZTP_DEV_STATE_DELETED  = 3;
+}
-- 
GitLab