diff --git a/my_deploy.sh b/my_deploy.sh
index 10f15bbbddc775273a72eb7b739bc6d9a8786d5e..f1b4b4f92f5370bc144e8aa4a48b2eee26f9c6b0 100755
--- a/my_deploy.sh
+++ b/my_deploy.sh
@@ -20,7 +20,7 @@
 export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/"
 
 # Set the list of components, separated by spaces, you want to build images for, and deploy.
-export TFS_COMPONENTS="alto context device pathcomp service slice nbi webui load_generator"
+export TFS_COMPONENTS=" context device pathcomp service slice nbi webui load_generator"
 
 # Uncomment to activate Monitoring
 #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring"
diff --git a/proto/context.proto b/proto/context.proto
index 9cfe8fe07c87926530f8973c964d68dd0d3fac83..40f54b13588ec40b244a5cfed0171069796faa59 100644
--- a/proto/context.proto
+++ b/proto/context.proto
@@ -16,6 +16,7 @@ syntax = "proto3";
 package context;
 
 import "acl.proto";
+import "ip_link.proto";
 import "kpi_sample_types.proto";
 
 service ContextService {
@@ -513,9 +514,9 @@ message ConfigRule_ACL {
   acl.AclRuleSet rule_set = 2;
 }
 
-message ConfigRule_IP_LIK {
-  EndPointId endpoint_id = 1;
-  string  subnet_ip = 2;
+message ConfigRule_IP_LINK {
+  EndPointId endpoint_id           = 1;
+  ip_link.IpLinkRuleSet rule_set = 2;
 }
 
 message ConfigRule {
@@ -523,7 +524,7 @@ message ConfigRule {
   oneof config_rule {
     ConfigRule_Custom custom  = 2;
     ConfigRule_ACL acl        = 3;
-    ConfigRule_IP_LIK ip_link = 4;
+    ConfigRule_IP_LINK ip_link = 4;
   }
 }
 
diff --git a/proto/ip_link.proto b/proto/ip_link.proto
new file mode 100644
index 0000000000000000000000000000000000000000..79a5bed5adbc749ba3e200b44d2cafea6bac7615
--- /dev/null
+++ b/proto/ip_link.proto
@@ -0,0 +1,24 @@
+// Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+package ip_link;
+
+
+
+message IpLinkRuleSet {
+  string  ip   = 1;
+  string  mask = 3;
+  string  vlan = 4;
+}
diff --git a/src/common/tools/object_factory/Service.py b/src/common/tools/object_factory/Service.py
index 32b99a31f22072874ab894de2a87ce2b7d56ba85..642aa5a032b88bc10eea99cb174f76d5b2884d0f 100644
--- a/src/common/tools/object_factory/Service.py
+++ b/src/common/tools/object_factory/Service.py
@@ -80,4 +80,14 @@ def json_service_p4_planned(
     return json_service(
         service_uuid, ServiceTypeEnum.SERVICETYPE_L2NM, context_id=json_context_id(context_uuid),
         status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints,
+        config_rules=config_rules)
+    
+def json_service_iplink_planned(
+        service_uuid : str, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [],
+        config_rules : List[Dict] = [], context_uuid : str = DEFAULT_CONTEXT_NAME
+    ):
+
+    return json_service(
+        service_uuid, ServiceTypeEnum.SERVICETYPE_IPLINK, context_id=json_context_id(context_uuid),
+        status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints,
         config_rules=config_rules)
\ No newline at end of file
diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py
index 90b7d976b0f6fff9d478ce7b40188240a8eea2d6..be4ae7a635a5e42cbb829ba9cbd8c492f01e3deb 100644
--- a/src/common/type_checkers/Assertions.py
+++ b/src/common/type_checkers/Assertions.py
@@ -116,6 +116,7 @@ def validate_uuid(message, allow_empty=False):
 CONFIG_RULE_TYPES = {
     'custom',
     'acl',
+    'ip_link'
 }
 def validate_config_rule(message):
     assert isinstance(message, dict)
diff --git a/src/context/service/database/ConfigRule.py b/src/context/service/database/ConfigRule.py
index 0f204c505005fdfe84755c3d179f3cea56c75355..e2b19420a617617cf1d4369befce20b9d45ff842 100644
--- a/src/context/service/database/ConfigRule.py
+++ b/src/context/service/database/ConfigRule.py
@@ -68,7 +68,7 @@ def compose_config_rules_data(
             _, _, endpoint_uuid = endpoint_get_uuid(config_rule.acl.endpoint_id, allow_random=False)
             rule_set_name = config_rule.acl.rule_set.name
             configrule_name = '{:s}:{:s}:{:s}:{:s}'.format(parent_kind, kind.value, endpoint_uuid, rule_set_name)
-        elif kind == ConfigRuleKindEnum.IP_LINK:
+        elif kind == ConfigRuleKindEnum.IP_LINK: 
             _, _, endpoint_uuid = endpoint_get_uuid(config_rule.ip_link.endpoint_id, allow_random=False)
             configrule_name = '{:s}:{:s}:{:s}'.format(parent_kind, kind.value, endpoint_uuid)
         else:
diff --git a/src/context/service/database/models/ConfigRuleModel.py b/src/context/service/database/models/ConfigRuleModel.py
index 73a667e6bc1d19a8182ff3f836e872c42766728d..5799934a76c0240535984ecf297b0baf54426450 100644
--- a/src/context/service/database/models/ConfigRuleModel.py
+++ b/src/context/service/database/models/ConfigRuleModel.py
@@ -23,7 +23,7 @@ from ._Base import _Base
 class ConfigRuleKindEnum(enum.Enum):
     CUSTOM  = 'custom'
     ACL     = 'acl'
-    IP_LINK = 'ip_link'
+    IP_LINK = 'ip_link' 
 
 class DeviceConfigRuleModel(_Base):
     __tablename__ = 'device_configrule'
diff --git a/src/context/service/database/models/enums/ServiceType.py b/src/context/service/database/models/enums/ServiceType.py
index 62d5380b56803b3cc21dd1456292ec9df470cb15..bcc5aff09da2899470d6c56e82cad5a6d0f00f95 100644
--- a/src/context/service/database/models/enums/ServiceType.py
+++ b/src/context/service/database/models/enums/ServiceType.py
@@ -29,6 +29,7 @@ class ORM_ServiceTypeEnum(enum.Enum):
     TE                        = ServiceTypeEnum.SERVICETYPE_TE
     E2E                       = ServiceTypeEnum.SERVICETYPE_E2E
     OPTICAL_CONNECTIVITY      = ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY
+    IP_LINK                   = ServiceTypeEnum.SERVICETYPE_IPLINK
 
 grpc_to_enum__service_type = functools.partial(
     grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum)
diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py
index 91926b9e59cccac2e233ac14bbac497bbb0ac15c..8eb25578488dd0fe5b1781a0e129709b86df69fa 100644
--- a/src/device/service/Tools.py
+++ b/src/device/service/Tools.py
@@ -306,7 +306,13 @@ def compute_rules_to_add_delete(
             ACL_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/acl_ruleset[{:s}]'
             key_or_path = ACL_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, acl_ruleset_name)            
             context_config_rules[key_or_path] = grpc_message_to_json(config_rule.acl)    # get the resource value of the acl
- 
+        elif config_rule_kind == 'ip_link':
+            device_uuid = config_rule.ip_link.endpoint_id.device_id.device_uuid.uuid # get the device name
+            endpoint_uuid = config_rule.ip_link.endpoint_id.endpoint_uuid.uuid       # get the endpoint name    request_config_rules = []
+            ip_link_ruleset_name = config_rule.ip_link.rule_set.name                     # get the ip_link name
+            IP_LINK_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/ip_link_ruleset[{:s}]'
+            key_or_path = IP_LINK_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, ip_link_ruleset_name)            
+            context_config_rules[key_or_path] = grpc_message_to_json(config_rule.ip_link)    # get the resource value of the ip_link
     request_config_rules = []
     for config_rule in request.device_config.config_rules:
         config_rule_kind = config_rule.WhichOneof('config_rule')
@@ -323,6 +329,15 @@ def compute_rules_to_add_delete(
             request_config_rules.append((
                 config_rule.action, key_or_path, grpc_message_to_json(config_rule.acl)
             ))
+        elif config_rule_kind == 'ip_link':  # resource management of "ip_link" rule  
+            device_uuid = config_rule.ip_link.endpoint_id.device_id.device_uuid.uuid
+            endpoint_uuid = config_rule.ip_link.endpoint_id.endpoint_uuid.uuid
+            ip_link_ruleset_name = config_rule.ip_link.rule_set.name
+            IP_LINK_KEY_TEMPLATE = '/device[{:s}]/endpoint[{:s}]/ip_link_ruleset[{:s}]'
+            key_or_path = IP_LINK_KEY_TEMPLATE.format(device_uuid, endpoint_uuid, ip_link_ruleset_name) 
+            request_config_rules.append((
+                config_rule.action, key_or_path, grpc_message_to_json(config_rule.ip_link)
+            ))
 
     resources_to_set    : List[Tuple[str, Any]] = [] # key, value
     resources_to_delete : List[Tuple[str, Any]] = [] # key, value
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json
deleted file mode 100644
index e57011be614cc6af8bc011b4fd8581e86f0856c5..0000000000000000000000000000000000000000
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json
+++ /dev/null
@@ -1,231 +0,0 @@
-{
-   "l3vpn-svc": {
-      "vpn-services": {
-         "vpn-service": [
-            {
-               "vpn-id": "vpn2"
-            }
-         ]
-      },
-      "sites": {
-         "site": [
-            {
-               "site-id": "site_OLT",
-               "locations": {
-                  "location": [
-                     {
-                        "location-id": "OLT"
-                     }
-                  ]
-               },
-               "devices": {
-                  "device": [
-                     {
-                        "device-id": "128.32.33.5",
-                        "location": "OLT"
-                     }
-                  ]
-               },
-               "management": {
-                  "type": "ietf-l3vpn-svc:provider-managed"
-               },
-               "routing-protocols": {
-                  "routing-protocol": [
-                     {
-                        "type": "ietf-l3vpn-svc:static",
-                        "static": {
-                           "cascaded-lan-prefixes": {
-                              "ipv4-lan-prefixes": [
-                                 {
-                                    "lan": "128.32.10.0/24",
-                                    "next-hop": "128.32.33.2",
-                                    "lan-tag": "vlan31"
-                                 },
-                                 {
-                                    "lan": "128.32.20.0/24",
-                                    "next-hop": "128.32.33.2",
-                                    "lan-tag": "vlan31"
-                                 }
-                              ]
-                           }
-                        }
-                     }
-                  ]
-               },
-               "site-network-accesses": {
-                  "site-network-access": [
-                     {
-                        "site-network-access-id": "500",
-                        "site-network-access-type": "ietf-l3vpn-svc:multipoint",
-                        "device-reference": "128.32.33.5",
-                        "ip-connection": {
-                           "ipv4": {
-                              "address-allocation-type": "ietf-l3vpn-svc:static-address",
-                              "addresses": {
-                                 "provider-address": "128.32.33.254",
-                                 "customer-address": "128.32.33.2",
-                                 "prefix-length": 24
-                              }
-                           }
-                        },
-                        "service": {
-                           "svc-input-bandwidth": 1000000000,
-                           "svc-output-bandwidth": 1000000000,
-                           "svc-mtu": 1500,
-                           "qos": {
-                              "qos-profile": {
-                                 "classes": {
-                                    "class": [
-                                       {
-                                          "class-id": "qos-realtime",
-                                          "direction": "ietf-l3vpn-svc:both",
-                                          "latency": {
-                                             "latency-boundary": 10
-                                          },
-                                          "bandwidth": {
-                                             "guaranteed-bw-percent": 100.0
-                                          }
-                                       }
-                                    ]
-                                 }
-                              }
-                           }
-                        },
-                        "routing-protocols": {
-                           "routing-protocol": [
-                              {
-                                 "type": "ietf-l3vpn-svc:static",
-                                 "static": {
-                                    "cascaded-lan-prefixes": {
-                                       "ipv4-lan-prefixes": [
-                                          {
-                                             "lan": "172.1.201.0/24",
-                                             "next-hop": "128.32.33.254",
-                                             "lan-tag": "vlan31"
-                                          }
-                                       ]
-                                    }
-                                 }
-                              }
-                           ]
-                        },
-                        "vpn-attachment": {
-                           "vpn-id": "vpn2",
-                           "site-role": "ietf-l3vpn-svc:spoke-role"
-                        }
-                     }
-                  ]
-               }
-            },
-            {
-               "site-id": "site_POP",
-               "locations": {
-                  "location": [
-                     {
-                        "location-id": "POP"
-                     }
-                  ]
-               },
-               "devices": {
-                  "device": [
-                     {
-                        "device-id": "172.10.33.5",
-                        "location": "POP"
-                     }
-                  ]
-               },
-               "management": {
-                  "type": "ietf-l3vpn-svc:provider-managed"
-               },
-               "routing-protocols": {
-                  "routing-protocol": [
-                     {
-                        "type": "ietf-l3vpn-svc:static",
-                        "static": {
-                           "cascaded-lan-prefixes": {
-                              "ipv4-lan-prefixes": [
-                                 {
-                                    "lan": "172.1.201.0/24",
-                                    "next-hop": "172.10.33.2",
-                                    "lan-tag": "vlan201"
-                                 }
-                              ]
-                           }
-                        }
-                     }
-                  ]
-               },
-               "site-network-accesses": {
-                  "site-network-access": [
-                     {
-                        "site-network-access-id": "500",
-                        "site-network-access-type": "ietf-l3vpn-svc:multipoint",
-                        "device-reference": "172.10.33.5",
-                        "ip-connection": {
-                           "ipv4": {
-                              "address-allocation-type": "ietf-l3vpn-svc:static-address",
-                              "addresses": {
-                                 "provider-address": "172.10.33.254",
-                                 "customer-address": "172.10.33.2",
-                                 "prefix-length": 24
-                              }
-                           }
-                        },
-                        "service": {
-                           "svc-input-bandwidth": 1000000000,
-                           "svc-output-bandwidth": 1000000000,
-                           "svc-mtu": 1500,
-                           "qos": {
-                              "qos-profile": {
-                                 "classes": {
-                                    "class": [
-                                       {
-                                          "class-id": "qos-realtime",
-                                          "direction": "ietf-l3vpn-svc:both",
-                                          "latency": {
-                                             "latency-boundary": 10
-                                          },
-                                          "bandwidth": {
-                                             "guaranteed-bw-percent": 100.0
-                                          }
-                                       }
-                                    ]
-                                 }
-                              }
-                           }
-                        },
-                        "routing-protocols": {
-                           "routing-protocol": [
-                              {
-                                 "type": "ietf-l3vpn-svc:static",
-                                 "static": {
-                                    "cascaded-lan-prefixes": {
-                                       "ipv4-lan-prefixes": [
-                                          {
-                                             "lan": "128.32.10.0/24",
-                                             "next-hop": "172.10.33.254",
-                                             "lan-tag": "vlan201"
-                                          },
-                                          {
-                                             "lan": "128.32.20.0/24",
-                                             "next-hop": "172.10.33.254",
-                                             "lan-tag": "vlan201"
-                                          }
-                                       ]
-                                    }
-                                 }
-                              }
-                           ]
-                        },
-                        "vpn-attachment": {
-                           "vpn-id": "vpn2",
-                           "site-role": "ietf-l3vpn-svc:hub-role"
-                        }
-                     }
-                  ]
-               }
-            }
-         ]
-      }
-   }
-}
\ No newline at end of file
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinition.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinition.py
deleted file mode 100644
index 2a42d2578efd7194a713804d8fe6a44bea3ec36a..0000000000000000000000000000000000000000
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/FunctionDefinition.py
+++ /dev/null
@@ -1,426 +0,0 @@
-from typing import Dict, List
-
-
-class FunctionDefinition:
-        
-    #Metodo para leer el campo vpn-services
-    @staticmethod  
-    def readVpnServices(vpnServicesALeer: Dict) -> List:
-        # Acceder al campo 'vpn-service' que es un array de Dict con un campo vpn-id
-        vpn_service_list = vpnServicesALeer.get('vpn-service', [])#Utilizar get para manejar casos donde 'vpn-service' no está presente
-        vpn_ids = [] #Array con todos los ID de cada VPN
-        
-        # Iterar sobre los elementos de la lista
-        for vpn_serviceEnFor in vpn_service_list:
-            vpn_id : str = vpn_serviceEnFor.get('vpn-id') #obtengo vpn-id del json iterando
-            # baseDatos["vpn-id_" + str(i)] = vpn_id  # Utilizar f-strings para formatear las claves
-            vpn_ids.append(vpn_id)
-            
-        return vpn_ids
-    
-    #Metodo para escribir en la baseDatos lo leido en vpn-services
-    @staticmethod
-    def writeVpnServices(listaVPNs : List, baseDatos : Dict) -> Dict:
-        
-        baseDatos["VPN_IDs"] = listaVPNs
-        #baseDatos.update({"VPN_IDs": vpn_ids}) #ambas lineas son validas para añadir al Dict
-        baseDatos["VPNs_Dict"] = {}
-        #Obtengo cada vpn-id del array y lo meto en VPNs_Dict
-        for clave in baseDatos["VPN_IDs"]:
-            baseDatos["VPNs_Dict"][clave] = {}#ya tengo un Dict asociado a cada clave
-            
-        return baseDatos #devuelve la baseDatos pasada por argumento pero actualizada
-                    
-    #Lee el array site
-    @staticmethod
-    def readSiteArray(site: List, baseDatos: Dict) -> Dict:
-        print("Elementos en site: " + str(len(site)))
-        temp : Dict = {} #Diccionario temporal que guarda el ultimo site leido
-        temp2 : Dict = {} #Diccionario temporal que registra todos los sites leidos
-        lista_SitesID = [] #lista que almacenara los IDs de cada site del array
-        i = 1
-        for siteElement in site:
-            temp = FunctionDefinition.readSiteElement(lista_SitesID, siteElement) #Leo un site y me quedo con ID y Dict
-            temp2.update({lista_SitesID[-1] : temp}) # Almaceno esos datos en temp2
-            i += 1            
-       
-        res : Dict = {}
-        res["sitesIDs"] = lista_SitesID
-        res["sites_Dict"] = temp2 # A sites_Dict se le añaden todos los sites
-        return res
-            
-    #Lee un site
-    @staticmethod
-    def readSiteElement(listaID: List, siteElement: Dict) -> Dict:
-        res: Dict = {}
-        listaID.append(siteElement.get('site-id'))
-        res.update({"site-id": siteElement.get('site-id')})
-        res.update({"management": siteElement.get("management")}) #revistar si está bien
-        res.update({"devices": FunctionDefinition.readDevices(siteElement.get('devices'))})
-        res.update({"routing-protocol" : FunctionDefinition.readRoutingProtocols(siteElement.get('routing-protocols'))})
-        res.update({"site-network-accesses" : FunctionDefinition.readSiteNetworkAccesses(siteElement.get('site-network-accesses'))})
-        return res
-    #FALTAN MUCHOS CAMPOS A LEER EN site, como locations, site-diversity, maximum-routes, security, service, traffic-protection
-
-    #Lee array de Devices. Si hay más de un device, cambiar código
-    @staticmethod
-    def readDevices(devices: Dict) -> Dict:
-        res: Dict = {}
-        devices_list: List = devices.get('device', [])  # Cambié 'devices' por 'device' aquí
-        if len(devices_list) > 1:
-            i: int = 1
-            for eachDevice in devices_list:
-                res.update({f"device-id {i}": eachDevice.get('device-id')})
-                i += 1
-        elif len(devices_list) == 1:
-            res.update({"device-id": devices_list[0].get('device-id')})
-        return res
-
-    @staticmethod
-    def readRoutingProtocols(routingProtocols : Dict) -> Dict:
-        res : Dict = {} #todo el dict routing-protocols
-        rpArray : List = routingProtocols.get("routing-protocol") #el array routing-protocol           
-        res.update({"type":rpArray[0].get("type")})#escribo el tipo en el diccionario
-        tipoRoutingProtocol : str = rpArray[0].get("type")#obtiene string "ietf-l3vpn-svc:static"
-        tipoResumido : str = tipoRoutingProtocol.split(":")[1] #se queda con la palabra, ospf, bgp, static,... sin el ietf...
-        #print("El tipo de routingProtocol es: "+ str(tipoResumido))
-        def case1():            
-            ospfEntrada : Dict = rpArray[0].get("ospf",{}) #Funciona solo en caso de que el array tenga un elemento
-            ospfIntermedio : Dict = {}
-            ospfIntermedio.update({"address-family":ospfEntrada.get("address-family",{})})
-            ospfIntermedio.update({"area-address":ospfEntrada.get("area-address",{})})
-            ospfIntermedio.update({"metric":ospfEntrada.get("metric",{})})
-            arrayShamLinksEntrada : List = ospfEntrada.get("sham-links",[]) #el array
-            shamLinksFinal : Dict = {}
-            arrayShamLinksFinal : List = []
-            for elemento in arrayShamLinksEntrada:
-                shamLinksFinal.update({"target-site":elemento.get("target-site")})
-                shamLinksFinal.update({"metric":elemento.get("metric")})
-                arrayShamLinksFinal.append(shamLinksFinal)
-                
-            ospfIntermedio.update({"sham-links":shamLinksFinal})
-                        
-            res.update({"ospf":ospfIntermedio})
-        
-        def case2():
-            bgpEntrada : Dict = rpArray[0].get("bgp",{})
-            bgpSalida : Dict = {}
-            bgpSalida.update({"autonomous-system":bgpEntrada.get("autonomous-system")})
-            bgpSalida.update({"address-family":bgpEntrada.get("address-family")})
-            res.update({"bgp":bgpSalida})
-        
-        def case3(): #SIMPLEMENTE COPIA, NO LEE. A IMPLEMENTAR
-            cascadedDict: Dict = rpArray[0].get("static", {}).get("cascaded-lan-prefixes", {})
-            valueRoutingProtocol = cascadedDict
-            res.update({"static":valueRoutingProtocol})   
-            print("se ha copiado 'static' pero no se ha leido, falta implementar")        
-             
-        def case4():
-            ripEntrada : Dict = rpArray[0].get("rip",{})
-            ripSalida : Dict = {}
-            ripSalida.update({"address-family":ripEntrada.get("address-family")})
-            res.update({"rip":ripSalida})
-            
-        def case5():
-            vrrpEntrada : Dict = rpArray[0].get("vrrp",{})
-            vrrpSalida : Dict = {}
-            vrrpSalida.update({"address-family":vrrpEntrada.get("address-family")})
-            res.update({"vrrp":vrrpSalida})        
-        
-        def default():
-            #return ValueError(f"Tipo de protocolo no compatible: {tipoResumido}")
-            print("TIPO DE PROTOCOLO NO VALIDO")
-        
-        switch = {
-            "ospf": case1,
-            "bgp": case2,
-            "static": case3,
-            "rip":case4,
-            "vrrp": case5
-        }
-        #res.update({tipoResumido: switch.get(tipoResumido, default)()})
-        switch.get(tipoResumido)()
-        # print("_______________________d____________"+str(res))
-        return res
-    
-    
-    @staticmethod
-    def readSiteNetworkAccesses(siteNetworkAccesses : Dict) -> Dict:
-        siteNetworkAccessArray : List = siteNetworkAccesses.get("site-network-access")
-        res : Dict = {} 
-        arrayDeIDs : List = []       
-        for siteNetworkAccess in siteNetworkAccessArray:
-            variableIncial : str = siteNetworkAccess.get("site-network-access-id")
-            variableIncial = variableIncial.replace(" ", "-")#ESTO LO HAGO PARA QUE NO HAYA PROBLEMAS CON LOS ESPACIOS A LA HORA DE LEER EL JSON
-            arrayDeIDs.append(variableIncial)
-        res.update({"site-network-accesses-IDs":arrayDeIDs})
-        dictSiteNetworkAccesses : Dict = {}
-        
-        for site in arrayDeIDs:
-            dictDeCadaID : Dict = {}
-            
-            # try:
-            dictDeCadaID.update({"site-network-access-type":siteNetworkAccess.get("site-network-access-type")})
-            dictDeCadaID.update({"site-role":siteNetworkAccess.get("vpn-attachment", {}).get("site-role")}) 
-            #dictDeCadaID.update({"location-flavor" : FunctionDefinition.readLocationFlavor(siteNetworkAccess.get("location-flavor"))}) DE MOMENTO DA ERROR PORQUE ESE CAMPO NO EXISTE
-            #dictDeCadaID.update({"access-diversity" : FunctionDefinition.readAccessDiversity(siteNetworkAccess.get("access-diversity"))}) CAMPO NO EXISTE AUN
-            #dictDeCadaID.update({"bearer" : FunctionDefinition.readBearer(siteNetworkAccess.get("bearer"))}) CAMPO NO EXISTE AUN
-            dictDeCadaID.update({"ip-connection":FunctionDefinition.readIpConnection(siteNetworkAccess.get("ip-connection"))})
-            dictDeCadaID.update({"routing-protocols":FunctionDefinition.readRoutingProtocols(siteNetworkAccess.get("routing-protocols"))})
-            dictDeCadaID.update({"service":FunctionDefinition.readService(siteNetworkAccess.get("service"))})
-            # except NoJSONFieldException as e:
-            #     print(e)
-                
-            dictSiteNetworkAccesses.update({site:dictDeCadaID})
-        
-        res.update({"site-network-accesses-Dict": dictSiteNetworkAccesses})       
-        return res
-    
-    @staticmethod
-    def readLocationFlavor(locationFlavor : Dict) -> Dict:
-        res : Dict = {}
-        
-        if "location" in locationFlavor:
-            res.update({"location-reference":locationFlavor.get("location", {}).get("location-reference")})
-        elif "device" in locationFlavor:
-            res.update({"device-reference":locationFlavor.get("device", {}).get("device-reference")})
-        else:
-            raise NoJSONFieldException("location-flavor")
-            
-        return res
-        
-    @staticmethod
-    def readAccessDiversity(accessDiversity : Dict) -> Dict:
-        res : Dict = {}
-        arrayGroupsIni : List = accessDiversity.get("groups",{}).get("group") #obtengo el array de objetos
-        arrayGroupsIDs : List = []
-        for groupId in arrayGroupsIni:
-            arrayGroupsIDs.append(groupId.get("group-id"))             
-        res.update({"groups":arrayGroupsIDs})
-        
-        arrayConstraintsIni : List = accessDiversity.get("constraints", {}).get("constraint")
-        arrayConstraintsFin : List = []
-        for constraint in arrayConstraintsIni: #recordar que constraint es un objeto dentro del array arrayConstraintsIni
-            dictConstraint : Dict = {}
-            dictConstraint.update({"constraint-type" : constraint.get("constraint-type")})
-            dictTargetFlavor : Dict = constraint.get("target", {}).get("target-flavor")
-            
-            if "id" in dictTargetFlavor:
-                dictConstraint.update({"target-flavor": dictTargetFlavor.get("id")})
-            elif "all-accesses" in dictTargetFlavor:
-                dictConstraint.update({"target-flavor": dictTargetFlavor.get("all-accesses")})
-            elif "all-groups" in dictTargetFlavor:
-                dictConstraint.update({"target-flavor": dictTargetFlavor.get("all-groups")}) 
-            else:
-                raise NoJSONFieldException("read-access-divesity>constraints>constraint>target>target-flavor")               
-            arrayConstraintsFin.append(dictConstraint)
-            
-        res.update({"constraints":arrayConstraintsFin})       
-        return res
-
-    @staticmethod
-    def readBearer(bearer : Dict) -> Dict:
-        res : Dict = {}
-        
-        res.update({"always-on":bearer.get("always-on")})
-        res.update({"bearer-reference":bearer.get("bearer-reference")})
-        res.update({"requested-type":bearer.get("requested-type",{}).get("requested-type")})
-        res.update({"strict":bearer.get("requested-type",{}).get("strict")})       
-        
-        return res
-    
-    @staticmethod
-    def readIpConnection(ipConnection : Dict) -> Dict: #IPV4 E IPV6 TIENEN LOS MISMOS CAMPOS
-        res : Dict = {}
-        if ipConnection.get("ipv4") is not None:
-            res.update({"ipv4": FunctionDefinition.readIpv4_Ipv6(ipConnection.get("ipv4"))})
-        elif ipConnection.get("ipv6") is not None:
-            res.update({"ipv6":FunctionDefinition.readIpv4_Ipv6(ipConnection.get("ipv6"))}) 
-        else:
-            raise NoJSONFieldException("ip-connection>ipv4/6")
-            
-        bfd : Dict = ipConnection.get("oam",{}).get("bfd")
-        if bfd is not None:
-            oamFinal : Dict = {}
-            oamFinal.update({"enabled":bfd.get("enabled")})
-            holdtime : Dict = bfd.get("holdtime")
-            holdtimeFinal : Dict = {}
-            if "fixed" in holdtime:
-                holdtimeFinal.update({"fixed":holdtime.get("fixed")})
-            elif "profile" in holdtime:
-                holdtimeFinal.update({"profile":holdtime.get("profile")})
-            else:
-                raise NoJSONFieldException("oam>bfd>holdtime")
-            
-            oamFinal.update({"holdtime":holdtimeFinal})
-            res.update({"oam":oamFinal})
-        
-        return res
-    
-    @staticmethod
-    def readIpv4_Ipv6(ipv : Dict) -> Dict :
-        res : Dict = {}
-        
-        res.update({"address-allocation-type":ipv.get("address-allocation-type")})
-        
-        providerDHCP : Dict = ipv.get("provider-dhcp") #None si no existe ese campo, puede mejorarse eficiencia
-        if providerDHCP is not None:            
-            providerDHCPFinal : Dict = {}        
-            providerDHCPFinal.update({"provider-dhcp":providerDHCP.get("provider-address")})
-            providerDHCPFinal.update({"prefix-length":providerDHCP.get("prefix-lenght")})
-            addressAsign : Dict = providerDHCP.get("address-asign")
-            addressAsignFinal : Dict = {}
-            if "number" in addressAsign:
-                addressAsignFinal.update({"number":addressAsign.get("number")})
-            elif "explicit" in addressAsign:
-                addressAsignFinal.update({"explicit":addressAsign.get("explicit")})
-            else:
-                raise NoJSONFieldException("ipv4/6>address-asign")
-            
-            providerDHCPFinal.update({"address-asign":addressAsignFinal})         
-            res.update({"provider-dhcp":providerDHCPFinal})
-        
-        
-        dhcpRelay : Dict = ipv.get("dhcp-relay")
-        if dhcpRelay is not None:
-            dhcpRelayFinal : Dict = {}
-            dhcpRelayFinal.update({"provider-address":dhcpRelay.get("provider-address")})
-            dhcpRelayFinal.update({"prefix-lenght":dhcpRelay.get("prefix-lenght")})
-            dhcpRelayFinal.update({"server-ip-address":dhcpRelay.get("customer-dhcp-servers",{}).get("server-ip-address")})
-            res.update({"dhcp-relay":dhcpRelayFinal})
-        
-        addresses : Dict = ipv.get("addresses")
-        if addresses is not None:
-            addressesFinal : Dict = {}
-            addressesFinal.update({"provider-address":addresses.get("provider-address")})
-            addressesFinal.update({"customer-address":addresses.get("customer-address")})
-            addressesFinal.update({"prefixe-length":addresses.get("prefixe-length")})
-            res.update({"addresses":addressesFinal})
-        
-        return res
-    
-    # @staticmethod
-    # def readSecurity(security : Dict) -> Dict:
-    #     res : Dict = {}
-        
-    #     res.update({"authentication" : security.get("authentication")})
-    #     encryption : Dict = security.get("encryption")
-    #     encryptionFinal : Dict = {}
-        
-        
-    #     return res
-    
-    #readService todavía me da error
-    @staticmethod
-    def readService(service : Dict) -> Dict :
-        res : Dict = {}
-        #try:                
-        
-        res.update({"svc-input-bandwidth":service.get("svc-input-bandwidth")})
-        res.update({"svc-output-bandwidth":service.get("svc-output-bandwidth")})
-        res.update({"svc-mtu":service.get("svc-mtu")})
-        qos : Dict = service.get("qos")
-        ruleArrayIni : List = qos.get("qos-classification-policy")
-        ruleArrayFin : List = []
-        for elementRule in ruleArrayIni : 
-            elementRuleFin : Dict = {}
-            elementRuleFin.update({"id":elementRule.get("id")})
-            matchTypeIni : Dict = elementRule.get("match-type")
-            #CUIDADO, FALTAN MUCHOS CAMPOS DENTRO DE match-flow Y match-application POR PROGRAMAR
-            matchTypeFin : Dict = {}
-            if "match-flow" in matchTypeIni:
-                matchTypeFin.update({"match-flow":matchTypeIni})
-            elif "match-application" in matchTypeIni:
-                matchTypeFin.update({"match-application" : matchTypeIni})
-            #else:
-                #raise NoJSONFieldException("qos>qos-classification-policy>match-type")
-                
-            
-            elementRuleFin.update({"match-type":matchTypeFin})
-            elementRuleFin.update({"target-class-id":elementRule.get("target-class-id")})
-            ruleArrayFin.append(elementRuleFin)
-        res.update({"qos-classification-policy":ruleArrayFin})
-        
-        #Clase que lee el campo custom varias lineas mas adelante
-        
-        def readCustom (custom : Dict) -> Dict: #custom es un Dict que es: {classes: [class-id: *, ..., latency: {flavor:{lowest:*///boundary:*}}],[...],[...]}
-            res : Dict = {}
-                        
-            arrayClassesEntrada : List = custom.get("class") #un array con elementos indeterminados
-            arrayClassesFinal : List = []
-            
-            for elementEntrada in arrayClassesEntrada:
-                elementFinal : Dict = {}
-                elementFinal.update({"class-id":elementEntrada.get("class-id")})
-                elementFinal.update({"direction":elementEntrada.get("direction")})
-                elementFinal.update({"rate-limit":elementEntrada.get("rate-limit")})
-                #Campo latency--->
-                    #latency: {
-                    #     flavor:{
-                    #         lowest:*
-                    #         o quizás
-                    #         boundary:*
-                    #     }
-                    # }
-                                    
-                latency : Dict = elementEntrada.get("latency")
-                jitter : Dict = elementEntrada.get("jitter")
-                if latency is not None:
-                    if "use-lowest-latency" in latency:
-                        elementFinal.update({"latency-flavor-lowest":latency.get("use-lowest-latency")})
-                    elif "latency-boundary" in latency:
-                        elementFinal.update({"latency-flavor-boundary":latency.get("latency-boundary")})
-                elif jitter is not None:
-                    if "use-lowest-jitter" in jitter:
-                        elementFinal.update({"jitter-flavor-lowest":jitter.get("use-lowest-jiter")})
-                    elif "latency-boundary" in jitter:
-                        elementFinal.update({"jitter-flavor-boundary":jitter.get("latency-boundary")})
-                    else:
-                        print("Debe haber un campo lowest o boundary dentro de jitter>flavor")
-                else:
-                    print("FALTAN CAMPOS EN CUSTOM")
-                
-                bandwidthEntrada : Dict = elementEntrada.get("bandwidth")
-                bandwidthSalida : Dict = {}
-                bandwidthSalida.update({"guaranteed-bw-percent":bandwidthEntrada.get("guaranteed-bw-percent")})
-                bandwidthSalida.update({"end-to-end":bandwidthEntrada.get("end-to-end")})
-                elementFinal.update({"bandwidth":bandwidthSalida})
-                
-                arrayClassesFinal.append(elementFinal)
-                
-            res.update({"latency-flavor":arrayClassesFinal})
-            return res
-            #END READCUSTOM
-        
-        qosProfileEntrada : Dict = qos.get("qos-profile")
-        qosProfileFin : Dict = {}
-                
-        if "profile" in qosProfileEntrada:
-            qosProfileFin.update({"standard-profile":qosProfileEntrada.get("standard")})
-        elif "classes" in qosProfileEntrada:
-            qosProfileFin.update({"custom-classes":readCustom(qosProfileEntrada.get("classes"))})
-        else:
-            print("falta campo en qos>qos-profile")
-            #raise NoJSONFieldException("qos>qos-profile")
-                    
-        res.update({"qos-profile":qosProfileFin})
-        res.update({"carrierscarrier":qos.get("carrierscarrier")})
-        multicastEntrada : Dict = qos.get("multicast")
-        multicastSalida : Dict = {}
-        multicastSalida.update({"multicast-site-type":multicastEntrada.get("multicast-site-type")})
-        multicastAddressSalida : Dict = {}
-        multicastAddressSalida.update({"ipv4":multicastEntrada.get("multicast-address-family", {}).get("ipv4")})
-        multicastAddressSalida.update({"ipv6":multicastEntrada.get("multicast-address-family", {}).get("ipv6")})
-        multicastSalida.update({"multicast-address-family":multicastAddressSalida})
-        multicastSalida.update({"protocol-type":multicastEntrada.get("protocol-type")})
-        
-        res.update({"multicast":multicastSalida})
-            
-        # except KeyError:
-        #     print("falta alguna clave en service")
-        # except NoJSONFieldException as e:
-        #     print(e)        
-        # finally:  
-        return res
-    
-    
\ No newline at end of file
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 1a87ec910262df5671fd681c4205c13b4a5678f6..f7329cb35666f423e85f99510e5f89a82e89b7f8 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
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import logging, netaddr, json
+import logging, netaddr
 from typing import Dict, List, Optional, Tuple
 from common.Constants import DEFAULT_CONTEXT_NAME
 from common.proto.context_pb2 import Service, ServiceStatusEnum, ServiceTypeEnum
@@ -26,9 +26,6 @@ from common.tools.grpc.EndPointIds import update_endpoint_ids
 from context.client.ContextClient import ContextClient
 from service.client.ServiceClient import ServiceClient
 
-from .ReadServiceFunctionDefinition import ReadServiceFunctionDefinition
-from .WriteNetworkFunctionDefinition import WriteNetworkFunctionDefinition
-
 LOGGER = logging.getLogger(__name__)
 
 def create_service(
@@ -212,55 +209,3 @@ def process_site(site : Dict, errors : List[Dict]) -> None:
     network_accesses : List[Dict] = site['site-network-accesses']['site-network-access']
     for network_access in network_accesses:
         process_site_network_access(site_id, network_access, site_static_routing, errors)
-        
-        
-def convert_l3sm_to_l3nm():
-    jsonRequest = open("/home/ubuntu/tfs-ctrl/src/nbi/tests/ietf_l3vpn_req_svc1.json", "r") #devuelve un file-object. "r" es por reading
-    output_file_path = "/home/ubuntu/tfs-ctrl/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/EstructuraIntermedia.json"
-    jsonRequestData : str = jsonRequest.read() #jsonRequestData es de tipo str. Para leer el contenido, usamos read()
-
-    jParse : Dict = json.loads(jsonRequestData) #jParse es de tipo Dict. esto va a parsear jsonRequetData.
-                        #cuando obtengamos el valor de una clave, y ese valor sea un array [], el tipo será 'list'
-                        #in order to convert a jsonObject to an string, you can use .dumps
-
-    ietf_l3vpn : Dict  = jParse['ietf-l3vpn-svc:l3vpn-svc']
-
-    vpn_services : Dict = ietf_l3vpn['vpn-services']
-    #print(vpn_services)
-    # vpn_service = vpn_services['vpn-service'] #ahora, al ser vpn-service un array, creo que la variable vpn_service es de tipo 'list'
-
-    # print(type(vpn_service)) #list
-    # print("VPN_SERVICE: " + str(vpn_service))
-    # print(vpn_service[0])
-    # print(vpn_service[0]['vpn-id'])
-
-    bd : Dict[any, any] = {} #Base datos de variables del JSON
-
-    #vpn_id = MiClase.readVpnServices(vpn_services) #vpn_id es variable de tipo List[str] con todas las vpn posibles
-
-    #try:
-    bd = ReadServiceFunctionDefinition.writeVpnServices( ReadServiceFunctionDefinition.readVpnServices(vpn_services) , bd)
-    #print(json.dumps(bd, indent=2))
-
-    sites : List = ietf_l3vpn['sites']['site'] #En sites se almacena el valor de la clave 'sites'.
-    #print(json.dumps(sites, indent=2))
-
-    #### PEQUEÑA CHAPUZA PARA CUANDO SOLO HAY UNA VPN ####
-    lista_VPNs : list = bd["VPN_IDs"]
-    bd["VPNs_Dict"][lista_VPNs[0]].update(ReadServiceFunctionDefinition.readSiteArray(sites, bd)) #vpn1.update Updatea el valor de vpn1
-    ####  #  ####
-
-
-    # except Exception as e: #En principio llegaran valueError o NoJSONFieldException
-    #     print(e)
-
-    #print(json.dumps(bd, indent=2))
-
-    #YANG SUITE ENTRADA, SERVICE ->creo que IETF-NBI-TEF-DECEMBER-23; ietf-l3vpn-svc; edit-config; none selected
-    #YANG SUITE SALIDA, RED (RFC9182) -> IETF-NBI-TEF-DECEMBER-23; ietf-l3vpn-ntw; edit-config; none selected
-    #Codigo para crear archivo .json de salida
-    with open(output_file_path, 'w') as json_file:
-        json.dump(bd, json_file, indent=3)
-        
-    jsonL3VPN_net : Dict = WriteNetworkFunctionDefinition.write()
-    # print(json.dumps(jsonL3VPN_net, indent = 3))
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Main.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Main.py
deleted file mode 100644
index d215a73ae373420011e18f95f2d02fe4b5b47ddd..0000000000000000000000000000000000000000
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/Main.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from src.nbi.service.rest_server.nbi_plugins.ietf_l3vpn import L3VPN_Services
-#from src.nbi.service.rest_server.nbi_plugins.ietf_l3vpn.L3VPN_Services import L3VPN_Services
-
-
-service : L3VPN_Services = L3VPN_Services()
-service.post()
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/ReadServiceFunctionDefinition.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/ReadServiceFunctionDefinition.py
deleted file mode 100644
index 3218a18ddd2d5205c170723b2adaaadf323caddd..0000000000000000000000000000000000000000
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/ReadServiceFunctionDefinition.py
+++ /dev/null
@@ -1,418 +0,0 @@
-from typing import Dict, List
-
-class ReadServiceFunctionDefinition:
-        
-    #Metodo para leer el campo vpn-services
-    @staticmethod  
-    def readVpnServices(vpnServicesALeer: Dict) -> List:
-        # Acceder al campo 'vpn-service' que es un array de Dict con un campo vpn-id
-        vpn_service_list = vpnServicesALeer.get('vpn-service', [])#Utilizar get para manejar casos donde 'vpn-service' no está presente
-        vpn_ids = [] #Array con todos los ID de cada VPN
-        
-        # Iterar sobre los elementos de la lista
-        for vpn_serviceEnFor in vpn_service_list:
-            vpn_id : str = vpn_serviceEnFor.get('vpn-id') #obtengo vpn-id del json iterando
-            # baseDatos["vpn-id_" + str(i)] = vpn_id  # Utilizar f-strings para formatear las claves
-            vpn_ids.append(vpn_id)
-            
-        return vpn_ids
-    
-    #Metodo para escribir en la baseDatos lo leido en vpn-services
-    @staticmethod
-    def writeVpnServices(listaVPNs : List, baseDatos : Dict) -> Dict:
-        
-        baseDatos["VPN_IDs"] = listaVPNs
-        #baseDatos.update({"VPN_IDs": vpn_ids}) #ambas lineas son validas para añadir al Dict
-        baseDatos["VPNs_Dict"] = {}
-        #Obtengo cada vpn-id del array y lo meto en VPNs_Dict
-        for clave in baseDatos["VPN_IDs"]:
-            baseDatos["VPNs_Dict"][clave] = {}#ya tengo un Dict asociado a cada clave
-            
-        return baseDatos #devuelve la baseDatos pasada por argumento pero actualizada
-                    
-    #Lee el array site
-    @staticmethod
-    def readSiteArray(site: List, baseDatos: Dict) -> Dict:
-        print("Elementos en site: " + str(len(site)))
-        temp : Dict = {} #Diccionario temporal que guarda el ultimo site leido
-        temp2 : Dict = {} #Diccionario temporal que registra todos los sites leidos
-        lista_SitesID = [] #lista que almacenara los IDs de cada site del array
-        i = 1
-        for siteElement in site:
-            temp = ReadServiceFunctionDefinition.readSiteElement(lista_SitesID, siteElement) #Leo un site y me quedo con ID y Dict
-            temp2.update({lista_SitesID[-1] : temp}) # Almaceno esos datos en temp2
-            i += 1            
-        
-        res : Dict = {}
-        res["sitesIDs"] = lista_SitesID
-        res["sites_Dict"] = temp2 # A sites_Dict se le añaden todos los sites
-        return res
-            
-    #Lee un site
-    @staticmethod
-    def readSiteElement(listaID: List, siteElement: Dict) -> Dict:
-        res: Dict = {}
-        listaID.append(siteElement.get('site-id'))
-        res.update({"site-id": siteElement.get('site-id')})
-        res.update({"management": siteElement.get("management")}) #revistar si está bien
-        res.update({"locations":siteElement.get("locations")})
-        res.update({"devices": ReadServiceFunctionDefinition.readDevices(siteElement.get('devices'))})
-        res.update({"routing-protocol" : ReadServiceFunctionDefinition.readRoutingProtocols(siteElement.get('routing-protocols'))})
-        res.update({"site-network-accesses" : ReadServiceFunctionDefinition.readSiteNetworkAccesses(siteElement.get('site-network-accesses'))})
-        print("readSiteElement Finiquitaoooo")
-        return res
-    #FALTAN MUCHOS CAMPOS A LEER EN site, como site-diversity, maximum-routes, security, service, traffic-protection
-
-    #Lee array de Devices. Si hay más de un device, cambiar código
-    @staticmethod
-    def readDevices(devices: Dict) -> Dict:
-        res: Dict = {}
-        devices_list: List = devices.get('device', [])  # Cambié 'devices' por 'device' aquí
-        if len(devices_list) > 1:
-            i: int = 1
-            for eachDevice in devices_list:
-                res.update({f"device-id {i}": eachDevice.get('device-id')})
-                i += 1
-        elif len(devices_list) == 1:
-            res.update({"device-id": devices_list[0].get('device-id')})
-        return res
-
-    @staticmethod
-    def readRoutingProtocols(routingProtocols : Dict) -> Dict:
-        res : Dict = {} #todo el dict routing-protocols
-        rpArray : List = routingProtocols.get("routing-protocol") #el array routing-protocol           
-        res.update({"type":rpArray[0].get("type")})#escribo el tipo en el diccionario
-        tipoRoutingProtocol : str = rpArray[0].get("type")#obtiene string "ietf-l3vpn-svc:static"
-        tipoResumido : str = tipoRoutingProtocol.split(":")[1] #se queda con la palabra, ospf, bgp, static,... sin el ietf...
-        #print("El tipo de routingProtocol es: "+ str(tipoResumido))
-        def case1():            
-            ospfEntrada : Dict = rpArray[0].get("ospf",{}) #Funciona solo en caso de que el array tenga un elemento
-            ospfIntermedio : Dict = {}
-            ospfIntermedio.update({"address-family":ospfEntrada.get("address-family",{})})
-            ospfIntermedio.update({"area-address":ospfEntrada.get("area-address",{})})
-            ospfIntermedio.update({"metric":ospfEntrada.get("metric",{})})
-            arrayShamLinksEntrada : List = ospfEntrada.get("sham-links",[]) #el array
-            shamLinksFinal : Dict = {}
-            arrayShamLinksFinal : List = []
-            for elemento in arrayShamLinksEntrada:
-                shamLinksFinal.update({"target-site":elemento.get("target-site")})
-                shamLinksFinal.update({"metric":elemento.get("metric")})
-                arrayShamLinksFinal.append(shamLinksFinal)
-                
-            ospfIntermedio.update({"sham-links":shamLinksFinal})
-                        
-            res.update({"ospf":ospfIntermedio})
-        
-        def case2():
-            bgpEntrada : Dict = rpArray[0].get("bgp",{})
-            bgpSalida : Dict = {}
-            bgpSalida.update({"autonomous-system":bgpEntrada.get("autonomous-system")})
-            bgpSalida.update({"address-family":bgpEntrada.get("address-family")})
-            res.update({"bgp":bgpSalida})
-        
-        def case3(): #SIMPLEMENTE COPIA, NO LEE. A IMPLEMENTAR
-            cascadedDict: Dict = rpArray[0].get("static", {}).get("cascaded-lan-prefixes", {})
-            valueRoutingProtocol = cascadedDict
-            res.update({"static":valueRoutingProtocol})   
-            print("se ha copiado 'static' pero no se ha leido, falta implementar")        
-             
-        def case4():
-            ripEntrada : Dict = rpArray[0].get("rip",{})
-            ripSalida : Dict = {}
-            ripSalida.update({"address-family":ripEntrada.get("address-family")})
-            res.update({"rip":ripSalida})
-            
-        def case5():
-            vrrpEntrada : Dict = rpArray[0].get("vrrp",{})
-            vrrpSalida : Dict = {}
-            vrrpSalida.update({"address-family":vrrpEntrada.get("address-family")})
-            res.update({"vrrp":vrrpSalida})        
-        
-        def default():
-            #return ValueError(f"Tipo de protocolo no compatible: {tipoResumido}")
-            print("TIPO DE PROTOCOLO NO VALIDO")
-        
-        switch = {
-            "ospf": case1,
-            "bgp": case2,
-            "static": case3,
-            "rip":case4,
-            "vrrp": case5
-        }
-        #res.update({tipoResumido: switch.get(tipoResumido, default)()})
-        switch.get(tipoResumido)()
-        # print("_______________________d____________"+str(res))
-        print("read Routing Protocols finished")
-        return res
-    
-    
-    @staticmethod
-    def readSiteNetworkAccesses(siteNetworkAccesses : Dict) -> Dict:
-        siteNetworkAccessArray : List = siteNetworkAccesses.get("site-network-access")
-        res : Dict = {}
-        arrayDeIDs : List = []
-        for siteNetworkAccess in siteNetworkAccessArray:
-            variableIncial : str = siteNetworkAccess.get("site-network-access-id")
-            variableIncial = variableIncial.replace(" ", "-")#ESTO LO HAGO PARA QUE NO HAYA PROBLEMAS CON LOS ESPACIOS A LA HORA DE LEER EL JSON
-            arrayDeIDs.append(variableIncial)
-        res.update({"site-network-accesses-IDs":arrayDeIDs})
-        dictSiteNetworkAccesses : Dict = {}
-        
-        for site in arrayDeIDs:
-            dictDeCadaID : Dict = {}
-            
-            # try:
-            dictDeCadaID.update({"site-network-access-type":siteNetworkAccess.get("site-network-access-type")})
-            dictDeCadaID.update({"device-reference":siteNetworkAccess.get("device-reference")})
-            dictDeCadaID.update({"site-role":siteNetworkAccess.get("vpn-attachment", {}).get("site-role")}) 
-            #dictDeCadaID.update({"location-flavor" : FunctionDefinition.readLocationFlavor(siteNetworkAccess.get("location-flavor"))}) DE MOMENTO DA ERROR PORQUE ESE CAMPO NO EXISTE
-            #dictDeCadaID.update({"access-diversity" : FunctionDefinition.readAccessDiversity(siteNetworkAccess.get("access-diversity"))}) CAMPO NO EXISTE AUN
-            #dictDeCadaID.update({"bearer" : FunctionDefinition.readBearer(siteNetworkAccess.get("bearer"))}) CAMPO NO EXISTE AUN
-            dictDeCadaID.update({"ip-connection":ReadServiceFunctionDefinition.readIpConnection(siteNetworkAccess.get("ip-connection"))})
-            dictDeCadaID.update({"routing-protocols":ReadServiceFunctionDefinition.readRoutingProtocols(siteNetworkAccess.get("routing-protocols"))})
-            dictDeCadaID.update({"service":ReadServiceFunctionDefinition.readService(siteNetworkAccess.get("service"))})
-            # except NoJSONFieldException as e:
-            #     print(e)
-                
-            dictSiteNetworkAccesses.update({site:dictDeCadaID})
-        print("readSiteNetworkAccesses finiquitaoo")
-        res.update({"site-network-accesses-Dict": dictSiteNetworkAccesses})       
-        return res
-    
-    @staticmethod
-    def readLocationFlavor(locationFlavor : Dict) -> Dict:
-        res : Dict = {}
-        
-        if "location" in locationFlavor:
-            res.update({"location-reference":locationFlavor.get("location", {}).get("location-reference")})
-        elif "device" in locationFlavor:
-            res.update({"device-reference":locationFlavor.get("device", {}).get("device-reference")})
-        # else:
-            # raise NoJSONFieldException("location-flavor")
-            
-        return res
-        
-    @staticmethod
-    def readAccessDiversity(accessDiversity : Dict) -> Dict:
-        res : Dict = {}
-        arrayGroupsIni : List = accessDiversity.get("groups",{}).get("group") #obtengo el array de objetos
-        arrayGroupsIDs : List = []
-        for groupId in arrayGroupsIni:
-            arrayGroupsIDs.append(groupId.get("group-id"))             
-        res.update({"groups":arrayGroupsIDs})
-        
-        arrayConstraintsIni : List = accessDiversity.get("constraints", {}).get("constraint")
-        arrayConstraintsFin : List = []
-        for constraint in arrayConstraintsIni: #recordar que constraint es un objeto dentro del array arrayConstraintsIni
-            dictConstraint : Dict = {}
-            dictConstraint.update({"constraint-type" : constraint.get("constraint-type")})
-            dictTargetFlavor : Dict = constraint.get("target", {}).get("target-flavor")
-            
-            if "id" in dictTargetFlavor:
-                dictConstraint.update({"target-flavor": dictTargetFlavor.get("id")})
-            elif "all-accesses" in dictTargetFlavor:
-                dictConstraint.update({"target-flavor": dictTargetFlavor.get("all-accesses")})
-            elif "all-groups" in dictTargetFlavor:
-                dictConstraint.update({"target-flavor": dictTargetFlavor.get("all-groups")}) 
-            # else:
-            #     raise NoJSONFieldException("read-access-divesity>constraints>constraint>target>target-flavor")               
-            arrayConstraintsFin.append(dictConstraint)
-            
-        res.update({"constraints":arrayConstraintsFin})       
-        return res
-
-    @staticmethod
-    def readBearer(bearer : Dict) -> Dict:
-        res : Dict = {}
-        
-        res.update({"always-on":bearer.get("always-on")})
-        res.update({"bearer-reference":bearer.get("bearer-reference")})
-        res.update({"requested-type":bearer.get("requested-type",{}).get("requested-type")})
-        res.update({"strict":bearer.get("requested-type",{}).get("strict")})       
-        
-        return res
-    
-    @staticmethod
-    def readIpConnection(ipConnection : Dict) -> Dict: #IPV4 E IPV6 TIENEN LOS MISMOS CAMPOS
-        res : Dict = {}
-        if ipConnection.get("ipv4") is not None:
-            res.update({"ipv4": ReadServiceFunctionDefinition.readIpv4_Ipv6(ipConnection.get("ipv4"))})
-        elif ipConnection.get("ipv6") is not None:
-            res.update({"ipv6":ReadServiceFunctionDefinition.readIpv4_Ipv6(ipConnection.get("ipv6"))}) 
-        # else:
-        #     raise NoJSONFieldException("ip-connection>ipv4/6")
-            
-        bfd : Dict = ipConnection.get("oam",{}).get("bfd")
-        if bfd is not None:
-            oamFinal : Dict = {}
-            oamFinal.update({"enabled":bfd.get("enabled")})
-            holdtime : Dict = bfd.get("holdtime")
-            holdtimeFinal : Dict = {}
-            if "fixed" in holdtime:
-                holdtimeFinal.update({"fixed":holdtime.get("fixed")})
-            elif "profile" in holdtime:
-                holdtimeFinal.update({"profile":holdtime.get("profile")})
-            # else:
-            #     raise NoJSONFieldException("oam>bfd>holdtime")
-            
-            oamFinal.update({"holdtime":holdtimeFinal})
-            res.update({"oam":oamFinal})
-        
-        return res
-    
-    @staticmethod
-    def readIpv4_Ipv6(ipv : Dict) -> Dict :
-        res : Dict = {}
-        
-        res.update({"address-allocation-type":ipv.get("address-allocation-type")})
-        
-        providerDHCP : Dict = ipv.get("provider-dhcp") #None si no existe ese campo, puede mejorarse eficiencia
-        if providerDHCP is not None:            
-            providerDHCPFinal : Dict = {}        
-            providerDHCPFinal.update({"provider-dhcp":providerDHCP.get("provider-address")})
-            providerDHCPFinal.update({"prefix-length":providerDHCP.get("prefix-lenght")})
-            addressAsign : Dict = providerDHCP.get("address-asign")
-            addressAsignFinal : Dict = {}
-            if "number" in addressAsign:
-                addressAsignFinal.update({"number":addressAsign.get("number")})
-            elif "explicit" in addressAsign:
-                addressAsignFinal.update({"explicit":addressAsign.get("explicit")})
-            # else:
-            #     raise NoJSONFieldException("ipv4/6>address-asign")
-            
-            providerDHCPFinal.update({"address-asign":addressAsignFinal})         
-            res.update({"provider-dhcp":providerDHCPFinal})
-        
-        
-        dhcpRelay : Dict = ipv.get("dhcp-relay")
-        if dhcpRelay is not None:
-            dhcpRelayFinal : Dict = {}
-            dhcpRelayFinal.update({"provider-address":dhcpRelay.get("provider-address")})
-            dhcpRelayFinal.update({"prefix-lenght":dhcpRelay.get("prefix-lenght")})
-            dhcpRelayFinal.update({"server-ip-address":dhcpRelay.get("customer-dhcp-servers",{}).get("server-ip-address")})
-            res.update({"dhcp-relay":dhcpRelayFinal})
-        
-        addresses : Dict = ipv.get("addresses")
-        if addresses is not None:
-            addressesFinal : Dict = {}
-            addressesFinal.update({"provider-address":addresses.get("provider-address")})
-            addressesFinal.update({"customer-address":addresses.get("customer-address")})
-            addressesFinal.update({"prefixe-length":addresses.get("prefixe-length")})
-            res.update({"addresses":addressesFinal})
-        
-        return res
-    
-    # @staticmethod
-    # def readSecurity(security : Dict) -> Dict:
-    #     res : Dict = {}
-        
-    #     res.update({"authentication" : security.get("authentication")})
-    #     encryption : Dict = security.get("encryption")
-    #     encryptionFinal : Dict = {}
-        
-        
-    #     return res
-    
-    #readService todavía me da error
-    @staticmethod
-    def readService(service : Dict) -> Dict :
-        res : Dict = {}
-        #try:                
-        res.update({"svc-mtu":service.get("svc-mtu")})
-        res.update({"svc-input-bandwidth":service.get("svc-input-bandwidth")})
-        res.update({"svc-output-bandwidth":service.get("svc-output-bandwidth")})
-        qos : Dict = service.get("qos")
-        
-        class_id = qos["qos-profile"]["classes"]["class"][0]["class-id"]
-        direction = qos["qos-profile"]["classes"]["class"][0]["direction"]
-        latency_boundary = qos["qos-profile"]["classes"]["class"][0]["latency"]["latency-boundary"]
-        guaranteed_bw_percent = qos["qos-profile"]["classes"]["class"][0]["bandwidth"]["guaranteed-bw-percent"]
-
-        res.update({"class-id":class_id})
-        res.update({"direction":direction})
-        res.update({"latency-boundary":latency_boundary})
-        res.update({"guaranteed-bw-percent":guaranteed_bw_percent})
-        
-        #Clase que lee el campo custom varias lineas mas adelante
-        
-        def readCustom (custom : Dict) -> Dict: #custom es un Dict que es: {classes: [class-id: *, ..., latency: {flavor:{lowest:*///boundary:*}}],[...],[...]}
-            res : Dict = {}
-                        
-            arrayClassesEntrada : List = custom.get("class") #un array con elementos indeterminados
-            arrayClassesFinal : List = []
-            
-            for elementEntrada in arrayClassesEntrada:
-                elementFinal : Dict = {}
-                elementFinal.update({"class-id":elementEntrada.get("class-id")})
-                elementFinal.update({"direction":elementEntrada.get("direction")})
-                elementFinal.update({"rate-limit":elementEntrada.get("rate-limit")})
-                #Campo latency--->
-                    #latency: {
-                    #     flavor:{
-                    #         lowest:*
-                    #         o quizás
-                    #         boundary:*
-                    #     }
-                    # }
-                                    
-                latency : Dict = elementEntrada.get("latency")
-                jitter : Dict = elementEntrada.get("jitter")
-                if latency is not None:
-                    if "use-lowest-latency" in latency:
-                        elementFinal.update({"latency-flavor-lowest":latency.get("use-lowest-latency")})
-                    elif "latency-boundary" in latency:
-                        elementFinal.update({"latency-flavor-boundary":latency.get("latency-boundary")})
-                elif jitter is not None:
-                    if "use-lowest-jitter" in jitter:
-                        elementFinal.update({"jitter-flavor-lowest":jitter.get("use-lowest-jiter")})
-                    elif "latency-boundary" in jitter:
-                        elementFinal.update({"jitter-flavor-boundary":jitter.get("latency-boundary")})
-                    else:
-                        print("Debe haber un campo lowest o boundary dentro de jitter>flavor")
-                else:
-                    print("FALTAN CAMPOS EN CUSTOM")
-                
-                bandwidthEntrada : Dict = elementEntrada.get("bandwidth")
-                bandwidthSalida : Dict = {}
-                bandwidthSalida.update({"guaranteed-bw-percent":bandwidthEntrada.get("guaranteed-bw-percent")})
-                bandwidthSalida.update({"end-to-end":bandwidthEntrada.get("end-to-end")})
-                elementFinal.update({"bandwidth":bandwidthSalida})
-                
-                arrayClassesFinal.append(elementFinal)
-                
-        #     res.update({"latency-flavor":arrayClassesFinal})
-        #     return res
-        #     #END READCUSTOM
-        
-        # qosProfileEntrada : Dict = qos.get("qos-profile")
-        # qosProfileFin : Dict = {}
-                
-        # if "profile" in qosProfileEntrada:
-        #     qosProfileFin.update({"standard-profile":qosProfileEntrada.get("standard")})
-        # elif "classes" in qosProfileEntrada:
-        #     qosProfileFin.update({"custom-classes":readCustom(qosProfileEntrada.get("classes"))})
-        # else:
-        #     print("falta campo en qos>qos-profile")
-        #     #raise NoJSONFieldException("qos>qos-profile")
-                    
-        # res.update({"qos-profile":qosProfileFin})
-        # res.update({"carrierscarrier":qos.get("carrierscarrier")})
-        # multicastEntrada : Dict = qos.get("multicast")
-        # multicastSalida : Dict = {}
-        # multicastSalida.update({"multicast-site-type":multicastEntrada.get("multicast-site-type")})
-        # multicastAddressSalida : Dict = {}
-        # multicastAddressSalida.update({"ipv4":multicastEntrada.get("multicast-address-family", {}).get("ipv4")})
-        # multicastAddressSalida.update({"ipv6":multicastEntrada.get("multicast-address-family", {}).get("ipv6")})
-        # multicastSalida.update({"multicast-address-family":multicastAddressSalida})
-        # multicastSalida.update({"protocol-type":multicastEntrada.get("protocol-type")})
-        
-        # res.update({"multicast":multicastSalida})
-            
-        # # except KeyError:
-        # #     print("falta alguna clave en service")
-        # # except NoJSONFieldException as e:
-        # #     print(e)        
-        # # finally:  
-        return res
-    
-    
\ No newline at end of file
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/WriteNetworkFunctionDefinition.py b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/WriteNetworkFunctionDefinition.py
deleted file mode 100644
index 5c22de56dee9140467fb747c4d43db2a3ab25bcc..0000000000000000000000000000000000000000
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/WriteNetworkFunctionDefinition.py
+++ /dev/null
@@ -1,288 +0,0 @@
-from typing import Dict, List
-import json, os
-
-class WriteNetworkFunctionDefinition:
-    
-    input_file_path : str ="/home/ubuntu/Documents/EstructuraIntermedia.json"
-    output_file_path = "/home/ubuntu/Documents/L3VPN_ntw.json"
-    
-    jsonEstructuraIntermedia = open(input_file_path, "r")
-    jsonEstructuraIntermediaData : str = jsonEstructuraIntermedia.read()
-    jsonEstIntDict : Dict = json.loads(jsonEstructuraIntermediaData)    
-    
-    @staticmethod
-    def numberVpnIDs() -> int :
-        vpnIDs : List = WriteNetworkFunctionDefinition.jsonEstIntDict["VPN_IDs"]
-        return len(vpnIDs)
-    
-    #Este metodo devuelve el numero de sites, y es necesario porque el numero de sites es el numero de nodes
-    @staticmethod
-    def numberSitesIDS() -> int :
-        sitesIDs : List = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"]
-        return len(sitesIDs)
-            
-    @staticmethod
-    def write() -> Dict: 
-        
-        jsonNet : Dict = {}
-        jsonNet.update({"vpn-service":{}}) #no tenemos en cuenta que puede haber más de 1 VPN. Codigo valido para 1 VPN solo
-        vpn_id : str = WriteNetworkFunctionDefinition.jsonEstIntDict["VPN_IDs"][0].split("vpn")[1] #obtengo el numero de vpn_id
-        jsonNet["vpn-service"]["vpn-id"] = vpn_id #de momento solo 1 vpn
-        # jsonNet["vpn-service"]["customer-name"] = "_"
-        # jsonNet["vpn-service"]["parent-service-id"] = "_"
-        jsonNet["vpn-service"]["vpn-type"] = "L3VRF"
-        jsonNet["vpn-service"]["vpn-service-topology"] = "custom" #modificar todos estos campos
-        jsonNet["vpn-service"]["vpn-nodes"] = {}
-        
-        arrayVpnNode : List = []
-        print(WriteNetworkFunctionDefinition.numberSitesIDS())
-        for numeroDeNode in range(WriteNetworkFunctionDefinition.numberSitesIDS()):
-          
-            sites : List = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"]
-            
-            site: str = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sitesIDs"][numeroDeNode]
-            service_access: str = WriteNetworkFunctionDefinition.jsonEstIntDict["VPNs_Dict"]["vpn1"]["sites_Dict"][sites[numeroDeNode]]["site-network-accesses"]["site-network-accesses-IDs"][0]
-            
-            #con los siguientes ifs hago que las politicas cambien de lugar, uno si y uno no constantemente
-            if numeroDeNode % 2 is 0:
-                arrayVpnNode.append(WriteNetworkFunctionDefinition.writeNode(WriteNetworkFunctionDefinition.jsonEstIntDict,site,service_access,numeroDeNode, 0))
-            if numeroDeNode % 2 is not 0:
-                arrayVpnNode.append(WriteNetworkFunctionDefinition.writeNode(WriteNetworkFunctionDefinition.jsonEstIntDict,site,service_access,numeroDeNode, 1))
-
-        jsonNet["vpn-service"]["vpn-nodes"]["vpn-node"] = arrayVpnNode
-                
-        #Codigo para crear el archivo y escribir el json en el
-        with open(WriteNetworkFunctionDefinition.output_file_path, 'w') as json_file:
-            json.dump(jsonNet, json_file, indent = 3)
-            
-        return jsonNet
-    
-    def writeNode(jsonEstDict : Dict, site: str, service_access : str, numeroDeNode : int, importExport : int) -> Dict:
-        res: Dict = {}
-        res.update({"ne-id": "pe" + str(numeroDeNode+1)})
-        res.update({"vpn-node-id": "PE" + str(numeroDeNode+1)})
-        res.update({"local-as":"65000"})
-        res.update({"router-id":"5.5.5.5"})
-        
-        #active-vpn-instance-profiles
-        activeVpnInstanceProfiles: Dict = {}
-        vpnInstanceProfilesArray: List = []
-        vpnInstanceProfilesArrayObject: Dict = {}
-        
-        vpnInstanceProfilesArrayObject.update({"profile-id": "1"})
-        routerID : Dict = {}
-        
-        #Pregutnar sobre si route distinguisher debe cambiar por cada node
-        directlyAsigned : Dict = {"rd": str(res.get("local-as")) + ":100"}
-        routerID.update({"directly-assigned":directlyAsigned})
-        vpnInstanceProfilesArrayObject.update({"router-id":routerID})
-        
-        addressFamily : List = []
-        addressFamilyElement : Dict = {}
-        addressFamilyElement.update({"address-family":""})
-        
-        vpnTargets: Dict = {}
-        vpnTarget: List = []
-        vpnTargetObject: Dict = {}
-        vpnTargetObject.update({"id": str(numeroDeNode+1)})
-        
-        routeTargets: List = []
-        routeTargetsElement: Dict = {}
-        routeTargetsElement.update({"route-target": str(res.get("local-as"))+":222"}) #Any to any
-        routeTargets.append(routeTargetsElement)
-        
-        vpnTargetObject.update({"route-targets": routeTargets})
-        vpnTargetObject.update({"route-target-type": "both"}) #Any to any
-        
-        vpnTarget.append(vpnTargetObject)      
-        vpnTargets.update({"vpn-target": vpnTarget})
-        
-        vpnPolicies: Dict = {}
-        #Codigo que en metodo write() hace que un nodo si y uno no vayan modificando el orden de politicas
-        if importExport is 0:
-            vpnPolicies.update({"import-policy":"politica 1"})
-            vpnPolicies.update({"export-policy":"politica 2"})
-        else:
-            vpnPolicies.update({"import-policy":"politica 2"})
-            vpnPolicies.update({"export-policy":"politica 1"})
-        vpnTargets.update({"vpn-policies":vpnPolicies})
-        
-        addressFamilyElement.update({"vpn-targets":vpnTargets})
-        addressFamily.append(addressFamilyElement)
-
-        vpnInstanceProfilesArrayObject.update({"address-family": addressFamily})
-        vpnInstanceProfilesArray.append(vpnInstanceProfilesArrayObject)        
-        activeVpnInstanceProfiles.update({"vpn-instance-profile": vpnInstanceProfilesArray})        
-        res.update({"active-vpn-instance-profiles": activeVpnInstanceProfiles})
-        
-        #vpn-network-accesses
-        vpnNetworkAccesses : Dict = {}
-        vpnNetworkAccessArray : List = []
-        vpnNetworkAccessElement : Dict = {}
-        vpnNetworkAccessElement.update({"id":"1-1-1"})
-        vpnNetworkAccessElement.update({"interface-id":"1-1-1"})
-        vpnNetworkAccessElement.update({"vpn-network-access-type":"l3ipvlan"})
-        vpnNetworkAccessElement.update({"vpn-instance-profiles":""})
-        
-        status : Dict = {
-            "admin-status":{
-                "status":"admin-up",
-                "last-change":""
-            },
-            "oper-status":{
-                "status":"",
-                "last-change":""
-            }           
-        }
-        vpnNetworkAccessElement.update({"status":status})
-        vpnNetworkAccessElement.update({"connection":WriteNetworkFunctionDefinition.writeConnection()})
-                      
-        vpn_id : str = jsonEstDict["VPN_IDs"][0]#valido cuando solo hay una VPN.
-                                                                                                                                                                            #de momento no IPv6, campo vacio, solo IPv4
-        provider_address: str = jsonEstDict["VPNs_Dict"][vpn_id]["sites_Dict"][site]["site-network-accesses"]["site-network-accesses-Dict"][service_access]["ip-connection"]["ipv4"]["addresses"]["provider-address"]
-        prefixe_length : int = jsonEstDict["VPNs_Dict"][vpn_id]["sites_Dict"][site]["site-network-accesses"]["site-network-accesses-Dict"][service_access]["ip-connection"]["ipv4"]["addresses"]["prefixe-length"]
-        customer_address : str = jsonEstDict["VPNs_Dict"][vpn_id]["sites_Dict"][site]["site-network-accesses"]["site-network-accesses-Dict"][service_access]["ip-connection"]["ipv4"]["addresses"]["customer-address"]
-        
-        ipConnectionAndRoutingProtocols : Dict = {
-            "ip-connection":{
-                "l3-termination-point":"1-1-1.222",
-                "ipv4":{
-                    "local-address":provider_address,
-                    "prefixe-length":prefixe_length,
-                    "address-allocation-type":"static-address",
-                    "static-addresses":{
-                        "primary-addresses":"1",
-                        "address":[
-                            {
-                                "address-id":"1",
-                                "customer-address":customer_address
-                            }
-                        ]
-                    }
-                },
-                "ipv6":{
-                    "local-address":"",
-                    "prefixe-lenght":"",
-                    "address-allocation-type":"static-address",
-                    "static-addresses":{
-                        "primary-addresses":"1",
-                        "address":[
-                            {
-                                "address-id":"1",
-                                "customer-address":""
-                            }
-                        ]
-                    }
-                }
-            },
-            "routing-protocols":{
-                "routing-protocol":[
-                    {
-                        "id":"ver con oscar",
-                        "type":"static-routing",
-                        "routing-profiles":[
-                            {
-                                "id":"",
-                                "type":""
-                                
-                            }
-                        ],
-                        "static":{
-                            "cascaded-lan-prefixes":{
-                                "ipv4-lan-prefixes":[
-                                   {
-                                    "lan":"",
-                                    "next-hop":"",
-                                    "lan-tag":"",
-                                    "bfd-enable":"",
-                                    "metric":"",
-                                    "preference":"",
-                                    "status":{
-                                        "admin-status":{
-                                            "status":"",
-                                            "last-change":""
-                                        },
-                                        "oper-status":{
-                                            "status":"",
-                                            "last-change":""
-                                        }
-                                    },
-                                    "tef-l3vpn-ntw:description":""
-                                   }                     
-                                ],
-                                "ipv6-lan-prefixes":[
-                                   {
-                                    "lan":"",
-                                    "next-hop":"",
-                                    "lan-tag":"",
-                                    "bfd-enable":"",
-                                    "metric":"",
-                                    "preference":"",
-                                    "status":{
-                                        "admin-status":{
-                                            "status":"",
-                                            "last-change":""
-                                        },
-                                        "oper-status":{
-                                            "status":"",
-                                            "last-change":""
-                                        }
-                                    },
-                                    "tef-l3vpn-ntw:description":""
-                                   }                     
-                                ]
-                            }
-                        }
-                    }
-                ]
-            }
-        }
-        
-        vpnNetworkAccessElement.update(ipConnectionAndRoutingProtocols)
-        
-        vpnNetworkAccessArray.append(vpnNetworkAccessElement)
-        vpnNetworkAccesses.update({"vpn-network-access":vpnNetworkAccessArray})
-        res.update({"vpn-network-accesses":vpnNetworkAccesses})        
-        return res
-
-
-    #Falta "l2vpn-id":"", que es la otra choice entre l2vpn-id y l2-tunnel-service
-    def writeConnection() -> Dict:
-        res : Dict = {            
-            "encapsulation":{
-                "type":"untagged"
-                # ,
-                # "dot1q":{
-                #     "tag-type":"cvlan",
-                #     "cvlan-id":"222"
-                # }
-                # ,
-                # "priotity-tagged":{
-                #     "tag-type":""
-                # },
-                # "quinq":{
-                #     "tag-type":"",
-                #     "svlan-id":"",
-                #     "cvlan-id":""
-                # }                                    
-            }
-            # ,            
-            # "l2-tunnel-service":{
-            #     "type":"",
-            #     "pseudowire":{
-            #         "vcid":"",
-            #         "far-end":""
-            #     },
-            #     "vpls":{
-            #         "vcid":"",
-            #         "far-end":""
-            #     },
-            #     "vxlan":{
-            #         "vni-id":"",
-            #         "peer-mode":"",
-            #         "peer-ip-address":""
-            #     }
-            # }
-        }
-        
-        return res
\ No newline at end of file
diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/orignal_L3VPN_Services b/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/orignal_L3VPN_Services
deleted file mode 100644
index 6bd57c8238c1af63ed3f504593f3c70cf8a68cc6..0000000000000000000000000000000000000000
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/orignal_L3VPN_Services
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import logging
-from typing import Dict, List
-from flask import request
-from flask.json import jsonify
-from flask_restful import Resource
-from werkzeug.exceptions import UnsupportedMediaType
-from nbi.service.rest_server.nbi_plugins.tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR
-from nbi.service.rest_server.nbi_plugins.tools.Authentication import HTTP_AUTH
-from .Handlers import process_site, process_vpn_service
-from .YangValidator import YangValidator
-
-LOGGER = logging.getLogger(__name__)
-
-class L3VPN_Services(Resource):
-    @HTTP_AUTH.login_required
-    def get(self):
-        return {}
-
-    @HTTP_AUTH.login_required
-    def post(self):
-        if not request.is_json: raise UnsupportedMediaType('JSON payload is required')
-        request_data : Dict = request.json
-        LOGGER.debug('Request: {:s}'.format(str(request_data)))
-
-        errors = list()
-        if 'ietf-l3vpn-svc:l3vpn-services' in request_data:
-            # processing multiple L3VPN service requests formatted as:
-            #{
-            #  "ietf-l3vpn-svc:l3vpn-services": {
-            #    "l3vpn-svc": [
-            #      {
-            #        "service-id": "vpn1",
-            #        "vpn-services": {
-            #          "vpn-service": [
-            for l3vpn_svc in request_data['ietf-l3vpn-svc:l3vpn-services']['l3vpn-svc']:
-                l3vpn_svc.pop('service-id', None)
-                l3vpn_svc_request_data = {'ietf-l3vpn-svc:l3vpn-svc': l3vpn_svc}
-                errors.extend(self._process_l3vpn(l3vpn_svc_request_data))
-        elif 'ietf-l3vpn-svc:l3vpn-svc' in request_data:
-            # processing single (standard) L3VPN service request formatted as:
-            #{
-            #  "ietf-l3vpn-svc:l3vpn-svc": {
-            #    "vpn-services": {
-            #      "vpn-service": [
-            errors.extend(self._process_l3vpn(request_data))
-        else:
-            errors.append('unexpected request: {:s}'.format(str(request_data)))
-
-        response = jsonify(errors)
-        response.status_code = HTTP_CREATED if len(errors) == 0 else HTTP_SERVERERROR
-        return response
-
-    def _process_l3vpn(self, request_data : Dict) -> List[Dict]:
-        yang_validator = YangValidator('ietf-l3vpn-svc')
-        request_data = yang_validator.parse_to_dict(request_data)
-        yang_validator.destroy()
-
-        errors = list()
-
-        for vpn_service in request_data['l3vpn-svc']['vpn-services']['vpn-service']:
-            process_vpn_service(vpn_service, errors)
-
-        for site in request_data['l3vpn-svc']['sites']['site']:
-            process_site(site, errors)
-
-        return errors
diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py
index 3ed2b13fb33ae06faeacc4286959a8016ca995d1..d4e4194497be44bf9eb0176bcc9014e9402e582c 100644
--- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py
+++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py
@@ -23,7 +23,7 @@ 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,
-    generate_neighbor_endpoint_config_rules
+    generate_neighbor_endpoint_config_rules, compose_iplink_config_rules
 )
 from .tools.ComposeRequest import compose_device, compose_link, compose_service
 from .tools.ComputeSubServices import (
@@ -182,6 +182,8 @@ class _Algorithm:
             compose_l3nm_config_rules(config_rules, service.service_config.config_rules)
         elif service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE:
             compose_tapi_config_rules(config_rules, service.service_config.config_rules)
+        if service_type == ServiceTypeEnum.SERVICETYPE_IPLINK:
+            compose_iplink_config_rules(config_rules, service.service_config.config_rules)
         else:
             MSG = 'Unhandled generic Config Rules for service {:s} {:s}'
             self.logger.warning(MSG.format(str(service_uuid), str(ServiceTypeEnum.Name(service_type))))
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
index f92f9b2fff11ab585813ab59e07c463f361413d2..823741a0f49aea74604d9bfd5c3f909e7611bb8c 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeConfigRules.py
@@ -103,7 +103,12 @@ def compose_tapi_config_rules(main_service_config_rules : List, subservice_confi
     ]
     for rule_name, defaults in CONFIG_RULES:
         compose_config_rules(main_service_config_rules, subservice_config_rules, rule_name, defaults)
-
+        
+def compose_iplink_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None:
+    CONFIG_RULES = [SETTINGS_RULE_NAME]
+    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,
     device_name_mapping : Dict[str, str], endpoint_name_mapping : Dict[Tuple[str, str], str]
@@ -152,6 +157,31 @@ def compose_device_config_rules(
             
             LOGGER.debug('[compose_device_config_rules]   adding acl config rule')
             subservice_config_rules.append(config_rule)
+            
+        elif config_rule.WhichOneof('config_rule') == 'ip_link':
+            LOGGER.debug('[compose_device_config_rules]   is ip_link')
+            endpoint_id = config_rule.ip_link.endpoint_id
+            device_uuid_or_name = endpoint_id.device_id.device_uuid.uuid
+            LOGGER.debug('[compose_device_config_rules]   device_uuid_or_name={:s}'.format(str(device_uuid_or_name)))
+            device_name_or_uuid = device_name_mapping.get(device_uuid_or_name, device_uuid_or_name)
+            LOGGER.debug('[compose_device_config_rules]   device_name_or_uuid={:s}'.format(str(device_name_or_uuid)))
+            device_keys = {device_uuid_or_name, device_name_or_uuid}
+            if len(device_keys.intersection(devices_traversed)) == 0: continue
+
+            endpoint_uuid = endpoint_id.endpoint_uuid.uuid
+            LOGGER.debug('[compose_device_config_rules]   endpoint_uuid={:s}'.format(str(endpoint_uuid)))
+            # given endpoint uuids link 'eth-1/0/20.533', remove last part after the '.'
+            endpoint_uuid_or_name = (endpoint_uuid[::-1].split('.', maxsplit=1)[-1])[::-1]
+            LOGGER.debug('[compose_device_config_rules]   endpoint_uuid_or_name={:s}'.format(str(endpoint_uuid_or_name)))
+            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}
+
+            device_endpoint_keys = set(itertools.product(device_keys, endpoint_keys))
+            if len(device_endpoint_keys.intersection(endpoints_traversed)) == 0: continue
+            
+            LOGGER.debug('[compose_device_config_rules]   adding ip_link config rule')
+            subservice_config_rules.append(config_rule)
 
         elif config_rule.WhichOneof('config_rule') == 'custom':
             LOGGER.debug('[compose_device_config_rules]   is custom')
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
index 2792a86639fbd6852a41499e928de7a4131ed408..07d874b939b572209e7eba80748eca4ad723cc49 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ServiceTypes.py
@@ -45,6 +45,8 @@ SERVICE_TYPE_L2NM = {ServiceTypeEnum.SERVICETYPE_L2NM}
 SERVICE_TYPE_L3NM = {ServiceTypeEnum.SERVICETYPE_L3NM}
 SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM}
 SERVICE_TYPE_TAPI = {ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE}
+SERVICE_TYPE_IPLINK = {ServiceTypeEnum.SERVICETYPE_IPLINK}
+
 
 def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum:
     if device_type in PACKET_DEVICE_TYPES and prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type
diff --git a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
index 4593770498216267b8d2f95dd728fccfbb9dc134..b0742e306183a7d5da1a03a54d7734b1e398d75c 100644
--- a/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
+++ b/src/policy/target/generated-sources/grpc/context/ContextOuterClass.java
@@ -504,6 +504,10 @@ public final class ContextOuterClass {
          * <code>SERVICETYPE_OPTICAL_CONNECTIVITY = 6;</code>
          */
         SERVICETYPE_OPTICAL_CONNECTIVITY(6),
+        /**
+         * <code>SERVICETYPE_IPLINK = 6;</code>
+         */
+        SERVICETYPE_IPLINK(7),
         UNRECOGNIZED(-1);
 
         /**
@@ -541,6 +545,11 @@ public final class ContextOuterClass {
          */
         public static final int SERVICETYPE_OPTICAL_CONNECTIVITY_VALUE = 6;
 
+        /**
+         * <code>SERVICETYPE_IPLINK = 7;</code>
+         */
+        public static final int SERVICETYPE_IPLINK_VALUE = 7;
+
         public final int getNumber() {
             if (this == UNRECOGNIZED) {
                 throw new java.lang.IllegalArgumentException("Can't get the number of an unknown enum value.");
@@ -578,6 +587,8 @@ public final class ContextOuterClass {
                     return SERVICETYPE_E2E;
                 case 6:
                     return SERVICETYPE_OPTICAL_CONNECTIVITY;
+                case 7:
+                    return SERVICETYPE_IPLINK;
                 default:
                     return null;
             }
diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py
index ca70fa9386e356e7e49397365701013e1d3a1697..e0811c0a9c53cb14fbd654797faf5f5e0522c4ac 100644
--- a/src/service/service/service_handler_api/FilterFields.py
+++ b/src/service/service/service_handler_api/FilterFields.py
@@ -26,7 +26,8 @@ SERVICE_TYPE_VALUES = {
     ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
     ServiceTypeEnum.SERVICETYPE_TE,
     ServiceTypeEnum.SERVICETYPE_E2E,
-    ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY
+    ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY,
+    ServiceTypeEnum.SERVICETYPE_IPLINK
 }
 
 DEVICE_DRIVER_VALUES = {
diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py
index 8b5e2b2834f37dc3d616907e46cf1fa5b2f1274f..a731e8176d51b32305bb13bd223e19bfb44c2b6e 100644
--- a/src/service/service/service_handlers/__init__.py
+++ b/src/service/service/service_handlers/__init__.py
@@ -27,6 +27,7 @@ from .tapi_tapi.TapiServiceHandler import TapiServiceHandler
 from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler
 from .e2e_orch.E2EOrchestratorServiceHandler import E2EOrchestratorServiceHandler
 from .oc.OCServiceHandler import OCServiceHandler
+from .ip_link.IP_LinkServiceHandler import IP_LinkServiceHandler
 
 SERVICE_HANDLERS = [
     (L2NMEmulatedServiceHandler, [
@@ -106,5 +107,11 @@ SERVICE_HANDLERS = [
             FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY,
             FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC,
         }
-    ])
+    ]),
+    (IP_LinkServiceHandler, [
+        {
+            FilterFieldEnum.SERVICE_TYPE  : ServiceTypeEnum.SERVICETYPE_IPLINK,
+            FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG,
+        }
+    ]),
 ]
diff --git a/src/service/service/service_handlers/ip_link/ConfigRules.py b/src/service/service/service_handlers/ip_link/ConfigRules.py
new file mode 100644
index 0000000000000000000000000000000000000000..74becce014cecbcd8f06550e420a68274318fa1f
--- /dev/null
+++ b/src/service/service/service_handlers/ip_link/ConfigRules.py
@@ -0,0 +1,296 @@
+# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Any, Dict, List, Optional, Tuple
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+from service.service.service_handler_api.AnyTreeTools import TreeNode
+
+def get_value(field_name : str, *containers, default=None) -> Optional[Any]:
+    if len(containers) == 0: raise Exception('No containers specified')
+    for container in containers:
+        if field_name not in container: continue
+        return container[field_name]
+    return default
+
+def setup_config_rules(
+    service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str,
+    service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode, endpoint_acls : List [Tuple], endpoint_ip_link : List [Tuple]
+) -> List[Dict]:
+
+    if service_settings  is None: return []
+    if device_settings   is None: return []
+    if endpoint_settings is None: return []
+
+    json_settings          : Dict = service_settings.value
+    json_device_settings   : Dict = device_settings.value
+    json_endpoint_settings : Dict = endpoint_settings.value
+
+    settings = (json_settings, json_endpoint_settings, json_device_settings)
+
+    mtu                       = get_value('mtu', *settings, default=1450)   # 1512
+    #address_families         = json_settings.get('address_families',             []       )  # ['IPV4']
+    bgp_as                    = get_value('bgp_as', *settings, default=65000)   # 65000
+
+    router_id                 = json_endpoint_settings.get('router_id',           '0.0.0.0')  # '10.95.0.10'
+    route_distinguisher       = json_settings.get('route_distinguisher',          '65000:101'    )  # '60001:801'
+    sub_interface_index       = json_endpoint_settings.get('sub_interface_index', 0        )  # 1
+    vlan_id                   = json_endpoint_settings.get('vlan_id',             1        )  # 400
+    address_ip                = json_endpoint_settings.get('address_ip',          '0.0.0.0')  # '2.2.2.1'
+    address_prefix            = json_endpoint_settings.get('address_prefix',      24       )  # 30
+
+    policy_import             = json_endpoint_settings.get('policy_AZ',            '2'     )  # 2
+    policy_export             = json_endpoint_settings.get('policy_ZA',            '7'     )  # 30
+    #network_interface_desc    = '{:s}-NetIf'.format(service_uuid)
+    network_interface_desc    = json_endpoint_settings.get('ni_description','')
+    #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid)
+    network_subinterface_desc = json_endpoint_settings.get('subif_description','')
+    #service_short_uuid       = service_uuid.split('-')[-1]
+    #network_instance_name    = '{:s}-NetInst'.format(service_short_uuid)
+    network_instance_name     = json_endpoint_settings.get('ni_name',          service_uuid.split('-')[-1])  #ELAN-AC:1
+
+    if_subif_name       = '{:s}.{:d}'.format(endpoint_name, vlan_id)
+
+    json_config_rules = [
+        # Configure Interface (not used)
+        #json_config_rule_set(
+        #    '/interface[{:s}]'.format(endpoint_name), {
+        #        'name': endpoint_name, 
+        #        'description': network_interface_desc, 
+        #        'mtu': mtu,
+        #}),
+
+        #Create network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]'.format(network_instance_name), {
+                'name': network_instance_name, 
+                'description': network_interface_desc, 
+                'type': 'L3VRF',
+                'route_distinguisher': route_distinguisher,
+                #'router_id': router_id,
+                #'address_families': address_families,
+        }),
+
+        #Add BGP protocol to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), {
+                'name': network_instance_name, 
+                'protocol_name': 'BGP', 
+                'identifier': 'BGP', 
+                'type': 'L3VRF',
+                'as': bgp_as,
+                'router_id': router_id, 
+        }),
+
+        #Add DIRECTLY CONNECTED protocol to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/protocols[DIRECTLY_CONNECTED]'.format(network_instance_name), {
+                'name': network_instance_name, 
+                'identifier': 'DIRECTLY_CONNECTED', 
+                'protocol_name': 'DIRECTLY_CONNECTED',
+        }),
+
+        #Add STATIC protocol to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/protocols[STATIC]'.format(network_instance_name), {
+                'name': network_instance_name, 
+                'identifier': 'STATIC', 
+                'protocol_name': 'STATIC',
+        }),
+
+        #Create interface with subinterface
+        json_config_rule_set(
+            '/interface[{:s}]/subinterface[{:d}]'.format(if_subif_name, sub_interface_index), {
+                'name'          : if_subif_name,
+                'type'          :'l3ipvlan',
+                'mtu'           : mtu,
+                'index'         : sub_interface_index,
+                'description'   : network_subinterface_desc, 
+                'vlan_id'       : vlan_id,
+                'address_ip'    : address_ip, 
+                'address_prefix': address_prefix,
+        }),
+
+        #Associate interface to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), {
+                'name'        : network_instance_name, 
+                'type'        : 'L3VRF',
+                'id'          : if_subif_name, 
+                'interface'   : if_subif_name,
+                'subinterface': sub_interface_index,
+        }), 
+
+        #Create routing policy
+        json_config_rule_set(
+            '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import,route_distinguisher), {
+                'ext_community_set_name': 'set_{:s}'.format(policy_import),
+                'ext_community_member'  : route_distinguisher,
+        }),
+        json_config_rule_set(
+            # pylint: disable=duplicate-string-formatting-argument
+            '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(policy_import, policy_import), {
+                'policy_name'           : policy_import,
+                'statement_name'        : 'stm_{:s}'.format(policy_import),
+                'ext_community_set_name': 'set_{:s}'.format(policy_import),
+                'policy_result'         : 'ACCEPT_ROUTE',
+        }),
+
+        #Associate routing policy to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_import), {
+                'name'         : network_instance_name,
+                'import_policy': policy_import,
+        }),
+
+        #Create routing policy
+        json_config_rule_set(
+            '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), {
+                'ext_community_set_name': 'set_{:s}'.format(policy_export),
+                'ext_community_member'  : route_distinguisher,
+        }),
+        json_config_rule_set(
+            # pylint: disable=duplicate-string-formatting-argument
+            '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(policy_export, policy_export), {
+                'policy_name'           : policy_export,
+                'statement_name'        : 'stm_{:s}'.format(policy_export),
+                'ext_community_set_name': 'set_{:s}'.format(policy_export),
+                'policy_result'         : 'ACCEPT_ROUTE',
+        }),
+
+        #Associate routing policy to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_export),{
+                'name'         : network_instance_name,
+                'export_policy': policy_export,
+        }),
+
+        #Create table connections
+        json_config_rule_set(
+            '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name), {
+                'name'                 : network_instance_name,
+                'src_protocol'         : 'DIRECTLY_CONNECTED',
+                'dst_protocol'         : 'BGP',
+                'address_family'       : 'IPV4',
+                'default_import_policy': 'ACCEPT_ROUTE',
+        }),
+
+        json_config_rule_set(
+            '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), {
+                'name'                 : network_instance_name,
+                'src_protocol'         : 'STATIC',
+                'dst_protocol'         : 'BGP',
+                'address_family'       : 'IPV4',
+                'default_import_policy': 'ACCEPT_ROUTE',
+        }),
+
+    ]
+
+    for res_key, res_value in endpoint_acls:
+        json_config_rules.append(
+               {'action': 1, 'acl': res_value}
+            )
+    return json_config_rules
+
+def teardown_config_rules(
+    service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str,
+    service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode
+) -> List[Dict]:
+
+    if service_settings  is None: return []
+    if device_settings   is None: return []
+    if endpoint_settings is None: return []
+
+    json_settings          : Dict = service_settings.value
+    json_device_settings   : Dict = device_settings.value
+    json_endpoint_settings : Dict = endpoint_settings.value
+
+    settings = (json_settings, json_endpoint_settings, json_device_settings)
+
+    service_short_uuid        = service_uuid.split('-')[-1]
+    network_instance_name     = '{:s}-NetInst'.format(service_short_uuid)
+    #network_interface_desc    = '{:s}-NetIf'.format(service_uuid)
+    #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid)
+
+    #mtu                       = get_value('mtu', *settings, default=1450)   # 1512
+    #address_families    = json_settings.get('address_families',             []       )  # ['IPV4']
+    #bgp_as                    = get_value('bgp_as', *settings, default=65000)   # 65000
+    route_distinguisher = json_settings.get('route_distinguisher',          '0:0'    )  # '60001:801'
+    #sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0        )  # 1
+    #router_id           = json_endpoint_settings.get('router_id',           '0.0.0.0')  # '10.95.0.10'
+    vlan_id             = json_endpoint_settings.get('vlan_id',             1        )  # 400
+    #address_ip          = json_endpoint_settings.get('address_ip',          '0.0.0.0')  # '2.2.2.1'
+    #address_prefix      = json_endpoint_settings.get('address_prefix',      24       )  # 30
+    policy_import       = json_endpoint_settings.get('policy_AZ',            '2'      )  # 2
+    policy_export       = json_endpoint_settings.get('policy_ZA',            '7'      )  # 30
+
+    if_subif_name             = '{:s}.{:d}'.format(endpoint_name, vlan_id)
+
+    json_config_rules = [
+        #Delete table connections
+        json_config_rule_delete(
+            '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name),{
+                'name'          : network_instance_name, 
+                'src_protocol'  : 'DIRECTLY_CONNECTED',
+                'dst_protocol'  : 'BGP',
+                'address_family': 'IPV4', 
+        }),
+
+        
+        json_config_rule_delete(
+            '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), {
+                'name'          : network_instance_name,
+                'src_protocol'  : 'STATIC',
+                'dst_protocol'  : 'BGP',
+                'address_family': 'IPV4',
+        }),
+
+        #Delete export routing policy 
+
+        json_config_rule_delete(
+            '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), {
+                'policy_name': '{:s}_export'.format(network_instance_name),
+        }),
+        json_config_rule_delete(
+            '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), {
+                'ext_community_set_name': 'set_{:s}'.format(policy_export),
+        }),
+
+        #Delete import routing policy 
+
+        json_config_rule_delete(
+            '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), {
+                'policy_name': '{:s}_import'.format(network_instance_name),
+        }),
+        json_config_rule_delete(
+            '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import, route_distinguisher), {
+                'ext_community_set_name': 'set_{:s}'.format(policy_import),
+        }),
+
+        #Delete interface; automatically deletes:
+        # - /interface[]/subinterface[]
+        json_config_rule_delete('/interface[{:s}]/subinterface[0]'.format(if_subif_name),
+        {
+            'name': if_subif_name,
+        }),
+
+        #Delete network instance; automatically deletes:
+        # - /network_instance[]/interface[]
+        # - /network_instance[]/protocols[]
+        # - /network_instance[]/inter_instance_policies[]
+        json_config_rule_delete('/network_instance[{:s}]'.format(network_instance_name),
+        {
+            'name': network_instance_name
+        }),
+    ]
+    return json_config_rules
diff --git a/src/service/service/service_handlers/ip_link/ConfigRules_test_ocnos.py b/src/service/service/service_handlers/ip_link/ConfigRules_test_ocnos.py
new file mode 100644
index 0000000000000000000000000000000000000000..5fa1d0b5b6931902b4ac50847c90bf67738032ba
--- /dev/null
+++ b/src/service/service/service_handlers/ip_link/ConfigRules_test_ocnos.py
@@ -0,0 +1,337 @@
+# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Dict, List, Tuple
+from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set
+from service.service.service_handler_api.AnyTreeTools import TreeNode
+
+def setup_config_rules(
+    service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str,
+    service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode, endpoint_acls : List [Tuple]
+) -> List[Dict]:
+
+    if service_settings  is None: return []
+    if device_settings   is None: return []
+    if endpoint_settings is None: return []
+
+    json_settings          : Dict = service_settings.value
+    json_device_settings   : Dict = device_settings.value
+    json_endpoint_settings : Dict = endpoint_settings.value
+
+    mtu                       = json_settings.get('mtu',                          1450     )  # 1512
+    #address_families         = json_settings.get('address_families',             []       )  # ['IPV4']
+    bgp_as                    = json_device_settings.get('bgp_as',                       65000    )  # 65000
+
+    router_id                 = json_device_settings.get('router_id',           '0.0.0.0')  # '10.95.0.10'
+    route_distinguisher       = json_device_settings.get('route_distinguisher',          '65000:101'    )  # '60001:801'
+    sub_interface_index       = json_endpoint_settings.get('sub_interface_index', 0        )  # 1
+    vlan_id                   = json_endpoint_settings.get('vlan_id',             1        )  # 400
+    address_ip                = json_endpoint_settings.get('address_ip',          '0.0.0.0')  # '2.2.2.1'
+    address_prefix            = json_endpoint_settings.get('address_prefix',      24       )  # 30
+
+    policy_import             = json_device_settings.get('policy_AZ',            '2'     )  # 2
+    policy_export             = json_device_settings.get('policy_ZA',            '7'     )  # 30
+    #network_interface_desc    = '{:s}-NetIf'.format(service_uuid)
+    network_interface_desc    = json_endpoint_settings.get('ni_description','')
+    #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid)
+    network_subinterface_desc = json_endpoint_settings.get('subif_description','')
+    #service_short_uuid       = service_uuid.split('-')[-1]
+    #network_instance_name    = '{:s}-NetInst'.format(service_short_uuid)
+    network_instance_name     = json_endpoint_settings.get('ni_name',          service_uuid.split('-')[-1])  #ELAN-AC:1
+
+    self_bgp_if_name          = json_device_settings.get('self_bgp_interface_name', '')
+    self_bgp_address_ip       = json_device_settings.get('self_bgp_interface_address_ip', '')
+    bgp_address_prefix        = json_device_settings.get('bgp_interface_address_prefix', '')
+    bgp_sub_interface_index   = json_device_settings.get('self_bgp_sub_interface_index', 0)
+    neighbor_bgp_if_address_ip= json_device_settings.get('neighbor_bgp_interface_address_ip', '0.0.0.0')  # '2.2.2.1' 
+
+    # if_subif_name       = '{:s}.{:d}'.format(endpoint_name, 0)
+    if_subif_name       = '{:s}'.format(endpoint_name[5:])
+
+    json_config_rules = [
+        # Configure Interface (not used)
+        #json_config_rule_set(
+        #    '/interface[{:s}]'.format(endpoint_name), {
+        #        'name': endpoint_name, 
+        #        'description': network_interface_desc, 
+        #        'mtu': mtu,
+        #}),
+
+        #Create network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]'.format(network_instance_name), {
+                'name': network_instance_name, 
+                'description': network_interface_desc, 
+                'type': 'L3VRF',
+                'route_distinguisher': route_distinguisher,
+                'router_id': router_id,
+                #'address_families': address_families,
+        }),
+
+        #Add BGP protocol to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), {
+                'name': network_instance_name, 
+                'protocol_name': bgp_as, 
+                'identifier': 'BGP', 
+                'type': 'L3VRF',
+                'as': bgp_as,
+                'router_id': router_id, 
+                'neighbors': [{'ip_address': neighbor_bgp_if_address_ip, 'remote_as': bgp_as}]
+        }),
+
+        #Add DIRECTLY CONNECTED protocol to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/protocols[DIRECTLY_CONNECTED]'.format(network_instance_name), {
+                'name': network_instance_name, 
+                'identifier': 'DIRECTLY_CONNECTED', 
+                'protocol_name': 'DIRECTLY_CONNECTED',
+        }),
+
+        #Add STATIC protocol to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/protocols[STATIC]'.format(network_instance_name), {
+                'name': network_instance_name, 
+                'identifier': 'STATIC', 
+                'protocol_name': 'STATIC',
+        }),
+
+        #Create interface with subinterface (without IP address)
+        json_config_rule_set(
+            '/interface[{:s}]/subinterface[{:d}]'.format(if_subif_name, sub_interface_index), {
+                'name'       : if_subif_name,
+                'type'       :'ethernetCsmacd',
+                'mtu'        : mtu,
+                'index'      : sub_interface_index,
+                'description': network_subinterface_desc, 
+                'vlan_id'    : vlan_id,
+        }),
+
+        #Associate interface to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), {
+                'name'          : network_instance_name, 
+                'type'          : 'L3VRF',
+                'id'            : if_subif_name, 
+                'interface'     : if_subif_name,
+                'subinterface'  : sub_interface_index,
+                'address_ip'    : address_ip, 
+                'address_prefix': address_prefix,
+        }), 
+
+        #Create interface with subinterface (with IP address)
+        json_config_rule_set(
+            '/interface[{:s}]/subinterface[{:d}]'.format(if_subif_name, sub_interface_index), {
+                'name'          : if_subif_name,
+                'type'          :'ethernetCsmacd',
+                'mtu'           : mtu,
+                'index'         : sub_interface_index,
+                'description'   : network_subinterface_desc, 
+                'vlan_id'       : vlan_id,
+                'address_ip'    : address_ip, 
+                'address_prefix': address_prefix,
+        }),
+
+        json_config_rule_set(
+            '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, self_bgp_if_name), {
+                'name'        : network_instance_name, 
+                'type'        : 'L3VRF',
+                'id'          : self_bgp_if_name, 
+                'interface'   : self_bgp_if_name,
+                'subinterface': bgp_sub_interface_index,
+                'address_ip'    : self_bgp_address_ip, 
+                'address_prefix': bgp_address_prefix,
+        }), 
+
+        #Create routing policy
+        json_config_rule_set(
+            '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import,route_distinguisher), {
+                'ext_community_set_name': 'set_{:s}'.format(policy_import),
+                'ext_community_member'  : route_distinguisher,
+        }),
+        json_config_rule_set(
+            # pylint: disable=duplicate-string-formatting-argument
+            '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(policy_import, policy_import), {
+                'policy_name'           : policy_import,
+                'statement_name'        : 'stm_{:s}'.format(policy_import), # OCNOS: '10',
+                'ext_community_set_name': 'set_{:s}'.format(policy_import),
+                'policy_result'         : 'ACCEPT_ROUTE',
+        }),
+
+        #Associate routing policy to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_import), {
+                'name'         : network_instance_name,
+                'import_policy': policy_import,
+        }),
+
+        #Create routing policy
+        json_config_rule_set(
+            '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), {
+                'ext_community_set_name': 'set_{:s}'.format(policy_export),
+                'ext_community_member'  : route_distinguisher,
+        }),
+        json_config_rule_set(
+            # pylint: disable=duplicate-string-formatting-argument
+            '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(policy_export, policy_export), {
+                'policy_name'           : policy_export,
+                'statement_name'        : 'stm_{:s}'.format(policy_export), # OCNOS: '10',
+                'ext_community_set_name': 'set_{:s}'.format(policy_export),
+                'policy_result'         : 'ACCEPT_ROUTE',
+        }),
+
+        #Associate routing policy to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/inter_instance_policies[{:s}]'.format(network_instance_name, policy_export),{
+                'name'         : network_instance_name,
+                'export_policy': policy_export,
+        }),
+
+        #Create table connections
+        json_config_rule_set(
+            '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format(network_instance_name), {
+                'name'                 : network_instance_name,
+                'src_protocol'         : 'DIRECTLY_CONNECTED',
+                'dst_protocol'         : 'BGP',
+                'address_family'       : 'IPV4',
+                'default_import_policy': 'ACCEPT_ROUTE',
+                'as'                   : bgp_as,
+        }),
+
+        json_config_rule_set(
+            '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), {
+                'name'                 : network_instance_name,
+                'src_protocol'         : 'STATIC',
+                'dst_protocol'         : 'BGP',
+                'address_family'       : 'IPV4',
+                'default_import_policy': 'ACCEPT_ROUTE',
+                'as'                   : bgp_as,
+        }),
+
+    ]
+
+    for res_key, res_value in endpoint_acls:
+        json_config_rules.append(
+               {'action': 1, 'acl': res_value}
+            )
+    return json_config_rules
+
+def teardown_config_rules(
+    service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, endpoint_name : str,
+    service_settings : TreeNode, device_settings : TreeNode, endpoint_settings : TreeNode
+) -> List[Dict]:
+
+    if service_settings  is None: return []
+    if device_settings   is None: return []
+    if endpoint_settings is None: return []
+
+    json_settings          : Dict = service_settings.value
+    json_device_settings   : Dict = device_settings.value
+    json_endpoint_settings : Dict = endpoint_settings.value
+
+    service_short_uuid        = service_uuid.split('-')[-1]
+    # network_instance_name     = '{:s}-NetInst'.format(service_short_uuid)
+    network_instance_name     = json_endpoint_settings.get('ni_name', service_short_uuid)  #ELAN-AC:1
+    #network_interface_desc    = '{:s}-NetIf'.format(service_uuid)
+    # network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid)
+    network_subinterface_desc = ''
+
+    mtu                 = json_settings.get('mtu',                          1450     )  # 1512
+    #address_families  = json_settings.get('address_families',             []       )  # ['IPV4']
+    #bgp_as              = json_device_settings.get('bgp_as',                       65000    )  # 65000
+    route_distinguisher = json_device_settings.get('route_distinguisher',          '0:0'    )  # '60001:801'
+    sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0        )  # 1
+    #router_id           = json_device_settings.get('router_id',           '0.0.0.0')  # '10.95.0.10'
+    vlan_id             = json_endpoint_settings.get('vlan_id',             1        )  # 400
+    address_ip          = json_endpoint_settings.get('address_ip',          '0.0.0.0')  # '2.2.2.1'
+    address_prefix      = json_endpoint_settings.get('address_prefix',      24       )  # 30
+    policy_import       = json_device_settings.get('policy_AZ',            '2'      )  # 2
+    policy_export       = json_device_settings.get('policy_ZA',            '7'      )  # 30
+
+    self_bgp_if_name          = json_device_settings.get('self_bgp_interface_name', '')
+    self_bgp_address_ip       = json_device_settings.get('self_bgp_interface_address_ip', '')
+    bgp_address_prefix        = json_device_settings.get('bgp_interface_address_prefix', '')
+    bgp_sub_interface_index   = json_device_settings.get('self_bgp_sub_interface_index', 0)
+
+    # if_subif_name             = '{:s}.{:d}'.format(endpoint_name, vlan_id)
+    if_subif_name             = '{:s}'.format(endpoint_name[5:])
+
+    json_config_rules = [
+        #Delete export routing policy 
+        json_config_rule_delete(
+            # pylint: disable=duplicate-string-formatting-argument
+            '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format(policy_export, policy_export), {
+                'policy_name'           : policy_export,
+                'statement_name'        : 'stm_{:s}'.format(policy_export), # OCNOS: '10',
+                'ext_community_set_name': 'set_{:s}'.format(policy_export),
+                'policy_result'         : 'ACCEPT_ROUTE',
+        }),
+        json_config_rule_delete(
+            '/routing_policy/bgp_defined_set[{:s}_rt_export][{:s}]'.format(policy_export, route_distinguisher), {
+                'ext_community_set_name': 'set_{:s}'.format(policy_export),
+                'ext_community_member'  : route_distinguisher,
+        }),
+
+        #Delete import routing policy 
+        json_config_rule_delete(
+            # pylint: disable=duplicate-string-formatting-argument
+            '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format(policy_import, policy_import), {
+                'policy_name'           : policy_import,
+                'statement_name'        : 'stm_{:s}'.format(policy_import), # OCNOS: '10',
+                'ext_community_set_name': 'set_{:s}'.format(policy_import),
+                'policy_result'         : 'ACCEPT_ROUTE',
+        }),
+        json_config_rule_delete(
+            '/routing_policy/bgp_defined_set[{:s}_rt_import][{:s}]'.format(policy_import, route_distinguisher), {
+                'ext_community_set_name': 'set_{:s}'.format(policy_import),
+                'ext_community_member'  : route_distinguisher,
+        }),
+
+        #Delete interface; automatically deletes:
+        # - /interface[]/subinterface[]
+        # json_config_rule_delete('/interface[{:s}]/subinterface[0]'.format(if_subif_name),
+        # {
+        #     'name': if_subif_name,
+        # }),
+
+        #Delete network instance; automatically deletes:
+        # - /network_instance[]/interface[]
+        # - /network_instance[]/protocols[]
+        # - /network_instance[]/inter_instance_policies[]
+
+        #Associate interface to network instance
+        json_config_rule_set(
+            '/network_instance[{:s}]/interface[{:s}]'.format('default', if_subif_name), {
+                'name'        : 'default', 
+                'id'          : if_subif_name, 
+                'interface'   : if_subif_name,
+                'subinterface': sub_interface_index,
+                'address_ip'    : address_ip, 
+                'address_prefix': address_prefix,
+        }), 
+        json_config_rule_set(
+            '/network_instance[{:s}]/interface[{:s}]'.format('default', self_bgp_if_name), {
+                'name'        : 'default', 
+                'id'          : self_bgp_if_name, 
+                'interface'   : self_bgp_if_name,
+                'subinterface': bgp_sub_interface_index,
+                'address_ip'    : self_bgp_address_ip, 
+                'address_prefix': bgp_address_prefix,
+        }), 
+        json_config_rule_delete('/network_instance[{:s}]'.format(network_instance_name),
+        {
+            'name': network_instance_name
+        }),
+    ]
+    return json_config_rules
diff --git a/src/service/service/service_handlers/ip_link/IP_LinkServiceHandler.py b/src/service/service/service_handlers/ip_link/IP_LinkServiceHandler.py
new file mode 100644
index 0000000000000000000000000000000000000000..51b454051c97a54571ea3f271b9cd40bb3995f36
--- /dev/null
+++ b/src/service/service/service_handlers/ip_link/IP_LinkServiceHandler.py
@@ -0,0 +1,167 @@
+# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging
+from typing import Any, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.proto.context_pb2 import ConfigRule, DeviceId, Service
+from common.tools.object_factory.Device import json_device_id
+from common.type_checkers.Checkers import chk_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 .ConfigRules import setup_config_rules, teardown_config_rules
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_openconfig'})
+
+class IP_LinkServiceHandler(_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)
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetEndpoint(
+        self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None
+    ) -> List[Union[bool, Exception]]:
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) == 0: return []
+
+        service_uuid = self.__service.service_id.service_uuid.uuid
+        settings = self.__settings_handler.get('/settings')
+
+        results = []
+        for endpoint in endpoints:
+            try:
+                device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint)
+
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                device_settings = self.__settings_handler.get_device_settings(device_obj)
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
+                endpoint_acls = self.__settings_handler.get_endpoint_acls(device_obj, endpoint_obj)
+                endpoint_ip_link = self.__settings_handler.get_endpoint_ip_link(device_obj, endpoint_obj)
+                endpoint_name = endpoint_obj.name
+
+                LOGGER.warning('DATOS({:s})'.format(str(endpoint_ip_link)))
+
+                json_config_rules = setup_config_rules(
+                    service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name,
+                    settings, device_settings, endpoint_settings, endpoint_acls, endpoint_ip_link)
+
+                if len(json_config_rules) > 0:
+                    del device_obj.device_config.config_rules[:]
+                    for json_config_rule in json_config_rules:
+                        device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                    self.__task_executor.configure_device(device_obj)
+
+                results.append(True)
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint)))
+                results.append(e)
+
+        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]]:
+        chk_type('endpoints', endpoints, list)
+        if len(endpoints) == 0: return []
+
+        service_uuid = self.__service.service_id.service_uuid.uuid
+        settings = self.__settings_handler.get('/settings')
+
+        results = []
+        for endpoint in endpoints:
+            try:
+                device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint)
+
+                device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid)))
+                device_settings = self.__settings_handler.get_device_settings(device_obj)
+                endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid)
+                endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj)
+                endpoint_name = endpoint_obj.name
+
+                json_config_rules = teardown_config_rules(
+                    service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name,
+                    settings, device_settings, endpoint_settings)
+
+                if len(json_config_rules) > 0:
+                    del device_obj.device_config.config_rules[:]
+                    for json_config_rule in json_config_rules:
+                        device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule))
+                    self.__task_executor.configure_device(device_obj)
+
+                results.append(True)
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))
+                results.append(e)
+
+        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/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh b/src/service/service/service_handlers/ip_link/__init__.py
old mode 100755
new mode 100644
similarity index 56%
rename from src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh
rename to src/service/service/service_handlers/ip_link/__init__.py
index 0bd133e16caac438c8da1a5795fce89e99230c9a..3ee6f7071f145e06c3aeaefc09a43ccd88e619e3
--- a/src/nbi/service/rest_server/nbi_plugins/ietf_l3vpn/run_tests_locally-nbi-ietf-l3vpn.sh
+++ b/src/service/service/service_handlers/ip_link/__init__.py
@@ -1,5 +1,4 @@
-#!/bin/bash
-# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -13,13 +12,3 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
-PROJECTDIR=`pwd`
-
-cd $PROJECTDIR/src
-RCFILE=$PROJECTDIR/coverage/.coveragerc
-
-# Run unitary tests and analyze coverage of code at same time
-# helpful pytest flags: --log-level=INFO -o log_cli=true --verbose --maxfail=1 --durations=0
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    nbi/tests/test_ietf_l3vpn.py
diff --git a/src/service/service/task_scheduler/TaskScheduler.py b/src/service/service/task_scheduler/TaskScheduler.py
index b492b4581b05cb34bed624efd9be2d4f62da5856..a791f1e9d29e119269ab15195a7b6aec00c308ad 100644
--- a/src/service/service/task_scheduler/TaskScheduler.py
+++ b/src/service/service/task_scheduler/TaskScheduler.py
@@ -245,6 +245,8 @@ class TasksScheduler:
         for task_key in ordered_task_keys:
             str_task_name = ('DRY ' if dry_run else '') + str(task_key)
             LOGGER.debug('[execute_all] starting task {:s}'.format(str_task_name))
+            LOGGER.debug('[execute_all] key={:s}'.format(str(task_key)))
+            LOGGER.debug('[execute_all] key={:s}'.format(str(task_key)))
             task = self._tasks.get(task_key)
             succeeded = True if dry_run else task.execute()
             results.append(succeeded)
diff --git a/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java b/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java
index 4593770498216267b8d2f95dd728fccfbb9dc134..b0742e306183a7d5da1a03a54d7734b1e398d75c 100644
--- a/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java
+++ b/src/ztp/target/generated-sources/grpc/context/ContextOuterClass.java
@@ -504,6 +504,10 @@ public final class ContextOuterClass {
          * <code>SERVICETYPE_OPTICAL_CONNECTIVITY = 6;</code>
          */
         SERVICETYPE_OPTICAL_CONNECTIVITY(6),
+        /**
+         * <code>SERVICETYPE_IPLINK = 6;</code>
+         */
+        SERVICETYPE_IPLINK(7),
         UNRECOGNIZED(-1);
 
         /**
@@ -541,6 +545,11 @@ public final class ContextOuterClass {
          */
         public static final int SERVICETYPE_OPTICAL_CONNECTIVITY_VALUE = 6;
 
+        /**
+         * <code>SERVICETYPE_IPLINK = 7;</code>
+         */
+        public static final int SERVICETYPE_IPLINK_VALUE = 7;
+
         public final int getNumber() {
             if (this == UNRECOGNIZED) {
                 throw new java.lang.IllegalArgumentException("Can't get the number of an unknown enum value.");
@@ -578,6 +587,8 @@ public final class ContextOuterClass {
                     return SERVICETYPE_E2E;
                 case 6:
                     return SERVICETYPE_OPTICAL_CONNECTIVITY;
+                case 7:
+                    return SERVICETYPE_IPLINK;
                 default:
                     return null;
             }