diff --git a/hackfest/mock_osm/MockOSM.py b/hackfest/mock_osm/MockOSM.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4e629d5845fd7115fa0fa2c0887eca1c7c816c4
--- /dev/null
+++ b/hackfest/mock_osm/MockOSM.py
@@ -0,0 +1,62 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# 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/hackfest/mock_osm/WimconnectorIETFL2VPN.py b/hackfest/mock_osm/WimconnectorIETFL2VPN.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1273b4e483a06df23d94bdf107005ce7585fb5e
--- /dev/null
+++ b/hackfest/mock_osm/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.message, 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/hackfest/mock_osm/__init__.py b/hackfest/mock_osm/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/hackfest/mock_osm/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
diff --git a/hackfest/mock_osm/__main__.py b/hackfest/mock_osm/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..410feda84d24e45949ac9db6303e16199ea54fd5
--- /dev/null
+++ b/hackfest/mock_osm/__main__.py
@@ -0,0 +1,94 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# 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 cmd, logging
+from .MockOSM import MockOSM
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+WIM_URL = 'http://10.0.2.10:80'
+WIM_USERNAME = 'admin'
+WIM_PASSWORD = 'admin'
+
+# Ref: https://osm.etsi.org/wikipub/index.php/WIM
+def create_port_mapping(device_id, port_id, service_endpoint_id, site_id):
+    bearer_ref = '{:s}:{:s}'.format(device_id, port_id)
+    return {
+        'device-id'           : device_id,              # pop_switch_dpid
+        'service_endpoint_id' : service_endpoint_id,    # wan_service_endpoint_id
+        'service_mapping_info': {                       # wan_service_mapping_info, other extra info
+            'bearer': {'bearer-reference': bearer_ref},
+            'site-id': site_id,
+        },
+    }
+
+WIM_PORT_MAPPING  = [
+    create_port_mapping('R1', '1/2', 'ep-R1-1/2', '1'),
+    create_port_mapping('R1', '1/3', 'ep-R1-1/3', '1'),
+    create_port_mapping('R2', '1/2', 'ep-R2-1/2', '2'),
+    create_port_mapping('R2', '1/3', 'ep-R2-1/3', '2'),
+    create_port_mapping('R3', '1/2', 'ep-R3-1/2', '3'),
+    create_port_mapping('R3', '1/3', 'ep-R3-1/3', '3'),
+    create_port_mapping('R4', '1/2', 'ep-R4-1/2', '4'),
+    create_port_mapping('R4', '1/3', 'ep-R4-1/3', '4'),
+]
+
+SERVICE_TYPE = 'ELINE'
+SERVICE_CONNECTION_POINTS = [
+    {'service_endpoint_id': 'ep-R1-1/2',
+        'service_endpoint_encapsulation_type': 'dot1q',
+        'service_endpoint_encapsulation_info': {'vlan': 1234}},
+    {'service_endpoint_id': 'ep-R4-1/3',
+        'service_endpoint_encapsulation_type': 'dot1q',
+        'service_endpoint_encapsulation_info': {'vlan': 1234}},
+]
+
+class MockOSMShell(cmd.Cmd):
+    intro = 'Welcome to the MockOSM shell.\nType help or ? to list commands.\n'
+    prompt = '(mock-osm) '
+    file = None
+
+    def __init__(self, *args, **kwargs) -> None:
+        super().__init__(*args, **kwargs)
+        self.mock_osm = MockOSM(WIM_URL, WIM_PORT_MAPPING, WIM_USERNAME, WIM_PASSWORD)
+
+    # ----- basic turtle commands -----
+    def do_create(self, arg):
+        'Create an ELINE (L2) service'
+        service_uuid = self.mock_osm.create_connectivity_service(
+            SERVICE_TYPE, SERVICE_CONNECTION_POINTS)
+        print('Service {:s} created'.format(service_uuid))
+
+    def do_status(self, arg):
+        'Retrieve status of services'
+        service_uuids = list(self.mock_osm.conn_info.keys())
+        for service_uuid in service_uuids:
+            status = self.mock_osm.get_connectivity_service_status(service_uuid)
+            print('Status of Service {:s} is {:s}'.format(service_uuid, str(status)))
+
+    def do_delete(self, arg):
+        'Delete all services'
+        service_uuids = list(self.mock_osm.conn_info.keys())
+        for service_uuid in service_uuids:
+            self.mock_osm.delete_connectivity_service(service_uuid)
+            print('Service {:s} deleted'.format(service_uuid))
+
+    def do_exit(self, arg):
+        'Exit MockOSM'
+        print('Bye!')
+        return True
+
+if __name__ == '__main__':
+    MockOSMShell().cmdloop()
diff --git a/hackfest/mock_osm/acknowledgements.txt b/hackfest/mock_osm/acknowledgements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b7ce926dd006d9bc8afaffbed212d90fb05adbef
--- /dev/null
+++ b/hackfest/mock_osm/acknowledgements.txt
@@ -0,0 +1,3 @@
+MockOSM 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/hackfest/mock_osm/commands.txt b/hackfest/mock_osm/commands.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4e21bac3d668f6f6b1c20ceb485d3a4ad679f95e
--- /dev/null
+++ b/hackfest/mock_osm/commands.txt
@@ -0,0 +1,25 @@
+############
+# Mock OSM
+############
+
+$ cd ~/tfs-ctrl/hackfest/
+$ python -m mock_osm
+Welcome to the MockOSM shell.
+Type help or ? to list commands.
+
+(mock-osm) create
+Service b8c99e2c-39d8-424d-9833-554634269555 created
+# Service should be created in TFS. Check "Slice" and "Service" pages on WebUI.
+# Check configuration rules in "Devices"
+
+(mock-osm) status
+response.status_code=200
+Status of Service b8c99e2c-39d8-424d-9833-554634269555 is {'sdn_status': 'ACTIVE'}
+
+(mock-osm) delete
+Service b8c99e2c-39d8-424d-9833-554634269555 deleted
+# Service should be removed from TFS. Check "Slice" and "Service" pages on WebUI.
+# Check configuration rules in "Devices"
+
+(mock-osm) exit
+Bye!
diff --git a/hackfest/mock_osm/sdnconn.py b/hackfest/mock_osm/sdnconn.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1849c9ef3e1a1260ff42bbadabc99f91a6435d7
--- /dev/null
+++ b/hackfest/mock_osm/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