diff --git a/proto/context.proto b/proto/context.proto
index 7570a4596a0abd254d93c77131f03fa432cf09c7..d5022ac292f04cd2e9b80f690be3077e7aedd868 100644
--- a/proto/context.proto
+++ b/proto/context.proto
@@ -202,6 +202,7 @@ enum DeviceDriverEnum {
   DEVICEDRIVER_IETF_L2VPN = 7;
   DEVICEDRIVER_GNMI_OPENCONFIG = 8;
   DEVICEDRIVER_FLEXSCALE = 9;
+  DEVICEDRIVER_IETF_ACTN = 10;
 }
 
 enum DeviceOperationalStatusEnum {
diff --git a/proto/generate_code_java.sh b/proto/generate_code_java.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4a3dbcf128b28e9feb1f96f95b7541b0f32cc664
--- /dev/null
+++ b/proto/generate_code_java.sh
@@ -0,0 +1,32 @@
+#!/bin/bash -eu    
+# 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.
+    
+    
+export JAVA_COMPONENTS="ztp policy"
+
+export TFS_ROOT_DIR=$(dirname $(dirname $(realpath $0)))
+    
+for COMPONENT in $JAVA_COMPONENTS; do
+  echo "\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/"
+  echo
+  echo "[TFS] Now building" $COMPONENT
+  echo
+  echo "\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/"
+  cd $TFS_ROOT_DIR/src/$COMPONENT
+    
+  ./mvnw spotless:apply
+  ./mvnw install -DskipUTs
+done
+
diff --git a/scripts/run_tests_locally-device-ietf-actn.sh b/scripts/run_tests_locally-device-ietf-actn.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8e602b31d9465821dfd798e8038de9b78f7dedc6
--- /dev/null
+++ b/scripts/run_tests_locally-device-ietf-actn.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# 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.
+
+
+PROJECTDIR=`pwd`
+
+cd $PROJECTDIR/src
+RCFILE=$PROJECTDIR/coverage/.coveragerc
+
+# Run unitary tests and analyze coverage of code at same time
+# helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0
+coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+    device/tests/test_unitary_ietf_actn.py
diff --git a/src/common/DeviceTypes.py b/src/common/DeviceTypes.py
index f88f931d4c465349a37a92b5891fd0acd8fe6a48..72b3e21fdecd1019099eec03b3b473f56bcd403a 100644
--- a/src/common/DeviceTypes.py
+++ b/src/common/DeviceTypes.py
@@ -22,6 +22,7 @@ class DeviceTypeEnum(Enum):
     # Emulated device types
     EMULATED_CLIENT                 = 'emu-client'
     EMULATED_DATACENTER             = 'emu-datacenter'
+    EMULATED_IP_SDN_CONTROLLER      = 'emu-ip-sdn-controller'
     EMULATED_MICROWAVE_RADIO_SYSTEM = 'emu-microwave-radio-system'
     EMULATED_OPEN_LINE_SYSTEM       = 'emu-open-line-system'
     EMULATED_OPTICAL_ROADM          = 'emu-optical-roadm'
@@ -36,6 +37,7 @@ class DeviceTypeEnum(Enum):
     # Real device types
     CLIENT                          = 'client'
     DATACENTER                      = 'datacenter'
+    IP_SDN_CONTROLLER               = 'ip-sdn-controller'
     MICROWAVE_RADIO_SYSTEM          = 'microwave-radio-system'
     OPEN_LINE_SYSTEM                = 'open-line-system'
     OPTICAL_ROADM                   = 'optical-roadm'
diff --git a/src/common/tools/descriptor/Loader.py b/src/common/tools/descriptor/Loader.py
index c5468c19ccc063387460ed7f7b28ee70c5f1d907..4ab33beae8987ff2b38e5e2bc0252bacf557120c 100644
--- a/src/common/tools/descriptor/Loader.py
+++ b/src/common/tools/descriptor/Loader.py
@@ -46,7 +46,7 @@ from slice.client.SliceClient import SliceClient
 from .Tools import (
     format_device_custom_config_rules, format_service_custom_config_rules, format_slice_custom_config_rules,
     get_descriptors_add_contexts, get_descriptors_add_services, get_descriptors_add_slices,
-    get_descriptors_add_topologies, split_devices_by_rules)
+    get_descriptors_add_topologies, split_controllers_and_network_devices, split_devices_by_rules)
 
 LOGGER = logging.getLogger(__name__)
 LOGGERS = {
@@ -56,9 +56,10 @@ LOGGERS = {
 }
 
 ENTITY_TO_TEXT = {
-    # name   => singular,    plural
+    # name      => singular,     plural
     'context'   : ('Context',    'Contexts'   ),
     'topology'  : ('Topology',   'Topologies' ),
+    'controller': ('Controller', 'Controllers'),
     'device'    : ('Device',     'Devices'    ),
     'link'      : ('Link',       'Links'      ),
     'service'   : ('Service',    'Services'   ),
@@ -68,8 +69,8 @@ ENTITY_TO_TEXT = {
 
 ACTION_TO_TEXT = {
     # action =>  infinitive,  past
-    'add'     : ('Add',       'Added'),
-    'update'  : ('Update',    'Updated'),
+    'add'     : ('Add',       'Added'     ),
+    'update'  : ('Update',    'Updated'   ),
     'config'  : ('Configure', 'Configured'),
 }
 
@@ -231,10 +232,12 @@ class DescriptorLoader:
 
     def _load_dummy_mode(self) -> None:
         # Dummy Mode: used to pre-load databases (WebUI debugging purposes) with no smart or automated tasks.
+        controllers, network_devices = split_controllers_and_network_devices(self.__devices)
         self.__ctx_cli.connect()
         self._process_descr('context',    'add',    self.__ctx_cli.SetContext,    Context,    self.__contexts_add  )
         self._process_descr('topology',   'add',    self.__ctx_cli.SetTopology,   Topology,   self.__topologies_add)
-        self._process_descr('device',     'add',    self.__ctx_cli.SetDevice,     Device,     self.__devices       )
+        self._process_descr('controller', 'add',    self.__ctx_cli.SetDevice,     Device,     controllers          )
+        self._process_descr('device',     'add',    self.__ctx_cli.SetDevice,     Device,     network_devices      )
         self._process_descr('link',       'add',    self.__ctx_cli.SetLink,       Link,       self.__links         )
         self._process_descr('service',    'add',    self.__ctx_cli.SetService,    Service,    self.__services      )
         self._process_descr('slice',      'add',    self.__ctx_cli.SetSlice,      Slice,      self.__slices        )
@@ -262,20 +265,23 @@ class DescriptorLoader:
         self.__services_add = get_descriptors_add_services(self.__services)
         self.__slices_add = get_descriptors_add_slices(self.__slices)
 
+        controllers_add, network_devices_add = split_controllers_and_network_devices(self.__devices_add)
+
         self.__ctx_cli.connect()
         self.__dev_cli.connect()
         self.__svc_cli.connect()
         self.__slc_cli.connect()
 
-        self._process_descr('context',  'add',    self.__ctx_cli.SetContext,      Context,  self.__contexts_add  )
-        self._process_descr('topology', 'add',    self.__ctx_cli.SetTopology,     Topology, self.__topologies_add)
-        self._process_descr('device',   'add',    self.__dev_cli.AddDevice,       Device,   self.__devices_add   )
-        self._process_descr('device',   'config', self.__dev_cli.ConfigureDevice, Device,   self.__devices_config)
-        self._process_descr('link',     'add',    self.__ctx_cli.SetLink,         Link,     self.__links         )
-        self._process_descr('service',  'add',    self.__svc_cli.CreateService,   Service,  self.__services_add  )
-        self._process_descr('service',  'update', self.__svc_cli.UpdateService,   Service,  self.__services      )
-        self._process_descr('slice',    'add',    self.__slc_cli.CreateSlice,     Slice,    self.__slices_add    )
-        self._process_descr('slice',    'update', self.__slc_cli.UpdateSlice,     Slice,    self.__slices        )
+        self._process_descr('context',    'add',    self.__ctx_cli.SetContext,      Context,  self.__contexts_add  )
+        self._process_descr('topology',   'add',    self.__ctx_cli.SetTopology,     Topology, self.__topologies_add)
+        self._process_descr('controller', 'add',    self.__dev_cli.AddDevice,       Device,   controllers_add      )
+        self._process_descr('device',     'add',    self.__dev_cli.AddDevice,       Device,   network_devices_add  )
+        self._process_descr('device',     'config', self.__dev_cli.ConfigureDevice, Device,   self.__devices_config)
+        self._process_descr('link',       'add',    self.__ctx_cli.SetLink,         Link,     self.__links         )
+        self._process_descr('service',    'add',    self.__svc_cli.CreateService,   Service,  self.__services_add  )
+        self._process_descr('service',    'update', self.__svc_cli.UpdateService,   Service,  self.__services      )
+        self._process_descr('slice',      'add',    self.__slc_cli.CreateSlice,     Slice,    self.__slices_add    )
+        self._process_descr('slice',      'update', self.__slc_cli.UpdateSlice,     Slice,    self.__slices        )
 
         # By default the Context component automatically assigns devices and links to topologies based on their
         # endpoints, and assigns topologies, services, and slices to contexts based on their identifiers.
diff --git a/src/common/tools/descriptor/Tools.py b/src/common/tools/descriptor/Tools.py
index 3126f2bcedb1aaeeda660674d41a249af03679fe..b4a76ff4f00d0f6886895cca0ab6f27f7aa8aa43 100644
--- a/src/common/tools/descriptor/Tools.py
+++ b/src/common/tools/descriptor/Tools.py
@@ -14,6 +14,7 @@
 
 import copy, json
 from typing import Dict, List, Optional, Tuple, Union
+from common.DeviceTypes import DeviceTypeEnum
 
 def get_descriptors_add_contexts(contexts : List[Dict]) -> List[Dict]:
     contexts_add = copy.deepcopy(contexts)
@@ -103,3 +104,24 @@ def split_devices_by_rules(devices : List[Dict]) -> Tuple[List[Dict], List[Dict]
             devices_config.append(device)
 
     return devices_add, devices_config
+
+CONTROLLER_DEVICE_TYPES = {
+    DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER.value,
+    DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM.value,
+    DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value,
+    DeviceTypeEnum.IP_SDN_CONTROLLER.value,
+    DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM.value,
+    DeviceTypeEnum.OPEN_LINE_SYSTEM.value,
+    DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value,
+}
+
+def split_controllers_and_network_devices(devices : List[Dict]) -> Tuple[List[Dict], List[Dict]]:
+    controllers     : List[Dict] = list()
+    network_devices : List[Dict] = list()
+    for device in devices:
+        device_type = device.get('device_type')
+        if device_type in CONTROLLER_DEVICE_TYPES:
+            controllers.append(device)
+        else:
+            network_devices.append(device)
+    return controllers, network_devices
diff --git a/src/common/tools/grpc/Constraints.py b/src/common/tools/grpc/Constraints.py
index 07f0b7782dbd93479774af6324683753f906c5a1..63e707c6f0232486de7761cc97b214ce16b524fd 100644
--- a/src/common/tools/grpc/Constraints.py
+++ b/src/common/tools/grpc/Constraints.py
@@ -18,11 +18,12 @@
 
 import json
 from typing import Any, Dict, List, Optional, Tuple
-from common.proto.context_pb2 import Constraint, EndPointId
+from common.proto.context_pb2 import Constraint, ConstraintActionEnum, EndPointId
 from common.tools.grpc.Tools import grpc_message_to_json_string
 
 def update_constraint_custom_scalar(
-    constraints, constraint_type : str, value : Any, raise_if_differs : bool = False
+    constraints, constraint_type : str, value : Any, raise_if_differs : bool = False,
+    new_action : ConstraintActionEnum = ConstraintActionEnum.CONSTRAINTACTION_SET
 ) -> Constraint:
 
     for constraint in constraints:
@@ -36,6 +37,8 @@ def update_constraint_custom_scalar(
         constraint.custom.constraint_type = constraint_type
         json_constraint_value = None
 
+    constraint.action = new_action
+
     if (json_constraint_value is None) or not raise_if_differs:
         # missing or raise_if_differs=False, add/update it
         json_constraint_value = value
@@ -47,7 +50,10 @@ def update_constraint_custom_scalar(
     constraint.custom.constraint_value = json.dumps(json_constraint_value, sort_keys=True)
     return constraint
 
-def update_constraint_custom_dict(constraints, constraint_type : str, fields : Dict[str, Tuple[Any, bool]]) -> Constraint:
+def update_constraint_custom_dict(
+    constraints, constraint_type : str, fields : Dict[str, Tuple[Any, bool]],
+    new_action : ConstraintActionEnum = ConstraintActionEnum.CONSTRAINTACTION_SET
+) -> Constraint:
     # fields: Dict[field_name : str, Tuple[field_value : Any, raise_if_differs : bool]]
 
     for constraint in constraints:
@@ -61,6 +67,8 @@ def update_constraint_custom_dict(constraints, constraint_type : str, fields : D
         constraint.custom.constraint_type = constraint_type
         json_constraint_value = {}
 
+    constraint.action = new_action
+
     for field_name,(field_value, raise_if_differs) in fields.items():
         if (field_name not in json_constraint_value) or not raise_if_differs:
             # missing or raise_if_differs=False, add/update it
@@ -75,7 +83,8 @@ def update_constraint_custom_dict(constraints, constraint_type : str, fields : D
 
 def update_constraint_endpoint_location(
     constraints, endpoint_id : EndPointId,
-    region : Optional[str] = None, gps_position : Optional[Tuple[float, float]] = None
+    region : Optional[str] = None, gps_position : Optional[Tuple[float, float]] = None,
+    new_action : ConstraintActionEnum = ConstraintActionEnum.CONSTRAINTACTION_SET
 ) -> Constraint:
     # gps_position: (latitude, longitude)
     if region is not None and gps_position is not None:
@@ -103,6 +112,8 @@ def update_constraint_endpoint_location(
         _endpoint_id.topology_id.topology_uuid.uuid = topology_uuid
         _endpoint_id.topology_id.context_id.context_uuid.uuid = context_uuid
 
+    constraint.action = new_action
+
     location = constraint.endpoint_location.location
     if region is not None:
         location.region = region
@@ -111,7 +122,10 @@ def update_constraint_endpoint_location(
         location.gps_position.longitude = gps_position[1]
     return constraint
 
-def update_constraint_endpoint_priority(constraints, endpoint_id : EndPointId, priority : int) -> Constraint:
+def update_constraint_endpoint_priority(
+    constraints, endpoint_id : EndPointId, priority : int,
+    new_action : ConstraintActionEnum = ConstraintActionEnum.CONSTRAINTACTION_SET
+) -> Constraint:
     endpoint_uuid = endpoint_id.endpoint_uuid.uuid
     device_uuid = endpoint_id.device_id.device_uuid.uuid
     topology_uuid = endpoint_id.topology_id.topology_uuid.uuid
@@ -134,10 +148,15 @@ def update_constraint_endpoint_priority(constraints, endpoint_id : EndPointId, p
         _endpoint_id.topology_id.topology_uuid.uuid = topology_uuid
         _endpoint_id.topology_id.context_id.context_uuid.uuid = context_uuid
 
+    constraint.action = new_action
+
     constraint.endpoint_priority.priority = priority
     return constraint
 
-def update_constraint_sla_capacity(constraints, capacity_gbps : float) -> Constraint:
+def update_constraint_sla_capacity(
+    constraints, capacity_gbps : float,
+    new_action : ConstraintActionEnum = ConstraintActionEnum.CONSTRAINTACTION_SET
+) -> Constraint:
     for constraint in constraints:
         if constraint.WhichOneof('constraint') != 'sla_capacity': continue
         break   # found, end loop
@@ -145,10 +164,15 @@ def update_constraint_sla_capacity(constraints, capacity_gbps : float) -> Constr
         # not found, add it
         constraint = constraints.add()      # pylint: disable=no-member
 
+    constraint.action = new_action
+
     constraint.sla_capacity.capacity_gbps = capacity_gbps
     return constraint
 
-def update_constraint_sla_latency(constraints, e2e_latency_ms : float) -> Constraint:
+def update_constraint_sla_latency(
+    constraints, e2e_latency_ms : float,
+    new_action : ConstraintActionEnum = ConstraintActionEnum.CONSTRAINTACTION_SET
+) -> Constraint:
     for constraint in constraints:
         if constraint.WhichOneof('constraint') != 'sla_latency': continue
         break   # found, end loop
@@ -156,11 +180,14 @@ def update_constraint_sla_latency(constraints, e2e_latency_ms : float) -> Constr
         # not found, add it
         constraint = constraints.add()      # pylint: disable=no-member
 
+    constraint.action = new_action
+
     constraint.sla_latency.e2e_latency_ms = e2e_latency_ms
     return constraint
 
 def update_constraint_sla_availability(
-    constraints, num_disjoint_paths : int, all_active : bool, availability : float
+    constraints, num_disjoint_paths : int, all_active : bool, availability : float,
+    new_action : ConstraintActionEnum = ConstraintActionEnum.CONSTRAINTACTION_SET
 ) -> Constraint:
     for constraint in constraints:
         if constraint.WhichOneof('constraint') != 'sla_availability': continue
@@ -169,12 +196,17 @@ def update_constraint_sla_availability(
         # not found, add it
         constraint = constraints.add()      # pylint: disable=no-member
 
+    constraint.action = new_action
+
     constraint.sla_availability.num_disjoint_paths = num_disjoint_paths
     constraint.sla_availability.all_active = all_active
     constraint.sla_availability.availability = availability
     return constraint
 
-def update_constraint_sla_isolation(constraints, isolation_levels : List[int]) -> Constraint:
+def update_constraint_sla_isolation(
+    constraints, isolation_levels : List[int],
+    new_action : ConstraintActionEnum = ConstraintActionEnum.CONSTRAINTACTION_SET
+) -> Constraint:
     for constraint in constraints:
         if constraint.WhichOneof('constraint') != 'sla_isolation': continue
         break   # found, end loop
@@ -182,6 +214,8 @@ def update_constraint_sla_isolation(constraints, isolation_levels : List[int]) -
         # not found, add it
         constraint = constraints.add()      # pylint: disable=no-member
 
+    constraint.action = new_action
+
     for isolation_level in isolation_levels:
         if isolation_level in constraint.sla_isolation.isolation_level: continue
         constraint.sla_isolation.isolation_level.append(isolation_level)
diff --git a/src/common/tools/object_factory/Device.py b/src/common/tools/object_factory/Device.py
index bc5c28740d5635df99c26ef56124c471d2c77d91..76959232a947ec50918afbc77a992d8af0d0723f 100644
--- a/src/common/tools/object_factory/Device.py
+++ b/src/common/tools/object_factory/Device.py
@@ -46,6 +46,10 @@ DEVICE_P4_DRIVERS   = [DeviceDriverEnum.DEVICEDRIVER_P4]
 DEVICE_TFS_TYPE    = DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value
 DEVICE_TFS_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN]
 
+DEVICE_IETF_ACTN_TYPE    = DeviceTypeEnum.OPEN_LINE_SYSTEM.value
+DEVICE_IETF_ACTN_DRIVERS = [DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN]
+
+
 def json_device_id(device_uuid : str):
     return {'device_uuid': {'uuid': device_uuid}}
 
@@ -136,6 +140,14 @@ def json_device_tfs_disabled(
         device_uuid, DEVICE_TFS_TYPE, DEVICE_DISABLED, name=name, endpoints=endpoints, config_rules=config_rules,
         drivers=drivers)
 
+def json_device_ietf_actn_disabled(
+        device_uuid : str, name : Optional[str] = None, endpoints : List[Dict] = [], config_rules : List[Dict] = [],
+        drivers : List[Dict] = DEVICE_IETF_ACTN_DRIVERS
+    ):
+    return json_device(
+        device_uuid, DEVICE_IETF_ACTN_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 9d39c3c5441c160328387f44a93c7d356d8fc661..87d8e54ee390fb1f56266317be5317731bb755b6 100644
--- a/src/common/type_checkers/Assertions.py
+++ b/src/common/type_checkers/Assertions.py
@@ -47,6 +47,7 @@ def validate_device_driver_enum(message):
         'DEVICEDRIVER_IETF_L2VPN',
         'DEVICEDRIVER_GNMI_OPENCONFIG',
         'DEVICEDRIVER_FLEXSCALE',
+        'DEVICEDRIVER_IETF_ACTN',
     ]
 
 def validate_device_operational_status_enum(message):
diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py
index 93f078e75545c93a2cd312cf48e8f64cdeea87ac..5aad7f9c9ff3a6fd063b1f364256e760f47e1c33 100644
--- a/src/context/service/ContextServiceServicerImpl.py
+++ b/src/context/service/ContextServiceServicerImpl.py
@@ -127,7 +127,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer
         return device_list_objs(self.db_engine)
 
     @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
-    def GetDevice(self, request : ContextId, context : grpc.ServicerContext) -> Device:
+    def GetDevice(self, request : DeviceId, context : grpc.ServicerContext) -> Device:
         return device_get(self.db_engine, request)
 
     @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
diff --git a/src/context/service/database/models/enums/DeviceDriver.py b/src/context/service/database/models/enums/DeviceDriver.py
index f8483360191dcea1a56cdc372b681ae2d03c9ef1..8e15bf058599eeed0629fc1249af0d052183db28 100644
--- a/src/context/service/database/models/enums/DeviceDriver.py
+++ b/src/context/service/database/models/enums/DeviceDriver.py
@@ -32,6 +32,7 @@ class ORM_DeviceDriverEnum(enum.Enum):
     IETF_L2VPN            = DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN
     GNMI_OPENCONFIG       = DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG
     FLEXSCALE             = DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE
+    IETF_ACTN             = DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN
 
 grpc_to_enum__device_driver = functools.partial(
     grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum)
diff --git a/src/device/.gitlab-ci.yml b/src/device/.gitlab-ci.yml
index 5fed57be6b7963f776425c2196e232aacddee04f..bcc2e05e50f63fd6552bb53b4d23c0ae0e2e7302 100644
--- a/src/device/.gitlab-ci.yml
+++ b/src/device/.gitlab-ci.yml
@@ -48,15 +48,26 @@ unit_test device:
     - build device
   before_script:
     - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
-    - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi
-    - if docker container ls | grep $IMAGE_NAME; then docker rm -f $IMAGE_NAME; else echo "$IMAGE_NAME image is not in the system"; fi
+    - >
+      if docker network list | grep teraflowbridge; then
+        echo "teraflowbridge is already created";
+      else
+        docker network create -d bridge teraflowbridge;
+      fi
+    - >
+      if docker container ls | grep $IMAGE_NAME; then
+        docker rm -f $IMAGE_NAME;
+      else
+        echo "$IMAGE_NAME image is not in the system";
+      fi
   script:
     - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
     - docker run --name $IMAGE_NAME -d -p 2020:2020 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 5
     - docker ps -a
     - docker logs $IMAGE_NAME
-    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary_emulated.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary_emulated.py --junitxml=/opt/results/${IMAGE_NAME}_report_emulated.xml"
+    - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary_ietf_actn.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_actn.xml"
     - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
   coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
   after_script:
@@ -77,7 +88,7 @@ unit_test device:
   artifacts:
       when: always
       reports:
-        junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml
+        junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report_*.xml
 
 ## Deployment of the service in Kubernetes Cluster
 #deploy device:
diff --git a/src/device/Dockerfile b/src/device/Dockerfile
index 6566625527f8ceaa8de4639d558c92572c4835cb..909ae3bd31817401412629cbc04b5c0577b40355 100644
--- a/src/device/Dockerfile
+++ b/src/device/Dockerfile
@@ -66,5 +66,11 @@ COPY src/context/. context/
 COPY src/device/. device/
 COPY src/monitoring/. monitoring/
 
+RUN mkdir -p tests/tools/mock_ietf_actn_sdn_ctrl
+RUN touch tests/__init__.py
+RUN touch tests/tools/__init__.py
+RUN touch tests/tools/mock_ietf_actn_sdn_ctrl/__init__.py
+COPY src/tests/tools/mock_ietf_actn_sdn_ctrl/. tests/tools/mock_ietf_actn_sdn_ctrl/
+
 # Start the service
 ENTRYPOINT ["python", "-m", "device.service"]
diff --git a/src/device/requirements.in b/src/device/requirements.in
index ece761571ec2ff9c3376b1062787d76047d71e7c..1a09542a3854e10fedc5be91b76546c341113d67 100644
--- a/src/device/requirements.in
+++ b/src/device/requirements.in
@@ -16,7 +16,12 @@
 anytree==2.8.0
 APScheduler==3.10.1
 cryptography==36.0.2
+deepdiff==6.7.*
+deepmerge==1.1.*
 #fastcache==1.1.0
+Flask==2.1.3
+Flask-HTTPAuth==4.5.0
+Flask-RESTful==0.3.9
 Jinja2==3.0.3
 ncclient==0.6.13
 p4runtime==1.3.0
@@ -32,9 +37,10 @@ tabulate
 ipaddress
 macaddress
 yattag
-pyang
+pyang==2.6.0
 git+https://github.com/robshakir/pyangbind.git
 websockets==10.4
+werkzeug==2.3.7
 
 # pip's dependency resolver does not take into account installed packages.
 # p4runtime does not specify the version of grpcio/protobuf it needs, so it tries to install latest one
diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py
index eeffdd7b0592b5166c06c1597e17f79adcfd25bb..3df7c482272804eb2589ed1e7569f0a2e822ad21 100644
--- a/src/device/service/DeviceServiceServicerImpl.py
+++ b/src/device/service/DeviceServiceServicerImpl.py
@@ -73,6 +73,13 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
             device.device_operational_status = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED
             device.device_drivers.extend(request.device_drivers)    # pylint: disable=no-member
             device.device_config.CopyFrom(request.device_config)    # pylint: disable=no-member
+
+            if request.HasField('controller_id'):
+                controller_id = request.controller_id
+                if controller_id.HasField('device_uuid'):
+                    controller_device_uuid = controller_id.device_uuid.uuid
+                    device.controller_id.device_uuid.uuid = controller_device_uuid
+
             device_id = context_client.SetDevice(device)
             device = get_device(context_client, device_id.device_uuid.uuid, rw_copy=True)
 
diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py
index 442acf839c8e4615237d338d7b485be297d1a4ff..27c61f89f15c735b44ad2724df01e08a51dda6ba 100644
--- a/src/device/service/drivers/__init__.py
+++ b/src/device/service/drivers/__init__.py
@@ -84,6 +84,15 @@ DRIVERS.append(
         }
     ]))
 
+from .ietf_actn.IetfActnDriver import IetfActnDriver # pylint: disable=wrong-import-position
+DRIVERS.append(
+    (IetfActnDriver, [
+        {
+            FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.OPEN_LINE_SYSTEM,
+            FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN,
+        }
+    ]))
+
 if LOAD_ALL_DEVICE_DRIVERS:
     from .openconfig.OpenConfigDriver import OpenConfigDriver # pylint: disable=wrong-import-position
     DRIVERS.append(
diff --git a/src/device/service/drivers/ietf_actn/IetfActnDriver.py b/src/device/service/drivers/ietf_actn/IetfActnDriver.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f80f5333cc55ccddebd971d4aadbfa1c195ee21
--- /dev/null
+++ b/src/device/service/drivers/ietf_actn/IetfActnDriver.py
@@ -0,0 +1,172 @@
+# 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, requests, threading
+from typing import Any, Iterator, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.type_checkers.Checkers import chk_string, chk_type
+from device.service.driver_api._Driver import _Driver, RESOURCE_ENDPOINTS, RESOURCE_SERVICES
+from .handlers.EthtServiceHandler import EthtServiceHandler
+from .handlers.OsuTunnelHandler import OsuTunnelHandler
+from .handlers.RestApiClient import RestApiClient
+from .Tools import get_etht_services, get_osu_tunnels, parse_resource_key
+
+LOGGER = logging.getLogger(__name__)
+
+ALL_RESOURCE_KEYS = [
+    RESOURCE_ENDPOINTS,
+    RESOURCE_SERVICES,
+]
+
+DRIVER_NAME = 'ietf_actn'
+METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME})
+
+class IetfActnDriver(_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()
+        self._rest_api_client = RestApiClient(address, port, settings=settings)
+        self._handler_osu_tunnel = OsuTunnelHandler(self._rest_api_client)
+        self._handler_etht_service = EthtServiceHandler(self._rest_api_client)
+
+    def Connect(self) -> bool:
+        with self.__lock:
+            if self.__started.is_set(): return True
+            try:
+                self._rest_api_client.get('Check Credentials', '')
+            except requests.exceptions.Timeout:
+                LOGGER.exception('Timeout exception checking connectivity')
+                return False
+            except Exception:  # pylint: disable=broad-except
+                LOGGER.exception('Unhandled exception checking connectivity')
+                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):
+                chk_string('resource_key[#{:d}]'.format(i), resource_key, allow_empty=False)
+
+                try:
+                    _results = list()
+
+                    if resource_key == RESOURCE_ENDPOINTS:
+                        # Add mgmt endpoint by default
+                        resource_key = '/endpoints/endpoint[mgmt]'
+                        resource_value = {'uuid': 'mgmt', 'name': 'mgmt', 'type': 'mgmt'}
+                        results.append((resource_key, resource_value))
+                    elif resource_key == RESOURCE_SERVICES:
+                        get_osu_tunnels(self._handler_osu_tunnel, _results)
+                        get_etht_services(self._handler_etht_service, _results)
+                    else:
+                        # check if resource key is for a specific OSU tunnel or ETHT service, and get them accordingly
+                        osu_tunnel_name, etht_service_name = parse_resource_key(resource_key)
+                        if osu_tunnel_name is not None:
+                            get_osu_tunnels(self._handler_osu_tunnel, _results, osu_tunnel_name=osu_tunnel_name)
+                        if etht_service_name is not None:
+                            get_etht_services(self._handler_etht_service, _results, etht_service_name=etht_service_name)
+
+                    results.extend(_results)
+                except Exception as e:
+                    results.append((resource_key, e))
+
+        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_key, resource_value in resources:
+                LOGGER.info('resource: key({:s}) => value({:s})'.format(str(resource_key), str(resource_value)))
+                try:
+                    _results = list()
+
+                    if isinstance(resource_value, str): resource_value = json.loads(resource_value)
+                    osu_tunnel_name, etht_service_name = parse_resource_key(resource_key)
+
+                    if osu_tunnel_name is not None:
+                        succeeded = self._handler_osu_tunnel.update(resource_value)
+                        _results.append(succeeded)
+
+                    if etht_service_name is not None:
+                        succeeded = self._handler_etht_service.update(resource_value)
+                        _results.append(succeeded)
+
+                    results.extend(_results)
+                except Exception as e:
+                    results.append(e)
+
+        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_key, resource_value in resources:
+                LOGGER.info('resource: key({:s}) => value({:s})'.format(str(resource_key), str(resource_value)))
+                try:
+                    _results = list()
+
+                    if isinstance(resource_value, str): resource_value = json.loads(resource_value)
+                    osu_tunnel_name, etht_service_name = parse_resource_key(resource_key)
+
+                    if osu_tunnel_name is not None:
+                        succeeded = self._handler_osu_tunnel.delete(osu_tunnel_name)
+                        _results.append(succeeded)
+
+                    if etht_service_name is not None:
+                        succeeded = self._handler_etht_service.delete(etht_service_name)
+                        _results.append(succeeded)
+
+                    results.extend(_results)
+                except Exception as e:
+                    results.append(e)
+
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        # TODO: IETF ACTN 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: IETF ACTN 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: IETF ACTN does not support monitoring by now
+        return []
diff --git a/src/device/service/drivers/ietf_actn/Tools.py b/src/device/service/drivers/ietf_actn/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..52f5b15c4d9f0c723b7e4eeeea4537d23c5bf758
--- /dev/null
+++ b/src/device/service/drivers/ietf_actn/Tools.py
@@ -0,0 +1,52 @@
+# 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, re
+from typing import Any, List, Optional, Tuple, Union
+from .handlers.EthtServiceHandler import EthtServiceHandler
+from .handlers.OsuTunnelHandler import OsuTunnelHandler
+
+LOGGER = logging.getLogger(__name__)
+
+RE_OSU_TUNNEL   = re.compile(r'^\/osu\_tunnels\/osu\_tunnel\[([^\]]+)\]$')
+RE_ETHT_SERVICE = re.compile(r'^\/etht\_services\/etht\_service\[([^\]]+)\]$')
+
+def parse_resource_key(resource_key : str) -> Tuple[Optional[str], Optional[str]]:
+    re_match_osu_tunnel   = RE_OSU_TUNNEL.match(resource_key)
+    osu_tunnel_name = None if re_match_osu_tunnel is None else re_match_osu_tunnel.group(1)
+
+    re_match_etht_service = RE_ETHT_SERVICE.match(resource_key)
+    etht_service_name = None if re_match_etht_service is None else re_match_etht_service.group(1)
+
+    return osu_tunnel_name, etht_service_name
+
+def get_osu_tunnels(
+    handler_osu_tunnel : OsuTunnelHandler, results : List[Tuple[str, Union[Any, None, Exception]]],
+    osu_tunnel_name : Optional[str] = None
+) -> None:
+    osu_tunnels = handler_osu_tunnel.get(osu_tunnel_name=osu_tunnel_name)
+    for osu_tunnel in osu_tunnels:
+        osu_tunnel_name = osu_tunnel['name']
+        resource_key = '/osu_tunnels/osu_tunnel[{:s}]'.format(osu_tunnel_name)
+        results.append((resource_key, osu_tunnel))
+
+def get_etht_services(
+    handler_etht_service : EthtServiceHandler, results : List[Tuple[str, Union[Any, None, Exception]]],
+    etht_service_name : Optional[str] = None
+) -> None:
+    etht_services = handler_etht_service.get(etht_service_name=etht_service_name)
+    for etht_service in etht_services:
+        etht_service_name = etht_service['name']
+        resource_key = '/etht_services/etht_service[{:s}]'.format(etht_service_name)
+        results.append((resource_key, etht_service))
diff --git a/src/device/service/drivers/ietf_actn/__init__.py b/src/device/service/drivers/ietf_actn/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..38d04994fb0fa1951fb465bc127eb72659dc2eaf
--- /dev/null
+++ b/src/device/service/drivers/ietf_actn/__init__.py
@@ -0,0 +1,13 @@
+# 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.
diff --git a/src/device/service/drivers/ietf_actn/handlers/EthtServiceHandler.py b/src/device/service/drivers/ietf_actn/handlers/EthtServiceHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..230d13797f224931f657aa32c83091f4eb1c1d63
--- /dev/null
+++ b/src/device/service/drivers/ietf_actn/handlers/EthtServiceHandler.py
@@ -0,0 +1,221 @@
+# 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 enum, logging
+from typing import Dict, List, Optional, Tuple, Union
+from .RestApiClient import HTTP_STATUS_CREATED, HTTP_STATUS_NO_CONTENT, HTTP_STATUS_OK, RestApiClient
+
+LOGGER = logging.getLogger(__name__)
+
+class BandwidthProfileTypeEnum(enum.Enum):
+    MEF_10_BWP = 'ietf-eth-tran-types:mef-10-bwp'
+
+class EndpointLayerSpecificAccessTypeEnum(enum.Enum):
+    PORT = 'port'
+
+class EndpointProtectionRoleEnum(enum.Enum):
+    WORK = 'work'
+
+class OptimizationMetricRole(enum.Enum):
+    WORK = 'work'
+
+class OptimizationMetricType(enum.Enum):
+    PATH_METRIC_TE = 'ietf-te-types:path-metric-te'
+
+class OuterTagTypeEnum(enum.Enum):
+    CLASSIFY_C_VLAN = 'ietf-eth-tran-types:classify-c-vlan'
+
+class ServiceClassificationTypeEnum(enum.Enum):
+    VLAN_CLASSIFICATION = 'ietf-eth-tran-type:vlan-classification'
+
+class ServiceTypeEnum(enum.Enum):
+    MP2MP = 'op-mp2mp-svc'
+    P2MP  = 'op-p2mp-svc'
+
+def compose_outer_tag(tag_type : OuterTagTypeEnum, vlan_value : int) -> Dict:
+    return {'tag-type': tag_type.value, 'vlan-value': vlan_value}
+
+def compose_ingress_egress_bandwidth_profile() -> Dict:
+    return {
+        'bandwidth-profile-type': BandwidthProfileTypeEnum.MEF_10_BWP.value,
+        'CIR': 10_000_000,
+        'EIR': 10_000_000,
+    }
+
+def compose_layer_specific_access_type() -> Dict:
+    return {'access-type': EndpointLayerSpecificAccessTypeEnum.PORT.value}
+
+def compose_static_route(prefix : str, mask : int, next_hop : str) -> Dict:
+    return {'destination': prefix, 'destination-mask': mask, 'next-hop': next_hop}
+
+def compose_static_route_list(static_routes : List[Tuple[str, int, str]]) -> List[Dict]:
+    return [
+        compose_static_route(prefix, mask, next_hop)
+        for prefix, mask, next_hop in static_routes
+    ]
+
+def compose_etht_service_endpoint(
+    node_id : str, tp_id : str, vlan_value : int, static_routes : List[Tuple[str, int, str]] = list()
+) -> Dict:
+    return {
+        'node-id'           : node_id,
+        'tp-id'             : tp_id,
+        'protection-role'   : EndpointProtectionRoleEnum.WORK.value,
+        'layer-specific'    : compose_layer_specific_access_type(),
+        'is-extendable'     : False,
+        'is-terminal'       : True,
+        'static-route-list' : compose_static_route_list(static_routes),
+        'outer-tag'         : compose_outer_tag(OuterTagTypeEnum.CLASSIFY_C_VLAN, vlan_value),
+        'service-classification-type'     : ServiceClassificationTypeEnum.VLAN_CLASSIFICATION.value,
+        'ingress-egress-bandwidth-profile': compose_ingress_egress_bandwidth_profile(),
+    }
+
+def compose_optimizations() -> Dict:
+    return {'optimization-metric': [{
+        'metric-role': OptimizationMetricRole.WORK.value,
+        'metric-type': OptimizationMetricType.PATH_METRIC_TE.value,
+    }]}
+
+def compose_etht_service(
+    name : str, service_type : ServiceTypeEnum, osu_tunnel_name : str,
+    src_node_id : str, src_tp_id : str, src_vlan_tag : int, dst_node_id : str, dst_tp_id : str, dst_vlan_tag : int,
+    src_static_routes : List[Tuple[str, int, str]] = list(), dst_static_routes : List[Tuple[str, int, str]] = list()
+) -> Dict:
+    return {'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': [{
+        'etht-svc-name' : name,
+        'etht-svc-title': name.upper(),
+        'etht-svc-type' : service_type.value,
+        'source-endpoints': {'source-endpoint': [
+            compose_etht_service_endpoint(src_node_id, src_tp_id, src_vlan_tag, src_static_routes),
+        ]},
+        'destination-endpoints': {'destination-endpoint': [
+            compose_etht_service_endpoint(dst_node_id, dst_tp_id, dst_vlan_tag, dst_static_routes),
+        ]},
+        'svc-tunnel': [{'tunnel-name': osu_tunnel_name}],
+        'optimizations': compose_optimizations(),
+    }]}}
+
+class EthtServiceHandler:
+    def __init__(self, rest_api_client : RestApiClient) -> None:
+        self._rest_api_client = rest_api_client
+        self._object_name  = 'EthtService'
+        self._subpath_root = '/ietf-eth-tran-service:etht-svc'
+        self._subpath_item = self._subpath_root + '/etht-svc-instances="{etht_service_name:s}"'
+
+    def _rest_api_get(self, etht_service_name : Optional[str] = None) -> Union[Dict, List]:
+        if etht_service_name is None:
+            subpath_url = self._subpath_root
+        else:
+            subpath_url = self._subpath_item.format(etht_service_name=etht_service_name)
+        return self._rest_api_client.get(
+            self._object_name, subpath_url, expected_http_status={HTTP_STATUS_OK}
+        )
+
+    def _rest_api_update(self, data : Dict) -> bool:
+        return self._rest_api_client.update(
+            self._object_name, self._subpath_root, data, expected_http_status={HTTP_STATUS_CREATED}
+        )
+
+    def _rest_api_delete(self, etht_service_name : str) -> bool:
+        if etht_service_name is None: raise Exception('etht_service_name is None')
+        subpath_url = self._subpath_item.format(etht_service_name=etht_service_name)
+        return self._rest_api_client.delete(
+            self._object_name, subpath_url, expected_http_status={HTTP_STATUS_NO_CONTENT}
+        )
+
+    def get(self, etht_service_name : Optional[str] = None) -> Union[Dict, List]:
+        data = self._rest_api_get(etht_service_name=etht_service_name)
+
+        if not isinstance(data, dict): raise ValueError('data should be a dict')
+        if 'ietf-eth-tran-service:etht-svc' not in data:
+            raise ValueError('data does not contain key "ietf-eth-tran-service:etht-svc"')
+        data = data['ietf-eth-tran-service:etht-svc']
+        if 'etht-svc-instances' not in data:
+            raise ValueError('data["ietf-eth-tran-service:etht-svc"] does not contain key "etht-svc-instances"')
+        data = data['etht-svc-instances']
+        if not isinstance(data, list):
+            raise ValueError('data["ietf-eth-tran-service:etht-svc"]["etht-svc-instances"] should be a list')
+
+        etht_services : List[Dict] = list()
+        for item in data:
+            src_endpoints = item['source-endpoints']['source-endpoint']
+            if len(src_endpoints) != 1:
+                MSG = 'EthtService({:s}) has zero/multiple source endpoints'
+                raise Exception(MSG.format(str(item)))
+            src_endpoint = src_endpoints[0]
+
+            dst_endpoints = item['destination-endpoints']['destination-endpoint']
+            if len(dst_endpoints) != 1:
+                MSG = 'EthtService({:s}) has zero/multiple destination endpoints'
+                raise Exception(MSG.format(str(item)))
+            dst_endpoint = dst_endpoints[0]
+
+            svc_tunnels = item['svc-tunnel']
+            if len(svc_tunnels) != 1:
+                MSG = 'EthtService({:s}) has zero/multiple service tunnels'
+                raise Exception(MSG.format(str(item)))
+            svc_tunnel = svc_tunnels[0]
+
+            etht_service = {
+                'name'             : item['etht-svc-name'],
+                'service_type'     : item['etht-svc-type'],
+                'osu_tunnel_name'  : svc_tunnel['tunnel-name'],
+
+                'src_node_id'      : src_endpoint['node-id'],
+                'src_tp_id'        : src_endpoint['tp-id'],
+                'src_vlan_tag'     : src_endpoint['outer-tag']['vlan-value'],
+                'src_static_routes': [
+                    [static_route['destination'], static_route['destination-mask'], static_route['next-hop']]
+                    for static_route in src_endpoint.get('static-route-list', list())
+                ],
+
+                'dst_node_id'      : dst_endpoint['node-id'],
+                'dst_tp_id'        : dst_endpoint['tp-id'],
+                'dst_vlan_tag'     : dst_endpoint['outer-tag']['vlan-value'],
+                'dst_static_routes': [
+                    [static_route['destination'], static_route['destination-mask'], static_route['next-hop']]
+                    for static_route in dst_endpoint.get('static-route-list', list())
+                ],
+            }
+            etht_services.append(etht_service)
+
+        return etht_services
+
+    def update(self, parameters : Dict) -> bool:
+        name              = parameters['name'           ]
+        service_type      = parameters['service_type'   ]
+        osu_tunnel_name   = parameters['osu_tunnel_name']
+
+        src_node_id       = parameters['src_node_id'    ]
+        src_tp_id         = parameters['src_tp_id'      ]
+        src_vlan_tag      = parameters['src_vlan_tag'   ]
+        src_static_routes = parameters.get('src_static_routes', [])
+
+        dst_node_id       = parameters['dst_node_id'    ]
+        dst_tp_id         = parameters['dst_tp_id'      ]
+        dst_vlan_tag      = parameters['dst_vlan_tag'   ]
+        dst_static_routes = parameters.get('dst_static_routes', [])
+
+        service_type = ServiceTypeEnum._value2member_map_[service_type]
+
+        data = compose_etht_service(
+            name, service_type, osu_tunnel_name,
+            src_node_id, src_tp_id, src_vlan_tag, dst_node_id, dst_tp_id, dst_vlan_tag,
+            src_static_routes=src_static_routes, dst_static_routes=dst_static_routes
+        )
+
+        return self._rest_api_update(data)
+
+    def delete(self, etht_service_name : str) -> bool:
+        return self._rest_api_delete(etht_service_name)
diff --git a/src/device/service/drivers/ietf_actn/handlers/OsuTunnelHandler.py b/src/device/service/drivers/ietf_actn/handlers/OsuTunnelHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..bcecdf89e41c1cbb7284dbc6f7f08e1f03329c91
--- /dev/null
+++ b/src/device/service/drivers/ietf_actn/handlers/OsuTunnelHandler.py
@@ -0,0 +1,176 @@
+# 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 enum, logging
+from typing import Dict, List, Optional, Union
+from .RestApiClient import HTTP_STATUS_CREATED, HTTP_STATUS_NO_CONTENT, HTTP_STATUS_OK, RestApiClient
+
+LOGGER = logging.getLogger(__name__)
+
+class EndpointProtectionRoleEnum(enum.Enum):
+    WORK = 'work'
+
+class LspProtectionTypeEnum(enum.Enum):
+    UNPROTECTED = 'ietf-te-types:lsp-protection-unprotected'
+
+class LspRestorationTypeEnum(enum.Enum):
+    NOT_APPLICABLE = 'ietf-te-types:lsp-restoration-not-applicable'
+
+class TunnelAdminStateEnum(enum.Enum):
+    UP = 'ietf-te-types:tunnel-admin-state-up'
+
+class OduTypeEnum(enum.Enum):
+    OSUFLEX = 'osuflex'
+
+def compose_osu_tunnel_endpoint(
+    node_id : str, tp_id : str, ttp_channel_name : str,
+    protection_role : EndpointProtectionRoleEnum = EndpointProtectionRoleEnum.WORK
+) -> Dict:
+    return {
+        'node-id': node_id, 'tp-id': tp_id, 'ttp-channel-name': ttp_channel_name,
+        'protection-role': protection_role.value
+    }
+
+def compose_osu_tunnel_te_bandwidth_odu(odu_type : OduTypeEnum, number : int) -> Dict:
+    return {'layer': 'odu', 'odu-type': odu_type.value, 'number': number}
+
+def compose_osu_tunnel_protection(
+    type_ : LspProtectionTypeEnum = LspProtectionTypeEnum.UNPROTECTED, reversion_disable : bool = True
+) -> Dict:
+    return {'protection-type': type_.value, 'protection-reversion-disable': reversion_disable}
+
+def compose_osu_tunnel_restoration(
+    type_ : LspRestorationTypeEnum = LspRestorationTypeEnum.NOT_APPLICABLE, restoration_lock : bool = False
+) -> Dict:
+    return {'restoration-type': type_.value, 'restoration-lock': restoration_lock}
+
+def compose_osu_tunnel(
+    name : str,
+    src_node_id : str, src_tp_id : str, src_ttp_channel_name : str,
+    dst_node_id : str, dst_tp_id : str, dst_ttp_channel_name : str,
+    odu_type : OduTypeEnum, osuflex_number : int,
+    delay : int, bidirectional : bool = True,
+    admin_state : TunnelAdminStateEnum = TunnelAdminStateEnum.UP
+) -> Dict:
+    return {'ietf-te:tunnel': [{
+        'name': name,
+        'title': name.upper(),
+        'admin-state': admin_state.value,
+        'delay': delay,
+        'te-bandwidth': compose_osu_tunnel_te_bandwidth_odu(odu_type, osuflex_number),
+        'bidirectional': bidirectional,
+        'source-endpoints': {'source-endpoint': [
+            compose_osu_tunnel_endpoint(src_node_id, src_tp_id, src_ttp_channel_name),
+        ]},
+        'destination-endpoints': {'destination-endpoint': [
+            compose_osu_tunnel_endpoint(dst_node_id, dst_tp_id, dst_ttp_channel_name),
+        ]},
+        'restoration': compose_osu_tunnel_restoration(),
+        'protection': compose_osu_tunnel_protection(),
+    }]}
+
+class OsuTunnelHandler:
+    def __init__(self, rest_api_client : RestApiClient) -> None:
+        self._rest_api_client = rest_api_client
+        self._object_name  = 'OsuTunnel'
+        self._subpath_root = '/ietf-te:te/tunnels'
+        self._subpath_item = self._subpath_root + '/tunnel="{osu_tunnel_name:s}"'
+
+    def _rest_api_get(self, osu_tunnel_name : Optional[str] = None) -> Union[Dict, List]:
+        if osu_tunnel_name is None:
+            subpath_url = self._subpath_root
+        else:
+            subpath_url = self._subpath_item.format(osu_tunnel_name=osu_tunnel_name)
+        return self._rest_api_client.get(
+            self._object_name, subpath_url, expected_http_status={HTTP_STATUS_OK}
+        )
+
+    def _rest_api_update(self, data : Dict) -> bool:
+        return self._rest_api_client.update(
+            self._object_name, self._subpath_root, data, expected_http_status={HTTP_STATUS_CREATED}
+        )
+
+    def _rest_api_delete(self, osu_tunnel_name : str) -> bool:
+        if osu_tunnel_name is None: raise Exception('osu_tunnel_name is None')
+        subpath_url = self._subpath_item.format(osu_tunnel_name=osu_tunnel_name)
+        return self._rest_api_client.delete(
+            self._object_name, subpath_url, expected_http_status={HTTP_STATUS_NO_CONTENT}
+        )
+
+    def get(self, osu_tunnel_name : Optional[str] = None) -> Union[Dict, List]:
+        data = self._rest_api_get(osu_tunnel_name=osu_tunnel_name)
+
+        if not isinstance(data, dict): raise ValueError('data should be a dict')
+        if 'ietf-te:tunnel' not in data: raise ValueError('data does not contain key "ietf-te:tunnel"')
+        data = data['ietf-te:tunnel']
+        if not isinstance(data, list): raise ValueError('data[ietf-te:tunnel] should be a list')
+
+        osu_tunnels : List[Dict] = list()
+        for item in data:
+            src_endpoints = item['source-endpoints']['source-endpoint']
+            if len(src_endpoints) != 1:
+                MSG = 'OsuTunnel({:s}) has zero/multiple source endpoints'
+                raise Exception(MSG.format(str(item)))
+            src_endpoint = src_endpoints[0]
+
+            dst_endpoints = item['destination-endpoints']['destination-endpoint']
+            if len(dst_endpoints) != 1:
+                MSG = 'OsuTunnel({:s}) has zero/multiple destination endpoints'
+                raise Exception(MSG.format(str(item)))
+            dst_endpoint = dst_endpoints[0]
+
+            osu_tunnel = {
+                'name'                : item['name'],
+                'src_node_id'         : src_endpoint['node-id'],
+                'src_tp_id'           : src_endpoint['tp-id'],
+                'src_ttp_channel_name': src_endpoint['ttp-channel-name'],
+                'dst_node_id'         : dst_endpoint['node-id'],
+                'dst_tp_id'           : dst_endpoint['tp-id'],
+                'dst_ttp_channel_name': dst_endpoint['ttp-channel-name'],
+                'odu_type'            : item['te-bandwidth']['odu-type'],
+                'osuflex_number'      : item['te-bandwidth']['number'],
+                'delay'               : item['delay'],
+                'bidirectional'       : item['bidirectional'],
+            }
+            osu_tunnels.append(osu_tunnel)
+
+        return osu_tunnels
+
+    def update(self, parameters : Dict) -> bool:
+        name                 = parameters['name'                ]
+
+        src_node_id          = parameters['src_node_id'         ]
+        src_tp_id            = parameters['src_tp_id'           ]
+        src_ttp_channel_name = parameters['src_ttp_channel_name']
+
+        dst_node_id          = parameters['dst_node_id'         ]
+        dst_tp_id            = parameters['dst_tp_id'           ]
+        dst_ttp_channel_name = parameters['dst_ttp_channel_name']
+
+        odu_type             = parameters.get('odu_type',       OduTypeEnum.OSUFLEX.value)
+        osuflex_number       = parameters.get('osuflex_number', 1                        )
+        delay                = parameters.get('delay',          20                       )
+        bidirectional        = parameters.get('bidirectional',  True                     )
+
+        odu_type = OduTypeEnum._value2member_map_[odu_type]
+
+        data = compose_osu_tunnel(
+            name, src_node_id, src_tp_id, src_ttp_channel_name, dst_node_id, dst_tp_id, dst_ttp_channel_name,
+            odu_type, osuflex_number, delay, bidirectional=bidirectional
+        )
+
+        return self._rest_api_update(data)
+
+    def delete(self, osu_tunnel_name : str) -> bool:
+        return self._rest_api_delete(osu_tunnel_name)
diff --git a/src/device/service/drivers/ietf_actn/handlers/RestApiClient.py b/src/device/service/drivers/ietf_actn/handlers/RestApiClient.py
new file mode 100644
index 0000000000000000000000000000000000000000..1eed066b90c90c7509674c92f8b13e8feefa3513
--- /dev/null
+++ b/src/device/service/drivers/ietf_actn/handlers/RestApiClient.py
@@ -0,0 +1,104 @@
+# 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 copy, json, logging, requests
+from requests.auth import HTTPBasicAuth
+from typing import Any, Dict, List, Set, Union
+
+LOGGER = logging.getLogger(__name__)
+
+DEFAULT_BASE_URL = '/restconf/v2/data'
+DEFAULT_SCHEME   = 'https'
+DEFAULT_TIMEOUT  = 120
+DEFAULT_VERIFY   = False
+
+HTTP_STATUS_OK         = 200
+HTTP_STATUS_CREATED    = 201
+HTTP_STATUS_ACCEPTED   = 202
+HTTP_STATUS_NO_CONTENT = 204
+
+HTTP_OK_CODES = {
+    HTTP_STATUS_OK,
+    HTTP_STATUS_CREATED,
+    HTTP_STATUS_ACCEPTED,
+    HTTP_STATUS_NO_CONTENT,
+}
+
+class RestApiClient:
+    def __init__(self, address : str, port : int, settings : Dict[str, Any] = dict()) -> None:
+        username = settings.get('username')
+        password = settings.get('password')
+        self._auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None
+
+        scheme   = settings.get('scheme',   DEFAULT_SCHEME  )
+        base_url = settings.get('base_url', DEFAULT_BASE_URL)
+        self._base_url = '{:s}://{:s}:{:d}{:s}'.format(scheme, address, int(port), base_url)
+
+        self._timeout = int(settings.get('timeout', DEFAULT_TIMEOUT))
+        self._verify  = bool(settings.get('verify', DEFAULT_VERIFY))
+
+    def get(
+        self, object_name : str, url : str,
+        expected_http_status : Set[int] = {HTTP_STATUS_OK}
+    ) -> Union[Dict, List]:
+        MSG = 'Get {:s}({:s})'
+        LOGGER.info(MSG.format(str(object_name), str(url)))
+        response = requests.get(
+            self._base_url + url,
+            timeout=self._timeout, verify=self._verify, auth=self._auth
+        )
+        LOGGER.info('  Response[{:s}]: {:s}'.format(str(response.status_code), str(response.content)))
+
+        if response.status_code in expected_http_status: return json.loads(response.content)
+
+        MSG = 'Could not get {:s}({:s}): status_code={:s} reply={:s}'
+        raise Exception(MSG.format(str(object_name), str(url), str(response.status_code), str(response)))
+
+    def update(
+        self, object_name : str, url : str, data : Dict, headers : Dict[str, Any] = dict(),
+        expected_http_status : Set[int] = HTTP_OK_CODES
+    ) -> None:
+        headers = copy.deepcopy(headers)
+        if 'content-type' not in {header_name.lower() for header_name in headers.keys()}:
+            headers.update({'content-type': 'application/json'})
+
+        MSG = 'Create/Update {:s}({:s}, {:s})'
+        LOGGER.info(MSG.format(str(object_name), str(url), str(data)))
+        response = requests.post(
+            self._base_url + url, data=json.dumps(data), headers=headers,
+            timeout=self._timeout, verify=self._verify, auth=self._auth
+        )
+        LOGGER.info('  Response[{:s}]: {:s}'.format(str(response.status_code), str(response.content)))
+
+        if response.status_code in expected_http_status: return
+
+        MSG = 'Could not create/update {:s}({:s}, {:s}): status_code={:s} reply={:s}'
+        raise Exception(MSG.format(str(object_name), str(url), str(data), str(response.status_code), str(response)))
+
+    def delete(
+        self, object_name : str, url : str,
+        expected_http_status : Set[int] = HTTP_OK_CODES
+    ) -> None:
+        MSG = 'Delete {:s}({:s})'
+        LOGGER.info(MSG.format(str(object_name), str(url)))
+        response = requests.delete(
+            self._base_url + url,
+            timeout=self._timeout, verify=self._verify, auth=self._auth
+        )
+        LOGGER.info('  Response[{:s}]: {:s}'.format(str(response.status_code), str(response.content)))
+
+        if response.status_code in expected_http_status: return
+
+        MSG = 'Could not delete {:s}({:s}): status_code={:s} reply={:s}'
+        raise Exception(MSG.format(str(object_name), str(url), str(response.status_code), str(response)))
diff --git a/src/device/service/drivers/ietf_actn/handlers/__init__.py b/src/device/service/drivers/ietf_actn/handlers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..38d04994fb0fa1951fb465bc127eb72659dc2eaf
--- /dev/null
+++ b/src/device/service/drivers/ietf_actn/handlers/__init__.py
@@ -0,0 +1,13 @@
+# 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.
diff --git a/src/device/service/drivers/openconfig/templates/Interfaces.py b/src/device/service/drivers/openconfig/templates/Interfaces.py
index 3d4c73fc11c686b4d4e181a1f98ed3f5922f7c15..51ee9fc66334a30a450144b4b8079575498aeef9 100644
--- a/src/device/service/drivers/openconfig/templates/Interfaces.py
+++ b/src/device/service/drivers/openconfig/templates/Interfaces.py
@@ -31,10 +31,6 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
 
         interface = {}
 
-        interface_name = xml_interface.find('oci:name', namespaces=NAMESPACES)
-        if interface_name is None or interface_name.text is None: continue
-        add_value_from_tag(interface, 'name', interface_name)
-
         #interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES)
         #add_value_from_tag(interface, 'type', interface_type)
 
@@ -42,8 +38,11 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
             interface_type = xml_interface.find('oci:config/oci:type', namespaces=NAMESPACES)
         elif xml_interface.find('oci:state/oci:type', namespaces=NAMESPACES) is not None:
             interface_type = xml_interface.find('oci:state/oci:type', namespaces=NAMESPACES)
-        else:
-            interface_type = ''
+        else: continue
+            
+        interface_name = xml_interface.find('oci:name', namespaces=NAMESPACES)
+        if interface_name is None or interface_name.text is None: continue
+        add_value_from_tag(interface, 'name', interface_name)
             
         # Get the type of interface according to the vendor's type
         if 'ianaift:' in interface_type.text:
diff --git a/src/device/service/drivers/openconfig/templates/Inventory.py b/src/device/service/drivers/openconfig/templates/Inventory.py
index 2ae67ba47dad162b8c8e4a15d3004b27359d4ca2..916af0478c51d381d670b7678095867537f3bdc9 100644
--- a/src/device/service/drivers/openconfig/templates/Inventory.py
+++ b/src/device/service/drivers/openconfig/templates/Inventory.py
@@ -54,7 +54,6 @@ XPATH_PORTS = "//ocp:components/ocp:component"
 
 def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
     response = []
-    LOGGER.debug("InventoryPrueba")
     parent_types = {}
     for xml_component in xml_data.xpath(XPATH_PORTS, namespaces=NAMESPACES):
         LOGGER.info('xml_component inventario = {:s}'.format(str(ET.tostring(xml_component))))
@@ -78,9 +77,9 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
             add_value_from_tag(inventory['attributes'], 'location', component_location)
         
         component_type = xml_component.find('ocp:state/ocp:type', namespaces=NAMESPACES)
-        component_type.text = component_type.text.replace('oc-platform-types:','')
-        if component_type is None: continue
-        add_value_from_tag(inventory, 'class', component_type)
+        if component_type is not None:
+            component_type.text = component_type.text.replace('oc-platform-types:','')
+            add_value_from_tag(inventory, 'class', component_type)
         
         if inventory['class'] == 'CPU' or inventory['class'] == 'STORAGE': continue
 
diff --git a/src/device/service/drivers/openconfig/templates/RoutingPolicy.py b/src/device/service/drivers/openconfig/templates/RoutingPolicy.py
index acafa021824f94f929e849117824e8120974d0b1..96dc1c5a49d0007f4b0a28b10c2f35868a18a71f 100644
--- a/src/device/service/drivers/openconfig/templates/RoutingPolicy.py
+++ b/src/device/service/drivers/openconfig/templates/RoutingPolicy.py
@@ -35,7 +35,7 @@ def parse(xml_data : ET.Element) -> List[Tuple[str, Dict[str, Any]]]:
         #LOGGER.info('xml_policy_definition = {:s}'.format(str(ET.tostring(xml_policy_definition))))
 
         policy_definition = {}
-
+        statement_name = ''
         policy_name = xml_policy_definition.find('ocrp:name', namespaces=NAMESPACES)
         if policy_name is None or policy_name.text is None: continue
         add_value_from_tag(policy_definition, 'policy_name', policy_name)
diff --git a/src/device/tests/data/ietf_actn/config_rules.json b/src/device/tests/data/ietf_actn/config_rules.json
new file mode 100644
index 0000000000000000000000000000000000000000..d106a5a8f9eb705e975c14aacd489ecfa042625f
--- /dev/null
+++ b/src/device/tests/data/ietf_actn/config_rules.json
@@ -0,0 +1,32 @@
+[
+    {"action": 1, "custom": {"resource_key": "/osu_tunnels/osu_tunnel[osu_tunnel_1]", "resource_value": {
+        "name": "osu_tunnel_1", "odu_type": "osuflex", "osuflex_number": 40, "bidirectional": true, "delay": 20,
+        "src_node_id": "10.0.10.1", "src_tp_id": "200", "src_ttp_channel_name": "och:1-odu2:1-oduflex:1-osuflex:2",
+        "dst_node_id": "10.0.30.1", "dst_tp_id": "200", "dst_ttp_channel_name": "och:1-odu2:1-oduflex:3-osuflex:1"
+    }}},
+    {"action": 1, "custom": {"resource_key": "/osu_tunnels/osu_tunnel[osu_tunnel_2]", "resource_value": {
+        "name": "osu_tunnel_2", "odu_type": "osuflex", "osuflex_number": 40, "bidirectional": true, "delay": 20,
+        "src_node_id": "10.0.10.1", "src_tp_id": "200", "src_ttp_channel_name": "och:1-odu2:1-oduflex:1-osuflex:2",
+        "dst_node_id": "10.0.30.1", "dst_tp_id": "200", "dst_ttp_channel_name": "och:1-odu2:1-oduflex:3-osuflex:1"
+    }}},
+    {"action": 1, "custom": {"resource_key": "/etht_services/etht_service[etht_service_1]", "resource_value": {
+        "name": "etht_service_1", "osu_tunnel_name": "osu_tunnel_1", "service_type": "op-mp2mp-svc",
+        "src_node_id": "10.0.10.1", "src_tp_id": "200", "src_vlan_tag": 21, "src_static_routes": [
+            ["128.32.10.5", 24, "128.32.33.5"],
+            ["128.32.20.5", 24, "128.32.33.5"]
+        ],
+        "dst_node_id": "10.0.30.1", "dst_tp_id": "200", "dst_vlan_tag": 101, "dst_static_routes": [
+            ["172.1.101.22", 24, "172.10.33.5"]
+        ]
+    }}},
+    {"action": 1, "custom": {"resource_key": "/etht_services/etht_service[etht_service_2]", "resource_value": {
+        "name": "etht_service_2", "osu_tunnel_name": "osu_tunnel_2", "service_type": "op-mp2mp-svc",
+        "src_node_id": "10.0.10.1", "src_tp_id": "200", "src_vlan_tag": 31, "src_static_routes": [
+            ["128.32.10.5", 24, "128.32.33.5"],
+            ["128.32.20.5", 24, "128.32.33.5"]
+        ],
+        "dst_node_id": "10.0.30.1", "dst_tp_id": "200", "dst_vlan_tag": 201, "dst_static_routes": [
+            ["172.1.201.22", 24, "172.10.33.5"]
+        ]
+    }}}
+]
diff --git a/src/device/tests/data/ietf_actn/deconfig_rules.json b/src/device/tests/data/ietf_actn/deconfig_rules.json
new file mode 100644
index 0000000000000000000000000000000000000000..f18e5fb2db8b4d077ea68a499d61577030ce7f48
--- /dev/null
+++ b/src/device/tests/data/ietf_actn/deconfig_rules.json
@@ -0,0 +1,14 @@
+[
+    {"action": 2, "custom": {"resource_key": "/osu_tunnels/osu_tunnel[osu_tunnel_1]", "resource_value": {
+        "name": "osu_tunnel_1"
+    }}},
+    {"action": 2, "custom": {"resource_key": "/osu_tunnels/osu_tunnel[osu_tunnel_2]", "resource_value": {
+        "name": "osu_tunnel_2"
+    }}},
+    {"action": 2, "custom": {"resource_key": "/etht_services/etht_service[etht_service_1]", "resource_value": {
+        "name": "etht_service_1"
+    }}},
+    {"action": 2, "custom": {"resource_key": "/etht_services/etht_service[etht_service_2]", "resource_value": {
+        "name": "etht_service_2"
+    }}}
+]
diff --git a/src/device/tests/data/ietf_actn/expected_etht_services.json b/src/device/tests/data/ietf_actn/expected_etht_services.json
new file mode 100644
index 0000000000000000000000000000000000000000..72c48e6b3351a4adc5737bbc1f63952363893f96
--- /dev/null
+++ b/src/device/tests/data/ietf_actn/expected_etht_services.json
@@ -0,0 +1,176 @@
+{
+    "ietf-eth-tran-service:etht-svc": {
+        "etht-svc-instances": [
+            {
+                "etht-svc-name": "etht_service_1",
+                "etht-svc-title": "ETHT_SERVICE_1",
+                "etht-svc-type": "op-mp2mp-svc",
+                "source-endpoints": {
+                    "source-endpoint": [
+                        {
+                            "node-id": "10.0.10.1",
+                            "tp-id": "200",
+                            "protection-role": "work",
+                            "layer-specific": {
+                                "access-type": "port"
+                            },
+                            "is-extendable": false,
+                            "is-terminal": true,
+                            "static-route-list": [
+                                {
+                                    "destination": "128.32.10.5",
+                                    "destination-mask": 24,
+                                    "next-hop": "128.32.33.5"
+                                },
+                                {
+                                    "destination": "128.32.20.5",
+                                    "destination-mask": 24,
+                                    "next-hop": "128.32.33.5"
+                                }
+                            ],
+                            "outer-tag": {
+                                "tag-type": "ietf-eth-tran-types:classify-c-vlan",
+                                "vlan-value": 21
+                            },
+                            "service-classification-type": "ietf-eth-tran-type:vlan-classification",
+                            "ingress-egress-bandwidth-profile" : {
+                                "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp",
+                                "CIR": 10000000,
+                                "EIR": 10000000
+                            }
+                        }
+                    ]
+                },
+                "destination-endpoints": {
+                    "destination-endpoint": [
+                        {
+                            "node-id": "10.0.30.1",
+                            "tp-id": "200",
+                            "protection-role": "work",
+                            "layer-specific": {
+                                "access-type": "port"
+                            },
+                            "is-extendable": false,
+                            "is-terminal": true,
+                            "static-route-list": [
+                                {
+                                    "destination": "172.1.101.22",
+                                    "destination-mask": 24,
+                                    "next-hop": "172.10.33.5"
+                                }
+                            ],
+                            "outer-tag": {
+                                "tag-type": "ietf-eth-tran-types:classify-c-vlan",
+                                "vlan-value": 101
+                            },
+                            "service-classification-type": "ietf-eth-tran-type:vlan-classification",
+                            "ingress-egress-bandwidth-profile" : {
+                                "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp",
+                                "CIR": 10000000,
+                                "EIR": 10000000
+                            }
+                        }
+                    ]
+                },
+                "svc-tunnel": [
+                    {
+                        "tunnel-name": "osu_tunnel_1"
+                    }
+                ],
+                "optimizations": {
+                    "optimization-metric": [
+                        {
+                            "metric-role": "work",
+                            "metric-type": "ietf-te-types:path-metric-te"
+                        }
+                    ]
+                }
+            },
+            {
+                "etht-svc-name": "etht_service_2",
+                "etht-svc-title": "ETHT_SERVICE_2",
+                "etht-svc-type": "op-mp2mp-svc",
+                "source-endpoints": {
+                    "source-endpoint": [
+                        {
+                            "node-id": "10.0.10.1",
+                            "tp-id": "200",
+                            "protection-role": "work",
+                            "layer-specific": {
+                                "access-type": "port"
+                            },
+                            "is-extendable": false,
+                            "is-terminal": true,
+                            "static-route-list": [
+                                {
+                                    "destination": "128.32.10.5",
+                                    "destination-mask": 24,
+                                    "next-hop": "128.32.33.5"
+                                },
+                                {
+                                    "destination": "128.32.20.5",
+                                    "destination-mask": 24,
+                                    "next-hop": "128.32.33.5"
+                                }
+                            ],
+                            "outer-tag": {
+                                "tag-type": "ietf-eth-tran-types:classify-c-vlan",
+                                "vlan-value": 31
+                            },
+                            "service-classification-type": "ietf-eth-tran-type:vlan-classification",
+                            "ingress-egress-bandwidth-profile" : {
+                                "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp",
+                                "CIR": 10000000,
+                                "EIR": 10000000
+                            }
+                        }
+                    ]
+                },
+                "destination-endpoints": {
+                    "destination-endpoint": [
+                        {
+                            "node-id": "10.0.30.1",
+                            "tp-id": "200",
+                            "protection-role": "work",
+                            "layer-specific": {
+                                "access-type": "port"
+                            },
+                            "is-extendable": false,
+                            "is-terminal": true,
+                            "static-route-list": [
+                                {
+                                    "destination": "172.1.201.22",
+                                    "destination-mask": 24,
+                                    "next-hop": "172.10.33.5"
+                                }
+                            ],
+                            "outer-tag": {
+                                "tag-type": "ietf-eth-tran-types:classify-c-vlan",
+                                "vlan-value": 201
+                            },
+                            "service-classification-type": "ietf-eth-tran-type:vlan-classification",
+                            "ingress-egress-bandwidth-profile" : {
+                                "bandwidth-profile-type": "ietf-eth-tran-types:mef-10-bwp",
+                                "CIR": 10000000,
+                                "EIR": 10000000
+                            }
+                        }
+                    ]
+                },
+                "svc-tunnel": [
+                    {
+                        "tunnel-name": "osu_tunnel_2"
+                    }
+                ],
+                "optimizations": {
+                    "optimization-metric": [
+                        {
+                            "metric-role": "work",
+                            "metric-type": "ietf-te-types:path-metric-te"
+                        }
+                    ]
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/src/device/tests/data/ietf_actn/expected_osu_tunnels.json b/src/device/tests/data/ietf_actn/expected_osu_tunnels.json
new file mode 100644
index 0000000000000000000000000000000000000000..1debf555b7219b1f62c4f04782aeaf0eefca62f9
--- /dev/null
+++ b/src/device/tests/data/ietf_actn/expected_osu_tunnels.json
@@ -0,0 +1,84 @@
+{
+    "ietf-te:tunnel": [
+        {
+            "name": "osu_tunnel_1",
+            "title": "OSU_TUNNEL_1",
+            "admin-state": "ietf-te-types:tunnel-admin-state-up",
+            "delay": 20,
+            "te-bandwidth": {
+                "layer": "odu",
+                "odu-type": "osuflex",
+                "number": 40
+            },
+            "bidirectional": true,
+            "destination-endpoints": {
+                "destination-endpoint": [
+                    {
+                        "node-id": "10.0.30.1",
+                        "tp-id": "200",
+                        "ttp-channel-name": "och:1-odu2:1-oduflex:3-osuflex:1",
+                        "protection-role": "work"
+                    }
+                ]
+            },
+            "source-endpoints": {
+                "source-endpoint": [
+                    {
+                        "node-id": "10.0.10.1",
+                        "tp-id": "200",
+                        "ttp-channel-name": "och:1-odu2:1-oduflex:1-osuflex:2",
+                        "protection-role": "work"
+                    }
+                ]
+            },
+            "restoration": {
+                "restoration-type": "ietf-te-types:lsp-restoration-not-applicable",
+                "restoration-lock": false
+            },
+            "protection": {
+                "protection-type": "ietf-te-types:lsp-protection-unprotected",
+                "protection-reversion-disable": true
+            }
+        },
+        {
+            "name": "osu_tunnel_2",
+            "title": "OSU_TUNNEL_2",
+            "admin-state": "ietf-te-types:tunnel-admin-state-up",
+            "delay": 20,
+            "te-bandwidth": {
+                "layer": "odu",
+                "odu-type": "osuflex",
+                "number": 40
+            },
+            "bidirectional": true,
+            "destination-endpoints": {
+                "destination-endpoint": [
+                    {
+                        "node-id": "10.0.30.1",
+                        "tp-id": "200",
+                        "ttp-channel-name": "och:1-odu2:1-oduflex:3-osuflex:1",
+                        "protection-role": "work"
+                    }
+                ]
+            },
+            "source-endpoints": {
+                "source-endpoint": [
+                    {
+                        "node-id": "10.0.10.1",
+                        "tp-id": "200",
+                        "ttp-channel-name": "och:1-odu2:1-oduflex:1-osuflex:2",
+                        "protection-role": "work"
+                    }
+                ]
+            },
+            "restoration": {
+                "restoration-type": "ietf-te-types:lsp-restoration-not-applicable",
+                "restoration-lock": false
+            },
+            "protection": {
+                "protection-type": "ietf-te-types:lsp-protection-unprotected",
+                "protection-reversion-disable": true
+            }
+        }
+    ]
+}
diff --git a/src/device/tests/test_unitary_ietf_actn.py b/src/device/tests/test_unitary_ietf_actn.py
new file mode 100644
index 0000000000000000000000000000000000000000..011b3ddbc54d9b68ec6c95cc6b0816385ad93eff
--- /dev/null
+++ b/src/device/tests/test_unitary_ietf_actn.py
@@ -0,0 +1,294 @@
+# 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 copy, deepdiff, json, logging, operator, os, pytest, time
+from flask import Flask, jsonify, make_response
+from flask_restful import Resource
+from common.proto.context_pb2 import ConfigActionEnum, Device, DeviceId
+from common.tools.descriptor.Tools import format_custom_config_rules
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from common.tools.object_factory.Device import (
+    json_device_connect_rules, json_device_id, json_device_ietf_actn_disabled
+)
+from common.tools.service.GenericRestServer import GenericRestServer
+from context.client.ContextClient import ContextClient
+from device.client.DeviceClient import DeviceClient
+from device.service.DeviceService import DeviceService
+from device.service.driver_api._Driver import _Driver
+from tests.tools.mock_ietf_actn_sdn_ctrl.ResourceEthServices import EthService, EthServices
+from tests.tools.mock_ietf_actn_sdn_ctrl.ResourceOsuTunnels import OsuTunnel, OsuTunnels
+
+os.environ['DEVICE_EMULATED_ONLY'] = 'TRUE'
+from .PrepareTestScenario import ( # pylint: disable=unused-import
+    # be careful, order of symbols is important here!
+    mock_service, device_service, context_client, device_client, monitoring_client, test_prepare_environment
+)
+
+DEVICE_UUID     = 'DEVICE-IETF-ACTN'
+DEVICE_ADDRESS  = '127.0.0.1'
+DEVICE_PORT     = 8080
+DEVICE_USERNAME = 'admin'
+DEVICE_PASSWORD = 'admin'
+DEVICE_SCHEME   = 'http'
+DEVICE_BASE_URL = '/restconf/v2/data'
+DEVICE_TIMEOUT  = 120
+DEVICE_VERIFY   = False
+
+DEVICE_ID = json_device_id(DEVICE_UUID)
+DEVICE    = json_device_ietf_actn_disabled(DEVICE_UUID)
+
+DEVICE_CONNECT_RULES = json_device_connect_rules(DEVICE_ADDRESS, DEVICE_PORT, {
+    'scheme'  : DEVICE_SCHEME,
+    'username': DEVICE_USERNAME,
+    'password': DEVICE_PASSWORD,
+    'base_url': DEVICE_BASE_URL,
+    'timeout' : DEVICE_TIMEOUT,
+    'verify'  : DEVICE_VERIFY,
+})
+
+DATA_FILE_CONFIG_RULES           = 'device/tests/data/ietf_actn/config_rules.json'
+DATA_FILE_DECONFIG_RULES         = 'device/tests/data/ietf_actn/deconfig_rules.json'
+DATA_FILE_EXPECTED_OSU_TUNNELS   = 'device/tests/data/ietf_actn/expected_osu_tunnels.json'
+DATA_FILE_EXPECTED_ETHT_SERVICES = 'device/tests/data/ietf_actn/expected_etht_services.json'
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+@pytest.fixture(scope='session')
+def ietf_actn_sdn_ctrl(
+    device_service : DeviceService,     # pylint: disable=redefined-outer-name
+) -> Flask:
+    _rest_server = GenericRestServer(DEVICE_PORT, DEVICE_BASE_URL, bind_address=DEVICE_ADDRESS)
+    _rest_server.app.config['DEBUG'      ] = True
+    _rest_server.app.config['ENV'        ] = 'development'
+    _rest_server.app.config['SERVER_NAME'] = '{:s}:{:d}'.format(DEVICE_ADDRESS, DEVICE_PORT)
+    _rest_server.app.config['TESTING'    ] = True
+
+    class Root(Resource):
+        def get(self):
+            return make_response(jsonify({}), 200)
+
+    add_rsrc = _rest_server.add_resource
+    add_rsrc(Root,        '/')
+    add_rsrc(OsuTunnels,  '/ietf-te:te/tunnels')
+    add_rsrc(OsuTunnel,   '/ietf-te:te/tunnels/tunnel="<string:osu_tunnel_name>"')
+    add_rsrc(EthServices, '/ietf-eth-tran-service:etht-svc')
+    add_rsrc(EthService,  '/ietf-eth-tran-service:etht-svc/etht-svc-instances="<string:etht_service_name>"')
+
+    _rest_server.start()
+    time.sleep(1) # bring time for the server to start
+    yield _rest_server
+    _rest_server.shutdown()
+    _rest_server.join()
+
+
+def test_device_ietf_actn_add(
+    device_client : DeviceClient,           # pylint: disable=redefined-outer-name
+    device_service : DeviceService,         # pylint: disable=redefined-outer-name
+    ietf_actn_sdn_ctrl : GenericRestServer, # pylint: disable=redefined-outer-name
+) -> None:
+    DEVICE_WITH_CONNECT_RULES = copy.deepcopy(DEVICE)
+    DEVICE_WITH_CONNECT_RULES['device_config']['config_rules'].extend(DEVICE_CONNECT_RULES)
+    device_client.AddDevice(Device(**DEVICE_WITH_CONNECT_RULES))
+    driver_instance_cache = device_service.device_servicer.driver_instance_cache
+    driver: _Driver = driver_instance_cache.get(DEVICE_UUID)
+    assert driver is not None
+
+
+def test_device_ietf_actn_get(
+    context_client : ContextClient,         # pylint: disable=redefined-outer-name
+    device_client : DeviceClient,           # pylint: disable=redefined-outer-name
+    ietf_actn_sdn_ctrl : GenericRestServer, # pylint: disable=redefined-outer-name
+) -> None:
+
+    initial_config = device_client.GetInitialConfig(DeviceId(**DEVICE_ID))
+    LOGGER.info('initial_config = {:s}'.format(grpc_message_to_json_string(initial_config)))
+
+    device_data = context_client.GetDevice(DeviceId(**DEVICE_ID))
+    LOGGER.info('device_data = {:s}'.format(grpc_message_to_json_string(device_data)))
+
+
+def test_device_ietf_actn_configure(
+    device_client : DeviceClient,           # pylint: disable=redefined-outer-name
+    device_service : DeviceService,         # pylint: disable=redefined-outer-name
+    ietf_actn_sdn_ctrl : GenericRestServer, # pylint: disable=redefined-outer-name
+) -> None:
+    ietf_actn_client = ietf_actn_sdn_ctrl.app.test_client()
+
+    driver_instance_cache = device_service.device_servicer.driver_instance_cache
+    driver : _Driver = driver_instance_cache.get(DEVICE_UUID)
+    assert driver is not None
+
+    retrieved_osu_tunnels = ietf_actn_client.get('/restconf/v2/data/ietf-te:te/tunnels')
+    retrieved_osu_tunnels = retrieved_osu_tunnels.json
+    LOGGER.info('osu_tunnels = {:s}'.format(str(retrieved_osu_tunnels)))
+    expected_osu_tunnels = {'ietf-te:tunnel': []}
+    osu_tunnels_diff = deepdiff.DeepDiff(expected_osu_tunnels, retrieved_osu_tunnels)
+    if len(osu_tunnels_diff) > 0:
+        LOGGER.error('PRE OSU TUNNELS - Differences:\n{:s}'.format(str(osu_tunnels_diff.pretty())))
+    assert len(osu_tunnels_diff) == 0
+
+    retrieved_etht_services = ietf_actn_client.get('/restconf/v2/data/ietf-eth-tran-service:etht-svc')
+    retrieved_etht_services = retrieved_etht_services.json
+    LOGGER.info('etht_services = {:s}'.format(str(retrieved_etht_services)))
+    expected_etht_services = {'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': []}}
+    etht_services_diff = deepdiff.DeepDiff(expected_etht_services, retrieved_etht_services)
+    if len(etht_services_diff) > 0:
+        LOGGER.error('PRE ETHT SERVICES - Differences:\n{:s}'.format(str(etht_services_diff.pretty())))
+    assert len(etht_services_diff) == 0
+
+    retrieved_driver_config_rules = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    LOGGER.info('driver_config = {:s}'.format(str(retrieved_driver_config_rules)))
+    assert isinstance(retrieved_driver_config_rules, list)
+    retrieved_driver_config_rules = [
+        (resource_key, resource_value)
+        for resource_key, resource_value in retrieved_driver_config_rules
+        if resource_key != '/endpoints/endpoint[mgmt]'
+    ]
+    if len(retrieved_driver_config_rules) > 0:
+        LOGGER.error('PRE DRIVER CONFIG RULES - Differences:\n{:s}'.format(str(retrieved_driver_config_rules)))
+    assert len(retrieved_driver_config_rules) == 0
+
+    DEVICE_WITH_CONFIG_RULES = copy.deepcopy(DEVICE)
+    with open(DATA_FILE_CONFIG_RULES, 'r', encoding='UTF-8') as f:
+        config_rules = format_custom_config_rules(json.load(f))
+        DEVICE_WITH_CONFIG_RULES['device_config']['config_rules'].extend(config_rules)
+    device_client.ConfigureDevice(Device(**DEVICE_WITH_CONFIG_RULES))
+
+    retrieved_osu_tunnels = ietf_actn_client.get('/restconf/v2/data/ietf-te:te/tunnels')
+    retrieved_osu_tunnels = retrieved_osu_tunnels.json
+    LOGGER.info('osu_tunnels = {:s}'.format(str(retrieved_osu_tunnels)))
+    with open(DATA_FILE_EXPECTED_OSU_TUNNELS, 'r', encoding='UTF-8') as f:
+        expected_osu_tunnels = json.load(f)
+    osu_tunnels_diff = deepdiff.DeepDiff(expected_osu_tunnels, retrieved_osu_tunnels)
+    if len(osu_tunnels_diff) > 0:
+        LOGGER.error('POST OSU TUNNELS - Differences:\n{:s}'.format(str(osu_tunnels_diff.pretty())))
+    assert len(osu_tunnels_diff) == 0
+
+    retrieved_etht_services = ietf_actn_client.get('/restconf/v2/data/ietf-eth-tran-service:etht-svc')
+    retrieved_etht_services = retrieved_etht_services.json
+    LOGGER.info('etht_services = {:s}'.format(str(retrieved_etht_services)))
+    with open(DATA_FILE_EXPECTED_ETHT_SERVICES, 'r', encoding='UTF-8') as f:
+        expected_etht_services = json.load(f)
+    etht_services_diff = deepdiff.DeepDiff(expected_etht_services, retrieved_etht_services)
+    if len(etht_services_diff) > 0:
+        LOGGER.error('POST ETHT SERVICES - Differences:\n{:s}'.format(str(etht_services_diff.pretty())))
+    assert len(etht_services_diff) == 0
+
+    retrieved_driver_config_rules = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    LOGGER.info('driver_config = {:s}'.format(str(retrieved_driver_config_rules)))
+    retrieved_driver_config_rules = [
+        {'action': 1, 'custom': {'resource_key': resource_key, 'resource_value': resource_value}}
+        for resource_key, resource_value in retrieved_driver_config_rules
+        if resource_key != '/endpoints/endpoint[mgmt]'
+    ]
+    with open(DATA_FILE_CONFIG_RULES, 'r', encoding='UTF-8') as f:
+        expected_driver_config_rules = sorted(json.load(f), key=lambda cr: cr['custom']['resource_key'])
+    driver_config_rules_diff = deepdiff.DeepDiff(expected_driver_config_rules, retrieved_driver_config_rules)
+    if len(driver_config_rules_diff) > 0:
+        LOGGER.error('POST DRIVER CONFIG RULES - Differences:\n{:s}'.format(str(driver_config_rules_diff.pretty())))
+    assert len(driver_config_rules_diff) == 0
+
+
+def test_device_ietf_actn_deconfigure(
+    device_client : DeviceClient,           # pylint: disable=redefined-outer-name
+    device_service : DeviceService,         # pylint: disable=redefined-outer-name
+    ietf_actn_sdn_ctrl : GenericRestServer, # pylint: disable=redefined-outer-name
+) -> None:
+    ietf_actn_client = ietf_actn_sdn_ctrl.app.test_client()
+
+    driver_instance_cache = device_service.device_servicer.driver_instance_cache
+    driver : _Driver = driver_instance_cache.get(DEVICE_UUID)
+    assert driver is not None
+
+    retrieved_osu_tunnels = ietf_actn_client.get('/restconf/v2/data/ietf-te:te/tunnels')
+    retrieved_osu_tunnels = retrieved_osu_tunnels.json
+    LOGGER.info('osu_tunnels = {:s}'.format(str(retrieved_osu_tunnels)))
+    with open(DATA_FILE_EXPECTED_OSU_TUNNELS, 'r', encoding='UTF-8') as f:
+        expected_osu_tunnels = json.load(f)
+    osu_tunnels_diff = deepdiff.DeepDiff(expected_osu_tunnels, retrieved_osu_tunnels)
+    if len(osu_tunnels_diff) > 0:
+        LOGGER.error('PRE OSU TUNNELS - Differences:\n{:s}'.format(str(osu_tunnels_diff.pretty())))
+    assert len(osu_tunnels_diff) == 0
+
+    retrieved_etht_services = ietf_actn_client.get('/restconf/v2/data/ietf-eth-tran-service:etht-svc')
+    retrieved_etht_services = retrieved_etht_services.json
+    LOGGER.info('etht_services = {:s}'.format(str(retrieved_etht_services)))
+    with open(DATA_FILE_EXPECTED_ETHT_SERVICES, 'r', encoding='UTF-8') as f:
+        expected_etht_services = json.load(f)
+    etht_services_diff = deepdiff.DeepDiff(expected_etht_services, retrieved_etht_services)
+    if len(etht_services_diff) > 0:
+        LOGGER.error('PRE ETHT SERVICES - Differences:\n{:s}'.format(str(etht_services_diff.pretty())))
+    assert len(etht_services_diff) == 0
+
+    retrieved_driver_config_rules = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    LOGGER.info('driver_config = {:s}'.format(str(retrieved_driver_config_rules)))
+    retrieved_driver_config_rules = [
+        {'action': 1, 'custom': {'resource_key': resource_key, 'resource_value': resource_value}}
+        for resource_key, resource_value in retrieved_driver_config_rules
+        if resource_key != '/endpoints/endpoint[mgmt]'
+    ]
+    with open(DATA_FILE_CONFIG_RULES, 'r', encoding='UTF-8') as f:
+        expected_driver_config_rules = sorted(json.load(f), key=lambda cr: cr['custom']['resource_key'])
+    driver_config_rules_diff = deepdiff.DeepDiff(expected_driver_config_rules, retrieved_driver_config_rules)
+    if len(driver_config_rules_diff) > 0:
+        LOGGER.error('PRE DRIVER CONFIG RULES - Differences:\n{:s}'.format(str(driver_config_rules_diff.pretty())))
+    assert len(driver_config_rules_diff) == 0
+
+    DEVICE_WITH_DECONFIG_RULES = copy.deepcopy(DEVICE)
+    with open(DATA_FILE_DECONFIG_RULES, 'r', encoding='UTF-8') as f:
+        deconfig_rules = format_custom_config_rules(json.load(f))
+        DEVICE_WITH_DECONFIG_RULES['device_config']['config_rules'].extend(deconfig_rules)
+    device_client.ConfigureDevice(Device(**DEVICE_WITH_DECONFIG_RULES))
+
+    retrieved_osu_tunnels = ietf_actn_client.get('/restconf/v2/data/ietf-te:te/tunnels')
+    retrieved_osu_tunnels = retrieved_osu_tunnels.json
+    LOGGER.info('osu_tunnels = {:s}'.format(str(retrieved_osu_tunnels)))
+    expected_osu_tunnels = {'ietf-te:tunnel': []}
+    osu_tunnels_diff = deepdiff.DeepDiff(expected_osu_tunnels, retrieved_osu_tunnels)
+    if len(osu_tunnels_diff) > 0:
+        LOGGER.error('POST OSU TUNNELS - Differences:\n{:s}'.format(str(osu_tunnels_diff.pretty())))
+    assert len(osu_tunnels_diff) == 0
+
+    retrieved_etht_services = ietf_actn_client.get('/restconf/v2/data/ietf-eth-tran-service:etht-svc')
+    retrieved_etht_services = retrieved_etht_services.json
+    LOGGER.info('etht_services = {:s}'.format(str(retrieved_etht_services)))
+    expected_etht_services = {'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': []}}
+    etht_services_diff = deepdiff.DeepDiff(expected_etht_services, retrieved_etht_services)
+    if len(etht_services_diff) > 0:
+        LOGGER.error('POST ETHT SERVICES - Differences:\n{:s}'.format(str(etht_services_diff.pretty())))
+    assert len(etht_services_diff) == 0
+
+    retrieved_driver_config_rules = sorted(driver.GetConfig(), key=operator.itemgetter(0))
+    LOGGER.info('retrieved_driver_config_rules = {:s}'.format(str(retrieved_driver_config_rules)))
+    assert isinstance(retrieved_driver_config_rules, list)
+    retrieved_driver_config_rules = [
+        (resource_key, resource_value)
+        for resource_key, resource_value in retrieved_driver_config_rules
+        if resource_key != '/endpoints/endpoint[mgmt]'
+    ]
+    if len(retrieved_driver_config_rules) > 0:
+        LOGGER.error('POST DRIVER CONFIG RULES - Differences:\n{:s}'.format(str(retrieved_driver_config_rules)))
+    assert len(retrieved_driver_config_rules) == 0
+
+
+def test_device_ietf_actn_delete(
+    device_client : DeviceClient,           # pylint: disable=redefined-outer-name
+    device_service : DeviceService,         # pylint: disable=redefined-outer-name
+    ietf_actn_sdn_ctrl : GenericRestServer, # pylint: disable=redefined-outer-name
+) -> None:
+    device_client.DeleteDevice(DeviceId(**DEVICE_ID))
+    driver_instance_cache = device_service.device_servicer.driver_instance_cache
+    driver : _Driver = driver_instance_cache.get(DEVICE_UUID, {})
+    assert driver is None
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py
index 2192ea94214201697d196ca7546a906a13197a93..80c7b32ddf6cabf8a6c124ec20ddae2f5cd181ad 100644
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py
+++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Handlers.py
@@ -55,7 +55,7 @@ def process_vpn_service(
 
 def update_service_endpoint(
     service_uuid : str, site_id : str, device_uuid : str, endpoint_uuid : str,
-    vlan_tag : int, ipv4_address : str, ipv4_prefix_length : int,
+    vlan_tag : int, ipv4_address : str, neighbor_ipv4_address : str, ipv4_prefix_length : int,
     capacity_gbps : Optional[float] = None, e2e_latency_ms : Optional[float] = None,
     availability : Optional[float] = None, mtu : Optional[int] = None,
     static_routing : Optional[Dict[Tuple[str, str], str]] = None,
@@ -91,12 +91,15 @@ def update_service_endpoint(
             for (ip_range, ip_prefix_len, lan_tag), next_hop in static_routing.items()
         })
 
-    ENDPOINT_SETTINGS_KEY = '/device[{:s}]/endpoint[{:s}]/vlan[{:d}]/settings'
-    endpoint_settings_key = ENDPOINT_SETTINGS_KEY.format(device_uuid, endpoint_uuid, vlan_tag)
+    #ENDPOINT_SETTINGS_KEY = '/device[{:s}]/endpoint[{:s}]/vlan[{:d}]/settings'
+    #endpoint_settings_key = ENDPOINT_SETTINGS_KEY.format(device_uuid, endpoint_uuid, vlan_tag)
+    ENDPOINT_SETTINGS_KEY = '/device[{:s}]/endpoint[{:s}]/settings'
+    endpoint_settings_key = ENDPOINT_SETTINGS_KEY.format(device_uuid, endpoint_uuid)
     field_updates = {}
-    if vlan_tag           is not None: field_updates['vlan_tag'     ] = (vlan_tag,           True)
-    if ipv4_address       is not None: field_updates['ip_address'   ] = (ipv4_address,       True)
-    if ipv4_prefix_length is not None: field_updates['prefix_length'] = (ipv4_prefix_length, True)
+    if vlan_tag              is not None: field_updates['vlan_tag'        ] = (vlan_tag,              True)
+    if ipv4_address          is not None: field_updates['ip_address'      ] = (ipv4_address,          True)
+    if neighbor_ipv4_address is not None: field_updates['neighbor_address'] = (neighbor_ipv4_address, True)
+    if ipv4_prefix_length    is not None: field_updates['prefix_length'   ] = (ipv4_prefix_length,    True)
     update_config_rule_custom(config_rules, endpoint_settings_key, field_updates)
 
     try:
@@ -131,7 +134,7 @@ def process_site_network_access(
         raise NotImplementedError(MSG.format(str(ipv4_allocation['address-allocation-type'])))
     ipv4_allocation_addresses = ipv4_allocation['addresses']
     ipv4_provider_address = ipv4_allocation_addresses['provider-address']
-    #ipv4_customer_address = ipv4_allocation_addresses['customer-address']
+    ipv4_customer_address = ipv4_allocation_addresses['customer-address']
     ipv4_prefix_length    = ipv4_allocation_addresses['prefix-length'   ]
 
     vlan_tag = None
@@ -176,7 +179,8 @@ def process_site_network_access(
         availability       = qos_profile_class['bandwidth']['guaranteed-bw-percent']
 
     exc = update_service_endpoint(
-        service_uuid, site_id, device_uuid, endpoint_uuid, vlan_tag, ipv4_provider_address, ipv4_prefix_length,
+        service_uuid, site_id, device_uuid, endpoint_uuid,
+        vlan_tag, ipv4_customer_address, ipv4_provider_address, ipv4_prefix_length,
         capacity_gbps=service_bandwidth_gbps, e2e_latency_ms=max_e2e_latency_ms, availability=availability,
         mtu=service_mtu, static_routing=site_static_routing
     )
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py
index 13d5c532478e93ef1bd3b1c644d7f3c3ba927af5..6bd57c8238c1af63ed3f504593f3c70cf8a68cc6 100644
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py
+++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/L3VPN_Services.py
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 import logging
-from typing import Dict
+from typing import Dict, List
 from flask import request
 from flask.json import jsonify
 from flask_restful import Resource
@@ -36,11 +36,40 @@ class L3VPN_Services(Resource):
         request_data : Dict = request.json
         LOGGER.debug('Request: {:s}'.format(str(request_data)))
 
+        errors = list()
+        if 'ietf-l3vpn-svc:l3vpn-services' in request_data:
+            # processing multiple L3VPN service requests formatted as:
+            #{
+            #  "ietf-l3vpn-svc:l3vpn-services": {
+            #    "l3vpn-svc": [
+            #      {
+            #        "service-id": "vpn1",
+            #        "vpn-services": {
+            #          "vpn-service": [
+            for l3vpn_svc in request_data['ietf-l3vpn-svc:l3vpn-services']['l3vpn-svc']:
+                l3vpn_svc.pop('service-id', None)
+                l3vpn_svc_request_data = {'ietf-l3vpn-svc:l3vpn-svc': l3vpn_svc}
+                errors.extend(self._process_l3vpn(l3vpn_svc_request_data))
+        elif 'ietf-l3vpn-svc:l3vpn-svc' in request_data:
+            # processing single (standard) L3VPN service request formatted as:
+            #{
+            #  "ietf-l3vpn-svc:l3vpn-svc": {
+            #    "vpn-services": {
+            #      "vpn-service": [
+            errors.extend(self._process_l3vpn(request_data))
+        else:
+            errors.append('unexpected request: {:s}'.format(str(request_data)))
+
+        response = jsonify(errors)
+        response.status_code = HTTP_CREATED if len(errors) == 0 else HTTP_SERVERERROR
+        return response
+
+    def _process_l3vpn(self, request_data : Dict) -> List[Dict]:
         yang_validator = YangValidator('ietf-l3vpn-svc')
         request_data = yang_validator.parse_to_dict(request_data)
         yang_validator.destroy()
 
-        errors = []
+        errors = list()
 
         for vpn_service in request_data['l3vpn-svc']['vpn-services']['vpn-service']:
             process_vpn_service(vpn_service, errors)
@@ -48,6 +77,4 @@ class L3VPN_Services(Resource):
         for site in request_data['l3vpn-svc']['sites']['site']:
             process_site(site, errors)
 
-        response = jsonify(errors)
-        response.status_code = HTTP_CREATED if len(errors) == 0 else HTTP_SERVERERROR
-        return response
+        return errors
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf_l3vpn_tree.txt b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf_l3vpn_tree.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e811c7c1b1a25214926341dc0205ee9f1b150c63
--- /dev/null
+++ b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/yang/ietf_l3vpn_tree.txt
@@ -0,0 +1,413 @@
+module: ietf-l3vpn-svc
+  +--rw l3vpn-svc
+     +--rw vpn-profiles
+     |  +--rw valid-provider-identifiers
+     |     +--rw cloud-identifier* [id] {cloud-access}?
+     |     |  +--rw id    string
+     |     +--rw encryption-profile-identifier* [id]
+     |     |  +--rw id    string
+     |     +--rw qos-profile-identifier* [id]
+     |     |  +--rw id    string
+     |     +--rw bfd-profile-identifier* [id]
+     |        +--rw id    string
+     +--rw vpn-services
+     |  +--rw vpn-service* [vpn-id]
+     |     +--rw vpn-id                  svc-id
+     |     +--rw customer-name?          string
+     |     +--rw vpn-service-topology?   identityref
+     |     +--rw cloud-accesses {cloud-access}?
+     |     |  +--rw cloud-access* [cloud-identifier]
+     |     |     +--rw cloud-identifier       -> /l3vpn-svc/vpn-profiles/valid-provider-identifiers/cloud-identifier/id
+     |     |     +--rw (list-flavor)?
+     |     |     |  +--:(permit-any)
+     |     |     |  |  +--rw permit-any?   empty
+     |     |     |  +--:(deny-any-except)
+     |     |     |  |  +--rw permit-site*   -> /l3vpn-svc/sites/site/site-id
+     |     |     |  +--:(permit-any-except)
+     |     |     |     +--rw deny-site*   -> /l3vpn-svc/sites/site/site-id
+     |     |     +--rw address-translation
+     |     |        +--rw nat44
+     |     |           +--rw enabled?                  boolean
+     |     |           +--rw nat44-customer-address?   inet:ipv4-address
+     |     +--rw multicast {multicast}?
+     |     |  +--rw enabled?                 boolean
+     |     |  +--rw customer-tree-flavors
+     |     |  |  +--rw tree-flavor*   identityref
+     |     |  +--rw rp
+     |     |     +--rw rp-group-mappings
+     |     |     |  +--rw rp-group-mapping* [id]
+     |     |     |     +--rw id                  uint16
+     |     |     |     +--rw provider-managed
+     |     |     |     |  +--rw enabled?                    boolean
+     |     |     |     |  +--rw rp-redundancy?              boolean
+     |     |     |     |  +--rw optimal-traffic-delivery?   boolean
+     |     |     |     +--rw rp-address          inet:ip-address
+     |     |     |     +--rw groups
+     |     |     |        +--rw group* [id]
+     |     |     |           +--rw id                uint16
+     |     |     |           +--rw (group-format)
+     |     |     |              +--:(singleaddress)
+     |     |     |              |  +--rw group-address?   inet:ip-address
+     |     |     |              +--:(startend)
+     |     |     |                 +--rw group-start?   inet:ip-address
+     |     |     |                 +--rw group-end?     inet:ip-address
+     |     |     +--rw rp-discovery
+     |     |        +--rw rp-discovery-type?   identityref
+     |     |        +--rw bsr-candidates
+     |     |           +--rw bsr-candidate-address*   inet:ip-address
+     |     +--rw carrierscarrier?        boolean {carrierscarrier}?
+     |     +--rw extranet-vpns {extranet-vpn}?
+     |        +--rw extranet-vpn* [vpn-id]
+     |           +--rw vpn-id              svc-id
+     |           +--rw local-sites-role?   identityref
+     +--rw sites
+        +--rw site* [site-id]
+           +--rw site-id                  svc-id
+           +--rw requested-site-start?    yang:date-and-time
+           +--rw requested-site-stop?     yang:date-and-time
+           +--rw locations
+           |  +--rw location* [location-id]
+           |     +--rw location-id     svc-id
+           |     +--rw address?        string
+           |     +--rw postal-code?    string
+           |     +--rw state?          string
+           |     +--rw city?           string
+           |     +--rw country-code?   string
+           +--rw devices
+           |  +--rw device* [device-id]
+           |     +--rw device-id     svc-id
+           |     +--rw location      -> ../../../locations/location/location-id
+           |     +--rw management
+           |        +--rw address-family?   address-family
+           |        +--rw address           inet:ip-address
+           +--rw site-diversity {site-diversity}?
+           |  +--rw groups
+           |     +--rw group* [group-id]
+           |        +--rw group-id    string
+           +--rw management
+           |  +--rw type    identityref
+           +--rw vpn-policies
+           |  +--rw vpn-policy* [vpn-policy-id]
+           |     +--rw vpn-policy-id    svc-id
+           |     +--rw entries* [id]
+           |        +--rw id         svc-id
+           |        +--rw filters
+           |        |  +--rw filter* [type]
+           |        |     +--rw type               identityref
+           |        |     +--rw lan-tag*           string {lan-tag}?
+           |        |     +--rw ipv4-lan-prefix*   inet:ipv4-prefix {ipv4}?
+           |        |     +--rw ipv6-lan-prefix*   inet:ipv6-prefix {ipv6}?
+           |        +--rw vpn* [vpn-id]
+           |           +--rw vpn-id       -> /l3vpn-svc/vpn-services/vpn-service/vpn-id
+           |           +--rw site-role?   identityref
+           +--rw site-vpn-flavor?         identityref
+           +--rw maximum-routes
+           |  +--rw address-family* [af]
+           |     +--rw af                address-family
+           |     +--rw maximum-routes?   uint32
+           +--rw security
+           |  +--rw authentication
+           |  +--rw encryption {encryption}?
+           |     +--rw enabled?              boolean
+           |     +--rw layer?                enumeration
+           |     +--rw encryption-profile
+           |        +--rw (profile)?
+           |           +--:(provider-profile)
+           |           |  +--rw profile-name?   -> /l3vpn-svc/vpn-profiles/valid-provider-identifiers/encryption-profile-identifier/id
+           |           +--:(customer-profile)
+           |              +--rw algorithm?    string
+           |              +--rw (key-type)?
+           |                 +--:(psk)
+           |                    +--rw preshared-key?   string
+           +--rw service
+           |  +--rw qos {qos}?
+           |  |  +--rw qos-classification-policy
+           |  |  |  +--rw rule* [id]
+           |  |  |     +--rw id                 string
+           |  |  |     +--rw (match-type)?
+           |  |  |     |  +--:(match-flow)
+           |  |  |     |  |  +--rw match-flow
+           |  |  |     |  |     +--rw dscp?                inet:dscp
+           |  |  |     |  |     +--rw dot1p?               uint8
+           |  |  |     |  |     +--rw ipv4-src-prefix?     inet:ipv4-prefix
+           |  |  |     |  |     +--rw ipv6-src-prefix?     inet:ipv6-prefix
+           |  |  |     |  |     +--rw ipv4-dst-prefix?     inet:ipv4-prefix
+           |  |  |     |  |     +--rw ipv6-dst-prefix?     inet:ipv6-prefix
+           |  |  |     |  |     +--rw l4-src-port?         inet:port-number
+           |  |  |     |  |     +--rw target-sites*        svc-id {target-sites}?
+           |  |  |     |  |     +--rw l4-src-port-range
+           |  |  |     |  |     |  +--rw lower-port?   inet:port-number
+           |  |  |     |  |     |  +--rw upper-port?   inet:port-number
+           |  |  |     |  |     +--rw l4-dst-port?         inet:port-number
+           |  |  |     |  |     +--rw l4-dst-port-range
+           |  |  |     |  |     |  +--rw lower-port?   inet:port-number
+           |  |  |     |  |     |  +--rw upper-port?   inet:port-number
+           |  |  |     |  |     +--rw protocol-field?      union
+           |  |  |     |  +--:(match-application)
+           |  |  |     |     +--rw match-application?   identityref
+           |  |  |     +--rw target-class-id?   string
+           |  |  +--rw qos-profile
+           |  |     +--rw (qos-profile)?
+           |  |        +--:(standard)
+           |  |        |  +--rw profile?   -> /l3vpn-svc/vpn-profiles/valid-provider-identifiers/qos-profile-identifier/id
+           |  |        +--:(custom)
+           |  |           +--rw classes {qos-custom}?
+           |  |              +--rw class* [class-id]
+           |  |                 +--rw class-id      string
+           |  |                 +--rw direction?    identityref
+           |  |                 +--rw rate-limit?   decimal64
+           |  |                 +--rw latency
+           |  |                 |  +--rw (flavor)?
+           |  |                 |     +--:(lowest)
+           |  |                 |     |  +--rw use-lowest-latency?   empty
+           |  |                 |     +--:(boundary)
+           |  |                 |        +--rw latency-boundary?   uint16
+           |  |                 +--rw jitter
+           |  |                 |  +--rw (flavor)?
+           |  |                 |     +--:(lowest)
+           |  |                 |     |  +--rw use-lowest-jitter?   empty
+           |  |                 |     +--:(boundary)
+           |  |                 |        +--rw latency-boundary?   uint32
+           |  |                 +--rw bandwidth
+           |  |                    +--rw guaranteed-bw-percent    decimal64
+           |  |                    +--rw end-to-end?              empty
+           |  +--rw carrierscarrier {carrierscarrier}?
+           |  |  +--rw signalling-type?   enumeration
+           |  +--rw multicast {multicast}?
+           |     +--rw multicast-site-type?        enumeration
+           |     +--rw multicast-address-family
+           |     |  +--rw ipv4?   boolean {ipv4}?
+           |     |  +--rw ipv6?   boolean {ipv6}?
+           |     +--rw protocol-type?              enumeration
+           +--rw traffic-protection {fast-reroute}?
+           |  +--rw enabled?   boolean
+           +--rw routing-protocols
+           |  +--rw routing-protocol* [type]
+           |     +--rw type      identityref
+           |     +--rw ospf {rtg-ospf}?
+           |     |  +--rw address-family*   address-family
+           |     |  +--rw area-address      yang:dotted-quad
+           |     |  +--rw metric?           uint16
+           |     |  +--rw sham-links {rtg-ospf-sham-link}?
+           |     |     +--rw sham-link* [target-site]
+           |     |        +--rw target-site    svc-id
+           |     |        +--rw metric?        uint16
+           |     +--rw bgp {rtg-bgp}?
+           |     |  +--rw autonomous-system    uint32
+           |     |  +--rw address-family*      address-family
+           |     +--rw static
+           |     |  +--rw cascaded-lan-prefixes
+           |     |     +--rw ipv4-lan-prefixes* [lan next-hop] {ipv4}?
+           |     |     |  +--rw lan         inet:ipv4-prefix
+           |     |     |  +--rw next-hop    inet:ipv4-address
+           |     |     |  +--rw lan-tag?    string
+           |     |     +--rw ipv6-lan-prefixes* [lan next-hop] {ipv6}?
+           |     |        +--rw lan         inet:ipv6-prefix
+           |     |        +--rw next-hop    inet:ipv6-address
+           |     |        +--rw lan-tag?    string
+           |     +--rw rip {rtg-rip}?
+           |     |  +--rw address-family*   address-family
+           |     +--rw vrrp {rtg-vrrp}?
+           |        +--rw address-family*   address-family
+           +--ro actual-site-start?       yang:date-and-time
+           +--ro actual-site-stop?        yang:date-and-time
+           +--rw site-network-accesses
+              +--rw site-network-access* [site-network-access-id]
+                 +--rw site-network-access-id      svc-id
+                 +--rw site-network-access-type?   identityref
+                 +--rw (location-flavor)
+                 |  +--:(location)
+                 |  |  +--rw location-reference?   -> ../../../locations/location/location-id
+                 |  +--:(device)
+                 |     +--rw device-reference?   -> ../../../devices/device/device-id
+                 +--rw access-diversity {site-diversity}?
+                 |  +--rw groups
+                 |  |  +--rw group* [group-id]
+                 |  |     +--rw group-id    string
+                 |  +--rw constraints
+                 |     +--rw constraint* [constraint-type]
+                 |        +--rw constraint-type    identityref
+                 |        +--rw target
+                 |           +--rw (target-flavor)?
+                 |              +--:(id)
+                 |              |  +--rw group* [group-id]
+                 |              |     +--rw group-id    string
+                 |              +--:(all-accesses)
+                 |              |  +--rw all-other-accesses?   empty
+                 |              +--:(all-groups)
+                 |                 +--rw all-other-groups?   empty
+                 +--rw bearer
+                 |  +--rw requested-type {requested-type}?
+                 |  |  +--rw requested-type?   string
+                 |  |  +--rw strict?           boolean
+                 |  +--rw always-on?          boolean {always-on}?
+                 |  +--rw bearer-reference?   string {bearer-reference}?
+                 +--rw ip-connection
+                 |  +--rw ipv4 {ipv4}?
+                 |  |  +--rw address-allocation-type?   identityref
+                 |  |  +--rw provider-dhcp
+                 |  |  |  +--rw provider-address?   inet:ipv4-address
+                 |  |  |  +--rw prefix-length?      uint8
+                 |  |  |  +--rw (address-assign)?
+                 |  |  |     +--:(number)
+                 |  |  |     |  +--rw number-of-dynamic-address?   uint16
+                 |  |  |     +--:(explicit)
+                 |  |  |        +--rw customer-addresses
+                 |  |  |           +--rw address-group* [group-id]
+                 |  |  |              +--rw group-id         string
+                 |  |  |              +--rw start-address?   inet:ipv4-address
+                 |  |  |              +--rw end-address?     inet:ipv4-address
+                 |  |  +--rw dhcp-relay
+                 |  |  |  +--rw provider-address?        inet:ipv4-address
+                 |  |  |  +--rw prefix-length?           uint8
+                 |  |  |  +--rw customer-dhcp-servers
+                 |  |  |     +--rw server-ip-address*   inet:ipv4-address
+                 |  |  +--rw addresses
+                 |  |     +--rw provider-address?   inet:ipv4-address
+                 |  |     +--rw customer-address?   inet:ipv4-address
+                 |  |     +--rw prefix-length?      uint8
+                 |  +--rw ipv6 {ipv6}?
+                 |  |  +--rw address-allocation-type?   identityref
+                 |  |  +--rw provider-dhcp
+                 |  |  |  +--rw provider-address?   inet:ipv6-address
+                 |  |  |  +--rw prefix-length?      uint8
+                 |  |  |  +--rw (address-assign)?
+                 |  |  |     +--:(number)
+                 |  |  |     |  +--rw number-of-dynamic-address?   uint16
+                 |  |  |     +--:(explicit)
+                 |  |  |        +--rw customer-addresses
+                 |  |  |           +--rw address-group* [group-id]
+                 |  |  |              +--rw group-id         string
+                 |  |  |              +--rw start-address?   inet:ipv6-address
+                 |  |  |              +--rw end-address?     inet:ipv6-address
+                 |  |  +--rw dhcp-relay
+                 |  |  |  +--rw provider-address?        inet:ipv6-address
+                 |  |  |  +--rw prefix-length?           uint8
+                 |  |  |  +--rw customer-dhcp-servers
+                 |  |  |     +--rw server-ip-address*   inet:ipv6-address
+                 |  |  +--rw addresses
+                 |  |     +--rw provider-address?   inet:ipv6-address
+                 |  |     +--rw customer-address?   inet:ipv6-address
+                 |  |     +--rw prefix-length?      uint8
+                 |  +--rw oam
+                 |     +--rw bfd {bfd}?
+                 |        +--rw enabled?      boolean
+                 |        +--rw (holdtime)?
+                 |           +--:(fixed)
+                 |           |  +--rw fixed-value?   uint32
+                 |           +--:(profile)
+                 |              +--rw profile-name?   -> /l3vpn-svc/vpn-profiles/valid-provider-identifiers/bfd-profile-identifier/id
+                 +--rw security
+                 |  +--rw authentication
+                 |  +--rw encryption {encryption}?
+                 |     +--rw enabled?              boolean
+                 |     +--rw layer?                enumeration
+                 |     +--rw encryption-profile
+                 |        +--rw (profile)?
+                 |           +--:(provider-profile)
+                 |           |  +--rw profile-name?   -> /l3vpn-svc/vpn-profiles/valid-provider-identifiers/encryption-profile-identifier/id
+                 |           +--:(customer-profile)
+                 |              +--rw algorithm?    string
+                 |              +--rw (key-type)?
+                 |                 +--:(psk)
+                 |                    +--rw preshared-key?   string
+                 +--rw service
+                 |  +--rw svc-input-bandwidth     uint64
+                 |  +--rw svc-output-bandwidth    uint64
+                 |  +--rw svc-mtu                 uint16
+                 |  +--rw qos {qos}?
+                 |  |  +--rw qos-classification-policy
+                 |  |  |  +--rw rule* [id]
+                 |  |  |     +--rw id                 string
+                 |  |  |     +--rw (match-type)?
+                 |  |  |     |  +--:(match-flow)
+                 |  |  |     |  |  +--rw match-flow
+                 |  |  |     |  |     +--rw dscp?                inet:dscp
+                 |  |  |     |  |     +--rw dot1p?               uint8
+                 |  |  |     |  |     +--rw ipv4-src-prefix?     inet:ipv4-prefix
+                 |  |  |     |  |     +--rw ipv6-src-prefix?     inet:ipv6-prefix
+                 |  |  |     |  |     +--rw ipv4-dst-prefix?     inet:ipv4-prefix
+                 |  |  |     |  |     +--rw ipv6-dst-prefix?     inet:ipv6-prefix
+                 |  |  |     |  |     +--rw l4-src-port?         inet:port-number
+                 |  |  |     |  |     +--rw target-sites*        svc-id {target-sites}?
+                 |  |  |     |  |     +--rw l4-src-port-range
+                 |  |  |     |  |     |  +--rw lower-port?   inet:port-number
+                 |  |  |     |  |     |  +--rw upper-port?   inet:port-number
+                 |  |  |     |  |     +--rw l4-dst-port?         inet:port-number
+                 |  |  |     |  |     +--rw l4-dst-port-range
+                 |  |  |     |  |     |  +--rw lower-port?   inet:port-number
+                 |  |  |     |  |     |  +--rw upper-port?   inet:port-number
+                 |  |  |     |  |     +--rw protocol-field?      union
+                 |  |  |     |  +--:(match-application)
+                 |  |  |     |     +--rw match-application?   identityref
+                 |  |  |     +--rw target-class-id?   string
+                 |  |  +--rw qos-profile
+                 |  |     +--rw (qos-profile)?
+                 |  |        +--:(standard)
+                 |  |        |  +--rw profile?   -> /l3vpn-svc/vpn-profiles/valid-provider-identifiers/qos-profile-identifier/id
+                 |  |        +--:(custom)
+                 |  |           +--rw classes {qos-custom}?
+                 |  |              +--rw class* [class-id]
+                 |  |                 +--rw class-id      string
+                 |  |                 +--rw direction?    identityref
+                 |  |                 +--rw rate-limit?   decimal64
+                 |  |                 +--rw latency
+                 |  |                 |  +--rw (flavor)?
+                 |  |                 |     +--:(lowest)
+                 |  |                 |     |  +--rw use-lowest-latency?   empty
+                 |  |                 |     +--:(boundary)
+                 |  |                 |        +--rw latency-boundary?   uint16
+                 |  |                 +--rw jitter
+                 |  |                 |  +--rw (flavor)?
+                 |  |                 |     +--:(lowest)
+                 |  |                 |     |  +--rw use-lowest-jitter?   empty
+                 |  |                 |     +--:(boundary)
+                 |  |                 |        +--rw latency-boundary?   uint32
+                 |  |                 +--rw bandwidth
+                 |  |                    +--rw guaranteed-bw-percent    decimal64
+                 |  |                    +--rw end-to-end?              empty
+                 |  +--rw carrierscarrier {carrierscarrier}?
+                 |  |  +--rw signalling-type?   enumeration
+                 |  +--rw multicast {multicast}?
+                 |     +--rw multicast-site-type?        enumeration
+                 |     +--rw multicast-address-family
+                 |     |  +--rw ipv4?   boolean {ipv4}?
+                 |     |  +--rw ipv6?   boolean {ipv6}?
+                 |     +--rw protocol-type?              enumeration
+                 +--rw routing-protocols
+                 |  +--rw routing-protocol* [type]
+                 |     +--rw type      identityref
+                 |     +--rw ospf {rtg-ospf}?
+                 |     |  +--rw address-family*   address-family
+                 |     |  +--rw area-address      yang:dotted-quad
+                 |     |  +--rw metric?           uint16
+                 |     |  +--rw sham-links {rtg-ospf-sham-link}?
+                 |     |     +--rw sham-link* [target-site]
+                 |     |        +--rw target-site    svc-id
+                 |     |        +--rw metric?        uint16
+                 |     +--rw bgp {rtg-bgp}?
+                 |     |  +--rw autonomous-system    uint32
+                 |     |  +--rw address-family*      address-family
+                 |     +--rw static
+                 |     |  +--rw cascaded-lan-prefixes
+                 |     |     +--rw ipv4-lan-prefixes* [lan next-hop] {ipv4}?
+                 |     |     |  +--rw lan         inet:ipv4-prefix
+                 |     |     |  +--rw next-hop    inet:ipv4-address
+                 |     |     |  +--rw lan-tag?    string
+                 |     |     +--rw ipv6-lan-prefixes* [lan next-hop] {ipv6}?
+                 |     |        +--rw lan         inet:ipv6-prefix
+                 |     |        +--rw next-hop    inet:ipv6-address
+                 |     |        +--rw lan-tag?    string
+                 |     +--rw rip {rtg-rip}?
+                 |     |  +--rw address-family*   address-family
+                 |     +--rw vrrp {rtg-vrrp}?
+                 |        +--rw address-family*   address-family
+                 +--rw availability
+                 |  +--rw access-priority?   uint32
+                 +--rw vpn-attachment
+                    +--rw (attachment-flavor)
+                       +--:(vpn-policy-id)
+                       |  +--rw vpn-policy-id?   -> ../../../../vpn-policies/vpn-policy/vpn-policy-id
+                       +--:(vpn-id)
+                          +--rw vpn-id?      -> /l3vpn-svc/vpn-services/vpn-service/vpn-id
+                          +--rw site-role?   identityref
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_network/ComposeNetwork.py b/src/nbi/service/rest_server/nbi_plugins/ietf_network/ComposeNetwork.py
index 6ffc85e387ce4f4691cdc9757d6bd60068bef991..2d3ef29fc1773b8e0c2f762e0978742797e8cce5 100644
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_network/ComposeNetwork.py
+++ b/src/nbi/service/rest_server/nbi_plugins/ietf_network/ComposeNetwork.py
@@ -28,9 +28,11 @@ IGNORE_DEVICE_TYPES = {
     DeviceTypeEnum.DATACENTER.value,
     DeviceTypeEnum.EMULATED_CLIENT.value,
     DeviceTypeEnum.EMULATED_DATACENTER.value,
+    DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER,
     DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM.value,
     DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value,
     DeviceTypeEnum.EMULATED_XR_CONSTELLATION.value,
+    DeviceTypeEnum.IP_SDN_CONTROLLER,
     DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM.value,
     DeviceTypeEnum.NETWORK.value,
     DeviceTypeEnum.OPEN_LINE_SYSTEM.value,
@@ -39,10 +41,10 @@ IGNORE_DEVICE_TYPES = {
 
 IGNORE_DEVICE_NAMES = {
     NetworkTypeEnum.TE_OTN_TOPOLOGY: {
-        '128.32.10.1', '128.32.33.5', '128.32.20.5', '128.32.20.1', '128.32.10.5', 'nce-t'
+        'nce-t', '128.32.10.1', '128.32.33.5', '128.32.20.5', '128.32.20.1', '128.32.10.5',
     },
     NetworkTypeEnum.TE_ETH_TRAN_TOPOLOGY: {
-
+        'nce-t',
     },
 }
 
diff --git a/src/nbi/tests/data/ietf_l3vpn_req_svc1.json b/src/nbi/tests/data/ietf_l3vpn_req_svc1.json
index 66e253cb5b99d3b758bba04e1dfa8799e1b13c08..bfeb93fb74c9513ef4d175d5962110127303a2a7 100644
--- a/src/nbi/tests/data/ietf_l3vpn_req_svc1.json
+++ b/src/nbi/tests/data/ietf_l3vpn_req_svc1.json
@@ -39,12 +39,12 @@
                       {
                         "lan": "128.32.10.1/24",
                         "lan-tag": "vlan21",
-                        "next-hop": "128.32.33.5"
+                        "next-hop": "128.32.33.2"
                       },
                       {
                         "lan": "128.32.20.1/24",
                         "lan-tag": "vlan21",
-                        "next-hop": "128.32.33.5"
+                        "next-hop": "128.32.33.2"
                       }
                     ]
                   }
@@ -82,7 +82,7 @@
                             {
                               "lan": "172.1.101.1/24",
                               "lan-tag": "vlan21",
-                              "next-hop": "10.0.10.1"
+                              "next-hop": "128.32.33.254"
                             }
                           ]
                         }
@@ -147,7 +147,7 @@
                       {
                         "lan": "172.1.101.1/24",
                         "lan-tag": "vlan101",
-                        "next-hop": "172.10.33.5"
+                        "next-hop": "172.10.33.2"
                       }
                     ]
                   }
@@ -185,12 +185,12 @@
                             {
                               "lan": "128.32.10.1/24",
                               "lan-tag": "vlan101",
-                              "next-hop": "10.0.30.1"
+                              "next-hop": "172.10.33.254"
                             },
                             {
                               "lan": "128.32.20.1/24",
                               "lan-tag": "vlan101",
-                              "next-hop": "10.0.30.1"
+                              "next-hop": "172.10.33.254"
                             }
                           ]
                         }
diff --git a/src/nbi/tests/data/ietf_l3vpn_req_svc2.json b/src/nbi/tests/data/ietf_l3vpn_req_svc2.json
index 2d2ea2c22e2bb490027b8033bb4fb94a39b35049..2cc512e595c820a8df42ec06af973fefa4601095 100644
--- a/src/nbi/tests/data/ietf_l3vpn_req_svc2.json
+++ b/src/nbi/tests/data/ietf_l3vpn_req_svc2.json
@@ -39,12 +39,12 @@
                       {
                         "lan": "128.32.10.1/24",
                         "lan-tag": "vlan31",
-                        "next-hop": "128.32.33.5"
+                        "next-hop": "128.32.33.2"
                       },
                       {
                         "lan": "128.32.20.1/24",
                         "lan-tag": "vlan31",
-                        "next-hop": "128.32.33.5"
+                        "next-hop": "128.32.33.2"
                       }
                     ]
                   }
@@ -80,9 +80,9 @@
                         "cascaded-lan-prefixes": {
                           "ipv4-lan-prefixes": [
                             {
-                              "lan": "172.1.101.1/24",
+                              "lan": "172.1.201.1/24",
                               "lan-tag": "vlan31",
-                              "next-hop": "10.0.10.1"
+                              "next-hop": "128.32.33.254"
                             }
                           ]
                         }
@@ -145,9 +145,9 @@
                   "cascaded-lan-prefixes": {
                     "ipv4-lan-prefixes": [
                       {
-                        "lan": "172.1.101.1/24",
+                        "lan": "172.1.201.1/24",
                         "lan-tag": "vlan201",
-                        "next-hop": "172.10.33.1"
+                        "next-hop": "172.10.33.2"
                       }
                     ]
                   }
@@ -185,12 +185,12 @@
                             {
                               "lan": "128.32.10.1/24",
                               "lan-tag": "vlan201",
-                              "next-hop": "10.0.30.1"
+                              "next-hop": "172.10.33.254"
                             },
                             {
                               "lan": "128.32.20.1/24",
                               "lan-tag": "vlan201",
-                              "next-hop": "10.0.30.1"
+                              "next-hop": "172.10.33.254"
                             }
                           ]
                         }
diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py
index 0a1b62040a81ee964d373132763e964381cbc19e..ca978310842d538efe2f83ed743446067f84c3eb 100644
--- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py
+++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py
@@ -15,12 +15,16 @@
 import json, logging, requests, uuid
 from typing import Dict, List, Optional, Tuple, Union
 from common.proto.context_pb2 import (
-    Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum)
+    ConfigRule, Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum
+)
 from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest
+from common.tools.grpc.Tools import grpc_message_list_to_json
 from pathcomp.frontend.Config import BACKEND_URL
 from .tools.EroPathToHops import eropath_to_hops
 from .tools.ComposeConfigRules import (
-    compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules)
+    compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules,
+    generate_neighbor_endpoint_config_rules
+)
 from .tools.ComposeRequest import compose_device, compose_link, compose_service
 from .tools.ComputeSubServices import (
     convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection)
@@ -227,12 +231,25 @@ class _Algorithm:
                 continue
 
             orig_config_rules = grpc_orig_service.service_config.config_rules
+            json_orig_config_rules = grpc_message_list_to_json(orig_config_rules)
 
             for service_path_ero in response['path']:
                 self.logger.debug('service_path_ero["devices"] = {:s}'.format(str(service_path_ero['devices'])))
                 _endpoint_to_link_dict = {k:v[0] for k,v in self.endpoint_to_link_dict.items()}
                 self.logger.debug('self.endpoint_to_link_dict = {:s}'.format(str(_endpoint_to_link_dict)))
                 path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict)
+
+                json_generated_config_rules = generate_neighbor_endpoint_config_rules(
+                    json_orig_config_rules, path_hops, self.device_name_mapping, self.endpoint_name_mapping
+                )
+                json_extended_config_rules = list()
+                json_extended_config_rules.extend(json_orig_config_rules)
+                json_extended_config_rules.extend(json_generated_config_rules)
+                extended_config_rules = [
+                    ConfigRule(**json_extended_config_rule)
+                    for json_extended_config_rule in json_extended_config_rules
+                ]
+
                 self.logger.debug('path_hops = {:s}'.format(str(path_hops)))
                 try:
                     _device_dict = {k:v[0] for k,v in self.device_dict.items()}
@@ -256,7 +273,7 @@ class _Algorithm:
                     if service_key in grpc_services: continue
                     grpc_service = self.add_service_to_reply(
                         reply, context_uuid, service_uuid, service_type, path_hops=path_hops,
-                        config_rules=orig_config_rules)
+                        config_rules=extended_config_rules)
                     grpc_services[service_key] = grpc_service
 
                 for connection in connections:
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
index 329552a914e478f0e927bd6f04fce6725bef0b5e..2d4ff4fd59187e3581c8426435f80bca958ad655 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
@@ -12,8 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import itertools, json, logging, re
-from typing import Dict, List, Optional, Tuple
+import copy, itertools, json, logging, re
+from typing import Dict, Iterable, List, Optional, Set, Tuple
 from common.proto.context_pb2 import ConfigRule
 from common.tools.grpc.Tools import grpc_message_to_json_string
 from common.tools.object_factory.ConfigRule import json_config_rule_set
@@ -21,19 +21,26 @@ from common.tools.object_factory.ConfigRule import json_config_rule_set
 LOGGER = logging.getLogger(__name__)
 
 SETTINGS_RULE_NAME = '/settings'
+STATIC_ROUTING_RULE_NAME = '/static_routing'
 
-DEVICE_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/settings')
-ENDPOINT_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings')
+RE_UUID = re.compile(r'([0-9a-fA-F]{8})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{4})\-([0-9a-fA-F]{12})')
+
+RE_DEVICE_SETTINGS        = re.compile(r'\/device\[([^\]]+)\]\/settings')
+RE_ENDPOINT_SETTINGS      = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings')
+RE_ENDPOINT_VLAN_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/vlan\[([^\]]+)\]\/settings')
+
+TMPL_ENDPOINT_SETTINGS      = '/device[{:s}]/endpoint[{:s}]/settings'
+TMPL_ENDPOINT_VLAN_SETTINGS = '/device[{:s}]/endpoint[{:s}]/vlan[{:s}]/settings'
 
 L2NM_SETTINGS_FIELD_DEFAULTS = {
-    'encapsulation_type': 'dot1q',
-    'vlan_id'           : 100,
+    #'encapsulation_type': 'dot1q',
+    #'vlan_id'           : 100,
     'mtu'               : 1450,
 }
 
 L3NM_SETTINGS_FIELD_DEFAULTS = {
-    'encapsulation_type': 'dot1q',
-    'vlan_id'           : 100,
+    #'encapsulation_type': 'dot1q',
+    #'vlan_id'           : 100,
     'mtu'               : 1450,
 }
 
@@ -54,26 +61,48 @@ def find_custom_config_rule(config_rules : List, resource_name : str) -> Optiona
     return resource_value
 
 def compose_config_rules(
-    main_service_config_rules : List, subservice_config_rules : List, field_defaults : Dict
+    main_service_config_rules : List, subservice_config_rules : List, settings_rule_name : str, field_defaults : Dict
 ) -> None:
-    settings = find_custom_config_rule(main_service_config_rules, SETTINGS_RULE_NAME)
+    settings = find_custom_config_rule(main_service_config_rules, settings_rule_name)
     if settings is None: return
 
     json_settings = {}
-    for field_name,default_value in field_defaults.items():
-        json_settings[field_name] = settings.get(field_name, default_value)
 
-    config_rule = ConfigRule(**json_config_rule_set('/settings', json_settings))
+    if len(field_defaults) == 0:
+        for field_name,field_value in settings.items():
+            json_settings[field_name] = field_value
+    else:
+        for field_name,default_value in field_defaults.items():
+            field_value = settings.get(field_name, default_value)
+            if field_value is None: continue
+            json_settings[field_name] = field_value
+
+    if len(json_settings) == 0: return
+
+    config_rule = ConfigRule(**json_config_rule_set(settings_rule_name, json_settings))
     subservice_config_rules.append(config_rule)
 
 def compose_l2nm_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None:
-    compose_config_rules(main_service_config_rules, subservice_config_rules, L2NM_SETTINGS_FIELD_DEFAULTS)
+    CONFIG_RULES = [
+        (SETTINGS_RULE_NAME, L2NM_SETTINGS_FIELD_DEFAULTS),
+    ]
+    for rule_name, defaults in CONFIG_RULES:
+        compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults)
 
 def compose_l3nm_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None:
-    compose_config_rules(main_service_config_rules, subservice_config_rules, L3NM_SETTINGS_FIELD_DEFAULTS)
+    CONFIG_RULES = [
+        (SETTINGS_RULE_NAME, L3NM_SETTINGS_FIELD_DEFAULTS),
+        (STATIC_ROUTING_RULE_NAME, {}),
+    ]
+    for rule_name, defaults in CONFIG_RULES:
+        compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults)
 
 def compose_tapi_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None:
-    compose_config_rules(main_service_config_rules, subservice_config_rules, TAPI_SETTINGS_FIELD_DEFAULTS)
+    CONFIG_RULES = [
+        (SETTINGS_RULE_NAME, TAPI_SETTINGS_FIELD_DEFAULTS),
+    ]
+    for rule_name, defaults in CONFIG_RULES:
+        compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults)
 
 def compose_device_config_rules(
     config_rules : List, subservice_config_rules : List, path_hops : List,
@@ -127,25 +156,31 @@ def compose_device_config_rules(
         elif config_rule.WhichOneof('config_rule') == 'custom':
             LOGGER.debug('[compose_device_config_rules]   is custom')
 
-            match = DEVICE_SETTINGS.match(config_rule.custom.resource_key)
+            match = RE_DEVICE_SETTINGS.match(config_rule.custom.resource_key)
             if match is not None:
                 device_uuid_or_name = match.group(1)
-                device_name_or_uuid = device_name_mapping[device_uuid_or_name]
-                device_keys = {device_uuid_or_name, device_name_or_uuid}
+                device_keys = {device_uuid_or_name}
+                device_name_or_uuid = device_name_mapping.get(device_uuid_or_name)
+                if device_name_or_uuid is not None: device_keys.add(device_name_or_uuid)
 
                 if len(device_keys.intersection(devices_traversed)) == 0: continue
                 subservice_config_rules.append(config_rule)
 
-            match = ENDPOINT_SETTINGS.match(config_rule.custom.resource_key)
+            match = RE_ENDPOINT_SETTINGS.match(config_rule.custom.resource_key)
+            if match is None:
+                match = RE_ENDPOINT_VLAN_SETTINGS.match(config_rule.custom.resource_key)
             if match is not None:
                 device_uuid_or_name = match.group(1)
-                device_name_or_uuid = device_name_mapping[device_uuid_or_name]
-                device_keys = {device_uuid_or_name, device_name_or_uuid}
+                device_keys = {device_uuid_or_name}
+                device_name_or_uuid = device_name_mapping.get(device_uuid_or_name)
+                if device_name_or_uuid is not None: device_keys.add(device_name_or_uuid)
 
                 endpoint_uuid_or_name = match.group(2)
-                endpoint_name_or_uuid_1 = endpoint_name_mapping[(device_uuid_or_name, endpoint_uuid_or_name)]
-                endpoint_name_or_uuid_2 = endpoint_name_mapping[(device_name_or_uuid, endpoint_uuid_or_name)]
-                endpoint_keys = {endpoint_uuid_or_name, endpoint_name_or_uuid_1, endpoint_name_or_uuid_2}
+                endpoint_keys = {endpoint_uuid_or_name}
+                endpoint_name_or_uuid_1 = endpoint_name_mapping.get((device_uuid_or_name, endpoint_uuid_or_name))
+                if endpoint_name_or_uuid_1 is not None: endpoint_keys.add(endpoint_name_or_uuid_1)
+                endpoint_name_or_uuid_2 = endpoint_name_mapping.get((device_name_or_uuid, endpoint_uuid_or_name))
+                if endpoint_name_or_uuid_2 is not None: endpoint_keys.add(endpoint_name_or_uuid_2)
 
                 device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys))
                 if len(device_endpoint_keys.intersection(endpoints_traversed)) == 0: continue
@@ -153,4 +188,146 @@ def compose_device_config_rules(
         else:
             continue
 
+    for config_rule in subservice_config_rules:
+        LOGGER.debug('[compose_device_config_rules] result config_rule: {:s}'.format(
+            grpc_message_to_json_string(config_rule)))
+
     LOGGER.debug('[compose_device_config_rules] end')
+
+def pairwise(iterable : Iterable) -> Tuple[Iterable, Iterable]:
+    # TODO: To be replaced by itertools.pairwise() when we move to Python 3.10
+    # Python 3.10 introduced method itertools.pairwise()
+    # Standalone method extracted from:
+    # - https://docs.python.org/3/library/itertools.html#itertools.pairwise
+    a, b = itertools.tee(iterable, 2)
+    next(b, None)
+    return zip(a, b)
+
+def compute_device_keys(
+    device_uuid_or_name : str, device_name_mapping : Dict[str, str]
+) -> Set[str]:
+    LOGGER.debug('[compute_device_keys] begin')
+    LOGGER.debug('[compute_device_keys] device_uuid_or_name={:s}'.format(str(device_uuid_or_name)))
+    #LOGGER.debug('[compute_device_keys] device_name_mapping={:s}'.format(str(device_name_mapping)))
+
+    device_keys = {device_uuid_or_name}
+    for k,v in device_name_mapping.items():
+        if device_uuid_or_name not in {k, v}: continue
+        device_keys.add(k)
+        device_keys.add(v)
+
+    LOGGER.debug('[compute_device_keys] device_keys={:s}'.format(str(device_keys)))
+    LOGGER.debug('[compute_device_keys] end')
+    return device_keys
+
+def compute_endpoint_keys(
+    device_keys : Set[str], endpoint_uuid_or_name : str, endpoint_name_mapping : Dict[str, str]
+) -> Set[str]:
+    LOGGER.debug('[compute_endpoint_keys] begin')
+    LOGGER.debug('[compute_endpoint_keys] device_keys={:s}'.format(str(device_keys)))
+    LOGGER.debug('[compute_endpoint_keys] endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name)))
+    #LOGGER.debug('[compute_device_endpoint_keys] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping)))
+
+    endpoint_keys = {endpoint_uuid_or_name}
+    for k,v in endpoint_name_mapping.items():
+        if (k[0] in device_keys or v in device_keys) and (endpoint_uuid_or_name in {k[1], v}):
+            endpoint_keys.add(k[1])
+            endpoint_keys.add(v)
+
+    LOGGER.debug('[compute_endpoint_keys] endpoint_keys={:s}'.format(str(endpoint_keys)))
+    LOGGER.debug('[compute_endpoint_keys] end')
+    return endpoint_keys
+
+def compute_device_endpoint_keys(
+    device_uuid_or_name : str, endpoint_uuid_or_name : str,
+    device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str]
+) -> Set[Tuple[str, str]]:
+    LOGGER.debug('[compute_device_endpoint_keys] begin')
+    LOGGER.debug('[compute_device_endpoint_keys] device_uuid_or_name={:s}'.format(str(device_uuid_or_name)))
+    LOGGER.debug('[compute_device_endpoint_keys] endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name)))
+    #LOGGER.debug('[compute_device_endpoint_keys] device_name_mapping={:s}'.format(str(device_name_mapping)))
+    #LOGGER.debug('[compute_device_endpoint_keys] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping)))
+
+    device_keys = compute_device_keys(device_uuid_or_name, device_name_mapping)
+    endpoint_keys = compute_endpoint_keys(device_keys, endpoint_uuid_or_name, endpoint_name_mapping)
+    device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys))
+
+    LOGGER.debug('[compute_device_endpoint_keys] device_endpoint_keys={:s}'.format(str(device_endpoint_keys)))
+    LOGGER.debug('[compute_device_endpoint_keys] end')
+    return device_endpoint_keys
+
+def generate_neighbor_endpoint_config_rules(
+    config_rules : List[Dict], path_hops : List[Dict],
+    device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str]
+) -> List[Dict]:
+    LOGGER.debug('[generate_neighbor_endpoint_config_rules] begin')
+    LOGGER.debug('[generate_neighbor_endpoint_config_rules] config_rules={:s}'.format(str(config_rules)))
+    LOGGER.debug('[generate_neighbor_endpoint_config_rules] path_hops={:s}'.format(str(path_hops)))
+    LOGGER.debug('[generate_neighbor_endpoint_config_rules] device_name_mapping={:s}'.format(str(device_name_mapping)))
+    LOGGER.debug('[generate_neighbor_endpoint_config_rules] endpoint_name_mapping={:s}'.format(str(endpoint_name_mapping)))
+
+    generated_config_rules = list()
+    for link_endpoint_a, link_endpoint_b in pairwise(path_hops):
+        LOGGER.debug('[generate_neighbor_endpoint_config_rules] loop begin')
+        LOGGER.debug('[generate_neighbor_endpoint_config_rules] link_endpoint_a={:s}'.format(str(link_endpoint_a)))
+        LOGGER.debug('[generate_neighbor_endpoint_config_rules] link_endpoint_b={:s}'.format(str(link_endpoint_b)))
+
+        device_endpoint_keys_a = compute_device_endpoint_keys(
+            link_endpoint_a['device'], link_endpoint_a['egress_ep'],
+            device_name_mapping, endpoint_name_mapping
+        )
+
+        device_endpoint_keys_b = compute_device_endpoint_keys(
+            link_endpoint_b['device'], link_endpoint_b['ingress_ep'],
+            device_name_mapping, endpoint_name_mapping
+        )
+
+        for config_rule in config_rules:
+            # Only applicable, by now, to Custom Config Rules for endpoint settings
+            if 'custom' not in config_rule: continue
+            match = RE_ENDPOINT_SETTINGS.match(config_rule['custom']['resource_key'])
+            if match is None:
+                match = RE_ENDPOINT_VLAN_SETTINGS.match(config_rule['custom']['resource_key'])
+            if match is None: continue
+
+            resource_key_values = match.groups()
+            if resource_key_values[0:2] in device_endpoint_keys_a:
+                resource_key_values = list(resource_key_values)
+                resource_key_values[0] = link_endpoint_b['device']
+                resource_key_values[1] = link_endpoint_b['ingress_ep']
+            elif resource_key_values[0:2] in device_endpoint_keys_b:
+                resource_key_values = list(resource_key_values)
+                resource_key_values[0] = link_endpoint_a['device']
+                resource_key_values[1] = link_endpoint_a['egress_ep']
+            else:
+                continue
+
+            device_keys = compute_device_keys(resource_key_values[0], device_name_mapping)
+            device_names = {device_key for device_key in device_keys if RE_UUID.match(device_key) is None}
+            if len(device_names) != 1:
+                MSG = 'Unable to identify name for Device({:s}): device_keys({:s})'
+                raise Exception(MSG.format(str(resource_key_values[0]), str(device_keys)))
+            resource_key_values[0] = device_names.pop()
+
+            endpoint_keys = compute_endpoint_keys(device_keys, resource_key_values[1], endpoint_name_mapping)
+            endpoint_names = {endpoint_key for endpoint_key in endpoint_keys if RE_UUID.match(endpoint_key) is None}
+            if len(endpoint_names) != 1:
+                MSG = 'Unable to identify name for Endpoint({:s}): endpoint_keys({:s})'
+                raise Exception(MSG.format(str(resource_key_values[1]), str(endpoint_keys)))
+            resource_key_values[1] = endpoint_names.pop()
+
+            resource_value : Dict = json.loads(config_rule['custom']['resource_value'])
+            if 'neighbor_address' not in resource_value: continue
+            resource_value['ip_address'] = resource_value.pop('neighbor_address')
+
+            # remove neighbor_address also from original rule as it is already consumed
+
+            resource_key_template = TMPL_ENDPOINT_VLAN_SETTINGS if len(match.groups()) == 3 else TMPL_ENDPOINT_SETTINGS
+            generated_config_rule = copy.deepcopy(config_rule)
+            generated_config_rule['custom']['resource_key'] = resource_key_template.format(*resource_key_values)
+            generated_config_rule['custom']['resource_value'] = json.dumps(resource_value)
+            generated_config_rules.append(generated_config_rule)
+
+    LOGGER.debug('[generate_neighbor_endpoint_config_rules] generated_config_rules={:s}'.format(str(generated_config_rules)))
+    LOGGER.debug('[generate_neighbor_endpoint_config_rules] end')
+    return generated_config_rules
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py
index 06b24031bd82b6b51add6569b9c35b360f0491fe..86a91d00aca2b2b5465bab654d7ab3de0fbe148b 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py
@@ -95,6 +95,36 @@ def convert_explicit_path_hops_to_connections(
             connections.append(connection)
             connection_stack.queue[-1][3].append(connection[0])
             #connection_stack.queue[-1][2].append(path_hop)
+        elif prv_res_class[2] is None and res_class[2] is not None:
+            # entering domain of a device controller, create underlying connection
+            LOGGER.debug('  entering domain of a device controller, create underlying connection')
+            sub_service_uuid = str(uuid.uuid4())
+            prv_service_type = connection_stack.queue[-1][1]
+            service_type = get_service_type(res_class[1], prv_service_type)
+            connection_stack.put((sub_service_uuid, service_type, [path_hop], []))
+        elif prv_res_class[2] is not None and res_class[2] is None:
+            # leaving domain of a device controller, terminate underlying connection
+            LOGGER.debug('  leaving domain of a device controller, terminate underlying connection')
+            connection = connection_stack.get()
+            connections.append(connection)
+            connection_stack.queue[-1][3].append(connection[0])
+            connection_stack.queue[-1][2].append(path_hop)
+        elif prv_res_class[2] is not None and res_class[2] is not None:
+            if prv_res_class[2] == res_class[2]:
+                # stay in domain of a device controller, connection continues
+                LOGGER.debug('  stay in domain of a device controller, connection continues')
+                connection_stack.queue[-1][2].append(path_hop)
+            else:
+                # switching to different device controller, chain connections
+                LOGGER.debug('  switching to different device controller, chain connections')
+                connection = connection_stack.get()
+                connections.append(connection)
+                connection_stack.queue[-1][3].append(connection[0])
+
+                sub_service_uuid = str(uuid.uuid4())
+                prv_service_type = connection_stack.queue[-1][1]
+                service_type = get_service_type(res_class[1], prv_service_type)
+                connection_stack.put((sub_service_uuid, service_type, [path_hop], []))
         elif prv_res_class[0] is None:
             # path ingress
             LOGGER.debug('  path ingress')
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py
index 843c41803805106e9f7575fb9ff6b1344d036994..7b5221c88fd1baa13ef44dc0a0c06e76cf8dc813 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ResourceGroups.py
@@ -24,6 +24,9 @@ DEVICE_TYPE_TO_DEEPNESS = {
     DeviceTypeEnum.DATACENTER.value                      : 90,
 
     DeviceTypeEnum.TERAFLOWSDN_CONTROLLER.value          : 80,
+    DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER.value      : 80,
+    DeviceTypeEnum.IP_SDN_CONTROLLER.value               : 80,
+
     DeviceTypeEnum.EMULATED_PACKET_ROUTER.value          : 70,
     DeviceTypeEnum.PACKET_ROUTER.value                   : 70,
 
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
index 73a741ae551c5179cb78268f6fb87040c8481c53..094baa1a674fab1a573a97c364a12386ca940cc9 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
@@ -22,6 +22,7 @@ NETWORK_DEVICE_TYPES = {
 
 PACKET_DEVICE_TYPES = {
     DeviceTypeEnum.TERAFLOWSDN_CONTROLLER,
+    DeviceTypeEnum.IP_SDN_CONTROLLER, DeviceTypeEnum.EMULATED_IP_SDN_CONTROLLER,
     DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER,
     DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH,
 }
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 b7de8ff99fe820ad80a555c97a42accfe21b1d7b..f777c77209b0d023da5a30270ab472de6117bb59 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
@@ -2284,6 +2284,12 @@ public class Serializer {
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_XR;
             case IETF_L2VPN:
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN;
+            case GNMI_OPENCONFIG:
+                return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG;
+            case FLEXSCALE:
+                return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE;
+            case IETF_ACTN:
+                return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN;
             case UNDEFINED:
             default:
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED;
@@ -2307,6 +2313,12 @@ public class Serializer {
                 return DeviceDriverEnum.XR;
             case DEVICEDRIVER_IETF_L2VPN:
                 return DeviceDriverEnum.IETF_L2VPN;
+            case DEVICEDRIVER_GNMI_OPENCONFIG:
+                return DeviceDriverEnum.GNMI_OPENCONFIG;
+            case DEVICEDRIVER_FLEXSCALE:
+                return DeviceDriverEnum.FLEXSCALE;
+            case DEVICEDRIVER_IETF_ACTN:
+                return DeviceDriverEnum.IETF_ACTN;
             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 63e96a4c61769fbb6a009acf208ef7b4c81200ad..72a1d7136c00d2bac93087d7f8b8f3b7626f9803 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
@@ -24,5 +24,8 @@ public enum DeviceDriverEnum {
     IETF_NETWORK_TOPOLOGY,
     ONF_TR_532,
     XR,
-    IETF_L2VPN
+    IETF_L2VPN,
+    GNMI_OPENCONFIG,
+    FLEXSCALE,
+    IETF_ACTN
 }
diff --git a/src/policy/src/test/java/org/etsi/tfs/policy/SerializerTest.java b/src/policy/src/test/java/org/etsi/tfs/policy/SerializerTest.java
index f29ae3697a8842c14dc28716e325adc85a5c45af..63fb1ad7a72d74cf52148027f4fc0e0546b0b58e 100644
--- a/src/policy/src/test/java/org/etsi/tfs/policy/SerializerTest.java
+++ b/src/policy/src/test/java/org/etsi/tfs/policy/SerializerTest.java
@@ -3614,6 +3614,13 @@ class SerializerTest {
                 Arguments.of(
                         DeviceDriverEnum.IETF_L2VPN,
                         ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN),
+                Arguments.of(
+                        DeviceDriverEnum.GNMI_OPENCONFIG,
+                        ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG),
+                Arguments.of(
+                        DeviceDriverEnum.FLEXSCALE, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE),
+                Arguments.of(
+                        DeviceDriverEnum.IETF_ACTN, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN),
                 Arguments.of(
                         DeviceDriverEnum.UNDEFINED, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED));
     }
diff --git a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
index 0672727a3716e8dc58d4c450dbbee41383ff99f0..d4873899b0113a7356c1c4d6bc2ea9aae2e8b4e5 100644
--- a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
+++ b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
@@ -189,6 +189,10 @@ public final class ContextOuterClass {
      * <code>DEVICEDRIVER_FLEXSCALE = 9;</code>
      */
     DEVICEDRIVER_FLEXSCALE(9),
+    /**
+     * <code>DEVICEDRIVER_IETF_ACTN = 10;</code>
+     */
+    DEVICEDRIVER_IETF_ACTN(10),
     UNRECOGNIZED(-1),
     ;
 
@@ -236,6 +240,10 @@ public final class ContextOuterClass {
      * <code>DEVICEDRIVER_FLEXSCALE = 9;</code>
      */
     public static final int DEVICEDRIVER_FLEXSCALE_VALUE = 9;
+    /**
+     * <code>DEVICEDRIVER_IETF_ACTN = 10;</code>
+     */
+    public static final int DEVICEDRIVER_IETF_ACTN_VALUE = 10;
 
 
     public final int getNumber() {
@@ -272,6 +280,7 @@ public final class ContextOuterClass {
         case 7: return DEVICEDRIVER_IETF_L2VPN;
         case 8: return DEVICEDRIVER_GNMI_OPENCONFIG;
         case 9: return DEVICEDRIVER_FLEXSCALE;
+        case 10: return DEVICEDRIVER_IETF_ACTN;
         default: return null;
       }
     }
@@ -75844,7 +75853,7 @@ public final class ContextOuterClass {
       "\0132\022.context.ContextId\022\025\n\rauthenticated\030\002" +
       " \001(\010*j\n\rEventTypeEnum\022\027\n\023EVENTTYPE_UNDEF" +
       "INED\020\000\022\024\n\020EVENTTYPE_CREATE\020\001\022\024\n\020EVENTTYP" +
-      "E_UPDATE\020\002\022\024\n\020EVENTTYPE_REMOVE\020\003*\265\002\n\020Dev" +
+      "E_UPDATE\020\002\022\024\n\020EVENTTYPE_REMOVE\020\003*\321\002\n\020Dev" +
       "iceDriverEnum\022\032\n\026DEVICEDRIVER_UNDEFINED\020" +
       "\000\022\033\n\027DEVICEDRIVER_OPENCONFIG\020\001\022\036\n\032DEVICE" +
       "DRIVER_TRANSPORT_API\020\002\022\023\n\017DEVICEDRIVER_P" +
@@ -75852,108 +75861,108 @@ public final class ContextOuterClass {
       "Y\020\004\022\033\n\027DEVICEDRIVER_ONF_TR_532\020\005\022\023\n\017DEVI" +
       "CEDRIVER_XR\020\006\022\033\n\027DEVICEDRIVER_IETF_L2VPN" +
       "\020\007\022 \n\034DEVICEDRIVER_GNMI_OPENCONFIG\020\010\022\032\n\026" +
-      "DEVICEDRIVER_FLEXSCALE\020\t*\217\001\n\033DeviceOpera" +
-      "tionalStatusEnum\022%\n!DEVICEOPERATIONALSTA" +
-      "TUS_UNDEFINED\020\000\022$\n DEVICEOPERATIONALSTAT" +
-      "US_DISABLED\020\001\022#\n\037DEVICEOPERATIONALSTATUS" +
-      "_ENABLED\020\002*\252\001\n\017ServiceTypeEnum\022\027\n\023SERVIC" +
-      "ETYPE_UNKNOWN\020\000\022\024\n\020SERVICETYPE_L3NM\020\001\022\024\n" +
-      "\020SERVICETYPE_L2NM\020\002\022)\n%SERVICETYPE_TAPI_" +
-      "CONNECTIVITY_SERVICE\020\003\022\022\n\016SERVICETYPE_TE" +
-      "\020\004\022\023\n\017SERVICETYPE_E2E\020\005*\304\001\n\021ServiceStatu" +
-      "sEnum\022\033\n\027SERVICESTATUS_UNDEFINED\020\000\022\031\n\025SE" +
-      "RVICESTATUS_PLANNED\020\001\022\030\n\024SERVICESTATUS_A" +
-      "CTIVE\020\002\022\032\n\026SERVICESTATUS_UPDATING\020\003\022!\n\035S" +
-      "ERVICESTATUS_PENDING_REMOVAL\020\004\022\036\n\032SERVIC" +
-      "ESTATUS_SLA_VIOLATED\020\005*\251\001\n\017SliceStatusEn" +
-      "um\022\031\n\025SLICESTATUS_UNDEFINED\020\000\022\027\n\023SLICEST" +
-      "ATUS_PLANNED\020\001\022\024\n\020SLICESTATUS_INIT\020\002\022\026\n\022" +
-      "SLICESTATUS_ACTIVE\020\003\022\026\n\022SLICESTATUS_DEIN" +
-      "IT\020\004\022\034\n\030SLICESTATUS_SLA_VIOLATED\020\005*]\n\020Co" +
-      "nfigActionEnum\022\032\n\026CONFIGACTION_UNDEFINED" +
-      "\020\000\022\024\n\020CONFIGACTION_SET\020\001\022\027\n\023CONFIGACTION" +
-      "_DELETE\020\002*m\n\024ConstraintActionEnum\022\036\n\032CON" +
-      "STRAINTACTION_UNDEFINED\020\000\022\030\n\024CONSTRAINTA" +
-      "CTION_SET\020\001\022\033\n\027CONSTRAINTACTION_DELETE\020\002" +
-      "*\203\002\n\022IsolationLevelEnum\022\020\n\014NO_ISOLATION\020" +
-      "\000\022\026\n\022PHYSICAL_ISOLATION\020\001\022\025\n\021LOGICAL_ISO" +
-      "LATION\020\002\022\025\n\021PROCESS_ISOLATION\020\003\022\035\n\031PHYSI" +
-      "CAL_MEMORY_ISOLATION\020\004\022\036\n\032PHYSICAL_NETWO" +
-      "RK_ISOLATION\020\005\022\036\n\032VIRTUAL_RESOURCE_ISOLA" +
-      "TION\020\006\022\037\n\033NETWORK_FUNCTIONS_ISOLATION\020\007\022" +
-      "\025\n\021SERVICE_ISOLATION\020\0102\245\026\n\016ContextServic" +
-      "e\022:\n\016ListContextIds\022\016.context.Empty\032\026.co" +
-      "ntext.ContextIdList\"\000\0226\n\014ListContexts\022\016." +
-      "context.Empty\032\024.context.ContextList\"\000\0224\n" +
-      "\nGetContext\022\022.context.ContextId\032\020.contex" +
-      "t.Context\"\000\0224\n\nSetContext\022\020.context.Cont" +
-      "ext\032\022.context.ContextId\"\000\0225\n\rRemoveConte" +
-      "xt\022\022.context.ContextId\032\016.context.Empty\"\000" +
-      "\022=\n\020GetContextEvents\022\016.context.Empty\032\025.c" +
-      "ontext.ContextEvent\"\0000\001\022@\n\017ListTopologyI" +
-      "ds\022\022.context.ContextId\032\027.context.Topolog" +
-      "yIdList\"\000\022=\n\016ListTopologies\022\022.context.Co" +
-      "ntextId\032\025.context.TopologyList\"\000\0227\n\013GetT" +
-      "opology\022\023.context.TopologyId\032\021.context.T" +
-      "opology\"\000\022E\n\022GetTopologyDetails\022\023.contex" +
-      "t.TopologyId\032\030.context.TopologyDetails\"\000" +
-      "\0227\n\013SetTopology\022\021.context.Topology\032\023.con" +
-      "text.TopologyId\"\000\0227\n\016RemoveTopology\022\023.co" +
-      "ntext.TopologyId\032\016.context.Empty\"\000\022?\n\021Ge" +
-      "tTopologyEvents\022\016.context.Empty\032\026.contex" +
-      "t.TopologyEvent\"\0000\001\0228\n\rListDeviceIds\022\016.c" +
-      "ontext.Empty\032\025.context.DeviceIdList\"\000\0224\n" +
-      "\013ListDevices\022\016.context.Empty\032\023.context.D" +
-      "eviceList\"\000\0221\n\tGetDevice\022\021.context.Devic" +
-      "eId\032\017.context.Device\"\000\0221\n\tSetDevice\022\017.co" +
-      "ntext.Device\032\021.context.DeviceId\"\000\0223\n\014Rem" +
-      "oveDevice\022\021.context.DeviceId\032\016.context.E" +
-      "mpty\"\000\022;\n\017GetDeviceEvents\022\016.context.Empt" +
-      "y\032\024.context.DeviceEvent\"\0000\001\022<\n\014SelectDev" +
-      "ice\022\025.context.DeviceFilter\032\023.context.Dev" +
-      "iceList\"\000\022I\n\021ListEndPointNames\022\027.context" +
-      ".EndPointIdList\032\031.context.EndPointNameLi" +
-      "st\"\000\0224\n\013ListLinkIds\022\016.context.Empty\032\023.co" +
-      "ntext.LinkIdList\"\000\0220\n\tListLinks\022\016.contex" +
-      "t.Empty\032\021.context.LinkList\"\000\022+\n\007GetLink\022" +
-      "\017.context.LinkId\032\r.context.Link\"\000\022+\n\007Set" +
-      "Link\022\r.context.Link\032\017.context.LinkId\"\000\022/" +
-      "\n\nRemoveLink\022\017.context.LinkId\032\016.context." +
-      "Empty\"\000\0227\n\rGetLinkEvents\022\016.context.Empty" +
-      "\032\022.context.LinkEvent\"\0000\001\022>\n\016ListServiceI" +
-      "ds\022\022.context.ContextId\032\026.context.Service" +
-      "IdList\"\000\022:\n\014ListServices\022\022.context.Conte" +
-      "xtId\032\024.context.ServiceList\"\000\0224\n\nGetServi" +
-      "ce\022\022.context.ServiceId\032\020.context.Service" +
-      "\"\000\0224\n\nSetService\022\020.context.Service\032\022.con" +
-      "text.ServiceId\"\000\0226\n\014UnsetService\022\020.conte" +
-      "xt.Service\032\022.context.ServiceId\"\000\0225\n\rRemo" +
-      "veService\022\022.context.ServiceId\032\016.context." +
-      "Empty\"\000\022=\n\020GetServiceEvents\022\016.context.Em" +
-      "pty\032\025.context.ServiceEvent\"\0000\001\022?\n\rSelect" +
-      "Service\022\026.context.ServiceFilter\032\024.contex" +
-      "t.ServiceList\"\000\022:\n\014ListSliceIds\022\022.contex" +
-      "t.ContextId\032\024.context.SliceIdList\"\000\0226\n\nL" +
-      "istSlices\022\022.context.ContextId\032\022.context." +
-      "SliceList\"\000\022.\n\010GetSlice\022\020.context.SliceI" +
-      "d\032\016.context.Slice\"\000\022.\n\010SetSlice\022\016.contex" +
-      "t.Slice\032\020.context.SliceId\"\000\0220\n\nUnsetSlic" +
-      "e\022\016.context.Slice\032\020.context.SliceId\"\000\0221\n" +
-      "\013RemoveSlice\022\020.context.SliceId\032\016.context" +
-      ".Empty\"\000\0229\n\016GetSliceEvents\022\016.context.Emp" +
-      "ty\032\023.context.SliceEvent\"\0000\001\0229\n\013SelectSli" +
-      "ce\022\024.context.SliceFilter\032\022.context.Slice" +
-      "List\"\000\022D\n\021ListConnectionIds\022\022.context.Se" +
-      "rviceId\032\031.context.ConnectionIdList\"\000\022@\n\017" +
-      "ListConnections\022\022.context.ServiceId\032\027.co" +
-      "ntext.ConnectionList\"\000\022=\n\rGetConnection\022" +
-      "\025.context.ConnectionId\032\023.context.Connect" +
-      "ion\"\000\022=\n\rSetConnection\022\023.context.Connect" +
-      "ion\032\025.context.ConnectionId\"\000\022;\n\020RemoveCo" +
-      "nnection\022\025.context.ConnectionId\032\016.contex" +
-      "t.Empty\"\000\022C\n\023GetConnectionEvents\022\016.conte" +
-      "xt.Empty\032\030.context.ConnectionEvent\"\0000\001b\006" +
-      "proto3"
+      "DEVICEDRIVER_FLEXSCALE\020\t\022\032\n\026DEVICEDRIVER" +
+      "_IETF_ACTN\020\n*\217\001\n\033DeviceOperationalStatus" +
+      "Enum\022%\n!DEVICEOPERATIONALSTATUS_UNDEFINE" +
+      "D\020\000\022$\n DEVICEOPERATIONALSTATUS_DISABLED\020" +
+      "\001\022#\n\037DEVICEOPERATIONALSTATUS_ENABLED\020\002*\252" +
+      "\001\n\017ServiceTypeEnum\022\027\n\023SERVICETYPE_UNKNOW" +
+      "N\020\000\022\024\n\020SERVICETYPE_L3NM\020\001\022\024\n\020SERVICETYPE" +
+      "_L2NM\020\002\022)\n%SERVICETYPE_TAPI_CONNECTIVITY" +
+      "_SERVICE\020\003\022\022\n\016SERVICETYPE_TE\020\004\022\023\n\017SERVIC" +
+      "ETYPE_E2E\020\005*\304\001\n\021ServiceStatusEnum\022\033\n\027SER" +
+      "VICESTATUS_UNDEFINED\020\000\022\031\n\025SERVICESTATUS_" +
+      "PLANNED\020\001\022\030\n\024SERVICESTATUS_ACTIVE\020\002\022\032\n\026S" +
+      "ERVICESTATUS_UPDATING\020\003\022!\n\035SERVICESTATUS" +
+      "_PENDING_REMOVAL\020\004\022\036\n\032SERVICESTATUS_SLA_" +
+      "VIOLATED\020\005*\251\001\n\017SliceStatusEnum\022\031\n\025SLICES" +
+      "TATUS_UNDEFINED\020\000\022\027\n\023SLICESTATUS_PLANNED" +
+      "\020\001\022\024\n\020SLICESTATUS_INIT\020\002\022\026\n\022SLICESTATUS_" +
+      "ACTIVE\020\003\022\026\n\022SLICESTATUS_DEINIT\020\004\022\034\n\030SLIC" +
+      "ESTATUS_SLA_VIOLATED\020\005*]\n\020ConfigActionEn" +
+      "um\022\032\n\026CONFIGACTION_UNDEFINED\020\000\022\024\n\020CONFIG" +
+      "ACTION_SET\020\001\022\027\n\023CONFIGACTION_DELETE\020\002*m\n" +
+      "\024ConstraintActionEnum\022\036\n\032CONSTRAINTACTIO" +
+      "N_UNDEFINED\020\000\022\030\n\024CONSTRAINTACTION_SET\020\001\022" +
+      "\033\n\027CONSTRAINTACTION_DELETE\020\002*\203\002\n\022Isolati" +
+      "onLevelEnum\022\020\n\014NO_ISOLATION\020\000\022\026\n\022PHYSICA" +
+      "L_ISOLATION\020\001\022\025\n\021LOGICAL_ISOLATION\020\002\022\025\n\021" +
+      "PROCESS_ISOLATION\020\003\022\035\n\031PHYSICAL_MEMORY_I" +
+      "SOLATION\020\004\022\036\n\032PHYSICAL_NETWORK_ISOLATION" +
+      "\020\005\022\036\n\032VIRTUAL_RESOURCE_ISOLATION\020\006\022\037\n\033NE" +
+      "TWORK_FUNCTIONS_ISOLATION\020\007\022\025\n\021SERVICE_I" +
+      "SOLATION\020\0102\245\026\n\016ContextService\022:\n\016ListCon" +
+      "textIds\022\016.context.Empty\032\026.context.Contex" +
+      "tIdList\"\000\0226\n\014ListContexts\022\016.context.Empt" +
+      "y\032\024.context.ContextList\"\000\0224\n\nGetContext\022" +
+      "\022.context.ContextId\032\020.context.Context\"\000\022" +
+      "4\n\nSetContext\022\020.context.Context\032\022.contex" +
+      "t.ContextId\"\000\0225\n\rRemoveContext\022\022.context" +
+      ".ContextId\032\016.context.Empty\"\000\022=\n\020GetConte" +
+      "xtEvents\022\016.context.Empty\032\025.context.Conte" +
+      "xtEvent\"\0000\001\022@\n\017ListTopologyIds\022\022.context" +
+      ".ContextId\032\027.context.TopologyIdList\"\000\022=\n" +
+      "\016ListTopologies\022\022.context.ContextId\032\025.co" +
+      "ntext.TopologyList\"\000\0227\n\013GetTopology\022\023.co" +
+      "ntext.TopologyId\032\021.context.Topology\"\000\022E\n" +
+      "\022GetTopologyDetails\022\023.context.TopologyId" +
+      "\032\030.context.TopologyDetails\"\000\0227\n\013SetTopol" +
+      "ogy\022\021.context.Topology\032\023.context.Topolog" +
+      "yId\"\000\0227\n\016RemoveTopology\022\023.context.Topolo" +
+      "gyId\032\016.context.Empty\"\000\022?\n\021GetTopologyEve" +
+      "nts\022\016.context.Empty\032\026.context.TopologyEv" +
+      "ent\"\0000\001\0228\n\rListDeviceIds\022\016.context.Empty" +
+      "\032\025.context.DeviceIdList\"\000\0224\n\013ListDevices" +
+      "\022\016.context.Empty\032\023.context.DeviceList\"\000\022" +
+      "1\n\tGetDevice\022\021.context.DeviceId\032\017.contex" +
+      "t.Device\"\000\0221\n\tSetDevice\022\017.context.Device" +
+      "\032\021.context.DeviceId\"\000\0223\n\014RemoveDevice\022\021." +
+      "context.DeviceId\032\016.context.Empty\"\000\022;\n\017Ge" +
+      "tDeviceEvents\022\016.context.Empty\032\024.context." +
+      "DeviceEvent\"\0000\001\022<\n\014SelectDevice\022\025.contex" +
+      "t.DeviceFilter\032\023.context.DeviceList\"\000\022I\n" +
+      "\021ListEndPointNames\022\027.context.EndPointIdL" +
+      "ist\032\031.context.EndPointNameList\"\000\0224\n\013List" +
+      "LinkIds\022\016.context.Empty\032\023.context.LinkId" +
+      "List\"\000\0220\n\tListLinks\022\016.context.Empty\032\021.co" +
+      "ntext.LinkList\"\000\022+\n\007GetLink\022\017.context.Li" +
+      "nkId\032\r.context.Link\"\000\022+\n\007SetLink\022\r.conte" +
+      "xt.Link\032\017.context.LinkId\"\000\022/\n\nRemoveLink" +
+      "\022\017.context.LinkId\032\016.context.Empty\"\000\0227\n\rG" +
+      "etLinkEvents\022\016.context.Empty\032\022.context.L" +
+      "inkEvent\"\0000\001\022>\n\016ListServiceIds\022\022.context" +
+      ".ContextId\032\026.context.ServiceIdList\"\000\022:\n\014" +
+      "ListServices\022\022.context.ContextId\032\024.conte" +
+      "xt.ServiceList\"\000\0224\n\nGetService\022\022.context" +
+      ".ServiceId\032\020.context.Service\"\000\0224\n\nSetSer" +
+      "vice\022\020.context.Service\032\022.context.Service" +
+      "Id\"\000\0226\n\014UnsetService\022\020.context.Service\032\022" +
+      ".context.ServiceId\"\000\0225\n\rRemoveService\022\022." +
+      "context.ServiceId\032\016.context.Empty\"\000\022=\n\020G" +
+      "etServiceEvents\022\016.context.Empty\032\025.contex" +
+      "t.ServiceEvent\"\0000\001\022?\n\rSelectService\022\026.co" +
+      "ntext.ServiceFilter\032\024.context.ServiceLis" +
+      "t\"\000\022:\n\014ListSliceIds\022\022.context.ContextId\032" +
+      "\024.context.SliceIdList\"\000\0226\n\nListSlices\022\022." +
+      "context.ContextId\032\022.context.SliceList\"\000\022" +
+      ".\n\010GetSlice\022\020.context.SliceId\032\016.context." +
+      "Slice\"\000\022.\n\010SetSlice\022\016.context.Slice\032\020.co" +
+      "ntext.SliceId\"\000\0220\n\nUnsetSlice\022\016.context." +
+      "Slice\032\020.context.SliceId\"\000\0221\n\013RemoveSlice" +
+      "\022\020.context.SliceId\032\016.context.Empty\"\000\0229\n\016" +
+      "GetSliceEvents\022\016.context.Empty\032\023.context" +
+      ".SliceEvent\"\0000\001\0229\n\013SelectSlice\022\024.context" +
+      ".SliceFilter\032\022.context.SliceList\"\000\022D\n\021Li" +
+      "stConnectionIds\022\022.context.ServiceId\032\031.co" +
+      "ntext.ConnectionIdList\"\000\022@\n\017ListConnecti" +
+      "ons\022\022.context.ServiceId\032\027.context.Connec" +
+      "tionList\"\000\022=\n\rGetConnection\022\025.context.Co" +
+      "nnectionId\032\023.context.Connection\"\000\022=\n\rSet" +
+      "Connection\022\023.context.Connection\032\025.contex" +
+      "t.ConnectionId\"\000\022;\n\020RemoveConnection\022\025.c" +
+      "ontext.ConnectionId\032\016.context.Empty\"\000\022C\n" +
+      "\023GetConnectionEvents\022\016.context.Empty\032\030.c" +
+      "ontext.ConnectionEvent\"\0000\001b\006proto3"
     };
     descriptor = com.google.protobuf.Descriptors.FileDescriptor
       .internalBuildGeneratedFileFrom(descriptorData,
diff --git a/src/service/requirements.in b/src/service/requirements.in
index 48fd76485d6bbaf53c3867147882614fc0cf1b04..a10f7da7aba3027a89bdd8e9b62bc4ab90fb5e02 100644
--- a/src/service/requirements.in
+++ b/src/service/requirements.in
@@ -15,6 +15,7 @@
 
 anytree==2.8.0
 geopy==2.3.0
+netaddr==0.9.0
 networkx==2.6.3
 pydot==1.4.2
 redis==4.1.2
diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py
index 35c45c99699b5bc640639cde3054ef72bbb6de50..e771e24f17b2ea97c61158b65cb62c87ee9f37c5 100644
--- a/src/service/service/service_handler_api/FilterFields.py
+++ b/src/service/service/service_handler_api/FilterFields.py
@@ -39,6 +39,7 @@ DEVICE_DRIVER_VALUES = {
     DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN,
     DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG,
     DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE,
+    DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN,
 }
 
 # 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 551d35c7ba8f5386831871fe70fdc962633e1a18..eaf8f715aeff435b67bce77928e726daeb4729e2 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -20,6 +20,7 @@ from .l2nm_openconfig.L2NMOpenConfigServiceHandler import L2NMOpenConfigServiceH
 from .l3nm_emulated.L3NMEmulatedServiceHandler import L3NMEmulatedServiceHandler
 from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceHandler
 from .l3nm_gnmi_openconfig.L3NMGnmiOpenConfigServiceHandler import L3NMGnmiOpenConfigServiceHandler
+from .l3nm_ietf_actn.L3NMIetfActnServiceHandler import L3NMIetfActnServiceHandler
 from .microwave.MicrowaveServiceHandler import MicrowaveServiceHandler
 from .p4.p4_service_handler import P4ServiceHandler
 from .tapi_tapi.TapiServiceHandler import TapiServiceHandler
@@ -57,6 +58,12 @@ SERVICE_HANDLERS = [
             FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG,
         }
     ]),
+    (L3NMIetfActnServiceHandler, [
+        {
+            FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_L3NM,
+            FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN,
+        }
+    ]),
     (TapiServiceHandler, [
         {
             FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
diff --git a/src/service/service/service_handlers/l3nm_ietf_actn/Constants.py b/src/service/service/service_handlers/l3nm_ietf_actn/Constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..62babd7c20491a2673a6dc3d10db33d33bb0a47a
--- /dev/null
+++ b/src/service/service/service_handlers/l3nm_ietf_actn/Constants.py
@@ -0,0 +1,52 @@
+# 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.
+
+# These hardcoded values will be updated with proper logic in second phase of the PoC
+
+VPN_VLAN_TAGS_TO_SERVICE_NAME = {
+    (21, 101): ('osu_tunnel_1', 'etht_service_1'),
+    (31, 201): ('osu_tunnel_2', 'etht_service_2'),
+}
+
+OSU_TUNNEL_SETTINGS = {
+    'osu_tunnel_1': {
+        'odu_type': 'osuflex',
+        'osuflex_number': 40,
+        'bidirectional': True,
+        'delay': 20,
+        'ttp_channel_names': {
+            ('10.0.10.1', '200'): 'och:1-odu2:1-oduflex:1-osuflex:2',
+            ('10.0.30.1', '200'): 'och:1-odu2:1-oduflex:3-osuflex:1',
+        }
+    },
+    'osu_tunnel_2': {
+        'odu_type': 'osuflex',
+        'osuflex_number': 40,
+        'bidirectional': True,
+        'delay': 20,
+        'ttp_channel_names': {
+            ('10.0.10.1', '200'): 'och:1-odu2:1-oduflex:1-osuflex:2',
+            ('10.0.30.1', '200'): 'och:1-odu2:1-oduflex:3-osuflex:1',
+        }
+    },
+}
+
+ETHT_SERVICE_SETTINGS = {
+    'etht_service_1': {
+        'service_type': 'op-mp2mp-svc',
+    },
+    'etht_service_2': {
+        'service_type': 'op-mp2mp-svc',
+    },
+}
diff --git a/src/service/service/service_handlers/l3nm_ietf_actn/L3NMIetfActnServiceHandler.py b/src/service/service/service_handlers/l3nm_ietf_actn/L3NMIetfActnServiceHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c20fdf96d30d0c12ef3f8c0a29189befd791a15
--- /dev/null
+++ b/src/service/service/service_handlers/l3nm_ietf_actn/L3NMIetfActnServiceHandler.py
@@ -0,0 +1,318 @@
+# 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, netaddr
+from typing import Any, Dict, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.proto.context_pb2 import ConfigRule, Device, DeviceId, EndPoint, Service
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+from common.tools.object_factory.Device import json_device_id
+from common.type_checkers.Checkers import chk_type
+from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching
+from service.service.service_handler_api._ServiceHandler import _ServiceHandler
+from service.service.service_handler_api.SettingsHandler import SettingsHandler
+from service.service.task_scheduler.TaskExecutor import TaskExecutor
+from .Constants import ETHT_SERVICE_SETTINGS, OSU_TUNNEL_SETTINGS, VPN_VLAN_TAGS_TO_SERVICE_NAME
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_ietf_actn'})
+
+class L3NMIetfActnServiceHandler(_ServiceHandler):
+    def __init__(   # pylint: disable=super-init-not-called
+        self, service : Service, task_executor : TaskExecutor, **settings
+    ) -> None:
+        self.__service = service
+        self.__task_executor = task_executor
+        self.__settings_handler = SettingsHandler(service.service_config, **settings)
+
+    def _get_endpoint_details(
+        self, endpoint : Tuple[str, str, Optional[str]]
+    ) -> Tuple[Device, EndPoint, Dict]:
+        device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint)
+        device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+        endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+        endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
+        device_name = device_obj.name
+        endpoint_name = endpoint_obj.name
+        if endpoint_settings is None:
+            MSG = 'Settings not found for Endpoint(device=[uuid={:s}, name={:s}], endpoint=[uuid={:s}, name={:s}])'
+            raise Exception(MSG.format(device_uuid, device_name, endpoint_uuid, endpoint_name))
+        endpoint_settings_dict : Dict = endpoint_settings.value
+        return device_obj, endpoint_obj, endpoint_settings_dict
+
+    def _get_service_names(
+        self,
+        src_endpoint_details : Tuple[Device, EndPoint, Dict],
+        dst_endpoint_details : Tuple[Device, EndPoint, Dict]
+    ) -> Tuple[str, str]:
+        _, _, src_endpoint_settings_dict = src_endpoint_details
+        src_vlan_tag = src_endpoint_settings_dict['vlan_tag']
+
+        _, _, dst_endpoint_settings_dict = dst_endpoint_details
+        dst_vlan_tag = dst_endpoint_settings_dict['vlan_tag']
+
+        service_names = VPN_VLAN_TAGS_TO_SERVICE_NAME.get((src_vlan_tag, dst_vlan_tag))
+        if service_names is None:
+            MSG = 'Unable to find service names from VLAN tags(src={:s}, dst={:s})'
+            raise Exception(MSG.format(str(src_vlan_tag), str(dst_vlan_tag)))
+        return service_names
+
+    def _compose_osu_tunnel(
+        self, osu_tunnel_name : str,
+        src_endpoint_details : Tuple[Device, EndPoint, Dict],
+        dst_endpoint_details : Tuple[Device, EndPoint, Dict],
+        is_delete : bool = False
+    ) -> ConfigRule:
+        osu_tunnel_resource_key = '/osu_tunnels/osu_tunnel[{:s}]'.format(osu_tunnel_name)
+        osu_tunnel_resource_value = {'name' : osu_tunnel_name}
+        if is_delete:
+            osu_tunnel_config_rule = json_config_rule_delete(osu_tunnel_resource_key, osu_tunnel_resource_value)
+        else:
+            src_device_obj, src_endpoint_obj, _ = src_endpoint_details
+            dst_device_obj, dst_endpoint_obj, _ = dst_endpoint_details
+
+            osu_tunnel_settings = OSU_TUNNEL_SETTINGS[osu_tunnel_name]
+            ttp_channel_names = osu_tunnel_settings['ttp_channel_names']
+            src_ttp_channel_name = ttp_channel_names[(src_device_obj.name, src_endpoint_obj.name)]
+            dst_ttp_channel_name = ttp_channel_names[(dst_device_obj.name, dst_endpoint_obj.name)]
+
+            osu_tunnel_resource_value.update({
+                'odu_type'            : osu_tunnel_settings['odu_type'],
+                'osuflex_number'      : osu_tunnel_settings['osuflex_number'],
+                'bidirectional'       : osu_tunnel_settings['bidirectional'],
+                'delay'               : osu_tunnel_settings['delay'],
+                'src_node_id'         : src_device_obj.name,
+                'src_tp_id'           : src_endpoint_obj.name,
+                'src_ttp_channel_name': src_ttp_channel_name,
+                'dst_node_id'         : dst_device_obj.name,
+                'dst_tp_id'           : dst_endpoint_obj.name,
+                'dst_ttp_channel_name': dst_ttp_channel_name,
+            })
+            osu_tunnel_config_rule = json_config_rule_set(osu_tunnel_resource_key, osu_tunnel_resource_value)
+        LOGGER.debug('osu_tunnel_config_rule = {:s}'.format(str(osu_tunnel_config_rule)))
+        return ConfigRule(**osu_tunnel_config_rule)
+
+    def _compose_static_routing(
+        self, src_vlan_tag : int, dst_vlan_tag : int
+    ) -> Tuple[List[Dict], List[Dict]]:
+        static_routing = self.__settings_handler.get('/static_routing')
+        if static_routing is None: raise Exception('static_routing not found')
+        static_routing_dict : Dict = static_routing.value
+        src_static_routes = list()
+        dst_static_routes = list()
+        for _, static_route in static_routing_dict.items():
+            vlan_id     = static_route['vlan-id']
+            ipn_cidr    = netaddr.IPNetwork(static_route['ip-network'])
+            ipn_network = str(ipn_cidr.network)
+            ipn_preflen = int(ipn_cidr.prefixlen)
+            next_hop = static_route['next-hop']
+            if vlan_id == src_vlan_tag:
+                src_static_routes.append([ipn_network, ipn_preflen, next_hop])
+            elif vlan_id == dst_vlan_tag:
+                dst_static_routes.append([ipn_network, ipn_preflen, next_hop])
+        return src_static_routes, dst_static_routes
+
+    def _compose_etht_service(
+        self, etht_service_name : str, osu_tunnel_name : str,
+        src_endpoint_details : Tuple[Device, EndPoint, Dict],
+        dst_endpoint_details : Tuple[Device, EndPoint, Dict],
+        is_delete : bool = False
+    ) -> ConfigRule:
+        etht_service_resource_key = '/etht_services/etht_service[{:s}]'.format(etht_service_name)
+        etht_service_resource_value = {'name' : etht_service_name}
+        if is_delete:
+            etht_service_config_rule = json_config_rule_delete(etht_service_resource_key, etht_service_resource_value)
+        else:
+            src_device_obj, src_endpoint_obj, src_endpoint_details = src_endpoint_details
+            src_vlan_tag = src_endpoint_details['vlan_tag']
+            dst_device_obj, dst_endpoint_obj, dst_endpoint_details = dst_endpoint_details
+            dst_vlan_tag = dst_endpoint_details['vlan_tag']
+            src_static_routes, dst_static_routes = self._compose_static_routing(src_vlan_tag, dst_vlan_tag)
+            etht_service_resource_value.update({
+                'osu_tunnel_name'  : osu_tunnel_name,
+                'service_type'     : ETHT_SERVICE_SETTINGS[etht_service_name]['service_type'],
+                'src_node_id'      : src_device_obj.name,
+                'src_tp_id'        : src_endpoint_obj.name,
+                'src_vlan_tag'     : src_vlan_tag,
+                'src_static_routes': src_static_routes,
+                'dst_node_id'      : dst_device_obj.name,
+                'dst_tp_id'        : dst_endpoint_obj.name,
+                'dst_vlan_tag'     : dst_vlan_tag,
+                'dst_static_routes': dst_static_routes,
+            })
+            etht_service_config_rule = json_config_rule_set(etht_service_resource_key, etht_service_resource_value)
+        LOGGER.debug('etht_service_config_rule = {:s}'.format(str(etht_service_config_rule)))
+        return ConfigRule(**etht_service_config_rule)
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        LOGGER.debug('endpoints = {:s}'.format(str(endpoints)))
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) < 2:
+            LOGGER.warning('nothing done: not enough endpoints')
+            return []
+        service_uuid = self.__service.service_id.service_uuid.uuid
+        LOGGER.debug('service_uuid = {:s}'.format(str(service_uuid)))
+        LOGGER.debug('self.__settings_handler = {:s}'.format(str(self.__settings_handler.dump_config_rules())))
+
+        results = []
+        try:
+            src_endpoint_details = self._get_endpoint_details(endpoints[0])
+            src_device_obj, _, _ = src_endpoint_details
+            src_controller = self.__task_executor.get_device_controller(src_device_obj)
+            if src_controller is None: src_controller = src_device_obj
+
+            dst_endpoint_details = self._get_endpoint_details(endpoints[-1])
+            dst_device_obj, _, _ = dst_endpoint_details
+            dst_controller = self.__task_executor.get_device_controller(dst_device_obj)
+            if dst_controller is None: dst_controller = dst_device_obj
+
+            if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid:
+                raise Exception('Different Src-Dst devices not supported by now')
+            controller = src_controller
+
+            osu_tunnel_name, etht_service_name = self._get_service_names(
+                src_endpoint_details, dst_endpoint_details
+            )
+
+            osu_tunnel_config_rule = self._compose_osu_tunnel(
+                osu_tunnel_name, src_endpoint_details, dst_endpoint_details,
+                is_delete=False
+            )
+
+            etht_service_config_rule = self._compose_etht_service(
+                etht_service_name, osu_tunnel_name, src_endpoint_details,
+                dst_endpoint_details, is_delete=False
+            )
+
+            del controller.device_config.config_rules[:]
+            controller.device_config.config_rules.append(osu_tunnel_config_rule)
+            controller.device_config.config_rules.append(etht_service_config_rule)
+            self.__task_executor.configure_device(controller)
+            results.append(True)
+        except Exception as e: # pylint: disable=broad-except
+            LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid)))
+            results.append(e)
+
+        LOGGER.debug('results = {:s}'.format(str(results)))
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        LOGGER.debug('endpoints = {:s}'.format(str(endpoints)))
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) < 2:
+            LOGGER.warning('nothing done: not enough endpoints')
+            return []
+        service_uuid = self.__service.service_id.service_uuid.uuid
+        LOGGER.debug('service_uuid = {:s}'.format(str(service_uuid)))
+        LOGGER.debug('self.__settings_handler = {:s}'.format(str(self.__settings_handler.dump_config_rules())))
+
+        results = []
+        try:
+            src_endpoint_details = self._get_endpoint_details(endpoints[0])
+            src_device_obj, _, _ = src_endpoint_details
+            src_controller = self.__task_executor.get_device_controller(src_device_obj)
+            if src_controller is None: src_controller = src_device_obj
+
+            dst_endpoint_details = self._get_endpoint_details(endpoints[-1])
+            dst_device_obj, _, _ = dst_endpoint_details
+            dst_controller = self.__task_executor.get_device_controller(dst_device_obj)
+            if dst_controller is None: dst_controller = dst_device_obj
+
+            if src_controller.device_id.device_uuid.uuid != dst_controller.device_id.device_uuid.uuid:
+                raise Exception('Different Src-Dst devices not supported by now')
+            controller = src_controller
+
+            osu_tunnel_name, etht_service_name = self._get_service_names(
+                src_endpoint_details, dst_endpoint_details
+            )
+
+            osu_tunnel_config_rule = self._compose_osu_tunnel(
+                osu_tunnel_name, src_endpoint_details, dst_endpoint_details,
+                is_delete=True
+            )
+
+            etht_service_config_rule = self._compose_etht_service(
+                etht_service_name, osu_tunnel_name, src_endpoint_details,
+                dst_endpoint_details, is_delete=True
+            )
+
+            del controller.device_config.config_rules[:]
+            controller.device_config.config_rules.append(osu_tunnel_config_rule)
+            controller.device_config.config_rules.append(etht_service_config_rule)
+            self.__task_executor.configure_device(controller)
+            results.append(True)
+        except Exception as e: # pylint: disable=broad-except
+            LOGGER.exception('Unable to DeleteEndpoint for Service({:s})'.format(str(service_uuid)))
+            results.append(e)
+
+        LOGGER.debug('results = {:s}'.format(str(results)))
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('constraints', constraints, list)
+        if len(constraints) == 0: return []
+
+        msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(constraints)))
+        return [True for _ in range(len(constraints))]
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('constraints', constraints, list)
+        if len(constraints) == 0: return []
+
+        msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.'
+        LOGGER.warning(msg.format(str(constraints)))
+        return [True for _ in range(len(constraints))]
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+
+        results = []
+        for resource in resources:
+            try:
+                resource_value = json.loads(resource[1])
+                self.__settings_handler.set(resource[0], resource_value)
+                results.append(True)
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource)))
+                results.append(e)
+
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        chk_type('resources', resources, list)
+        if len(resources) == 0: return []
+
+        results = []
+        for resource in resources:
+            try:
+                self.__settings_handler.delete(resource[0])
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource)))
+                results.append(e)
+
+        return results
diff --git a/src/service/service/service_handlers/l3nm_ietf_actn/__init__.py b/src/service/service/service_handlers/l3nm_ietf_actn/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/service/service/service_handlers/l3nm_ietf_actn/__init__.py
@@ -0,0 +1,14 @@
+# 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.
+
diff --git a/src/tests/f5g-poc-camara/POC-CAMARA-Guide.md b/src/tests/f5g-poc-camara/POC-CAMARA-Guide.md
new file mode 100644
index 0000000000000000000000000000000000000000..85ec44cb6df77089194b9c8d7a7f5309a8513677
--- /dev/null
+++ b/src/tests/f5g-poc-camara/POC-CAMARA-Guide.md
@@ -0,0 +1,162 @@
+# TeraFlowSDN - ETSI F5G PoC CAMARA Guide
+
+This guide describes how to:
+1. Configure and Deploy TeraFlowSDN for the ETSI F5G PoC CAMARA
+2. Start Mock IETF ACTN SDN Controller (for testing and debugging)
+3. Onboard the network topology descriptor
+4. Expose the topology through the RESTConf IETF Network endpoint
+5. Create Services through RESTConf IETF L3VPN endpoint
+6. Get State of Services through RESTConf IETF L3VPN endpoint
+7. Check configurations done in the Mock IETF ACTN SDN Controller (for testing and debugging)
+8. Destroy Services through RESTConf IETF L3VPN endpoint
+
+
+## 1. Configure and Deploy TeraFlowSDN for the ETSI F5G PoC CAMARA
+
+This guide assumes the user pre-configured a physical/virtual machine based on the steps described in
+the official
+[ETSI TeraFlowSDN - Deployment Guide](https://labs.etsi.org/rep/tfs/controller/-/wikis/1.-Deployment-Guide).
+
+__NOTE__: When you perform step _1.3. Deploy TeraFlowSDN_, configure the `my_deploy.sh` script modifying
+the following settings:
+```bash
+# ...
+export TFS_COMPONENTS="context device pathcomp service slice nbi webui"
+# ...
+export CRDB_DROP_DATABASE_IF_EXISTS="YES"
+# ...
+export QDB_DROP_TABLES_IF_EXIST="YES"
+# ...
+```
+
+After modifying the file, deploy the TeraFlowSDN using the regular script `./deploy/all.sh`.
+The script might take a while to run, especially the first time, since it needs to build the TeraFlowSDN
+microservices.
+
+
+## 2. Start Mock IETF ACTN SDN Controller (for testing and debugging)
+
+__NOTE__: This step is not needed when using the real NCE-T controller.
+
+Start the Mock IETF ACTN SDN Controller. This controller is a simple Python script that accepts requests
+based on agreed F5G PoC CAMARA and stores it in memory, mimicking the NCE-T controller.
+
+Run the Mock IETF ACTN SDN Controller as follows:
+```bash
+python src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py
+```
+
+
+## 3. Onboard the network topology descriptor
+
+The network topology descriptor is a TeraFlowSDN configuration file describing the different elements to be
+managed by the SDN controller, such as devices, links, networks, etc. A preliminary descriptor has been
+prepared for the PoC CAMARA. The file is named as `topology-real.json`.
+
+**NOTE**: Before onboarding file `topology-real.json`, update settings of device `nce-t` to match the IP
+address, port, username, password, HTTP scheme, etc. of the real NCE-T.
+
+To onboard the descriptor file, navigate to the [TeraFlowSDN WebUI](http://127.0.0.1/webui) > Home.
+Browse the file through the _Descriptors_ field, and click the _Submit_ button that is next to the field.
+The onboarding should take few seconds and the WebUI should report that 1 context, 1 topology, 1 controller,
+10 devices, and 24 links were added. Also, it should report that 1 topology was updated.
+
+Next, select in the field _Ctx/Topo_ the entry named as `Context(admin):Topology(admin)`, and click the
+_Submit_ button that is next to the field. The topology should be displayed just below.
+
+Then, navigate to the WebUI > Devices and WebUI > Links sections to familiarize with the details provided
+for each entity. You can check the details of each entity by clicking the eye-shaped icon on the right
+side of each row.
+
+The underlying devices are configured as EMULATED entities while the NCE-T controller is configured as an
+IP SDN controller. Auto-discovery of devices is not implemented as this will fall in PoC phase two.
+
+
+## 4. Expose the topology through the RESTConf IETF Network endpoint
+
+The TeraFlowSDN controller features an NBI component that exposes RESTConf-based endpoints. To retrieve the
+topology following the IETF Network data model, use the following `curl` (or similar) command:
+
+```bash
+curl -u admin:admin http://127.0.0.1/restconf/data/ietf-network:networks/
+```
+
+__NOTE__: The command requires to interrogate the complete database and might take few seconds to complete.
+
+
+## 5. Create Services through RESTConf IETF L3VPN endpoint
+
+The TeraFlowSDN controller's NBI component also exposes the IETF L3VPN endpoints to
+create/check_status/delete services. To try them, use the following `curl` (or similar) commands:
+
+```bash
+curl -u admin:admin -X POST -H "Content-Type: application/json" -d @src/nbi/tests/data/ietf_l3vpn_req_svc1.json http://127.0.0.1/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services
+curl -u admin:admin -X POST -H "Content-Type: application/json" -d @src/nbi/tests/data/ietf_l3vpn_req_svc2.json http://127.0.0.1/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services
+```
+
+__NOTE 1__: This command uses the provided descriptors for creating the VPN services with some adaptations
+to adjust to the official data model.
+
+__NOTE 2__: This command retrieves no data if everything succeeds, in case of error, it will be reported.
+
+This step will create the services in TeraFlowSDN and create the appropriate configuration rules in the
+NCE-T controller through the appropriate service handlers and SBI drivers.
+
+When the services are created, navigate to the WebUI > Services section to familiarize with the details
+provided for each service. You can check the details of the service by clicking the eye-shaped icon on
+the right side of each row.
+
+Note that two services are created per requested VPN. The reason for that is because those services named
+as "vpnX" (the name provided in the request) correspond to end-to-end services, while the others with a UUID
+as a name are generated by TeraFlowSDN to represent the transport segment managed through the NCE-T.
+TeraFlowSDN gathers the settings from the upper-layer end-to-end service and contructs the NCE-T-bound
+services.
+
+Also, you can navigate to the WebUI > Devices section, click on the eye-shaped icon next to the `nce-t`
+device and check the configuration rules (defined using an internal data model, not IETF ACTN) that are
+later converted into the IETF ACTN configuration instructions sent to the NCE-T.
+
+You should see in configuration rules of the `nce-t` device rules with a resource key formatted as
+`/osu_tunnels/osu_tunnel[{osu-tunnel-name}]` for each OSU tunnel, and others with resource key like
+`/etht_services/etht_service[{etht-service-name}]` for each EthT service.
+
+
+## 6. Get State of Services through RESTConf IETF L3VPN endpoint
+
+To get the status of the services, use the following `curl` (or similar) commands:
+
+```bash
+curl -u admin:admin http://127.0.0.1/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service=vpn1
+curl -u admin:admin http://127.0.0.1/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service=vpn2
+```
+
+__NOTE__: This command retrieves an empty dictionary with no error if the service is ready and ACTIVE.
+
+
+## 7. Check configurations done in the Mock IETF ACTN SDN Controller (for testing and debugging)
+
+__NOTE__: This step is not needed when using the real NCE-T controller.
+
+While running the Mock IETF ACTN SDN Controller, you can interrogate the OSU tunnels and EthT Services
+created using the following commands:
+
+```bash
+curl --insecure https://127.0.0.1:8443/restconf/v2/data/ietf-te:te/tunnels
+curl --insecure https://127.0.0.1:8443/restconf/v2/data/ietf-eth-tran-service:etht-svc
+```
+
+
+## 8. Destroy Services through RESTConf IETF L3VPN endpoint
+
+To destroy the services, use the following `curl` (or similar) commands:
+
+```bash
+curl -u admin:admin -X DELETE http://127.0.0.1/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service=vpn1
+curl -u admin:admin -X DELETE http://127.0.0.1/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services/vpn-service=vpn2
+```
+
+__NOTE__: This command retrieves no data when it succeeds.
+
+When the services are deleted, navigate to the WebUI > Services section verify that no service is present.
+Besides, navigate to the WebUI > Devices section, and inspect the NCE-T device to verify that the OSU
+tunnel and ETHT service configuration rules disapeared.
diff --git a/src/tests/f5g-poc-camara/data/topology-real.json b/src/tests/f5g-poc-camara/data/topology-real.json
new file mode 100644
index 0000000000000000000000000000000000000000..c8c146ce28101cb727ceb526ef8fcd3aeefb4e53
--- /dev/null
+++ b/src/tests/f5g-poc-camara/data/topology-real.json
@@ -0,0 +1,260 @@
+{
+    "contexts": [
+        {"context_id": {"context_uuid": {"uuid": "admin"}}}
+    ],
+
+    "topologies": [
+        {"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}}}
+    ],
+
+    "devices": [
+        {"device_id": {"device_uuid": {"uuid": "nce-t"}}, "name": "nce-t", "device_type": "ip-sdn-controller",
+         "device_operational_status": 1, "device_drivers": [10], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "10.0.2.10"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {
+                "scheme": "https", "username": "admin", "password": "admin", "base_url": "/restconf/v2/data",
+                "timeout": 120, "verify": false
+            }}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "name": "10.0.10.1", "device_type": "emu-packet-router",
+         "controller_id": {"device_uuid": {"uuid": "nce-t"}},
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "mgmt", "name": "mgmt", "type": "mgmt"   },
+                {"uuid": "200",  "name": "200",  "type": "copper" },
+                {"uuid": "500",  "name": "500",  "type": "optical"},
+                {"uuid": "501",  "name": "501",  "type": "optical"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "name": "10.0.20.1", "device_type": "emu-packet-router",
+         "controller_id": {"device_uuid": {"uuid": "nce-t"}},
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "mgmt", "name": "mgmt", "type": "mgmt"   },
+                {"uuid": "500",  "name": "500",  "type": "optical"},
+                {"uuid": "501",  "name": "501",  "type": "optical"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "name": "10.0.30.1", "device_type": "emu-packet-router",
+         "controller_id": {"device_uuid": {"uuid": "nce-t"}},
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "mgmt", "name": "mgmt", "type": "mgmt"   },
+                {"uuid": "200",  "name": "200",  "type": "copper" },
+                {"uuid": "500",  "name": "500",  "type": "optical"},
+                {"uuid": "501",  "name": "501",  "type": "optical"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "name": "10.0.40.1", "device_type": "emu-packet-router",
+         "controller_id": {"device_uuid": {"uuid": "nce-t"}},
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "mgmt", "name": "mgmt", "type": "mgmt"   },
+                {"uuid": "500",  "name": "500",  "type": "optical"},
+                {"uuid": "501",  "name": "501",  "type": "optical"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "128.32.10.5"}}, "name": "128.32.10.5", "device_type": "emu-client",
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "eth1", "name": "eth1", "type": "copper"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "128.32.10.1"}}, "name": "128.32.10.1", "device_type": "emu-packet-router",
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "200", "name": "200", "type": "copper"},
+                {"uuid": "500", "name": "500", "type": "copper"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "128.32.20.5"}}, "name": "128.32.20.5", "device_type": "emu-client",
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "eth1", "name": "eth1", "type": "copper"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "128.32.20.1"}}, "name": "128.32.20.1", "device_type": "emu-packet-router",
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "200", "name": "200", "type": "copper"},
+                {"uuid": "500", "name": "500", "type": "copper"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "128.32.33.5"}}, "name": "128.32.33.5", "device_type": "emu-packet-router",
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "200", "name": "200", "type": "copper"},
+                {"uuid": "201", "name": "201", "type": "copper"},
+                {"uuid": "500", "name": "500", "type": "copper"}
+            ]}}}
+        ]}},
+
+        {"device_id": {"device_uuid": {"uuid": "172.10.33.5"}}, "name": "172.10.33.5", "device_type": "emu-datacenter",
+         "device_operational_status": 1, "device_drivers": [0], "device_config": {"config_rules": [
+            {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}},
+            {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}},
+            {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [
+                {"uuid": "200", "name": "200", "type": "copper"},
+                {"uuid": "201", "name": "201", "type": "copper"},
+                {"uuid": "500", "name": "500", "type": "copper"}
+            ]}}}
+        ]}}
+    ],
+
+    "links": [
+        {"link_id": {"link_uuid": {"uuid": "nce-t/mgmt==10.0.10.1/mgmt"}}, "name": "nce-t/mgmt==10.0.10.1/mgmt", "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "nce-t"    }}, "endpoint_uuid": {"uuid": "mgmt"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "mgmt"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "nce-t/mgmt==10.0.20.1/mgmt"}}, "name": "nce-t/mgmt==10.0.20.1/mgmt", "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "nce-t"    }}, "endpoint_uuid": {"uuid": "mgmt"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "mgmt"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "nce-t/mgmt==10.0.30.1/mgmt"}}, "name": "nce-t/mgmt==10.0.30.1/mgmt", "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "nce-t"    }}, "endpoint_uuid": {"uuid": "mgmt"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "mgmt"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "nce-t/mgmt==10.0.40.1/mgmt"}}, "name": "nce-t/mgmt==10.0.40.1/mgmt", "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "nce-t"    }}, "endpoint_uuid": {"uuid": "mgmt"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "mgmt"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "10.0.10.1-501"}}, "name": "10.0.10.1-501",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "501"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "501"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "10.0.20.1-501"}}, "name": "10.0.20.1-501",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "501"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "501"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "10.0.10.1-500"}}, "name": "10.0.10.1-500",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "500"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "500"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "10.0.40.1-500"}}, "name": "10.0.40.1-500",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "500"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "500"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "10.0.20.1-500"}}, "name": "10.0.20.1-500",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "500"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "500"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "10.0.30.1-500"}}, "name": "10.0.30.1-500",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "500"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.20.1"}}, "endpoint_uuid": {"uuid": "500"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "10.0.40.1-501"}}, "name": "10.0.40.1-501",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "501"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "501"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "10.0.30.1-501"}}, "name": "10.0.30.1-501",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "501"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.40.1"}}, "endpoint_uuid": {"uuid": "501"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "128.32.10.5-eth1"}}, "name": "128.32.10.5-eth1",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.10.5"}}, "endpoint_uuid": {"uuid": "eth1"}},
+            {"device_id": {"device_uuid": {"uuid": "128.32.10.1"}}, "endpoint_uuid": {"uuid": "200" }}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "128.32.10.1-200"}}, "name": "128.32.10.1-200",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.10.1"}}, "endpoint_uuid": {"uuid": "200" }},
+            {"device_id": {"device_uuid": {"uuid": "128.32.10.5"}}, "endpoint_uuid": {"uuid": "eth1"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "128.32.10.1-500"}}, "name": "128.32.10.1-500",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.10.1"}}, "endpoint_uuid": {"uuid": "500"}},
+            {"device_id": {"device_uuid": {"uuid": "128.32.33.5"}}, "endpoint_uuid": {"uuid": "200"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "128.32.33.5-200"}}, "name": "128.32.33.5-200",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.33.5"}}, "endpoint_uuid": {"uuid": "200"}},
+            {"device_id": {"device_uuid": {"uuid": "128.32.10.1"}}, "endpoint_uuid": {"uuid": "500"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "128.32.20.5-eth1"}}, "name": "128.32.20.5-eth1",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.20.5"}}, "endpoint_uuid": {"uuid": "eth1"}},
+            {"device_id": {"device_uuid": {"uuid": "128.32.20.1"}}, "endpoint_uuid": {"uuid": "200" }}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "128.32.20.1-200"}}, "name": "128.32.20.1-200",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.20.1"}}, "endpoint_uuid": {"uuid": "200" }},
+            {"device_id": {"device_uuid": {"uuid": "128.32.20.5"}}, "endpoint_uuid": {"uuid": "eth1"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "128.32.20.1-500"}}, "name": "128.32.20.1-500",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.20.1"}}, "endpoint_uuid": {"uuid": "500"}},
+            {"device_id": {"device_uuid": {"uuid": "128.32.33.5"}}, "endpoint_uuid": {"uuid": "201"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "128.32.33.5-201"}}, "name": "128.32.33.5-201",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.33.5"}}, "endpoint_uuid": {"uuid": "201"}},
+            {"device_id": {"device_uuid": {"uuid": "128.32.20.1"}}, "endpoint_uuid": {"uuid": "500"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "128.32.33.5-500"}}, "name": "128.32.33.5-500",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "128.32.33.5"}}, "endpoint_uuid": {"uuid": "500"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "200"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "10.0.10.1-200"}}, "name": "10.0.10.1-200",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.10.1"}}, "endpoint_uuid": {"uuid": "200"}},
+            {"device_id": {"device_uuid": {"uuid": "128.32.33.5"}}, "endpoint_uuid": {"uuid": "500"}}
+        ]},
+
+        {"link_id": {"link_uuid": {"uuid": "172.10.33.5-500"}}, "name": "172.10.33.5-500",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "172.10.33.5"}}, "endpoint_uuid": {"uuid": "500"}},
+            {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "200"}}
+        ]},
+        {"link_id": {"link_uuid": {"uuid": "10.0.30.1-200"}}, "name": "10.0.30.1-200",
+         "attributes": {"total_capacity_gbps": 10, "used_capacity_gbps": 0}, "link_endpoint_ids": [
+            {"device_id": {"device_uuid": {"uuid": "10.0.30.1"}}, "endpoint_uuid": {"uuid": "200"}},
+            {"device_id": {"device_uuid": {"uuid": "172.10.33.5"}}, "endpoint_uuid": {"uuid": "500"}}
+        ]}
+    ]
+}
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/Dockerfile b/src/tests/tools/mock_ietf_actn_sdn_ctrl/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..05c785fb1a353db544d58d0953bc1cc99881a693
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/Dockerfile
@@ -0,0 +1,37 @@
+# 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 python:3.9-slim
+
+# Set Python to show logs as they occur
+ENV PYTHONUNBUFFERED=0
+
+# Get generic Python packages
+RUN python3 -m pip install --upgrade pip
+RUN python3 -m pip install --upgrade setuptools wheel
+RUN python3 -m pip install --upgrade pip-tools
+
+# Create component sub-folders, and copy content
+RUN mkdir -p /var/teraflow/mock_ietf_actn_sdn_ctrl
+WORKDIR /var/teraflow/mock_ietf_actn_sdn_ctrl
+COPY . .
+
+# Get specific Python packages
+RUN pip-compile --quiet --output-file=requirements.txt requirements.in
+RUN python3 -m pip install -r requirements.txt
+
+RUN python3 -m pip list
+
+# Start the service
+ENTRYPOINT ["python", "MockIetfActnSdnCtrl.py"]
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py b/src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py
new file mode 100644
index 0000000000000000000000000000000000000000..26243e2b6bd8e8d8bd2b64e9552be7d9d36c853e
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py
@@ -0,0 +1,80 @@
+# 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.
+
+# Mock IETF ACTN SDN controller
+# -----------------------------
+# REST server implementing minimal support for:
+# - IETF YANG Data Model for Transport Network Client Signals
+#       Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html
+# - IETF YANG Data Model for Traffic Engineering Tunnels, Label Switched Paths and Interfaces
+#       Ref: https://www.ietf.org/archive/id/draft-ietf-teas-yang-te-34.html
+
+
+import functools, logging, sys, time
+from flask import Flask, jsonify, make_response, request
+from flask_restful import Api, Resource
+from ResourceEthServices import EthService, EthServices
+from ResourceOsuTunnels import OsuTunnel, OsuTunnels
+
+BIND_ADDRESS = '0.0.0.0'
+BIND_PORT    = 8443
+BASE_URL     = '/restconf/v2/data'
+STR_ENDPOINT = 'https://{:s}:{:s}{:s}'.format(str(BIND_ADDRESS), str(BIND_PORT), str(BASE_URL))
+LOG_LEVEL    = logging.DEBUG
+
+logging.basicConfig(level=LOG_LEVEL, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s")
+LOGGER = logging.getLogger(__name__)
+
+logging.getLogger('werkzeug').setLevel(logging.WARNING)
+
+def log_request(logger : logging.Logger, response):
+    timestamp = time.strftime('[%Y-%b-%d %H:%M]')
+    logger.info('%s %s %s %s %s', timestamp, request.remote_addr, request.method, request.full_path, response.status)
+    return response
+
+class Health(Resource):
+    def get(self):
+        return make_response(jsonify({}), 200)
+
+def main():
+    LOGGER.info('Starting...')
+    
+    app = Flask(__name__)
+    app.after_request(functools.partial(log_request, LOGGER))
+
+    api = Api(app, prefix=BASE_URL)
+    api.add_resource(
+        Health, '/'
+    )
+    api.add_resource(
+        OsuTunnels, '/ietf-te:te/tunnels'
+    )
+    api.add_resource(
+        OsuTunnel, '/ietf-te:te/tunnels/tunnel="<string:osu_tunnel_name>"'
+    )
+    api.add_resource(
+        EthServices, '/ietf-eth-tran-service:etht-svc'
+    )
+    api.add_resource(
+        EthService, '/ietf-eth-tran-service:etht-svc/etht-svc-instances="<string:etht_service_name>"'
+    )
+
+    LOGGER.info('Listening on {:s}...'.format(str(STR_ENDPOINT)))
+    app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT, ssl_context='adhoc')
+
+    LOGGER.info('Bye')
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/README.md b/src/tests/tools/mock_ietf_actn_sdn_ctrl/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..52aa2922d0699b1d286319e11cc25f8707fda686
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/README.md
@@ -0,0 +1,33 @@
+# Mock IETF ACTN SDN Controller
+
+This REST server implements very basic support for the following YANG data models:
+- IETF YANG Data Model for Transport Network Client Signals (draft-ietf-ccamp-client-signal-yang-10)
+  - Ref: https://datatracker.ietf.org/doc/draft-ietf-ccamp-client-signal-yang/
+- IETF YANG Data Model for Traffic Engineering Tunnels, Label Switched Paths and Interfaces (draft-ietf-teas-yang-te-34)
+  - Ref: https://datatracker.ietf.org/doc/draft-ietf-teas-yang-te/
+
+The aim of this server is to enable testing the IetfActnDeviceDriver and the IetfActnServiceHandler.
+
+
+## 1. Install requirements for the Mock IETF ACTN SDN controller
+__NOTE__: if you run the Mock IETF ACTN SDN controller from the PyEnv used for developing on the TeraFlowSDN
+framework and you followed the official steps in
+[Development Guide > Configure Environment > Python](https://labs.etsi.org/rep/tfs/controller/-/wikis/2.-Development-Guide/2.1.-Configure-Environment/2.1.1.-Python),
+all the requirements are already in place. Install them only if you execute it in a separate/standalone environment.
+
+Install the required dependencies as follows:
+```bash
+pip install -r src/tests/tools/mock_ietf_actn_sdn_ctrl/requirements.in
+```
+
+Run the Mock IETF ACTN SDN Controller as follows:
+```bash
+python src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py
+```
+
+
+## 2. Run the Mock IETF ACTN SDN controller
+Run the Mock IETF ACTN SDN Controller as follows:
+```bash
+python src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py
+```
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/ResourceEthServices.py b/src/tests/tools/mock_ietf_actn_sdn_ctrl/ResourceEthServices.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e08bbdaa48df2ae7338a96d727fdc9f876784e6
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/ResourceEthServices.py
@@ -0,0 +1,53 @@
+# 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.
+
+# REST-API resource implementing minimal support for "IETF YANG Data Model for Transport Network Client Signals".
+# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-10.html
+
+from flask import abort, jsonify, make_response, request
+from flask_restful import Resource
+
+ETHT_SERVICES = {}
+
+class EthServices(Resource):
+    def get(self):
+        etht_services = [etht_service for etht_service in ETHT_SERVICES.values()]
+        data = {'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': etht_services}}
+        return make_response(jsonify(data), 200)
+
+    def post(self):
+        json_request = request.get_json()
+        if not json_request: abort(400)
+        if not isinstance(json_request, dict): abort(400)
+        if 'ietf-eth-tran-service:etht-svc' not in json_request: abort(400)
+        json_request = json_request['ietf-eth-tran-service:etht-svc']
+        if 'etht-svc-instances' not in json_request: abort(400)
+        etht_services = json_request['etht-svc-instances']
+        if not isinstance(etht_services, list): abort(400)
+        if len(etht_services) != 1: abort(400)
+        etht_service = etht_services[0]
+        etht_service_name = etht_service['etht-svc-name']
+        ETHT_SERVICES[etht_service_name] = etht_service
+        return make_response(jsonify({}), 201)
+
+class EthService(Resource):
+    def get(self, etht_service_name : str):
+        etht_service = ETHT_SERVICES.get(etht_service_name, None)
+        data,status = ({}, 404) if etht_service is None else (etht_service, 200)
+        return make_response(jsonify(data), status)
+
+    def delete(self, etht_service_name : str):
+        etht_service = ETHT_SERVICES.pop(etht_service_name, None)
+        data,status = ({}, 404) if etht_service is None else (etht_service, 204)
+        return make_response(jsonify(data), status)
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/ResourceOsuTunnels.py b/src/tests/tools/mock_ietf_actn_sdn_ctrl/ResourceOsuTunnels.py
new file mode 100644
index 0000000000000000000000000000000000000000..914f7096da4101a138ac9b0cdd2911dfcf22bb61
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/ResourceOsuTunnels.py
@@ -0,0 +1,53 @@
+# 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.
+
+# REST-API resource implementing minimal support for "IETF YANG Data Model for Traffic Engineering Tunnels,
+# Label Switched Paths and Interfaces".
+# Ref: https://www.ietf.org/archive/id/draft-ietf-teas-yang-te-34.html
+
+
+from flask import abort, jsonify, make_response, request
+from flask_restful import Resource
+
+OSU_TUNNELS = {}
+
+class OsuTunnels(Resource):
+    def get(self):
+        osu_tunnels = [osu_tunnel for osu_tunnel in OSU_TUNNELS.values()]
+        data = {'ietf-te:tunnel': osu_tunnels}
+        return make_response(jsonify(data), 200)
+
+    def post(self):
+        json_request = request.get_json()
+        if not json_request: abort(400)
+        if not isinstance(json_request, dict): abort(400)
+        if 'ietf-te:tunnel' not in json_request: abort(400)
+        osu_tunnels = json_request['ietf-te:tunnel']
+        if not isinstance(osu_tunnels, list): abort(400)
+        if len(osu_tunnels) != 1: abort(400)
+        osu_tunnel = osu_tunnels[0]
+        osu_tunnel_name = osu_tunnel['name']
+        OSU_TUNNELS[osu_tunnel_name] = osu_tunnel
+        return make_response(jsonify({}), 201)
+
+class OsuTunnel(Resource):
+    def get(self, osu_tunnel_name : str):
+        osu_tunnel = OSU_TUNNELS.get(osu_tunnel_name, None)
+        data,status = ({}, 404) if osu_tunnel is None else (osu_tunnel, 200)
+        return make_response(jsonify(data), status)
+
+    def delete(self, osu_tunnel_name : str):
+        osu_tunnel = OSU_TUNNELS.pop(osu_tunnel_name, None)
+        data,status = ({}, 404) if osu_tunnel is None else (osu_tunnel, 204)
+        return make_response(jsonify(data), status)
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/__init__.py b/src/tests/tools/mock_ietf_actn_sdn_ctrl/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/__init__.py
@@ -0,0 +1,14 @@
+# 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.
+
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/build.sh b/src/tests/tools/mock_ietf_actn_sdn_ctrl/build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..fe958995ac303ca003aee9557b7fc07905933fce
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/build.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+# 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.
+
+# Make folder containing the script the root folder for its execution
+cd $(dirname $0)
+
+docker build -t mock-ietf-actn-sdn-ctrl:test -f Dockerfile .
+docker tag mock-ietf-actn-sdn-ctrl:test localhost:32000/tfs/mock-ietf-actn-sdn-ctrl:test
+docker push localhost:32000/tfs/mock-ietf-actn-sdn-ctrl:test
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/deploy.sh b/src/tests/tools/mock_ietf_actn_sdn_ctrl/deploy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..47270dc4beafc3828b69ca77eed443f2e16528a5
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/deploy.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# 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.
+
+kubectl delete namespace mocks
+kubectl --namespace mocks apply -f mock-ietf-actn-sdn-ctrl.yaml
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/mock-ietf-actn-sdn-ctrl.yaml b/src/tests/tools/mock_ietf_actn_sdn_ctrl/mock-ietf-actn-sdn-ctrl.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..32cfd922896f6e8860ae07049657b97b0da90c3a
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/mock-ietf-actn-sdn-ctrl.yaml
@@ -0,0 +1,64 @@
+# 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.
+
+kind: Namespace
+apiVersion: v1
+metadata:
+  name: mocks
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: mock-ietf-actn-sdn-ctrl
+spec:
+  selector:
+    matchLabels:
+      app: mock-ietf-actn-sdn-ctrl
+  replicas: 1
+  template:
+    metadata:
+      annotations:
+        config.linkerd.io/skip-inbound-ports: "8443"
+      labels:
+        app: mock-ietf-actn-sdn-ctrl
+    spec:
+      terminationGracePeriodSeconds: 5
+      containers:
+      - name: server
+        image: localhost:32000/tfs/mock-ietf-actn-sdn-ctrl:test
+        imagePullPolicy: IfNotPresent
+        ports:
+        - containerPort: 8443
+        resources:
+          requests:
+            cpu: 250m
+            memory: 512Mi
+          limits:
+            cpu: 700m
+            memory: 1024Mi
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: mock-ietf-actn-sdn-ctrl
+  labels:
+    app: mock-ietf-actn-sdn-ctrl
+spec:
+  type: ClusterIP
+  selector:
+    app: mock-ietf-actn-sdn-ctrl
+  ports:
+  - name: https
+    port: 8443
+    targetPort: 8443
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/requirements.in b/src/tests/tools/mock_ietf_actn_sdn_ctrl/requirements.in
new file mode 100644
index 0000000000000000000000000000000000000000..d91775403366e93a4612296faa6a7d5fa527249a
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/requirements.in
@@ -0,0 +1,22 @@
+# 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.
+
+cryptography==39.0.1
+pyopenssl==23.0.0
+Flask==2.1.3
+Flask-HTTPAuth==4.5.0
+Flask-RESTful==0.3.9
+jsonschema==4.4.0
+requests==2.27.1
+werkzeug==2.3.7
diff --git a/src/tests/tools/mock_ietf_actn_sdn_ctrl/run.sh b/src/tests/tools/mock_ietf_actn_sdn_ctrl/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..48a23f2e41d6d30d244ef01c72ac9700b588b140
--- /dev/null
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/run.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# 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.
+
+# Make folder containing the script the root folder for its execution
+cd $(dirname $0)
+
+python MockIetfActnSdnCtrl.py
diff --git a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/build.sh b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/build.sh
index 4df315cec178cef13eaa059a739bc22efc011d4d..5501614c4fd5079d98b81e7ec78bf56b581c888a 100755
--- a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/build.sh
+++ b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/build.sh
@@ -1,4 +1,17 @@
 #!/bin/bash
+# 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.
 
 docker build -t mock-mw-sdn-ctrl:test -f Dockerfile .
 docker tag mock-mw-sdn-ctrl:test localhost:32000/tfs/mock-mw-sdn-ctrl:test
diff --git a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/deploy.sh b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/deploy.sh
index ded232e5c50f8cd5ed448ec0193f58c43626f4ad..ed77bcfbc1200609aa6a6effeb13e723d9f9979e 100755
--- a/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/deploy.sh
+++ b/src/tests/tools/mock_mw_sdn_ctrl/ssl_not_working/deploy.sh
@@ -1,4 +1,17 @@
 #!/bin/bash
+# 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.
 
 kubectl delete namespace mocks
 kubectl --namespace mocks apply -f mock-mw-sdn-ctrl.yaml
diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py
index bcd5804d32927763344d08371320fdde5f2fcab7..4c04bbfe12d8d39e51bd8275021064a5a7ad4fc3 100644
--- a/src/webui/service/device/forms.py
+++ b/src/webui/service/device/forms.py
@@ -31,6 +31,8 @@ class AddDeviceForm(FlaskForm):
     device_drivers_xr = BooleanField('XR')
     device_drivers_ietf_l2vpn = BooleanField('IETF L2VPN')
     device_drivers_gnmi_openconfig = BooleanField('GNMI OPENCONFIG')
+    device_drivers_flexscale = BooleanField('FLEXSCALE')
+    device_drivers_ietf_actn = BooleanField('IETF ACTN')
 
     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 4459deeebd000642cfe235f4f46ebf65670ae2ee..b75e9fb9f09bddcb9f321995465fd8cca5263277 100644
--- a/src/webui/service/device/routes.py
+++ b/src/webui/service/device/routes.py
@@ -125,6 +125,10 @@ def add():
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN)
         if form.device_drivers_gnmi_openconfig.data:
             device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG)
+        if form.device_drivers_flexscale.data:
+            device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE)
+        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
 
         try:
diff --git a/src/webui/service/static/topology_icons/Acknowledgements.txt b/src/webui/service/static/topology_icons/Acknowledgements.txt
index 43ecee79806682317bd5d2a8ebff16bac8767220..08e9ed27ce3e87e05d8d1d1176704b53b93c8ef3 100644
--- a/src/webui/service/static/topology_icons/Acknowledgements.txt
+++ b/src/webui/service/static/topology_icons/Acknowledgements.txt
@@ -31,5 +31,9 @@ https://symbols.getvecta.com/stencil_241/158_local-director.6b38eab9e4.png => em
 https://symbols.getvecta.com/stencil_240/197_radio-tower.b6138c8c29.png => radio-router.png
 https://symbols.getvecta.com/stencil_241/216_radio-tower.5159339bc0.png => emu-radio-router.png
 
-https://symbols.getvecta.com/stencil_240/124_laptop.be264ceb77.png => laptop.png
-https://symbols.getvecta.com/stencil_241/154_laptop.c01910b6c8.png => emu-laptop.png
+https://symbols.getvecta.com/stencil_240/124_laptop.be264ceb77.png => client.png
+https://symbols.getvecta.com/stencil_241/154_laptop.c01910b6c8.png => emu-client.png
+
+https://symbols.getvecta.com/stencil_240/16_atm-tag-switch-router.3149d7e933.png => ip-sdn-controller.png
+https://symbols.getvecta.com/stencil_241/46_atm-tag-sw-rtr.776719c0b0.png => emu-ip-sdn-controller.png
+
diff --git a/src/webui/service/static/topology_icons/emu-ip-sdn-controller.png b/src/webui/service/static/topology_icons/emu-ip-sdn-controller.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff4c69120e28b434df3d5a8db46fe304dbdd03e7
Binary files /dev/null and b/src/webui/service/static/topology_icons/emu-ip-sdn-controller.png differ
diff --git a/src/webui/service/static/topology_icons/ip-sdn-controller.png b/src/webui/service/static/topology_icons/ip-sdn-controller.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0b1abe8738b7e34faaa34d286157a778069fdbb
Binary files /dev/null and b/src/webui/service/static/topology_icons/ip-sdn-controller.png differ
diff --git a/src/webui/service/templates/device/add.html b/src/webui/service/templates/device/add.html
index c115657aa08828849172345ca50caaeb4150fe0f..c4d7f16858b7c63bd87e730cff6d586dc702e0c9 100644
--- a/src/webui/service/templates/device/add.html
+++ b/src/webui/service/templates/device/add.html
@@ -92,6 +92,9 @@
                 {{ form.device_drivers_xr }} {{ form.device_drivers_xr.label(class="col-sm-3 col-form-label") }}
                 {{ form.device_drivers_ietf_l2vpn }} {{ form.device_drivers_ietf_l2vpn.label(class="col-sm-3 col-form-label") }}
                 {{ form.device_drivers_gnmi_openconfig }} {{ form.device_drivers_gnmi_openconfig.label(class="col-sm-3 col-form-label") }}
+                <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") }}
                 {% endif %}
             </div>
         </div>
diff --git a/src/ztp/src/main/java/org/etsi/tfs/ztp/Serializer.java b/src/ztp/src/main/java/org/etsi/tfs/ztp/Serializer.java
index cf49280a856e5fb7f4afaef394b565b02e44a8c2..feb65b77c9f45c760474f5e25a82b68eac8a7a01 100644
--- a/src/ztp/src/main/java/org/etsi/tfs/ztp/Serializer.java
+++ b/src/ztp/src/main/java/org/etsi/tfs/ztp/Serializer.java
@@ -863,6 +863,12 @@ public class Serializer {
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_XR;
             case IETF_L2VPN:
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN;
+            case GNMI_OPENCONFIG:
+                return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG;
+            case FLEXSCALE:
+                return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE;
+            case IETF_ACTN:
+                return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN;
             case UNDEFINED:
             default:
                 return ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED;
@@ -886,6 +892,12 @@ public class Serializer {
                 return DeviceDriverEnum.XR;
             case DEVICEDRIVER_IETF_L2VPN:
                 return DeviceDriverEnum.IETF_L2VPN;
+            case DEVICEDRIVER_GNMI_OPENCONFIG:
+                return DeviceDriverEnum.GNMI_OPENCONFIG;
+            case DEVICEDRIVER_FLEXSCALE:
+                return DeviceDriverEnum.FLEXSCALE;
+            case DEVICEDRIVER_IETF_ACTN:
+                return DeviceDriverEnum.IETF_ACTN;
             case DEVICEDRIVER_UNDEFINED:
             case UNRECOGNIZED:
             default:
diff --git a/src/ztp/src/main/java/org/etsi/tfs/ztp/context/model/DeviceDriverEnum.java b/src/ztp/src/main/java/org/etsi/tfs/ztp/context/model/DeviceDriverEnum.java
index 7c87b0638272edb58ef3cd57d3aad7aa3365cca8..8e89be8a6ddc993e7d90794c756f406fa72104f2 100644
--- a/src/ztp/src/main/java/org/etsi/tfs/ztp/context/model/DeviceDriverEnum.java
+++ b/src/ztp/src/main/java/org/etsi/tfs/ztp/context/model/DeviceDriverEnum.java
@@ -24,5 +24,8 @@ public enum DeviceDriverEnum {
     IETF_NETWORK_TOPOLOGY,
     ONF_TR_532,
     XR,
-    IETF_L2VPN
+    IETF_L2VPN,
+    GNMI_OPENCONFIG,
+    FLEXSCALE,
+    IETF_ACTN
 }
diff --git a/src/ztp/src/test/java/org/etsi/tfs/ztp/SerializerTest.java b/src/ztp/src/test/java/org/etsi/tfs/ztp/SerializerTest.java
index 5a7887a049526e530874a597b2b0f96e2646a4f9..67048119d0b0cdb8d1fb2df2dcb2659b0870efb3 100644
--- a/src/ztp/src/test/java/org/etsi/tfs/ztp/SerializerTest.java
+++ b/src/ztp/src/test/java/org/etsi/tfs/ztp/SerializerTest.java
@@ -1223,6 +1223,13 @@ class SerializerTest {
                 Arguments.of(
                         DeviceDriverEnum.IETF_L2VPN,
                         ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN),
+                Arguments.of(
+                        DeviceDriverEnum.GNMI_OPENCONFIG,
+                        ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG),
+                Arguments.of(
+                        DeviceDriverEnum.FLEXSCALE, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE),
+                Arguments.of(
+                        DeviceDriverEnum.IETF_ACTN, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN),
                 Arguments.of(
                         DeviceDriverEnum.UNDEFINED, ContextOuterClass.DeviceDriverEnum.DEVICEDRIVER_UNDEFINED));
     }
diff --git a/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java b/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java
index a25798b884d9006f9c1b218c133634784f8bf392..d4873899b0113a7356c1c4d6bc2ea9aae2e8b4e5 100644
--- a/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java
+++ b/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java
@@ -185,6 +185,14 @@ public final class ContextOuterClass {
      * <code>DEVICEDRIVER_GNMI_OPENCONFIG = 8;</code>
      */
     DEVICEDRIVER_GNMI_OPENCONFIG(8),
+    /**
+     * <code>DEVICEDRIVER_FLEXSCALE = 9;</code>
+     */
+    DEVICEDRIVER_FLEXSCALE(9),
+    /**
+     * <code>DEVICEDRIVER_IETF_ACTN = 10;</code>
+     */
+    DEVICEDRIVER_IETF_ACTN(10),
     UNRECOGNIZED(-1),
     ;
 
@@ -228,6 +236,14 @@ public final class ContextOuterClass {
      * <code>DEVICEDRIVER_GNMI_OPENCONFIG = 8;</code>
      */
     public static final int DEVICEDRIVER_GNMI_OPENCONFIG_VALUE = 8;
+    /**
+     * <code>DEVICEDRIVER_FLEXSCALE = 9;</code>
+     */
+    public static final int DEVICEDRIVER_FLEXSCALE_VALUE = 9;
+    /**
+     * <code>DEVICEDRIVER_IETF_ACTN = 10;</code>
+     */
+    public static final int DEVICEDRIVER_IETF_ACTN_VALUE = 10;
 
 
     public final int getNumber() {
@@ -263,6 +279,8 @@ public final class ContextOuterClass {
         case 6: return DEVICEDRIVER_XR;
         case 7: return DEVICEDRIVER_IETF_L2VPN;
         case 8: return DEVICEDRIVER_GNMI_OPENCONFIG;
+        case 9: return DEVICEDRIVER_FLEXSCALE;
+        case 10: return DEVICEDRIVER_IETF_ACTN;
         default: return null;
       }
     }
@@ -461,6 +479,10 @@ public final class ContextOuterClass {
      * <code>SERVICETYPE_TE = 4;</code>
      */
     SERVICETYPE_TE(4),
+    /**
+     * <code>SERVICETYPE_E2E = 5;</code>
+     */
+    SERVICETYPE_E2E(5),
     UNRECOGNIZED(-1),
     ;
 
@@ -484,6 +506,10 @@ public final class ContextOuterClass {
      * <code>SERVICETYPE_TE = 4;</code>
      */
     public static final int SERVICETYPE_TE_VALUE = 4;
+    /**
+     * <code>SERVICETYPE_E2E = 5;</code>
+     */
+    public static final int SERVICETYPE_E2E_VALUE = 5;
 
 
     public final int getNumber() {
@@ -515,6 +541,7 @@ public final class ContextOuterClass {
         case 2: return SERVICETYPE_L2NM;
         case 3: return SERVICETYPE_TAPI_CONNECTIVITY_SERVICE;
         case 4: return SERVICETYPE_TE;
+        case 5: return SERVICETYPE_E2E;
         default: return null;
       }
     }
@@ -75826,114 +75853,116 @@ public final class ContextOuterClass {
       "\0132\022.context.ContextId\022\025\n\rauthenticated\030\002" +
       " \001(\010*j\n\rEventTypeEnum\022\027\n\023EVENTTYPE_UNDEF" +
       "INED\020\000\022\024\n\020EVENTTYPE_CREATE\020\001\022\024\n\020EVENTTYP" +
-      "E_UPDATE\020\002\022\024\n\020EVENTTYPE_REMOVE\020\003*\231\002\n\020Dev" +
+      "E_UPDATE\020\002\022\024\n\020EVENTTYPE_REMOVE\020\003*\321\002\n\020Dev" +
       "iceDriverEnum\022\032\n\026DEVICEDRIVER_UNDEFINED\020" +
       "\000\022\033\n\027DEVICEDRIVER_OPENCONFIG\020\001\022\036\n\032DEVICE" +
       "DRIVER_TRANSPORT_API\020\002\022\023\n\017DEVICEDRIVER_P" +
       "4\020\003\022&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOG" +
       "Y\020\004\022\033\n\027DEVICEDRIVER_ONF_TR_532\020\005\022\023\n\017DEVI" +
       "CEDRIVER_XR\020\006\022\033\n\027DEVICEDRIVER_IETF_L2VPN" +
-      "\020\007\022 \n\034DEVICEDRIVER_GNMI_OPENCONFIG\020\010*\217\001\n" +
-      "\033DeviceOperationalStatusEnum\022%\n!DEVICEOP" +
-      "ERATIONALSTATUS_UNDEFINED\020\000\022$\n DEVICEOPE" +
-      "RATIONALSTATUS_DISABLED\020\001\022#\n\037DEVICEOPERA" +
-      "TIONALSTATUS_ENABLED\020\002*\225\001\n\017ServiceTypeEn" +
-      "um\022\027\n\023SERVICETYPE_UNKNOWN\020\000\022\024\n\020SERVICETY" +
-      "PE_L3NM\020\001\022\024\n\020SERVICETYPE_L2NM\020\002\022)\n%SERVI" +
-      "CETYPE_TAPI_CONNECTIVITY_SERVICE\020\003\022\022\n\016SE" +
-      "RVICETYPE_TE\020\004*\304\001\n\021ServiceStatusEnum\022\033\n\027" +
-      "SERVICESTATUS_UNDEFINED\020\000\022\031\n\025SERVICESTAT" +
-      "US_PLANNED\020\001\022\030\n\024SERVICESTATUS_ACTIVE\020\002\022\032" +
-      "\n\026SERVICESTATUS_UPDATING\020\003\022!\n\035SERVICESTA" +
-      "TUS_PENDING_REMOVAL\020\004\022\036\n\032SERVICESTATUS_S" +
-      "LA_VIOLATED\020\005*\251\001\n\017SliceStatusEnum\022\031\n\025SLI" +
-      "CESTATUS_UNDEFINED\020\000\022\027\n\023SLICESTATUS_PLAN" +
-      "NED\020\001\022\024\n\020SLICESTATUS_INIT\020\002\022\026\n\022SLICESTAT" +
-      "US_ACTIVE\020\003\022\026\n\022SLICESTATUS_DEINIT\020\004\022\034\n\030S" +
-      "LICESTATUS_SLA_VIOLATED\020\005*]\n\020ConfigActio" +
-      "nEnum\022\032\n\026CONFIGACTION_UNDEFINED\020\000\022\024\n\020CON" +
-      "FIGACTION_SET\020\001\022\027\n\023CONFIGACTION_DELETE\020\002" +
-      "*m\n\024ConstraintActionEnum\022\036\n\032CONSTRAINTAC" +
-      "TION_UNDEFINED\020\000\022\030\n\024CONSTRAINTACTION_SET" +
-      "\020\001\022\033\n\027CONSTRAINTACTION_DELETE\020\002*\203\002\n\022Isol" +
-      "ationLevelEnum\022\020\n\014NO_ISOLATION\020\000\022\026\n\022PHYS" +
-      "ICAL_ISOLATION\020\001\022\025\n\021LOGICAL_ISOLATION\020\002\022" +
-      "\025\n\021PROCESS_ISOLATION\020\003\022\035\n\031PHYSICAL_MEMOR" +
-      "Y_ISOLATION\020\004\022\036\n\032PHYSICAL_NETWORK_ISOLAT" +
-      "ION\020\005\022\036\n\032VIRTUAL_RESOURCE_ISOLATION\020\006\022\037\n" +
-      "\033NETWORK_FUNCTIONS_ISOLATION\020\007\022\025\n\021SERVIC" +
-      "E_ISOLATION\020\0102\245\026\n\016ContextService\022:\n\016List" +
-      "ContextIds\022\016.context.Empty\032\026.context.Con" +
-      "textIdList\"\000\0226\n\014ListContexts\022\016.context.E" +
-      "mpty\032\024.context.ContextList\"\000\0224\n\nGetConte" +
-      "xt\022\022.context.ContextId\032\020.context.Context" +
-      "\"\000\0224\n\nSetContext\022\020.context.Context\032\022.con" +
-      "text.ContextId\"\000\0225\n\rRemoveContext\022\022.cont" +
-      "ext.ContextId\032\016.context.Empty\"\000\022=\n\020GetCo" +
-      "ntextEvents\022\016.context.Empty\032\025.context.Co" +
-      "ntextEvent\"\0000\001\022@\n\017ListTopologyIds\022\022.cont" +
-      "ext.ContextId\032\027.context.TopologyIdList\"\000" +
-      "\022=\n\016ListTopologies\022\022.context.ContextId\032\025" +
-      ".context.TopologyList\"\000\0227\n\013GetTopology\022\023" +
-      ".context.TopologyId\032\021.context.Topology\"\000" +
-      "\022E\n\022GetTopologyDetails\022\023.context.Topolog" +
-      "yId\032\030.context.TopologyDetails\"\000\0227\n\013SetTo" +
-      "pology\022\021.context.Topology\032\023.context.Topo" +
-      "logyId\"\000\0227\n\016RemoveTopology\022\023.context.Top" +
-      "ologyId\032\016.context.Empty\"\000\022?\n\021GetTopology" +
-      "Events\022\016.context.Empty\032\026.context.Topolog" +
-      "yEvent\"\0000\001\0228\n\rListDeviceIds\022\016.context.Em" +
-      "pty\032\025.context.DeviceIdList\"\000\0224\n\013ListDevi" +
-      "ces\022\016.context.Empty\032\023.context.DeviceList" +
-      "\"\000\0221\n\tGetDevice\022\021.context.DeviceId\032\017.con" +
-      "text.Device\"\000\0221\n\tSetDevice\022\017.context.Dev" +
-      "ice\032\021.context.DeviceId\"\000\0223\n\014RemoveDevice" +
-      "\022\021.context.DeviceId\032\016.context.Empty\"\000\022;\n" +
-      "\017GetDeviceEvents\022\016.context.Empty\032\024.conte" +
-      "xt.DeviceEvent\"\0000\001\022<\n\014SelectDevice\022\025.con" +
-      "text.DeviceFilter\032\023.context.DeviceList\"\000" +
-      "\022I\n\021ListEndPointNames\022\027.context.EndPoint" +
-      "IdList\032\031.context.EndPointNameList\"\000\0224\n\013L" +
-      "istLinkIds\022\016.context.Empty\032\023.context.Lin" +
-      "kIdList\"\000\0220\n\tListLinks\022\016.context.Empty\032\021" +
-      ".context.LinkList\"\000\022+\n\007GetLink\022\017.context" +
-      ".LinkId\032\r.context.Link\"\000\022+\n\007SetLink\022\r.co" +
-      "ntext.Link\032\017.context.LinkId\"\000\022/\n\nRemoveL" +
-      "ink\022\017.context.LinkId\032\016.context.Empty\"\000\0227" +
-      "\n\rGetLinkEvents\022\016.context.Empty\032\022.contex" +
-      "t.LinkEvent\"\0000\001\022>\n\016ListServiceIds\022\022.cont" +
-      "ext.ContextId\032\026.context.ServiceIdList\"\000\022" +
-      ":\n\014ListServices\022\022.context.ContextId\032\024.co" +
-      "ntext.ServiceList\"\000\0224\n\nGetService\022\022.cont" +
-      "ext.ServiceId\032\020.context.Service\"\000\0224\n\nSet" +
-      "Service\022\020.context.Service\032\022.context.Serv" +
-      "iceId\"\000\0226\n\014UnsetService\022\020.context.Servic" +
-      "e\032\022.context.ServiceId\"\000\0225\n\rRemoveService" +
-      "\022\022.context.ServiceId\032\016.context.Empty\"\000\022=" +
-      "\n\020GetServiceEvents\022\016.context.Empty\032\025.con" +
-      "text.ServiceEvent\"\0000\001\022?\n\rSelectService\022\026" +
-      ".context.ServiceFilter\032\024.context.Service" +
-      "List\"\000\022:\n\014ListSliceIds\022\022.context.Context" +
-      "Id\032\024.context.SliceIdList\"\000\0226\n\nListSlices" +
-      "\022\022.context.ContextId\032\022.context.SliceList" +
-      "\"\000\022.\n\010GetSlice\022\020.context.SliceId\032\016.conte" +
-      "xt.Slice\"\000\022.\n\010SetSlice\022\016.context.Slice\032\020" +
-      ".context.SliceId\"\000\0220\n\nUnsetSlice\022\016.conte" +
-      "xt.Slice\032\020.context.SliceId\"\000\0221\n\013RemoveSl" +
-      "ice\022\020.context.SliceId\032\016.context.Empty\"\000\022" +
-      "9\n\016GetSliceEvents\022\016.context.Empty\032\023.cont" +
-      "ext.SliceEvent\"\0000\001\0229\n\013SelectSlice\022\024.cont" +
-      "ext.SliceFilter\032\022.context.SliceList\"\000\022D\n" +
-      "\021ListConnectionIds\022\022.context.ServiceId\032\031" +
-      ".context.ConnectionIdList\"\000\022@\n\017ListConne" +
-      "ctions\022\022.context.ServiceId\032\027.context.Con" +
-      "nectionList\"\000\022=\n\rGetConnection\022\025.context" +
-      ".ConnectionId\032\023.context.Connection\"\000\022=\n\r" +
-      "SetConnection\022\023.context.Connection\032\025.con" +
-      "text.ConnectionId\"\000\022;\n\020RemoveConnection\022" +
-      "\025.context.ConnectionId\032\016.context.Empty\"\000" +
-      "\022C\n\023GetConnectionEvents\022\016.context.Empty\032" +
-      "\030.context.ConnectionEvent\"\0000\001b\006proto3"
+      "\020\007\022 \n\034DEVICEDRIVER_GNMI_OPENCONFIG\020\010\022\032\n\026" +
+      "DEVICEDRIVER_FLEXSCALE\020\t\022\032\n\026DEVICEDRIVER" +
+      "_IETF_ACTN\020\n*\217\001\n\033DeviceOperationalStatus" +
+      "Enum\022%\n!DEVICEOPERATIONALSTATUS_UNDEFINE" +
+      "D\020\000\022$\n DEVICEOPERATIONALSTATUS_DISABLED\020" +
+      "\001\022#\n\037DEVICEOPERATIONALSTATUS_ENABLED\020\002*\252" +
+      "\001\n\017ServiceTypeEnum\022\027\n\023SERVICETYPE_UNKNOW" +
+      "N\020\000\022\024\n\020SERVICETYPE_L3NM\020\001\022\024\n\020SERVICETYPE" +
+      "_L2NM\020\002\022)\n%SERVICETYPE_TAPI_CONNECTIVITY" +
+      "_SERVICE\020\003\022\022\n\016SERVICETYPE_TE\020\004\022\023\n\017SERVIC" +
+      "ETYPE_E2E\020\005*\304\001\n\021ServiceStatusEnum\022\033\n\027SER" +
+      "VICESTATUS_UNDEFINED\020\000\022\031\n\025SERVICESTATUS_" +
+      "PLANNED\020\001\022\030\n\024SERVICESTATUS_ACTIVE\020\002\022\032\n\026S" +
+      "ERVICESTATUS_UPDATING\020\003\022!\n\035SERVICESTATUS" +
+      "_PENDING_REMOVAL\020\004\022\036\n\032SERVICESTATUS_SLA_" +
+      "VIOLATED\020\005*\251\001\n\017SliceStatusEnum\022\031\n\025SLICES" +
+      "TATUS_UNDEFINED\020\000\022\027\n\023SLICESTATUS_PLANNED" +
+      "\020\001\022\024\n\020SLICESTATUS_INIT\020\002\022\026\n\022SLICESTATUS_" +
+      "ACTIVE\020\003\022\026\n\022SLICESTATUS_DEINIT\020\004\022\034\n\030SLIC" +
+      "ESTATUS_SLA_VIOLATED\020\005*]\n\020ConfigActionEn" +
+      "um\022\032\n\026CONFIGACTION_UNDEFINED\020\000\022\024\n\020CONFIG" +
+      "ACTION_SET\020\001\022\027\n\023CONFIGACTION_DELETE\020\002*m\n" +
+      "\024ConstraintActionEnum\022\036\n\032CONSTRAINTACTIO" +
+      "N_UNDEFINED\020\000\022\030\n\024CONSTRAINTACTION_SET\020\001\022" +
+      "\033\n\027CONSTRAINTACTION_DELETE\020\002*\203\002\n\022Isolati" +
+      "onLevelEnum\022\020\n\014NO_ISOLATION\020\000\022\026\n\022PHYSICA" +
+      "L_ISOLATION\020\001\022\025\n\021LOGICAL_ISOLATION\020\002\022\025\n\021" +
+      "PROCESS_ISOLATION\020\003\022\035\n\031PHYSICAL_MEMORY_I" +
+      "SOLATION\020\004\022\036\n\032PHYSICAL_NETWORK_ISOLATION" +
+      "\020\005\022\036\n\032VIRTUAL_RESOURCE_ISOLATION\020\006\022\037\n\033NE" +
+      "TWORK_FUNCTIONS_ISOLATION\020\007\022\025\n\021SERVICE_I" +
+      "SOLATION\020\0102\245\026\n\016ContextService\022:\n\016ListCon" +
+      "textIds\022\016.context.Empty\032\026.context.Contex" +
+      "tIdList\"\000\0226\n\014ListContexts\022\016.context.Empt" +
+      "y\032\024.context.ContextList\"\000\0224\n\nGetContext\022" +
+      "\022.context.ContextId\032\020.context.Context\"\000\022" +
+      "4\n\nSetContext\022\020.context.Context\032\022.contex" +
+      "t.ContextId\"\000\0225\n\rRemoveContext\022\022.context" +
+      ".ContextId\032\016.context.Empty\"\000\022=\n\020GetConte" +
+      "xtEvents\022\016.context.Empty\032\025.context.Conte" +
+      "xtEvent\"\0000\001\022@\n\017ListTopologyIds\022\022.context" +
+      ".ContextId\032\027.context.TopologyIdList\"\000\022=\n" +
+      "\016ListTopologies\022\022.context.ContextId\032\025.co" +
+      "ntext.TopologyList\"\000\0227\n\013GetTopology\022\023.co" +
+      "ntext.TopologyId\032\021.context.Topology\"\000\022E\n" +
+      "\022GetTopologyDetails\022\023.context.TopologyId" +
+      "\032\030.context.TopologyDetails\"\000\0227\n\013SetTopol" +
+      "ogy\022\021.context.Topology\032\023.context.Topolog" +
+      "yId\"\000\0227\n\016RemoveTopology\022\023.context.Topolo" +
+      "gyId\032\016.context.Empty\"\000\022?\n\021GetTopologyEve" +
+      "nts\022\016.context.Empty\032\026.context.TopologyEv" +
+      "ent\"\0000\001\0228\n\rListDeviceIds\022\016.context.Empty" +
+      "\032\025.context.DeviceIdList\"\000\0224\n\013ListDevices" +
+      "\022\016.context.Empty\032\023.context.DeviceList\"\000\022" +
+      "1\n\tGetDevice\022\021.context.DeviceId\032\017.contex" +
+      "t.Device\"\000\0221\n\tSetDevice\022\017.context.Device" +
+      "\032\021.context.DeviceId\"\000\0223\n\014RemoveDevice\022\021." +
+      "context.DeviceId\032\016.context.Empty\"\000\022;\n\017Ge" +
+      "tDeviceEvents\022\016.context.Empty\032\024.context." +
+      "DeviceEvent\"\0000\001\022<\n\014SelectDevice\022\025.contex" +
+      "t.DeviceFilter\032\023.context.DeviceList\"\000\022I\n" +
+      "\021ListEndPointNames\022\027.context.EndPointIdL" +
+      "ist\032\031.context.EndPointNameList\"\000\0224\n\013List" +
+      "LinkIds\022\016.context.Empty\032\023.context.LinkId" +
+      "List\"\000\0220\n\tListLinks\022\016.context.Empty\032\021.co" +
+      "ntext.LinkList\"\000\022+\n\007GetLink\022\017.context.Li" +
+      "nkId\032\r.context.Link\"\000\022+\n\007SetLink\022\r.conte" +
+      "xt.Link\032\017.context.LinkId\"\000\022/\n\nRemoveLink" +
+      "\022\017.context.LinkId\032\016.context.Empty\"\000\0227\n\rG" +
+      "etLinkEvents\022\016.context.Empty\032\022.context.L" +
+      "inkEvent\"\0000\001\022>\n\016ListServiceIds\022\022.context" +
+      ".ContextId\032\026.context.ServiceIdList\"\000\022:\n\014" +
+      "ListServices\022\022.context.ContextId\032\024.conte" +
+      "xt.ServiceList\"\000\0224\n\nGetService\022\022.context" +
+      ".ServiceId\032\020.context.Service\"\000\0224\n\nSetSer" +
+      "vice\022\020.context.Service\032\022.context.Service" +
+      "Id\"\000\0226\n\014UnsetService\022\020.context.Service\032\022" +
+      ".context.ServiceId\"\000\0225\n\rRemoveService\022\022." +
+      "context.ServiceId\032\016.context.Empty\"\000\022=\n\020G" +
+      "etServiceEvents\022\016.context.Empty\032\025.contex" +
+      "t.ServiceEvent\"\0000\001\022?\n\rSelectService\022\026.co" +
+      "ntext.ServiceFilter\032\024.context.ServiceLis" +
+      "t\"\000\022:\n\014ListSliceIds\022\022.context.ContextId\032" +
+      "\024.context.SliceIdList\"\000\0226\n\nListSlices\022\022." +
+      "context.ContextId\032\022.context.SliceList\"\000\022" +
+      ".\n\010GetSlice\022\020.context.SliceId\032\016.context." +
+      "Slice\"\000\022.\n\010SetSlice\022\016.context.Slice\032\020.co" +
+      "ntext.SliceId\"\000\0220\n\nUnsetSlice\022\016.context." +
+      "Slice\032\020.context.SliceId\"\000\0221\n\013RemoveSlice" +
+      "\022\020.context.SliceId\032\016.context.Empty\"\000\0229\n\016" +
+      "GetSliceEvents\022\016.context.Empty\032\023.context" +
+      ".SliceEvent\"\0000\001\0229\n\013SelectSlice\022\024.context" +
+      ".SliceFilter\032\022.context.SliceList\"\000\022D\n\021Li" +
+      "stConnectionIds\022\022.context.ServiceId\032\031.co" +
+      "ntext.ConnectionIdList\"\000\022@\n\017ListConnecti" +
+      "ons\022\022.context.ServiceId\032\027.context.Connec" +
+      "tionList\"\000\022=\n\rGetConnection\022\025.context.Co" +
+      "nnectionId\032\023.context.Connection\"\000\022=\n\rSet" +
+      "Connection\022\023.context.Connection\032\025.contex" +
+      "t.ConnectionId\"\000\022;\n\020RemoveConnection\022\025.c" +
+      "ontext.ConnectionId\032\016.context.Empty\"\000\022C\n" +
+      "\023GetConnectionEvents\022\016.context.Empty\032\030.c" +
+      "ontext.ConnectionEvent\"\0000\001b\006proto3"
     };
     descriptor = com.google.protobuf.Descriptors.FileDescriptor
       .internalBuildGeneratedFileFrom(descriptorData,
diff --git a/src/ztp/target/kubernetes/kubernetes.yml b/src/ztp/target/kubernetes/kubernetes.yml
index f3e4a6d6dda261c4eac983552b01bb6a4f901e9f..d2a59eb05f056d69f021d897e89f1ab9cbb102ce 100644
--- a/src/ztp/target/kubernetes/kubernetes.yml
+++ b/src/ztp/target/kubernetes/kubernetes.yml
@@ -3,8 +3,8 @@ apiVersion: v1
 kind: Service
 metadata:
   annotations:
-    app.quarkus.io/commit-id: 46486023929121fc955e9550fc8fd625ded433d2
-    app.quarkus.io/build-timestamp: 2023-12-15 - 12:04:12 +0000
+    app.quarkus.io/commit-id: 5f8866be9cb91871607627819258b0b375410467
+    app.quarkus.io/build-timestamp: 2024-01-26 - 16:39:32 +0000
     prometheus.io/scrape: "true"
     prometheus.io/path: /q/metrics
     prometheus.io/port: "8080"
@@ -15,12 +15,12 @@ metadata:
   name: ztpservice
 spec:
   ports:
-    - name: grpc-server
-      port: 5050
-      targetPort: 5050
     - name: http
       port: 9192
       targetPort: 8080
+    - name: grpc-server
+      port: 5050
+      targetPort: 5050
   selector:
     app.kubernetes.io/name: ztpservice
   type: ClusterIP
@@ -29,8 +29,8 @@ apiVersion: apps/v1
 kind: Deployment
 metadata:
   annotations:
-    app.quarkus.io/commit-id: 46486023929121fc955e9550fc8fd625ded433d2
-    app.quarkus.io/build-timestamp: 2023-12-15 - 12:04:12 +0000
+    app.quarkus.io/commit-id: 5f8866be9cb91871607627819258b0b375410467
+    app.quarkus.io/build-timestamp: 2024-01-26 - 16:39:32 +0000
     prometheus.io/scrape: "true"
     prometheus.io/path: /q/metrics
     prometheus.io/port: "8080"
@@ -47,8 +47,8 @@ spec:
   template:
     metadata:
       annotations:
-        app.quarkus.io/commit-id: 46486023929121fc955e9550fc8fd625ded433d2
-        app.quarkus.io/build-timestamp: 2023-12-15 - 12:04:12 +0000
+        app.quarkus.io/commit-id: 5f8866be9cb91871607627819258b0b375410467
+        app.quarkus.io/build-timestamp: 2024-01-26 - 16:39:32 +0000
         prometheus.io/scrape: "true"
         prometheus.io/path: /q/metrics
         prometheus.io/port: "8080"
@@ -81,12 +81,12 @@ spec:
             timeoutSeconds: 10
           name: ztpservice
           ports:
-            - containerPort: 5050
-              name: grpc-server
-              protocol: TCP
             - containerPort: 8080
               name: http
               protocol: TCP
+            - containerPort: 5050
+              name: grpc-server
+              protocol: TCP
           readinessProbe:
             failureThreshold: 3
             httpGet: