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/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/ietf_actn/IetfActnDriver.py b/src/device/service/drivers/ietf_actn/IetfActnDriver.py
index a33c403f3202ca5ee3025a7b7808ad53a89ede4a..5f80f5333cc55ccddebd971d4aadbfa1c195ee21 100644
--- a/src/device/service/drivers/ietf_actn/IetfActnDriver.py
+++ b/src/device/service/drivers/ietf_actn/IetfActnDriver.py
@@ -16,7 +16,7 @@ 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_SERVICES
+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
@@ -25,6 +25,7 @@ from .Tools import get_etht_services, get_osu_tunnels, parse_resource_key
 LOGGER = logging.getLogger(__name__)
 
 ALL_RESOURCE_KEYS = [
+    RESOURCE_ENDPOINTS,
     RESOURCE_SERVICES,
 ]
 
@@ -78,7 +79,12 @@ class IetfActnDriver(_Driver):
                 try:
                     _results = list()
 
-                    if resource_key == RESOURCE_SERVICES:
+                    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:
diff --git a/src/device/tests/data/ietf_actn/config_rules.json b/src/device/tests/data/ietf_actn/config_rules.json
index d73a68674731cf4af7321044d345470c03d68134..d106a5a8f9eb705e975c14aacd489ecfa042625f 100644
--- a/src/device/tests/data/ietf_actn/config_rules.json
+++ b/src/device/tests/data/ietf_actn/config_rules.json
@@ -26,7 +26,7 @@
             ["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.101.22", 24, "172.10.33.5"]
+            ["172.1.201.22", 24, "172.10.33.5"]
         ]
     }}}
 ]
diff --git a/src/device/tests/data/ietf_actn/expected_etht_services.json b/src/device/tests/data/ietf_actn/expected_etht_services.json
index d9f41052692936fd5fffd855b1ba3f3e0478a3b6..72c48e6b3351a4adc5737bbc1f63952363893f96 100644
--- a/src/device/tests/data/ietf_actn/expected_etht_services.json
+++ b/src/device/tests/data/ietf_actn/expected_etht_services.json
@@ -139,7 +139,7 @@
                             "is-terminal": true,
                             "static-route-list": [
                                 {
-                                    "destination": "172.1.101.22",
+                                    "destination": "172.1.201.22",
                                     "destination-mask": 24,
                                     "next-hop": "172.10.33.5"
                                 }
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/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/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_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/MockIetfActnSdnCtrl.py b/src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py
index c459c294ccb1c457c258a4e80018a90244702b8b..26243e2b6bd8e8d8bd2b64e9552be7d9d36c853e 100644
--- a/src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py
+++ b/src/tests/tools/mock_ietf_actn_sdn_ctrl/MockIetfActnSdnCtrl.py
@@ -29,7 +29,7 @@ from ResourceOsuTunnels import OsuTunnel, OsuTunnels
 
 BIND_ADDRESS = '0.0.0.0'
 BIND_PORT    = 8443
-BASE_URL     = '/restconf/data'
+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
 
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