diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py
index 469abcad387dc055ba17770e4f405db1d1ceaa3b..b3b485a471899dd96a4985fedb4bb6ede2432921 100644
--- a/src/device/service/drivers/__init__.py
+++ b/src/device/service/drivers/__init__.py
@@ -74,6 +74,15 @@ DRIVERS.append(
         #}
     ]))
 
+from .ietf_l2vpn.IetfL2VpnDriver import IetfL2VpnDriver # pylint: disable=wrong-import-position
+DRIVERS.append(
+    (IetfL2VpnDriver, [
+        {
+            FilterFieldEnum.DEVICE_TYPE: DeviceTypeEnum.TERAFLOWSDN_CONTROLLER,
+            FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_IETF_L2VPN,
+        }
+    ]))
+
 if LOAD_ALL_DEVICE_DRIVERS:
     from .openconfig.OpenConfigDriver import OpenConfigDriver # pylint: disable=wrong-import-position
     DRIVERS.append(
diff --git a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py
index 1b12176361fb226df694b8aa0a1d14848b7b1635..3823f569be2d408f81431d1381c5942bb51bc604 100644
--- a/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py
+++ b/src/device/service/drivers/ietf_l2vpn/IetfL2VpnDriver.py
@@ -12,6 +12,118 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-class IetfL2VpnDriver:
-    def __init__(self) -> None:
-        pass
+import logging, requests, threading
+from requests.auth import HTTPBasicAuth
+from typing import Any, Iterator, List, Optional, Tuple, Union
+from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method
+from common.type_checkers.Checkers import chk_string, chk_type
+from device.service.driver_api._Driver import _Driver
+from . import ALL_RESOURCE_KEYS
+from .Tools import create_connectivity_service, find_key, config_getter, delete_connectivity_service
+
+LOGGER = logging.getLogger(__name__)
+
+METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'ietf_l2vpn'})
+
+class IetfL2VpnDriver(_Driver):
+    def __init__(self, address: str, port: int, **settings) -> None:    # pylint: disable=super-init-not-called
+        self.__lock = threading.Lock()
+        self.__started = threading.Event()
+        self.__terminate = threading.Event()
+        username = settings.get('username')
+        password = settings.get('password')
+        self.__auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None
+        scheme = settings.get('scheme', 'http')
+        self.__tapi_root = '{:s}://{:s}:{:d}'.format(scheme, address, int(port))
+        self.__timeout = int(settings.get('timeout', 120))
+
+    def Connect(self) -> bool:
+        url = self.__tapi_root + '/restconf/data/tapi-common:context'
+        with self.__lock:
+            if self.__started.is_set(): return True
+            try:
+                requests.get(url, timeout=self.__timeout, verify=False, auth=self.__auth)
+            except requests.exceptions.Timeout:
+                LOGGER.exception('Timeout connecting {:s}'.format(str(self.__tapi_root)))
+                return False
+            except Exception:  # pylint: disable=broad-except
+                LOGGER.exception('Exception connecting {:s}'.format(str(self.__tapi_root)))
+                return False
+            else:
+                self.__started.set()
+                return True
+
+    def Disconnect(self) -> bool:
+        with self.__lock:
+            self.__terminate.set()
+            return True
+
+    @metered_subclass_method(METRICS_POOL)
+    def GetInitialConfig(self) -> List[Tuple[str, Any]]:
+        with self.__lock:
+            return []
+
+    @metered_subclass_method(METRICS_POOL)
+    def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]:
+        chk_type('resources', resource_keys, list)
+        results = []
+        with self.__lock:
+            if len(resource_keys) == 0: resource_keys = ALL_RESOURCE_KEYS
+            for i, resource_key in enumerate(resource_keys):
+                str_resource_name = 'resource_key[#{:d}]'.format(i)
+                chk_string(str_resource_name, resource_key, allow_empty=False)
+                results.extend(config_getter(
+                    self.__tapi_root, resource_key, timeout=self.__timeout, auth=self.__auth))
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        results = []
+        if len(resources) == 0:
+            return results
+        with self.__lock:
+            for resource in resources:
+                LOGGER.info('resource = {:s}'.format(str(resource)))
+
+                input_sip = find_key(resource, 'input_sip')
+                output_sip = find_key(resource, 'output_sip')
+                uuid = find_key(resource, 'uuid')
+                capacity_value = find_key(resource, 'capacity_value')
+                capacity_unit = find_key(resource, 'capacity_unit')
+                layer_protocol_name = find_key(resource, 'layer_protocol_name')
+                layer_protocol_qualifier = find_key(resource, 'layer_protocol_qualifier')
+                direction = find_key(resource, 'direction')
+
+                data = create_connectivity_service(
+                    self.__tapi_root, uuid, input_sip, output_sip, direction, capacity_value, capacity_unit,
+                    layer_protocol_name, layer_protocol_qualifier, timeout=self.__timeout, auth=self.__auth)
+                results.extend(data)
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def DeleteConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]:
+        results = []
+        if len(resources) == 0: return results
+        with self.__lock:
+            for resource in resources:
+                LOGGER.info('resource = {:s}'.format(str(resource)))
+                uuid = find_key(resource, 'uuid')
+                results.extend(delete_connectivity_service(
+                    self.__tapi_root, uuid, timeout=self.__timeout, auth=self.__auth))
+        return results
+
+    @metered_subclass_method(METRICS_POOL)
+    def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        # TODO: TAPI does not support monitoring by now
+        return [False for _ in subscriptions]
+
+    @metered_subclass_method(METRICS_POOL)
+    def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]:
+        # TODO: TAPI does not support monitoring by now
+        return [False for _ in subscriptions]
+
+    def GetState(
+        self, blocking=False, terminate : Optional[threading.Event] = None
+    ) -> Iterator[Tuple[float, str, Any]]:
+        # TODO: TAPI does not support monitoring by now
+        return []
diff --git a/src/device/service/drivers/ietf_l2vpn/MockOSM.py b/src/device/service/drivers/ietf_l2vpn/MockOSM.py
new file mode 100644
index 0000000000000000000000000000000000000000..338db0e19becc8a9dd277beec7f3b4ceb2e765a3
--- /dev/null
+++ b/src/device/service/drivers/ietf_l2vpn/MockOSM.py
@@ -0,0 +1,62 @@
+# 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 .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN
+
+LOGGER = logging.getLogger(__name__)
+
+class MockOSM:
+    def __init__(self, url, mapping, username, password):
+        wim = {'wim_url': url}
+        wim_account = {'user': username, 'password': password}
+        config = {'mapping_not_needed': False, 'service_endpoint_mapping': mapping}
+        self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config)
+        self.conn_info = {} # internal database emulating OSM storage provided to WIM Connectors
+
+    def create_connectivity_service(self, service_type, connection_points):
+        LOGGER.info('[create_connectivity_service] service_type={:s}'.format(str(service_type)))
+        LOGGER.info('[create_connectivity_service] connection_points={:s}'.format(str(connection_points)))
+        self.wim.check_credentials()
+        result = self.wim.create_connectivity_service(service_type, connection_points)
+        LOGGER.info('[create_connectivity_service] result={:s}'.format(str(result)))
+        service_uuid, conn_info = result
+        self.conn_info[service_uuid] = conn_info
+        return service_uuid
+
+    def get_connectivity_service_status(self, service_uuid):
+        LOGGER.info('[get_connectivity_service] service_uuid={:s}'.format(str(service_uuid)))
+        conn_info = self.conn_info.get(service_uuid)
+        if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid)))
+        LOGGER.info('[get_connectivity_service] conn_info={:s}'.format(str(conn_info)))
+        self.wim.check_credentials()
+        result = self.wim.get_connectivity_service_status(service_uuid, conn_info=conn_info)
+        LOGGER.info('[get_connectivity_service] result={:s}'.format(str(result)))
+        return result
+
+    def edit_connectivity_service(self, service_uuid, connection_points):
+        LOGGER.info('[edit_connectivity_service] service_uuid={:s}'.format(str(service_uuid)))
+        LOGGER.info('[edit_connectivity_service] connection_points={:s}'.format(str(connection_points)))
+        conn_info = self.conn_info.get(service_uuid)
+        if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid)))
+        LOGGER.info('[edit_connectivity_service] conn_info={:s}'.format(str(conn_info)))
+        self.wim.edit_connectivity_service(service_uuid, conn_info=conn_info, connection_points=connection_points)
+
+    def delete_connectivity_service(self, service_uuid):
+        LOGGER.info('[delete_connectivity_service] service_uuid={:s}'.format(str(service_uuid)))
+        conn_info = self.conn_info.get(service_uuid)
+        if conn_info is None: raise Exception('ServiceId({:s}) not found'.format(str(service_uuid)))
+        LOGGER.info('[delete_connectivity_service] conn_info={:s}'.format(str(conn_info)))
+        self.wim.check_credentials()
+        self.wim.delete_connectivity_service(service_uuid, conn_info=conn_info)
diff --git a/src/device/service/drivers/ietf_l2vpn/Tools.py b/src/device/service/drivers/ietf_l2vpn/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..8188c77df116154677b7b94d77e3d2bae8c7b690
--- /dev/null
+++ b/src/device/service/drivers/ietf_l2vpn/Tools.py
@@ -0,0 +1,184 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json, logging, operator, requests
+from requests.auth import HTTPBasicAuth
+from typing import Dict, Optional
+from device.service.driver_api._Driver import RESOURCE_ENDPOINTS
+
+LOGGER = logging.getLogger(__name__)
+
+HTTP_OK_CODES = {
+    200,    # OK
+    201,    # Created
+    202,    # Accepted
+    204,    # No Content
+}
+
+def find_key(resource, key):
+    return json.loads(resource[1])[key]
+
+
+def config_getter(
+    root_url : str, resource_key : str, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None
+):
+    url = '{:s}/restconf/data/tapi-common:context'.format(root_url)
+    result = []
+    try:
+        response = requests.get(url, timeout=timeout, verify=False, auth=auth)
+    except requests.exceptions.Timeout:
+        LOGGER.exception('Timeout connecting {:s}'.format(url))
+        return result
+    except Exception as e:  # pylint: disable=broad-except
+        LOGGER.exception('Exception retrieving {:s}'.format(resource_key))
+        result.append((resource_key, e))
+        return result
+
+    try:
+        context = json.loads(response.content)
+    except Exception as e:  # pylint: disable=broad-except
+        LOGGER.warning('Unable to decode reply: {:s}'.format(str(response.content)))
+        result.append((resource_key, e))
+        return result
+
+    if resource_key != RESOURCE_ENDPOINTS: return result
+
+    if 'tapi-common:context' in context:
+        context = context['tapi-common:context']
+    elif 'context' in context:
+        context = context['context']
+
+    for sip in context['service-interface-point']:
+        layer_protocol_name = sip.get('layer-protocol-name', '?')
+        supportable_spectrum = sip.get('tapi-photonic-media:media-channel-service-interface-point-spec', {})
+        supportable_spectrum = supportable_spectrum.get('mc-pool', {})
+        supportable_spectrum = supportable_spectrum.get('supportable-spectrum', [])
+        supportable_spectrum = supportable_spectrum[0] if len(supportable_spectrum) == 1 else {}
+        grid_type = supportable_spectrum.get('frequency-constraint', {}).get('grid-type')
+        granularity = supportable_spectrum.get('frequency-constraint', {}).get('adjustment-granularity')
+        direction = sip.get('direction', '?')
+        endpoint_type = [layer_protocol_name, grid_type, granularity, direction]
+        str_endpoint_type = ':'.join(filter(lambda i: operator.is_not(i, None), endpoint_type))
+        endpoint_url = '/endpoints/endpoint[{:s}]'.format(sip['uuid'])
+        endpoint_data = {'uuid': sip['uuid'], 'type': str_endpoint_type}
+        result.append((endpoint_url, endpoint_data))
+
+    return result
+
+def create_connectivity_service(
+    root_url, uuid, input_sip, output_sip, direction, capacity_value, capacity_unit, layer_protocol_name,
+    layer_protocol_qualifier,
+    auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None
+):
+
+    url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context'.format(root_url)
+    headers = {'content-type': 'application/json'}
+    data = {
+        'tapi-connectivity:connectivity-service': [
+            {
+                'uuid': uuid,
+                'connectivity-constraint': {
+                    'requested-capacity': {
+                        'total-size': {
+                            'value': capacity_value,
+                            'unit': capacity_unit
+                        }
+                    },
+                    'connectivity-direction': direction
+                },
+                'end-point': [
+                    {
+                        'service-interface-point': {
+                            'service-interface-point-uuid': input_sip
+                        },
+                        'layer-protocol-name': layer_protocol_name,
+                        'layer-protocol-qualifier': layer_protocol_qualifier,
+                        'local-id': input_sip
+                    },
+                    {
+                        'service-interface-point': {
+                            'service-interface-point-uuid': output_sip
+                        },
+                        'layer-protocol-name': layer_protocol_name,
+                        'layer-protocol-qualifier': layer_protocol_qualifier,
+                        'local-id': output_sip
+                    }
+                ]
+            }
+        ]
+    }
+    results = []
+    try:
+        LOGGER.info('Connectivity service {:s}: {:s}'.format(str(uuid), str(data)))
+        response = requests.post(
+            url=url, data=json.dumps(data), timeout=timeout, headers=headers, verify=False, auth=auth)
+        LOGGER.info('TAPI response: {:s}'.format(str(response)))
+    except Exception as e:  # pylint: disable=broad-except
+        LOGGER.exception('Exception creating ConnectivityService(uuid={:s}, data={:s})'.format(str(uuid), str(data)))
+        results.append(e)
+    else:
+        if response.status_code not in HTTP_OK_CODES:
+            msg = 'Could not create ConnectivityService(uuid={:s}, data={:s}). status_code={:s} reply={:s}'
+            LOGGER.error(msg.format(str(uuid), str(data), str(response.status_code), str(response)))
+        results.append(response.status_code in HTTP_OK_CODES)
+    return results
+
+def delete_connectivity_service(root_url, uuid, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None):
+    url = '{:s}/restconf/data/tapi-common:context/tapi-connectivity:connectivity-context/connectivity-service={:s}'
+    url = url.format(root_url, uuid)
+    results = []
+    try:
+        response = requests.delete(url=url, timeout=timeout, verify=False, auth=auth)
+    except Exception as e:  # pylint: disable=broad-except
+        LOGGER.exception('Exception deleting ConnectivityService(uuid={:s})'.format(str(uuid)))
+        results.append(e)
+    else:
+        if response.status_code not in HTTP_OK_CODES:
+            msg = 'Could not delete ConnectivityService(uuid={:s}). status_code={:s} reply={:s}'
+            LOGGER.error(msg.format(str(uuid), str(response.status_code), str(response)))
+        results.append(response.status_code in HTTP_OK_CODES)
+    return results
+
+def compose_service_endpoint_id(site_id : str, endpoint_id : Dict):
+    device_uuid = endpoint_id['device_id']['device_uuid']['uuid']
+    endpoint_uuid = endpoint_id['endpoint_uuid']['uuid']
+    return ':'.join([site_id, device_uuid, endpoint_uuid])
+
+def wim_mapping(site_id, ce_endpoint_id, pe_device_id : Optional[Dict] = None, priority=None, redundant=[]):
+    ce_device_uuid = ce_endpoint_id['device_id']['device_uuid']['uuid']
+    ce_endpoint_uuid = ce_endpoint_id['endpoint_uuid']['uuid']
+    service_endpoint_id = compose_service_endpoint_id(site_id, ce_endpoint_id)
+    if pe_device_id is None:
+        bearer = '{:s}:{:s}'.format(ce_device_uuid, ce_endpoint_uuid)
+    else:
+        pe_device_uuid = pe_device_id['device_uuid']['uuid']
+        bearer = '{:s}:{:s}'.format(ce_device_uuid, pe_device_uuid)
+    mapping = {
+        'service_endpoint_id': service_endpoint_id,
+        'datacenter_id': site_id, 'device_id': ce_device_uuid, 'device_interface_id': ce_endpoint_uuid,
+        'service_mapping_info': {
+            'site-id': site_id,
+            'bearer': {'bearer-reference': bearer},
+        }
+    }
+    if priority is not None: mapping['service_mapping_info']['priority'] = priority
+    if len(redundant) > 0: mapping['service_mapping_info']['redundant'] = redundant
+    return service_endpoint_id, mapping
+
+def connection_point(service_endpoint_id : str, encapsulation_type : str, vlan_id : int):
+    return {
+        'service_endpoint_id': service_endpoint_id,
+        'service_endpoint_encapsulation_type': encapsulation_type,
+        'service_endpoint_encapsulation_info': {'vlan': vlan_id}
+    }
diff --git a/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa4ca045f41ffdc69d2ebf2fcd9b5db99ce45dbe
--- /dev/null
+++ b/src/device/service/drivers/ietf_l2vpn/WimconnectorIETFL2VPN.py
@@ -0,0 +1,543 @@
+# -*- coding: utf-8 -*-
+##
+# Copyright 2018 Telefonica
+# All Rights Reserved.
+#
+# Contributors: Oscar Gonzalez de Dios, Manuel Lopez Bravo, Guillermo Pajares Martin
+# 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.
+#
+# This work has been performed in the context of the Metro-Haul project -
+# funded by the European Commission under Grant number 761727 through the
+# Horizon 2020 program.
+##
+"""The SDN/WIM connector is responsible for establishing wide area network
+connectivity.
+
+This SDN/WIM connector implements the standard IETF RFC 8466 "A YANG Data
+ Model for Layer 2 Virtual Private Network (L2VPN) Service Delivery"
+
+It receives the endpoints and the necessary details to request
+the Layer 2 service.
+"""
+import requests
+import uuid
+import logging
+import copy
+#from osm_ro_plugin.sdnconn import SdnConnectorBase, SdnConnectorError
+from .sdnconn import SdnConnectorBase, SdnConnectorError
+
+"""Check layer where we move it"""
+
+
+class WimconnectorIETFL2VPN(SdnConnectorBase):
+    def __init__(self, wim, wim_account, config=None, logger=None):
+        """IETF L2VPN WIM connector
+
+        Arguments: (To be completed)
+            wim (dict): WIM record, as stored in the database
+            wim_account (dict): WIM account record, as stored in the database
+        """
+        self.logger = logging.getLogger("ro.sdn.ietfl2vpn")
+        super().__init__(wim, wim_account, config, logger)
+        self.headers = {"Content-Type": "application/json"}
+        self.mappings = {
+            m["service_endpoint_id"]: m for m in self.service_endpoint_mapping
+        }
+        self.user = wim_account.get("user")
+        self.passwd = wim_account.get("password")           # replace "passwordd" -> "password"
+
+        if self.user and self.passwd is not None:
+            self.auth = (self.user, self.passwd)
+        else:
+            self.auth = None
+
+        self.logger.info("IETFL2VPN Connector Initialized.")
+
+    def check_credentials(self):
+        endpoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(
+            self.wim["wim_url"]
+        )
+
+        try:
+            response = requests.get(endpoint, auth=self.auth)
+            http_code = response.status_code
+        except requests.exceptions.RequestException as e:
+            raise SdnConnectorError(e.response, http_code=503)
+
+        if http_code != 200:
+            raise SdnConnectorError("Failed while authenticating", http_code=http_code)
+
+        self.logger.info("Credentials checked")
+
+    def get_connectivity_service_status(self, service_uuid, conn_info=None):
+        """Monitor the status of the connectivity service stablished
+
+        Arguments:
+            service_uuid: Connectivity service unique identifier
+
+        Returns:
+            Examples::
+                {'sdn_status': 'ACTIVE'}
+                {'sdn_status': 'INACTIVE'}
+                {'sdn_status': 'DOWN'}
+                {'sdn_status': 'ERROR'}
+        """
+        try:
+            self.logger.info("Sending get connectivity service stuatus")
+            servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format(
+                self.wim["wim_url"], service_uuid
+            )
+            response = requests.get(servicepoint, auth=self.auth)
+            self.logger.warning('response.status_code={:s}'.format(str(response.status_code)))
+            if response.status_code != requests.codes.ok:
+                raise SdnConnectorError(
+                    "Unable to obtain connectivity servcice status",
+                    http_code=response.status_code,
+                )
+
+            service_status = {"sdn_status": "ACTIVE"}
+
+            return service_status
+        except requests.exceptions.ConnectionError:
+            raise SdnConnectorError("Request Timeout", http_code=408)
+
+    def search_mapp(self, connection_point):
+        id = connection_point["service_endpoint_id"]
+        if id not in self.mappings:
+            raise SdnConnectorError("Endpoint {} not located".format(str(id)))
+        else:
+            return self.mappings[id]
+
+    def create_connectivity_service(self, service_type, connection_points, **kwargs):
+        """Stablish WAN connectivity between the endpoints
+
+        Arguments:
+            service_type (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2),
+                ``L3``.
+            connection_points (list): each point corresponds to
+                an entry point from the DC to the transport network. One
+                connection point serves to identify the specific access and
+                some other service parameters, such as encapsulation type.
+                Represented by a dict as follows::
+
+                    {
+                      "service_endpoint_id": ..., (str[uuid])
+                      "service_endpoint_encapsulation_type": ...,
+                           (enum: none, dot1q, ...)
+                      "service_endpoint_encapsulation_info": {
+                        ... (dict)
+                        "vlan": ..., (int, present if encapsulation is dot1q)
+                        "vni": ... (int, present if encapsulation is vxlan),
+                        "peers": [(ipv4_1), (ipv4_2)]
+                            (present if encapsulation is vxlan)
+                      }
+                    }
+
+              The service endpoint ID should be previously informed to the WIM
+              engine in the RO when the WIM port mapping is registered.
+
+        Keyword Arguments:
+            bandwidth (int): value in kilobytes
+            latency (int): value in milliseconds
+
+        Other QoS might be passed as keyword arguments.
+
+        Returns:
+            tuple: ``(service_id, conn_info)`` containing:
+               - *service_uuid* (str): UUID of the established connectivity
+                  service
+               - *conn_info* (dict or None): Information to be stored at the
+                 database (or ``None``). This information will be provided to
+                 the :meth:`~.edit_connectivity_service` and :obj:`~.delete`.
+                 **MUST** be JSON/YAML-serializable (plain data structures).
+
+        Raises:
+            SdnConnectorException: In case of error.
+        """
+        SETTINGS = {    # min_endpoints, max_endpoints, vpn_service_type
+            'ELINE': (2,    2, 'vpws'), # Virtual Private Wire Service
+            'ELAN' : (2, None, 'vpls'), # Virtual Private LAN  Service
+        }
+        settings = SETTINGS.get(service_type)
+        if settings is None: raise NotImplementedError('Unsupported service_type({:s})'.format(str(service_type)))
+        min_endpoints, max_endpoints, vpn_service_type = settings
+
+        if max_endpoints is not None and len(connection_points) > max_endpoints:
+            msg = "Connections between more than {:d} endpoints are not supported for service_type {:s}"
+            raise SdnConnectorError(msg.format(max_endpoints, service_type))
+
+        if min_endpoints is not None and len(connection_points) < min_endpoints:
+            msg = "Connections must be of at least {:d} endpoints for service_type {:s}"
+            raise SdnConnectorError(msg.format(min_endpoints, service_type))
+
+        """First step, create the vpn service"""
+        uuid_l2vpn = str(uuid.uuid4())
+        vpn_service = {}
+        vpn_service["vpn-id"] = uuid_l2vpn
+        vpn_service["vpn-svc-type"] = vpn_service_type
+        vpn_service["svc-topo"] = "any-to-any"
+        vpn_service["customer-name"] = "osm"
+        vpn_service_list = []
+        vpn_service_list.append(vpn_service)
+        vpn_service_l = {"ietf-l2vpn-svc:vpn-service": vpn_service_list}
+        response_service_creation = None
+        conn_info = []
+        self.logger.info("Sending vpn-service :{}".format(vpn_service_l))
+
+        try:
+            endpoint_service_creation = (
+                "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(
+                    self.wim["wim_url"]
+                )
+            )
+            response_service_creation = requests.post(
+                endpoint_service_creation,
+                headers=self.headers,
+                json=vpn_service_l,
+                auth=self.auth,
+            )
+        except requests.exceptions.ConnectionError:
+            raise SdnConnectorError(
+                "Request to create service Timeout", http_code=408
+            )
+
+        if response_service_creation.status_code == 409:
+            raise SdnConnectorError(
+                "Service already exists",
+                http_code=response_service_creation.status_code,
+            )
+        elif response_service_creation.status_code != requests.codes.created:
+            raise SdnConnectorError(
+                "Request to create service not accepted",
+                http_code=response_service_creation.status_code,
+            )
+
+        self.logger.info('connection_points = {:s}'.format(str(connection_points)))
+
+        # Check if protected paths are requested
+        extended_connection_points = []
+        for connection_point in connection_points:
+            extended_connection_points.append(connection_point)
+
+            connection_point_wan_info = self.search_mapp(connection_point)
+            service_mapping_info = connection_point_wan_info.get('service_mapping_info', {})
+            redundant_service_endpoint_ids = service_mapping_info.get('redundant')
+
+            if redundant_service_endpoint_ids is None: continue
+            if len(redundant_service_endpoint_ids) == 0: continue
+
+            for redundant_service_endpoint_id in redundant_service_endpoint_ids:
+                redundant_connection_point = copy.deepcopy(connection_point)
+                redundant_connection_point['service_endpoint_id'] = redundant_service_endpoint_id
+                extended_connection_points.append(redundant_connection_point)
+
+        self.logger.info('extended_connection_points = {:s}'.format(str(extended_connection_points)))
+
+        """Second step, create the connections and vpn attachments"""
+        for connection_point in extended_connection_points:
+            connection_point_wan_info = self.search_mapp(connection_point)
+            site_network_access = {}
+            connection = {}
+
+            if connection_point["service_endpoint_encapsulation_type"] != "none":
+                if (
+                    connection_point["service_endpoint_encapsulation_type"]
+                    == "dot1q"
+                ):
+                    """The connection is a VLAN"""
+                    connection["encapsulation-type"] = "dot1q-vlan-tagged"
+                    tagged = {}
+                    tagged_interf = {}
+                    service_endpoint_encapsulation_info = connection_point[
+                        "service_endpoint_encapsulation_info"
+                    ]
+
+                    if service_endpoint_encapsulation_info["vlan"] is None:
+                        raise SdnConnectorError("VLAN must be provided")
+
+                    tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info[
+                        "vlan"
+                    ]
+                    tagged["dot1q-vlan-tagged"] = tagged_interf
+                    connection["tagged-interface"] = tagged
+                else:
+                    raise NotImplementedError("Encapsulation type not implemented")
+
+            site_network_access["connection"] = connection
+            self.logger.info("Sending connection:{}".format(connection))
+            vpn_attach = {}
+            vpn_attach["vpn-id"] = uuid_l2vpn
+            vpn_attach["site-role"] = vpn_service["svc-topo"] + "-role"
+            site_network_access["vpn-attachment"] = vpn_attach
+            self.logger.info("Sending vpn-attachement :{}".format(vpn_attach))
+            uuid_sna = str(uuid.uuid4())
+            site_network_access["network-access-id"] = uuid_sna
+            site_network_access["bearer"] = connection_point_wan_info[
+                "service_mapping_info"
+            ]["bearer"]
+
+            access_priority = connection_point_wan_info["service_mapping_info"].get("priority")
+            if access_priority is not None:
+                availability = {}
+                availability["access-priority"] = access_priority
+                availability["single-active"] = [None]
+                site_network_access["availability"] = availability
+
+                constraint = {}
+                constraint['constraint-type'] = 'end-to-end-diverse'
+                constraint['target'] = {'all-other-accesses': [None]}
+
+                access_diversity = {}
+                access_diversity['constraints'] = {'constraint': []}
+                access_diversity['constraints']['constraint'].append(constraint)
+                site_network_access["access-diversity"] = access_diversity
+
+            site_network_accesses = {}
+            site_network_access_list = []
+            site_network_access_list.append(site_network_access)
+            site_network_accesses[
+                "ietf-l2vpn-svc:site-network-access"
+            ] = site_network_access_list
+            conn_info_d = {}
+            conn_info_d["site"] = connection_point_wan_info["service_mapping_info"][
+                "site-id"
+            ]
+            conn_info_d["site-network-access-id"] = site_network_access[
+                "network-access-id"
+            ]
+            conn_info_d["mapping"] = None
+            conn_info.append(conn_info_d)
+
+            try:
+                endpoint_site_network_access_creation = (
+                    "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/"
+                    "sites/site={}/site-network-accesses/".format(
+                        self.wim["wim_url"],
+                        connection_point_wan_info["service_mapping_info"][
+                            "site-id"
+                        ],
+                    )
+                )
+                response_endpoint_site_network_access_creation = requests.post(
+                    endpoint_site_network_access_creation,
+                    headers=self.headers,
+                    json=site_network_accesses,
+                    auth=self.auth,
+                )
+
+                if (
+                    response_endpoint_site_network_access_creation.status_code
+                    == 409
+                ):
+                    self.delete_connectivity_service(vpn_service["vpn-id"])
+
+                    raise SdnConnectorError(
+                        "Site_Network_Access with ID '{}' already exists".format(
+                            site_network_access["network-access-id"]
+                        ),
+                        http_code=response_endpoint_site_network_access_creation.status_code,
+                    )
+                elif (
+                    response_endpoint_site_network_access_creation.status_code
+                    == 400
+                ):
+                    self.delete_connectivity_service(vpn_service["vpn-id"])
+
+                    raise SdnConnectorError(
+                        "Site {} does not exist".format(
+                            connection_point_wan_info["service_mapping_info"][
+                                "site-id"
+                            ]
+                        ),
+                        http_code=response_endpoint_site_network_access_creation.status_code,
+                    )
+                elif (
+                    response_endpoint_site_network_access_creation.status_code
+                    != requests.codes.created
+                    and response_endpoint_site_network_access_creation.status_code
+                    != requests.codes.no_content
+                ):
+                    self.delete_connectivity_service(vpn_service["vpn-id"])
+
+                    raise SdnConnectorError(
+                        "Request not accepted",
+                        http_code=response_endpoint_site_network_access_creation.status_code,
+                    )
+            except requests.exceptions.ConnectionError:
+                self.delete_connectivity_service(vpn_service["vpn-id"])
+
+                raise SdnConnectorError("Request Timeout", http_code=408)
+
+        return uuid_l2vpn, conn_info
+
+    def delete_connectivity_service(self, service_uuid, conn_info=None):
+        """Disconnect multi-site endpoints previously connected
+
+        This method should receive as the first argument the UUID generated by
+        the ``create_connectivity_service``
+        """
+        try:
+            self.logger.info("Sending delete")
+            servicepoint = "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services/vpn-service={}/".format(
+                self.wim["wim_url"], service_uuid
+            )
+            response = requests.delete(servicepoint, auth=self.auth)
+
+            if response.status_code != requests.codes.no_content:
+                raise SdnConnectorError(
+                    "Error in the request", http_code=response.status_code
+                )
+        except requests.exceptions.ConnectionError:
+            raise SdnConnectorError("Request Timeout", http_code=408)
+
+    def edit_connectivity_service(
+        self, service_uuid, conn_info=None, connection_points=None, **kwargs
+    ):
+        """Change an existing connectivity service, see
+        ``create_connectivity_service``"""
+        # sites = {"sites": {}}
+        # site_list = []
+        vpn_service = {}
+        vpn_service["svc-topo"] = "any-to-any"
+        counter = 0
+
+        for connection_point in connection_points:
+            site_network_access = {}
+            connection_point_wan_info = self.search_mapp(connection_point)
+            params_site = {}
+            params_site["site-id"] = connection_point_wan_info["service_mapping_info"][
+                "site-id"
+            ]
+            params_site["site-vpn-flavor"] = "site-vpn-flavor-single"
+            device_site = {}
+            device_site["device-id"] = connection_point_wan_info["device-id"]
+            params_site["devices"] = device_site
+            # network_access = {}
+            connection = {}
+
+            if connection_point["service_endpoint_encapsulation_type"] != "none":
+                if connection_point["service_endpoint_encapsulation_type"] == "dot1q":
+                    """The connection is a VLAN"""
+                    connection["encapsulation-type"] = "dot1q-vlan-tagged"
+                    tagged = {}
+                    tagged_interf = {}
+                    service_endpoint_encapsulation_info = connection_point[
+                        "service_endpoint_encapsulation_info"
+                    ]
+
+                    if service_endpoint_encapsulation_info["vlan"] is None:
+                        raise SdnConnectorError("VLAN must be provided")
+
+                    tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info[
+                        "vlan"
+                    ]
+                    tagged["dot1q-vlan-tagged"] = tagged_interf
+                    connection["tagged-interface"] = tagged
+                else:
+                    raise NotImplementedError("Encapsulation type not implemented")
+
+            site_network_access["connection"] = connection
+            vpn_attach = {}
+            vpn_attach["vpn-id"] = service_uuid
+            vpn_attach["site-role"] = vpn_service["svc-topo"] + "-role"
+            site_network_access["vpn-attachment"] = vpn_attach
+            uuid_sna = conn_info[counter]["site-network-access-id"]
+            site_network_access["network-access-id"] = uuid_sna
+            site_network_access["bearer"] = connection_point_wan_info[
+                "service_mapping_info"
+            ]["bearer"]
+            site_network_accesses = {}
+            site_network_access_list = []
+            site_network_access_list.append(site_network_access)
+            site_network_accesses[
+                "ietf-l2vpn-svc:site-network-access"
+            ] = site_network_access_list
+
+            try:
+                endpoint_site_network_access_edit = (
+                    "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/"
+                    "sites/site={}/site-network-accesses/".format(
+                        self.wim["wim_url"],
+                        connection_point_wan_info["service_mapping_info"]["site-id"],
+                    )
+                )
+                response_endpoint_site_network_access_creation = requests.put(
+                    endpoint_site_network_access_edit,
+                    headers=self.headers,
+                    json=site_network_accesses,
+                    auth=self.auth,
+                )
+
+                if response_endpoint_site_network_access_creation.status_code == 400:
+                    raise SdnConnectorError(
+                        "Service does not exist",
+                        http_code=response_endpoint_site_network_access_creation.status_code,
+                    )
+                elif (
+                    response_endpoint_site_network_access_creation.status_code != 201
+                    and response_endpoint_site_network_access_creation.status_code
+                    != 204
+                ):
+                    raise SdnConnectorError(
+                        "Request no accepted",
+                        http_code=response_endpoint_site_network_access_creation.status_code,
+                    )
+            except requests.exceptions.ConnectionError:
+                raise SdnConnectorError("Request Timeout", http_code=408)
+
+            counter += 1
+
+        return None
+
+    def clear_all_connectivity_services(self):
+        """Delete all WAN Links corresponding to a WIM"""
+        try:
+            self.logger.info("Sending clear all connectivity services")
+            servicepoint = (
+                "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(
+                    self.wim["wim_url"]
+                )
+            )
+            response = requests.delete(servicepoint, auth=self.auth)
+
+            if response.status_code != requests.codes.no_content:
+                raise SdnConnectorError(
+                    "Unable to clear all connectivity services",
+                    http_code=response.status_code,
+                )
+        except requests.exceptions.ConnectionError:
+            raise SdnConnectorError("Request Timeout", http_code=408)
+
+    def get_all_active_connectivity_services(self):
+        """Provide information about all active connections provisioned by a
+        WIM
+        """
+        try:
+            self.logger.info("Sending get all connectivity services")
+            servicepoint = (
+                "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format(
+                    self.wim["wim_url"]
+                )
+            )
+            response = requests.get(servicepoint, auth=self.auth)
+
+            if response.status_code != requests.codes.ok:
+                raise SdnConnectorError(
+                    "Unable to get all connectivity services",
+                    http_code=response.status_code,
+                )
+
+            return response
+        except requests.exceptions.ConnectionError:
+            raise SdnConnectorError("Request Timeout", http_code=408)
diff --git a/src/device/service/drivers/ietf_l2vpn/__init__.py b/src/device/service/drivers/ietf_l2vpn/__init__.py
index 38d04994fb0fa1951fb465bc127eb72659dc2eaf..2d3f6df3276f063cd9b414f47bba41b656682049 100644
--- a/src/device/service/drivers/ietf_l2vpn/__init__.py
+++ b/src/device/service/drivers/ietf_l2vpn/__init__.py
@@ -11,3 +11,17 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
+from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
+
+ALL_RESOURCE_KEYS = [
+    RESOURCE_ENDPOINTS,
+    RESOURCE_INTERFACES,
+    RESOURCE_NETWORK_INSTANCES,
+]
+
+RESOURCE_KEY_MAPPINGS = {
+    RESOURCE_ENDPOINTS        : 'component',
+    RESOURCE_INTERFACES       : 'interface',
+    RESOURCE_NETWORK_INSTANCES: 'network_instance',
+}
diff --git a/src/device/service/drivers/ietf_l2vpn/acknowledgements.txt b/src/device/service/drivers/ietf_l2vpn/acknowledgements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3a7ed47ad6626ad13f4176bd696ec7e7dbab20ee
--- /dev/null
+++ b/src/device/service/drivers/ietf_l2vpn/acknowledgements.txt
@@ -0,0 +1,3 @@
+IETF L2VPN Driver is based on source code taken from:
+https://osm.etsi.org/gitlab/osm/ro/-/blob/master/RO-plugin/osm_ro_plugin/sdnconn.py
+https://osm.etsi.org/gitlab/osm/ro/-/blob/master/RO-SDN-ietfl2vpn/osm_rosdn_ietfl2vpn/wimconn_ietfl2vpn.py
diff --git a/src/device/service/drivers/ietf_l2vpn/sdnconn.py b/src/device/service/drivers/ietf_l2vpn/sdnconn.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1849c9ef3e1a1260ff42bbadabc99f91a6435d7
--- /dev/null
+++ b/src/device/service/drivers/ietf_l2vpn/sdnconn.py
@@ -0,0 +1,242 @@
+# -*- coding: utf-8 -*-
+##
+# Copyright 2018 University of Bristol - High Performance Networks Research
+# Group
+# All Rights Reserved.
+#
+# Contributors: Anderson Bravalheri, Dimitrios Gkounis, Abubakar Siddique
+# Muqaddas, Navdeep Uniyal, Reza Nejabati and Dimitra Simeonidou
+#
+# 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.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: <highperformance-networks@bristol.ac.uk>
+#
+# Neither the name of the University of Bristol nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# This work has been performed in the context of DCMS UK 5G Testbeds
+# & Trials Programme and in the framework of the Metro-Haul project -
+# funded by the European Commission under Grant number 761727 through the
+# Horizon 2020 and 5G-PPP programmes.
+##
+"""The SDN connector is responsible for establishing both wide area network connectivity (WIM)
+and intranet SDN connectivity.
+
+It receives information from ports to be connected .
+"""
+
+import logging
+from http import HTTPStatus
+
+
+class SdnConnectorError(Exception):
+    """Base Exception for all connector related errors
+    provide the parameter 'http_code' (int) with the error code:
+        Bad_Request = 400
+        Unauthorized = 401  (e.g. credentials are not valid)
+        Not_Found = 404    (e.g. try to edit or delete a non existing connectivity service)
+        Forbidden = 403
+        Method_Not_Allowed = 405
+        Not_Acceptable = 406
+        Request_Timeout = 408  (e.g timeout reaching server, or cannot reach the server)
+        Conflict = 409
+        Service_Unavailable = 503
+        Internal_Server_Error = 500
+    """
+
+    def __init__(self, message, http_code=HTTPStatus.INTERNAL_SERVER_ERROR.value):
+        Exception.__init__(self, message)
+        self.http_code = http_code
+
+
+class SdnConnectorBase(object):
+    """Abstract base class for all the SDN connectors
+
+    Arguments:
+        wim (dict): WIM record, as stored in the database
+        wim_account (dict): WIM account record, as stored in the database
+        config
+    The arguments of the constructor are converted to object attributes.
+    An extra property, ``service_endpoint_mapping`` is created from ``config``.
+    """
+
+    def __init__(self, wim, wim_account, config=None, logger=None):
+        """
+        :param wim: (dict). Contains among others 'wim_url'
+        :param wim_account: (dict). Contains among others 'uuid' (internal id), 'name',
+            'sdn' (True if is intended for SDN-assist or False if intended for WIM), 'user', 'password'.
+        :param config: (dict or None): Particular information of plugin. These keys if present have a common meaning:
+            'mapping_not_needed': (bool) False by default or if missing, indicates that mapping is not needed.
+            'service_endpoint_mapping': (list) provides the internal endpoint mapping. The meaning is:
+                KEY                     meaning for WIM             meaning for SDN assist
+                --------                --------                    --------
+                device_id               pop_switch_dpid             compute_id
+                device_interface_id     pop_switch_port             compute_pci_address
+                service_endpoint_id     wan_service_endpoint_id     SDN_service_endpoint_id
+                service_mapping_info    wan_service_mapping_info    SDN_service_mapping_info
+                    contains extra information if needed. Text in Yaml format
+                switch_dpid             wan_switch_dpid             SDN_switch_dpid
+                switch_port             wan_switch_port             SDN_switch_port
+                datacenter_id           vim_account                 vim_account
+            id: (internal, do not use)
+            wim_id: (internal, do not use)
+        :param logger (logging.Logger): optional logger object. If none is passed 'openmano.sdn.sdnconn' is used.
+        """
+        self.logger = logger or logging.getLogger("ro.sdn")
+        self.wim = wim
+        self.wim_account = wim_account
+        self.config = config or {}
+        self.service_endpoint_mapping = self.config.get("service_endpoint_mapping", [])
+
+    def check_credentials(self):
+        """Check if the connector itself can access the SDN/WIM with the provided url (wim.wim_url),
+            user (wim_account.user), and password (wim_account.password)
+
+        Raises:
+            SdnConnectorError: Issues regarding authorization, access to
+                external URLs, etc are detected.
+        """
+        raise NotImplementedError
+
+    def get_connectivity_service_status(self, service_uuid, conn_info=None):
+        """Monitor the status of the connectivity service established
+
+        Arguments:
+            service_uuid (str): UUID of the connectivity service
+            conn_info (dict or None): Information returned by the connector
+                during the service creation/edition and subsequently stored in
+                the database.
+
+        Returns:
+            dict: JSON/YAML-serializable dict that contains a mandatory key
+                ``sdn_status`` associated with one of the following values::
+
+                    {'sdn_status': 'ACTIVE'}
+                        # The service is up and running.
+
+                    {'sdn_status': 'INACTIVE'}
+                        # The service was created, but the connector
+                        # cannot determine yet if connectivity exists
+                        # (ideally, the caller needs to wait and check again).
+
+                    {'sdn_status': 'DOWN'}
+                        # Connection was previously established,
+                        # but an error/failure was detected.
+
+                    {'sdn_status': 'ERROR'}
+                        # An error occurred when trying to create the service/
+                        # establish the connectivity.
+
+                    {'sdn_status': 'BUILD'}
+                        # Still trying to create the service, the caller
+                        # needs to wait and check again.
+
+                Additionally ``error_msg``(**str**) and ``sdn_info``(**dict**)
+                keys can be used to provide additional status explanation or
+                new information available for the connectivity service.
+        """
+        raise NotImplementedError
+
+    def create_connectivity_service(self, service_type, connection_points, **kwargs):
+        """
+        Establish SDN/WAN connectivity between the endpoints
+        :param service_type: (str): ``ELINE`` (L2), ``ELAN`` (L2), ``ETREE`` (L2), ``L3``.
+        :param connection_points:  (list): each point corresponds to
+            an entry point to be connected. For WIM: from the DC to the transport network.
+            For SDN: Compute/PCI to the transport network. One
+            connection point serves to identify the specific access and
+            some other service parameters, such as encapsulation type.
+            Each item of the list is a dict with:
+                "service_endpoint_id": (str)(uuid)  Same meaning that for 'service_endpoint_mapping' (see __init__)
+                    In case the config attribute mapping_not_needed is True, this value is not relevant. In this case
+                    it will contain the string "device_id:device_interface_id"
+                "service_endpoint_encapsulation_type": None, "dot1q", ...
+                "service_endpoint_encapsulation_info": (dict) with:
+                    "vlan": ..., (int, present if encapsulation is dot1q)
+                    "vni": ... (int, present if encapsulation is vxlan),
+                    "peers": [(ipv4_1), (ipv4_2)] (present if encapsulation is vxlan)
+                    "mac": ...
+                    "device_id": ..., same meaning that for 'service_endpoint_mapping' (see __init__)
+                    "device_interface_id": same meaning that for 'service_endpoint_mapping' (see __init__)
+                    "switch_dpid": ..., present if mapping has been found for this device_id,device_interface_id
+                    "swith_port": ... present if mapping has been found for this device_id,device_interface_id
+                    "service_mapping_info": present if mapping has been found for this device_id,device_interface_id
+        :param kwargs: For future versions:
+            bandwidth (int): value in kilobytes
+            latency (int): value in milliseconds
+            Other QoS might be passed as keyword arguments.
+        :return: tuple: ``(service_id, conn_info)`` containing:
+            - *service_uuid* (str): UUID of the established connectivity service
+            - *conn_info* (dict or None): Information to be stored at the database (or ``None``).
+                This information will be provided to the :meth:`~.edit_connectivity_service` and :obj:`~.delete`.
+                **MUST** be JSON/YAML-serializable (plain data structures).
+        :raises: SdnConnectorException: In case of error. Nothing should be created in this case.
+            Provide the parameter http_code
+        """
+        raise NotImplementedError
+
+    def delete_connectivity_service(self, service_uuid, conn_info=None):
+        """
+        Disconnect multi-site endpoints previously connected
+
+        :param service_uuid: The one returned by create_connectivity_service
+        :param conn_info: The one returned by last call to 'create_connectivity_service' or 'edit_connectivity_service'
+            if they do not return None
+        :return: None
+        :raises: SdnConnectorException: In case of error. The parameter http_code must be filled
+        """
+        raise NotImplementedError
+
+    def edit_connectivity_service(
+        self, service_uuid, conn_info=None, connection_points=None, **kwargs
+    ):
+        """Change an existing connectivity service.
+
+        This method's arguments and return value follow the same convention as
+        :meth:`~.create_connectivity_service`.
+
+        :param service_uuid: UUID of the connectivity service.
+        :param conn_info: (dict or None): Information previously returned by last call to create_connectivity_service
+            or edit_connectivity_service
+        :param connection_points: (list): If provided, the old list of connection points will be replaced.
+        :param kwargs: Same meaning that create_connectivity_service
+        :return: dict or None: Information to be updated and stored at the database.
+                When ``None`` is returned, no information should be changed.
+                When an empty dict is returned, the database record will be deleted.
+                **MUST** be JSON/YAML-serializable (plain data structures).
+        Raises:
+            SdnConnectorException: In case of error.
+        """
+
+    def clear_all_connectivity_services(self):
+        """Delete all WAN Links in a WIM.
+
+        This method is intended for debugging only, and should delete all the
+        connections controlled by the WIM/SDN, not only the  connections that
+        a specific RO is aware of.
+
+        Raises:
+            SdnConnectorException: In case of error.
+        """
+        raise NotImplementedError
+
+    def get_all_active_connectivity_services(self):
+        """Provide information about all active connections provisioned by a
+        WIM.
+
+        Raises:
+            SdnConnectorException: In case of error.
+        """
+        raise NotImplementedError