diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1e35c2cb390fdc44a4d3f1da0ffdfc32e5d96c07..c8f78f4dfdebb51bfa985485f404b124a4c4ae60 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -13,6 +13,7 @@ include:
   - local: '/manifests/.gitlab-ci.yml'
   - local: '/src/monitoring/.gitlab-ci.yml'
   #- local: '/src/centralizedattackdetector/.gitlab-ci.yml'
+  - local: '/src/compute/.gitlab-ci.yml'
   - local: '/src/context/.gitlab-ci.yml'
   - local: '/src/device/.gitlab-ci.yml'
   - local: '/src/service/.gitlab-ci.yml'
diff --git a/manifests/computeservice.yaml b/manifests/computeservice.yaml
index cdca52eb890d832cfb56457b89d44a045ee4af57..73380f75daeb7d60891c3d093ca7651ba4280e58 100644
--- a/manifests/computeservice.yaml
+++ b/manifests/computeservice.yaml
@@ -17,6 +17,7 @@ spec:
         image: registry.gitlab.com/teraflow-h2020/controller/compute:latest
         imagePullPolicy: Always
         ports:
+        - containerPort: 8080
         - containerPort: 9090
         env:
         - name: LOG_LEVEL
@@ -44,7 +45,12 @@ spec:
   selector:
     app: computeservice
   ports:
+  - name: http
+    protocol: TCP
+    port: 8080
+    targetPort: 8080
   - name: grpc
+    protocol: TCP
     port: 9090
     targetPort: 9090
 ---
@@ -59,6 +65,10 @@ spec:
   selector:
     app: computeservice
   ports:
+  - name: http
+    protocol: TCP
+    port: 8080
+    targetPort: 8080
   - name: grpc
     protocol: TCP
     port: 9090
diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml
index c0ea046e0ae1d9e4bb6b6a70de5a8b26844981fc..7ccf3e4f0dc9abb41f228d4fb7c4ec18fad93954 100644
--- a/manifests/contextservice.yaml
+++ b/manifests/contextservice.yaml
@@ -34,6 +34,8 @@ spec:
         env:
         - name: DB_BACKEND
           value: "redis"
+        - name: MB_BACKEND
+          value: "redis"
         - name: REDIS_DATABASE_ID
           value: "0"
         - name: LOG_LEVEL
@@ -64,9 +66,11 @@ spec:
     app: contextservice
   ports:
   - name: grpc
+    protocol: TCP
     port: 1010
     targetPort: 1010
   - name: http
+    protocol: TCP
     port: 8080
     targetPort: 8080
 ---
diff --git a/src/common/type_checkers/Checkers.py b/src/common/type_checkers/Checkers.py
index d0eddcf213143c4c3d99c9edfafd1305384e777b..f78395c9c5f480bf75b4c9344dfeb3b48f9da062 100644
--- a/src/common/type_checkers/Checkers.py
+++ b/src/common/type_checkers/Checkers.py
@@ -1,5 +1,5 @@
 import re
-from typing import Any, Container, List, Optional, Pattern, Set, Sized, Tuple, Union
+from typing import Any, Container, Dict, List, Optional, Pattern, Set, Sized, Tuple, Union
 
 def chk_none(name : str, value : Any, reason=None) -> Any:
     if value is None: return value
@@ -11,6 +11,11 @@ def chk_not_none(name : str, value : Any, reason=None) -> Any:
     if reason is None: reason = 'must not be None.'
     raise ValueError('{}({}) {}'.format(str(name), str(value), str(reason)))
 
+def chk_attribute(name : str, container : Dict, container_name : str, **kwargs):
+    if name in container: return container[name]
+    if 'default' in kwargs: return kwargs['default']
+    raise AttributeError('Missing object({:s}) in container({:s})'.format(str(name), str(container_name)))
+
 def chk_type(name : str, value : Any, type_or_types : Union[type, Set[type]] = set()) -> Any:
     if isinstance(value, type_or_types): return value
     msg = '{}({}) is of a wrong type({}). Accepted type_or_types({}).'
diff --git a/src/compute/.gitlab-ci.yml b/src/compute/.gitlab-ci.yml
index 948092ea3b896ce5e487bd7b9e42a6aff6743626..d10d5acfbcafa90a3fa6c0bd2cad9164e67dc020 100644
--- a/src/compute/.gitlab-ci.yml
+++ b/src/compute/.gitlab-ci.yml
@@ -10,8 +10,8 @@ build compute:
     - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile ./src/
     - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
     - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-#  after_script:
-#    - docker rmi $(docker images --quiet --filter=dangling=true)
+  after_script:
+    - docker images --filter="dangling=true" --quiet | xargs -r docker rmi
   rules:
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
     - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' 
@@ -36,9 +36,10 @@ unit test compute:
     - if docker container ls | grep $IMAGE_NAME; then docker rm -f $IMAGE_NAME; else echo "$IMAGE_NAME image is not in the system"; fi
   script:
     - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-    - docker run --name $IMAGE_NAME -d -p 9090:9090 --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 9090:9090 --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 5
     - docker ps -a
+    - docker logs $IMAGE_NAME
     - docker exec -i $IMAGE_NAME bash -c "pytest --log-level=DEBUG --verbose $IMAGE_NAME/tests/test_unitary.py"
   after_script:
     - docker rm -f $IMAGE_NAME
diff --git a/src/compute/Config.py b/src/compute/Config.py
index e95740493fd16940cee2d1e780472a1e90801303..b2d3179fab6e55368ba751aac48de507551c4516 100644
--- a/src/compute/Config.py
+++ b/src/compute/Config.py
@@ -1,4 +1,5 @@
 import logging
+from werkzeug.security import generate_password_hash
 
 # General settings
 LOG_LEVEL = logging.WARNING
@@ -10,7 +11,10 @@ GRPC_GRACE_PERIOD = 60
 
 # REST-API settings
 RESTAPI_SERVICE_PORT = 8080
-RESTAPI_BASE_URL = '/api'
+RESTAPI_BASE_URL = '/restconf/data'
+RESTAPI_USERS = {   # TODO: implement a database of credentials and permissions
+    'admin': generate_password_hash('admin'),
+}
 
 # Prometheus settings
 METRICS_PORT = 9192
diff --git a/src/compute/Dockerfile b/src/compute/Dockerfile
index 83e4fad35704dd8febb8aa627936d54c02e89f68..99d4e3ed1adc8a688874f3291f4891112543b3ff 100644
--- a/src/compute/Dockerfile
+++ b/src/compute/Dockerfile
@@ -30,6 +30,8 @@ RUN python3 -m pip install -r compute/requirements.in
 # Add files into working directory
 COPY common/. common
 COPY compute/. compute
+COPY context/. context
+COPY service/. service
 
 # Start compute service
 ENTRYPOINT ["python", "-m", "compute.service"]
diff --git a/src/compute/requirements.in b/src/compute/requirements.in
index 1da334a54b1c42b01eb8f731d8fd5bd975edd2cf..42a56f905a011aec0df5c21c9e3b8196d7896395 100644
--- a/src/compute/requirements.in
+++ b/src/compute/requirements.in
@@ -1,5 +1,10 @@
+Flask
+Flask-HTTPAuth
+Flask-RESTful
 grpcio-health-checking
 grpcio
+jsonschema
 prometheus-client
 pytest
 pytest-benchmark
+requests
diff --git a/src/compute/service/ComputeService.py b/src/compute/service/ComputeService.py
index 36d43283c43c82faff1748428943cb9a9687c840..51a15472bc83416ebbfc3c421e5cfed2a9682bdc 100644
--- a/src/compute/service/ComputeService.py
+++ b/src/compute/service/ComputeService.py
@@ -24,9 +24,9 @@ class ComputeService:
         self.server = None
 
     def start(self):
-        self.endpoint = '{}:{}'.format(self.address, self.port)
-        LOGGER.debug('Starting Service (tentative endpoint: {}, max_workers: {})...'.format(
-            self.endpoint, self.max_workers))
+        self.endpoint = '{:s}:{:s}'.format(str(self.address), str(self.port))
+        LOGGER.debug('Starting Service (tentative endpoint: {:s}, max_workers: {:s})...'.format(
+            str(self.endpoint), str(self.max_workers)))
 
         self.pool = futures.ThreadPoolExecutor(max_workers=self.max_workers)
         self.server = grpc.server(self.pool) # , interceptors=(tracer_interceptor,))
@@ -39,15 +39,15 @@ class ComputeService:
         add_HealthServicer_to_server(self.health_servicer, self.server)
 
         port = self.server.add_insecure_port(self.endpoint)
-        self.endpoint = '{}:{}'.format(self.address, port)
-        LOGGER.info('Listening on {}...'.format(self.endpoint))
+        self.endpoint = '{:s}:{:s}'.format(str(self.address), str(port))
+        LOGGER.info('Listening on {:s}...'.format(str(self.endpoint)))
         self.server.start()
         self.health_servicer.set(OVERALL_HEALTH, HealthCheckResponse.SERVING) # pylint: disable=maybe-no-member
 
         LOGGER.debug('Service started')
 
     def stop(self):
-        LOGGER.debug('Stopping service (grace period {} seconds)...'.format(self.grace_period))
+        LOGGER.debug('Stopping service (grace period {:s} seconds)...'.format(str(self.grace_period)))
         self.health_servicer.enter_graceful_shutdown()
         self.server.stop(self.grace_period)
         LOGGER.debug('Service stopped')
diff --git a/src/compute/service/__main__.py b/src/compute/service/__main__.py
index f45af374c471222bb4fdb089860418c5895d6321..eacc1f6c464112192194fca5827033aedc57385c 100644
--- a/src/compute/service/__main__.py
+++ b/src/compute/service/__main__.py
@@ -4,9 +4,9 @@ from common.Settings import get_setting
 from compute.Config import (
     GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, RESTAPI_SERVICE_PORT, RESTAPI_BASE_URL,
     METRICS_PORT)
-from compute.service.ComputeService import ComputeService
-from compute.service.rest_server.Server import Server
-from compute.service.rest_server.resources.Compute import Compute
+from .ComputeService import ComputeService
+from .rest_server.RestServer import RestServer
+from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn
 
 terminate = threading.Event()
 LOGGER = None
@@ -41,9 +41,8 @@ def main():
     grpc_service = ComputeService(port=grpc_service_port, max_workers=max_workers, grace_period=grace_period)
     grpc_service.start()
 
-    rest_server = Server(port=restapi_service_port, base_url=restapi_base_url)
-    rest_server.add_resource(
-        Compute, '/restconf/config/compute', endpoint='api.compute')
+    rest_server = RestServer(port=restapi_service_port, base_url=restapi_base_url)
+    register_ietf_l2vpn(rest_server)
     rest_server.start()
 
     # Wait for Ctrl+C or termination signal
diff --git a/src/compute/service/rest_server/Server.py b/src/compute/service/rest_server/RestServer.py
similarity index 66%
rename from src/compute/service/rest_server/Server.py
rename to src/compute/service/rest_server/RestServer.py
index c68515e915a6de82b3b08525d3383ac21b6c25b2..8ed8dbbbf69bc89c9c76fdf31e16b0687d47856e 100644
--- a/src/compute/service/rest_server/Server.py
+++ b/src/compute/service/rest_server/RestServer.py
@@ -1,6 +1,6 @@
-import logging, threading
-from flask import Flask
-from flask_restful import Api
+import logging, threading, time
+from flask import Flask, request
+from flask_restful import Api, Resource
 from werkzeug.serving import make_server
 from compute.Config import RESTAPI_BASE_URL, RESTAPI_SERVICE_PORT
 
@@ -9,16 +9,24 @@ logging.getLogger('werkzeug').setLevel(logging.WARNING)
 BIND_ADDRESS = '0.0.0.0'
 LOGGER = logging.getLogger(__name__)
 
-class Server(threading.Thread):
+def log_request(response):
+    timestamp = time.strftime('[%Y-%b-%d %H:%M]')
+    LOGGER.info('%s %s %s %s %s', timestamp, request.remote_addr, request.method, request.full_path, response.status)
+    return response
+
+class RestServer(threading.Thread):
     def __init__(self, host=BIND_ADDRESS, port=RESTAPI_SERVICE_PORT, base_url=RESTAPI_BASE_URL):
         threading.Thread.__init__(self, daemon=True)
         self.host = host
         self.port = port
         self.base_url = base_url
+        self.srv = None
+        self.ctx = None
         self.app = Flask(__name__)
+        self.app.after_request(log_request)
         self.api = Api(self.app, prefix=self.base_url)
 
-    def add_resource(self, resource, *urls, **kwargs):
+    def add_resource(self, resource : Resource, *urls, **kwargs):
         self.api.add_resource(resource, *urls, **kwargs)
 
     def run(self):
diff --git a/src/compute/service/rest_server/resources/__init__.py b/src/compute/service/rest_server/nbi_plugins/__init__.py
similarity index 100%
rename from src/compute/service/rest_server/resources/__init__.py
rename to src/compute/service/rest_server/nbi_plugins/__init__.py
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..87c32c444d39acb048ede9105c9a0dc2c7e3899e
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py
@@ -0,0 +1,3 @@
+DEFAULT_MTU = 1512
+DEFAULT_ADDRESS_FAMILIES = ['IPV4']
+DEFAULT_SUB_INTERFACE_INDEX = 0
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py
new file mode 100644
index 0000000000000000000000000000000000000000..752a027ad0d41f67f6a2312ee166a51ebcbc23bd
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py
@@ -0,0 +1,69 @@
+import logging
+from typing import Dict, List
+from flask import request
+from flask.json import jsonify
+from flask_restful import Resource
+from werkzeug.exceptions import UnsupportedMediaType
+from common.Constants import DEFAULT_CONTEXT_UUID
+from common.Settings import get_setting
+from context.client.ContextClient import ContextClient
+from context.proto.context_pb2 import ServiceId
+from service.client.ServiceClient import ServiceClient
+from service.proto.context_pb2 import Service, ServiceStatusEnum, ServiceTypeEnum
+from .tools.Authentication import HTTP_AUTH
+from .tools.HttpStatusCodes import HTTP_CREATED, HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR
+
+LOGGER = logging.getLogger(__name__)
+
+class L2VPN_Service(Resource):
+    def __init__(self) -> None:
+        super().__init__()
+        self.context_client = ContextClient(
+            get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC'))
+        self.service_client = ServiceClient(
+            get_setting('SERVICESERVICE_SERVICE_HOST'), get_setting('SERVICESERVICE_SERVICE_PORT_GRPC'))
+
+    @HTTP_AUTH.login_required
+    def get(self, vpn_id : str):
+        LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id)))
+        LOGGER.debug('Request: {:s}'.format(str(request)))
+
+        # pylint: disable=no-member
+        service_id_request = ServiceId()
+        service_id_request.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID
+        service_id_request.service_uuid.uuid = vpn_id
+
+        try:
+            service_reply = self.context_client.GetService(service_id_request)
+            if service_reply.service_id != service_id_request: # pylint: disable=no-member
+                raise Exception('Service retrieval failed. Wrong Service Id was returned')
+
+            service_ready_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE
+            service_status = service_reply.service_status.service_status
+            response = jsonify({})
+            response.status_code = HTTP_OK if service_status == service_ready_status else HTTP_GATEWAYTIMEOUT
+        except Exception as e: # pylint: disable=broad-except
+            LOGGER.exception('Something went wrong Retrieving Service {:s}'.format(str(request)))
+            response = jsonify({'error': str(e)})
+            response.status_code = HTTP_SERVERERROR
+        return response
+
+    @HTTP_AUTH.login_required
+    def delete(self, vpn_id : str):
+        LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id)))
+        LOGGER.debug('Request: {:s}'.format(str(request)))
+
+        # pylint: disable=no-member
+        service_id_request = ServiceId()
+        service_id_request.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID
+        service_id_request.service_uuid.uuid = vpn_id
+
+        try:
+            self.service_client.DeleteService(service_id_request)
+            response = jsonify({})
+            response.status_code = HTTP_NOCONTENT
+        except Exception as e: # pylint: disable=broad-except
+            LOGGER.exception('Something went wrong Deleting Service {:s}'.format(str(request)))
+            response = jsonify({'error': str(e)})
+            response.status_code = HTTP_SERVERERROR
+        return response
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ed0293f0729c6d4617a445034702f706a6daa25
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py
@@ -0,0 +1,55 @@
+import logging
+from typing import Dict, List
+from flask import request
+from flask.json import jsonify
+from flask_restful import Resource
+from werkzeug.exceptions import UnsupportedMediaType
+from common.Constants import DEFAULT_CONTEXT_UUID
+from common.Settings import get_setting
+from service.client.ServiceClient import ServiceClient
+from service.proto.context_pb2 import Service, ServiceStatusEnum, ServiceTypeEnum
+from .schemas.vpn_service import SCHEMA_VPN_SERVICE
+from .tools.Authentication import HTTP_AUTH
+from .tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR
+from .tools.Validator import validate_message
+
+LOGGER = logging.getLogger(__name__)
+
+class L2VPN_Services(Resource):
+    def __init__(self) -> None:
+        super().__init__()
+        self.service_client = ServiceClient(
+            get_setting('SERVICESERVICE_SERVICE_HOST'), get_setting('SERVICESERVICE_SERVICE_PORT_GRPC'))
+
+    @HTTP_AUTH.login_required
+    def get(self):
+        return {}
+
+    @HTTP_AUTH.login_required
+    def post(self):
+        if not request.is_json: raise UnsupportedMediaType('JSON payload is required')
+        request_data : Dict = request.json
+        LOGGER.debug('Request: {:s}'.format(str(request_data)))
+        validate_message(SCHEMA_VPN_SERVICE, request_data)
+
+        vpn_services : List[Dict] = request_data['ietf-l2vpn-svc:vpn-service']
+        for vpn_service in vpn_services:
+            # pylint: disable=no-member
+            service_request = Service()
+            service_request.service_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID
+            service_request.service_id.service_uuid.uuid = vpn_service['vpn-id']
+            service_request.service_type = ServiceTypeEnum.SERVICETYPE_L3NM
+            service_request.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED
+
+            try:
+                service_reply = self.service_client.CreateService(service_request)
+                if service_reply != service_request.service_id: # pylint: disable=no-member
+                    raise Exception('Service creation failed. Wrong Service Id was returned')
+
+                response = jsonify({})
+                response.status_code = HTTP_CREATED
+            except Exception as e: # pylint: disable=broad-except
+                LOGGER.exception('Something went wrong Creating Service {:s}'.format(str(request)))
+                response = jsonify({'error': str(e)})
+                response.status_code = HTTP_SERVERERROR
+        return response
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py
new file mode 100644
index 0000000000000000000000000000000000000000..639e8c63f5a2e64b166ab976632fd83437673484
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py
@@ -0,0 +1,161 @@
+import json, logging
+from typing import Dict
+from flask import request
+from flask.json import jsonify
+from flask.wrappers import Response
+from flask_restful import Resource
+from werkzeug.exceptions import UnsupportedMediaType
+from common.Constants import DEFAULT_CONTEXT_UUID
+from common.Settings import get_setting
+from context.client.ContextClient import ContextClient
+from context.proto.context_pb2 import Service, ServiceId, ServiceStatusEnum
+from service.client.ServiceClient import ServiceClient
+from .schemas.site_network_access import SCHEMA_SITE_NETWORK_ACCESS
+from .tools.Authentication import HTTP_AUTH
+from .tools.HttpStatusCodes import HTTP_NOCONTENT, HTTP_SERVERERROR
+from .tools.Validator import validate_message
+from .Constants import DEFAULT_ADDRESS_FAMILIES, DEFAULT_MTU, DEFAULT_SUB_INTERFACE_INDEX
+
+LOGGER = logging.getLogger(__name__)
+
+def process_site_network_access(context_client : ContextClient, site_network_access : Dict) -> Service:
+    vpn_id = site_network_access['vpn-attachment']['vpn-id']
+    cvlan_id = site_network_access['connection']['tagged-interface']['dot1q-vlan-tagged']['cvlan-id']
+    bearer_reference = site_network_access['bearer']['bearer-reference']
+
+    # Assume bearer_reference    = '<device_uuid>:<endpoint_uuid>:<router_id>'
+    # Assume route_distinguisher = 0:<cvlan_id>
+    device_uuid,endpoint_uuid,router_id = bearer_reference.split(':')
+    route_distinguisher = '0:{:d}'.format(cvlan_id)
+
+    # pylint: disable=no-member
+    service_id = ServiceId()
+    service_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID
+    service_id.service_uuid.uuid = vpn_id
+
+    service_readonly = context_client.GetService(service_id)
+    service = Service()
+    service.CopyFrom(service_readonly)
+
+    for endpoint_id in service.service_endpoint_ids:                        # pylint: disable=no-member
+        if endpoint_id.device_id.device_uuid.uuid != device_uuid: continue
+        if endpoint_id.endpoint_uuid.uuid != endpoint_uuid: continue
+        break   # found, do nothing
+    else:
+        # not found, add it
+        endpoint_id = service.service_endpoint_ids.add()                    # pylint: disable=no-member
+        endpoint_id.device_id.device_uuid.uuid = device_uuid
+        endpoint_id.endpoint_uuid.uuid = endpoint_uuid
+
+    for config_rule in service.service_config.config_rules:                 # pylint: disable=no-member
+        if config_rule.resource_key != 'settings': continue
+        json_settings = json.loads(config_rule.resource_value)
+
+        if 'route_distinguisher' not in json_settings:                      # missing, add it
+            json_settings['route_distinguisher'] = route_distinguisher
+        elif json_settings['route_distinguisher'] != route_distinguisher:   # differs, raise exception
+            msg = 'Specified RouteDistinguisher({:s}) differs from Service RouteDistinguisher({:s})'
+            raise Exception(msg.format(str(json_settings['route_distinguisher']), str(route_distinguisher)))
+
+        if 'mtu' not in json_settings:                                      # missing, add it
+            json_settings['mtu'] = DEFAULT_MTU
+        elif json_settings['mtu'] != DEFAULT_MTU:                           # differs, raise exception
+            msg = 'Specified MTU({:s}) differs from Service MTU({:s})'
+            raise Exception(msg.format(str(json_settings['mtu']), str(DEFAULT_MTU)))
+
+        if 'address_families' not in json_settings:                         # missing, add it
+            json_settings['address_families'] = DEFAULT_ADDRESS_FAMILIES
+        elif json_settings['address_families'] != DEFAULT_ADDRESS_FAMILIES: # differs, raise exception
+            msg = 'Specified AddressFamilies({:s}) differs from Service AddressFamilies({:s})'
+            raise Exception(msg.format(str(json_settings['address_families']), str(DEFAULT_ADDRESS_FAMILIES)))
+
+        config_rule.resource_value = json.dumps(json_settings, sort_keys=True)
+        break
+    else:
+        # not found, add it
+        config_rule = service.service_config.config_rules.add()             # pylint: disable=no-member
+        config_rule.resource_key = 'settings'
+        config_rule.resource_value = json.dumps({
+            'route_distinguisher': route_distinguisher,
+            'mtu': DEFAULT_MTU,
+            'address_families': DEFAULT_ADDRESS_FAMILIES,
+        }, sort_keys=True)
+
+    endpoint_settings_key = 'device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid)
+    for config_rule in service.service_config.config_rules:                 # pylint: disable=no-member
+        if config_rule.resource_key != endpoint_settings_key: continue
+        json_settings = json.loads(config_rule.resource_value)
+
+        if 'router_id' not in json_settings:                                # missing, add it
+            json_settings['router_id'] = router_id
+        elif json_settings['router_id'] != router_id:                       # differs, raise exception
+            msg = 'Specified RouterId({:s}) differs from Service RouterId({:s})'
+            raise Exception(msg.format(str(json_settings['router_id']), str(router_id)))
+
+        if 'sub_interface_index' not in json_settings:                      # missing, add it
+            json_settings['sub_interface_index'] = DEFAULT_SUB_INTERFACE_INDEX
+        elif json_settings['sub_interface_index'] != DEFAULT_SUB_INTERFACE_INDEX:   # differs, raise exception
+            msg = 'Specified SubInterfaceIndex({:s}) differs from Service SubInterfaceIndex({:s})'
+            raise Exception(msg.format(
+                str(json_settings['sub_interface_index']), str(DEFAULT_SUB_INTERFACE_INDEX)))
+
+        config_rule.resource_value = json.dumps(json_settings, sort_keys=True)
+        break
+    else:
+        # not found, add it
+        config_rule = service.service_config.config_rules.add()             # pylint: disable=no-member
+        config_rule.resource_key = endpoint_settings_key
+        config_rule.resource_value = json.dumps({
+            'router_id': router_id,
+            'sub_interface_index': DEFAULT_SUB_INTERFACE_INDEX,
+        }, sort_keys=True)
+
+    if len(service.service_endpoint_ids) >= 2:
+        service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE
+
+    return service
+
+def process_list_site_network_access(
+    context_client : ContextClient, service_client : ServiceClient, request_data : Dict) -> Response:
+
+    LOGGER.debug('Request: {:s}'.format(str(request_data)))
+    validate_message(SCHEMA_SITE_NETWORK_ACCESS, request_data)
+
+    errors = []
+    for site_network_access in request_data['ietf-l2vpn-svc:site-network-access']:
+        try:
+            service_request = process_site_network_access(context_client, site_network_access)
+            service_reply = service_client.CreateService(service_request)
+            if service_reply != service_request.service_id: # pylint: disable=no-member
+                raise Exception('Service update failed. Wrong Service Id was returned')
+        except Exception as e: # pylint: disable=broad-except
+            LOGGER.exception('Something went wrong Updating Service {:s}'.format(str(request)))
+            errors.append({'error': str(e)})
+
+    response = jsonify(errors)
+    response.status_code = HTTP_NOCONTENT if len(errors) == 0 else HTTP_SERVERERROR
+    return response
+
+class L2VPN_SiteNetworkAccesses(Resource):
+    def __init__(self) -> None:
+        super().__init__()
+        self.context_client = ContextClient(
+            get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC'))
+        self.service_client = ServiceClient(
+            get_setting('SERVICESERVICE_SERVICE_HOST'), get_setting('SERVICESERVICE_SERVICE_PORT_GRPC'))
+
+    #@HTTP_AUTH.login_required
+    #def get(self):
+    #    return {}
+
+    @HTTP_AUTH.login_required
+    def post(self, site_id : str):
+        if not request.is_json: raise UnsupportedMediaType('JSON payload is required')
+        LOGGER.debug('Site_Id: {:s}'.format(str(site_id)))
+        return process_list_site_network_access(self.context_client, self.service_client, request.json)
+
+    @HTTP_AUTH.login_required
+    def put(self, site_id : str):
+        if not request.is_json: raise UnsupportedMediaType('JSON payload is required')
+        LOGGER.debug('Site_Id: {:s}'.format(str(site_id)))
+        return process_list_site_network_access(self.context_client, self.service_client, request.json)
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/__init__.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..979c8a3bc1903381516bf0f9683bbe4e4f2c3cb3
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/__init__.py
@@ -0,0 +1,22 @@
+# RFC 8466 - L2VPN Service Model (L2SM)
+# Ref: https://datatracker.ietf.org/doc/html/rfc8466
+
+from flask_restful import Resource
+from compute.service.rest_server.RestServer import RestServer
+from .L2VPN_Services import L2VPN_Services
+from .L2VPN_Service import L2VPN_Service
+from .L2VPN_SiteNetworkAccesses import L2VPN_SiteNetworkAccesses
+
+URL_PREFIX      = '/ietf-l2vpn-svc:l2vpn-svc'
+
+def _add_resource(rest_server : RestServer, resource : Resource, *urls, **kwargs):
+    urls = [(URL_PREFIX + url) for url in urls]
+    rest_server.add_resource(resource, *urls, **kwargs)
+
+def register_ietf_l2vpn(rest_server : RestServer):
+    _add_resource(rest_server, L2VPN_Services,
+        '/vpn-services')
+    _add_resource(rest_server, L2VPN_Service,
+        '/vpn-services/vpn-service=<vpn_id>', '/vpn-services/vpn-service=<vpn_id>/')
+    _add_resource(rest_server, L2VPN_SiteNetworkAccesses,
+        '/sites/site=<site_id>/site-network-accesses', '/sites/site=<site_id>/site-network-accesses/')
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/Common.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/Common.py
new file mode 100644
index 0000000000000000000000000000000000000000..f54da792b526cede52b94892ee9946fb63c6b015
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/Common.py
@@ -0,0 +1,2 @@
+# String pattern for UUIDs such as '3fd942ee-2dc3-41d1-aeec-65aa85d117b2'
+REGEX_UUID = r'[a-fA-F0-9]{8}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{4}\-[a-fA-F0-9]{12}'
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/__init__.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/site_network_access.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/site_network_access.py
new file mode 100644
index 0000000000000000000000000000000000000000..33ba8cc7fe5be76f82fbd74cd3608703f37e76a0
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/site_network_access.py
@@ -0,0 +1,66 @@
+# Example request:
+# request = {'ietf-l2vpn-svc:site-network-access': [{
+#     'network-access-id': '3fd942ee-2dc3-41d1-aeec-65aa85d117b2',
+#     'vpn-attachment': {'vpn-id': '954b1b53-4a8c-406d-9eff-750ec2c9a258',
+#         'site-role': 'any-to-any-role'},
+#     'connection': {'encapsulation-type': 'dot1q-vlan-tagged', 'tagged-interface': {
+#         'dot1q-vlan-tagged': {'cvlan-id': 1234}}},
+#     'bearer': {'bearer-reference': '1a'}
+# }]}
+
+from .Common import REGEX_UUID
+
+SCHEMA_SITE_NETWORK_ACCESS = {
+    '$schema': 'https://json-schema.org/draft/2020-12/schema',
+    'type': 'object',
+    'required': ['ietf-l2vpn-svc:site-network-access'],
+    'properties': {
+        'ietf-l2vpn-svc:site-network-access': {
+            'type': 'array',
+            'minItems': 1,
+            'maxItems': 1,  # by now we do not support multiple site-network-access in the same message
+            'items': {
+                'type': 'object',
+                'required': ['network-access-id', 'vpn-attachment', 'connection', 'bearer'],
+                'properties': {
+                    'network-access-id': {'type': 'string', 'pattern': REGEX_UUID},
+                    'vpn-attachment': {
+                        'type': 'object',
+                        'required': ['vpn-id', 'site-role'],
+                        'properties': {
+                            'vpn-id': {'type': 'string', 'pattern': REGEX_UUID},
+                            'site-role': {'type': 'string', 'minLength': 1},
+                        },
+                    },
+                    'connection': {
+                        'type': 'object',
+                        'required': ['encapsulation-type', 'tagged-interface'],
+                        'properties': {
+                            'encapsulation-type': {'enum': ['dot1q-vlan-tagged']},
+                            'tagged-interface': {
+                                'type': 'object',
+                                'required': ['dot1q-vlan-tagged'],
+                                'properties': {
+                                    'dot1q-vlan-tagged': {
+                                        'type': 'object',
+                                        'required': ['cvlan-id'],
+                                        'properties': {
+                                            'cvlan-id': {'type': 'integer', 'minimum': 1, 'maximum': 4094},
+                                        },
+                                    },
+                                },
+                            },
+                        },
+                    },
+                    'bearer': {
+                        'type': 'object',
+                        'required': ['bearer-reference'],
+                        'properties': {
+                            'bearer-reference': {'type': 'string', 'minLength': 1},
+                        },
+                    },
+                },
+            },
+        },
+    },
+}
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/vpn_service.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/vpn_service.py
new file mode 100644
index 0000000000000000000000000000000000000000..54e9c53163b8d764a37b613501f6b427d6e1773d
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/vpn_service.py
@@ -0,0 +1,32 @@
+# Example request:
+# request = {'ietf-l2vpn-svc:vpn-service': [{
+#   'vpn-id': 'c6270231-f1de-4687-b2ed-7b58f9105775',
+#   'vpn-svc-type': 'vpws',
+#   'svc-topo': 'any-to-any',
+#   'customer-name': 'osm'
+# }]}
+
+from .Common import REGEX_UUID
+
+SCHEMA_VPN_SERVICE = {
+    '$schema': 'https://json-schema.org/draft/2020-12/schema',
+    'type': 'object',
+    'required': ['ietf-l2vpn-svc:vpn-service'],
+    'properties': {
+        'ietf-l2vpn-svc:vpn-service': {
+            'type': 'array',
+            'minItems': 1,
+            'maxItems': 1,  # by now we do not support multiple vpn-service in the same message
+            'items': {
+                'type': 'object',
+                'required': ['vpn-id', 'vpn-svc-type', 'svc-topo', 'customer-name'],
+                'properties': {
+                    'vpn-id': {'type': 'string', 'pattern': REGEX_UUID},
+                    'vpn-svc-type': {'enum': ['vpws']},
+                    'svc-topo': {'enum': ['any-to-any']},
+                    'customer-name': {'const': 'osm'},
+                },
+            }
+        }
+    },
+}
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/Authentication.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/Authentication.py
new file mode 100644
index 0000000000000000000000000000000000000000..de7c9eafd7b2d5afdc39b82a4d02bea20127fa4a
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/Authentication.py
@@ -0,0 +1,11 @@
+from flask_httpauth import HTTPBasicAuth
+from werkzeug.security import check_password_hash
+from compute.Config import RESTAPI_USERS
+
+HTTP_AUTH = HTTPBasicAuth()
+
+@HTTP_AUTH.verify_password
+def verify_password(username, password):
+    if username not in RESTAPI_USERS: return None
+    if not check_password_hash(RESTAPI_USERS[username], password): return None
+    return username
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/HttpStatusCodes.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/HttpStatusCodes.py
new file mode 100644
index 0000000000000000000000000000000000000000..5879670102e861bf1598104ace80f1f0cdb931ca
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/HttpStatusCodes.py
@@ -0,0 +1,6 @@
+HTTP_OK             = 200
+HTTP_CREATED        = 201
+HTTP_NOCONTENT      = 204
+HTTP_BADREQUEST     = 400
+HTTP_SERVERERROR    = 500
+HTTP_GATEWAYTIMEOUT = 504
\ No newline at end of file
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/Validator.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/Validator.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c126d71beba72ebb7b69d9852927cb31ac2a614
--- /dev/null
+++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/Validator.py
@@ -0,0 +1,21 @@
+from typing import List
+from flask.json import jsonify
+from jsonschema import _utils
+from jsonschema.validators import validator_for
+from jsonschema.protocols import Validator
+from jsonschema.exceptions import ValidationError
+from werkzeug.exceptions import BadRequest
+from .HttpStatusCodes import HTTP_BADREQUEST
+
+def validate_message(schema, message):
+    validator_class = validator_for(schema)
+    validator : Validator = validator_class(schema)
+    errors : List[ValidationError] = sorted(validator.iter_errors(message), key=str)
+    if len(errors) == 0: return
+    response = jsonify([
+        {'message': str(error.message), 'schema': str(error.schema), 'validator': str(error.validator),
+         'where': str(_utils.format_as_index(container='message', indices=error.relative_path))}
+        for error in errors
+    ])
+    response.status_code = HTTP_BADREQUEST
+    raise BadRequest(response=response)
diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/__init__.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/compute/service/rest_server/resources/Compute.py b/src/compute/service/rest_server/resources/Compute.py
deleted file mode 100644
index 4b845be2edd20c512bd0669739d402207d71fa94..0000000000000000000000000000000000000000
--- a/src/compute/service/rest_server/resources/Compute.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import logging
-from flask.json import jsonify
-from flask_restful import Resource
-from common.Settings import get_setting
-from common.Constants import DEFAULT_CONTEXT_UUID
-from service.client.ServiceClient import ServiceClient
-from service.proto.context_pb2 import Service, ServiceStatusEnum, ServiceTypeEnum
-
-LOGGER = logging.getLogger(__name__)
-
-class Compute(Resource):
-    def __init__(self) -> None:
-        super().__init__()
-
-    def get(self):
-        # Here implement HTTP GET method
-        raise NotImplementedError()
-
-    def post(self):
-        # Here implement HTTP POST method
-
-        # Retrieve required data from request
-        new_service_context_id = DEFAULT_CONTEXT_UUID
-        new_service_id = 'my-service-id'
-
-        # Find Service address/port from environment and instantiate client
-        service_host = get_setting('SERVICESERVICE_SERVICE_HOST')
-        service_port = get_setting('SERVICESERVICE_SERVICE_PORT_GRPC')
-        service_client = ServiceClient(service_host, service_port)
-
-        # Compose a dummy CreateService request
-        request = Service()
-        request.service_id.context_id.context_uuid.uuid = new_service_context_id
-        request.service_id.service_uuid.uuid = new_service_id
-        request.service_type = ServiceTypeEnum.SERVICETYPE_L2NM
-        request.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED
-        
-        try:
-            # Issue gRPC request to Service component
-            reply = service_client.CreateService(request)
-
-            # Parse CreateService reply, here we check that obtained service Id and context are the expected ones.
-            reply_context_uuid = reply.context_id.context_uuid.uuid
-            reply_service_uuid = reply.service_uuid.uuid
-            #succeeded = (reply_context_uuid == new_service_context_id) and (reply_service_uuid == new_service_id)
-            succeeded = True
-            reply = {'succeeded': succeeded}
-        except Exception as e:
-            LOGGER.exception('Something went wrong Creating Service {:s}'.format(str(request)))
-            reply = {'succeeded': False, 'error': str(e)}
-
-        return jsonify(reply)
diff --git a/src/compute/tests/MockService.py b/src/compute/tests/MockService.py
new file mode 100644
index 0000000000000000000000000000000000000000..54b420f5aa1cf015c90f09b874f9b37225e07328
--- /dev/null
+++ b/src/compute/tests/MockService.py
@@ -0,0 +1,41 @@
+import grpc, logging
+from concurrent import futures
+
+GRPC_MAX_WORKERS  = 10
+GRPC_GRACE_PERIOD = 60
+
+class MockService:
+    def __init__(self, address, port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD, cls_name=__name__):
+        self.logger = logging.getLogger(cls_name)
+        self.address = address
+        self.port = port
+        self.endpoint = None
+        self.max_workers = max_workers
+        self.grace_period = grace_period
+        self.pool = None
+        self.server = None
+
+    def install_servicers(self):
+        pass
+
+    def start(self):
+        self.endpoint = '{:s}:{:s}'.format(str(self.address), str(self.port))
+        self.logger.info('Starting Service (tentative endpoint: {:s}, max_workers: {:s})...'.format(
+            str(self.endpoint), str(self.max_workers)))
+
+        self.pool = futures.ThreadPoolExecutor(max_workers=self.max_workers)
+        self.server = grpc.server(self.pool) # , interceptors=(tracer_interceptor,))
+
+        self.install_servicers()
+
+        port = self.server.add_insecure_port(self.endpoint)
+        self.endpoint = '{:s}:{:s}'.format(str(self.address), str(port))
+        self.logger.info('Listening on {:s}...'.format(str(self.endpoint)))
+        self.server.start()
+
+        self.logger.debug('Service started')
+
+    def stop(self):
+        self.logger.debug('Stopping service (grace period {:s} seconds)...'.format(str(self.grace_period)))
+        self.server.stop(self.grace_period)
+        self.logger.debug('Service stopped')
diff --git a/src/compute/tests/MockServicerImpl_Context.py b/src/compute/tests/MockServicerImpl_Context.py
new file mode 100644
index 0000000000000000000000000000000000000000..d79a755d49773dff4b298abdba6dfa38d9e69d57
--- /dev/null
+++ b/src/compute/tests/MockServicerImpl_Context.py
@@ -0,0 +1,188 @@
+import grpc, logging
+from typing import Any, Dict, Iterator, List
+from context.proto.context_pb2 import (
+    Context, ContextEvent, ContextId, ContextIdList, ContextList, Device, DeviceEvent, DeviceId, DeviceIdList,
+    DeviceList, Empty, Link, LinkEvent, LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList,
+    ServiceList, Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList)
+from context.proto.context_pb2_grpc import ContextServiceServicer
+from .Tools import grpc_message_to_json_string
+
+LOGGER = logging.getLogger(__name__)
+
+def get_container(database : Dict[str, Dict[str, Any]], container_name : str) -> Dict[str, Any]:
+    return database.setdefault(container_name, {})
+
+def get_entries(database : Dict[str, Dict[str, Any]], container_name : str) -> List[Any]:
+    container = get_container(database, container_name)
+    return [container[entry_uuid] for entry_uuid in sorted(container.keys())]
+
+def get_entry(
+    context : grpc.ServicerContext, database : Dict[str, Dict[str, Any]], container_name : str, entry_uuid : str
+) -> Any:
+    LOGGER.debug('[get_entry] AFTER database={:s}'.format(str(database)))
+    container = get_container(database, container_name)
+    if entry_uuid not in container:
+        context.abort(grpc.StatusCode.INTERNAL, str('{:s}({:s}) not found'.format(container_name, entry_uuid)))
+    return container[entry_uuid]
+
+def set_entry(database : Dict[str, Dict[str, Any]], container_name : str, entry_uuid : str, entry : Any) -> Any:
+    container = get_container(database, container_name)
+    LOGGER.debug('[set_entry] BEFORE database={:s}'.format(str(database)))
+    container[entry_uuid] = entry
+    LOGGER.debug('[set_entry] AFTER database={:s}'.format(str(database)))
+    return entry
+
+def del_entry(
+    context : grpc.ServicerContext, database : Dict[str, Dict[str, Any]], container_name : str, entry_uuid : str
+) -> Any:
+    container = get_container(database, container_name)
+    if entry_uuid not in container:
+        context.abort(grpc.StatusCode.INTERNAL, str('{:s}({:s}) not found'.format(container_name, entry_uuid)))
+    del container[entry_uuid]
+    return Empty()
+
+class MockServicerImpl_Context(ContextServiceServicer):
+    def __init__(self):
+        LOGGER.info('[__init__] Creating Servicer...')
+        self.database : Dict[str, Any] = {}
+        LOGGER.info('[__init__] Servicer Created')
+
+    # ----- Context ----------------------------------------------------------------------------------------------------
+
+    def ListContextIds(self, request: Empty, context : grpc.ServicerContext) -> ContextIdList:
+        LOGGER.info('[ListContextIds] request={:s}'.format(grpc_message_to_json_string(request)))
+        return ContextIdList(context_ids=[context.context_id for context in get_entries(self.database, 'context')])
+
+    def ListContexts(self, request: Empty, context : grpc.ServicerContext) -> ContextList:
+        LOGGER.info('[ListContexts] request={:s}'.format(grpc_message_to_json_string(request)))
+        return ContextList(contexts=get_entries(self.database, 'context'))
+
+    def GetContext(self, request: ContextId, context : grpc.ServicerContext) -> Context:
+        LOGGER.info('[GetContext] request={:s}'.format(grpc_message_to_json_string(request)))
+        return get_entry(context, self.database, 'context', request.context_uuid.uuid)
+
+    def SetContext(self, request: Context, context : grpc.ServicerContext) -> ContextId:
+        LOGGER.info('[SetContext] request={:s}'.format(grpc_message_to_json_string(request)))
+        return set_entry(self.database, 'context', request.context_uuid.uuid, request).context_id
+
+    def RemoveContext(self, request: ContextId, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[RemoveContext] request={:s}'.format(grpc_message_to_json_string(request)))
+        return del_entry(context, self.database, 'context', request.context_uuid.uuid)
+
+    def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]:
+        LOGGER.info('[GetContextEvents] request={:s}'.format(grpc_message_to_json_string(request)))
+
+
+    # ----- Topology ---------------------------------------------------------------------------------------------------
+
+    def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList:
+        LOGGER.info('[ListTopologyIds] request={:s}'.format(grpc_message_to_json_string(request)))
+        topologies = get_entries(self.database, 'topology[{:s}]'.format(str(request.context_id.context_uuid.uuid)))
+        return TopologyIdList(topology_ids=[topology.topology_id for topology in topologies])
+
+    def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList:
+        LOGGER.info('[ListTopologies] request={:s}'.format(grpc_message_to_json_string(request)))
+        topologies = get_entries(self.database, 'topology[{:s}]'.format(str(request.context_id.context_uuid.uuid)))
+        return TopologyList(topologies=[topology for topology in topologies])
+
+    def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology:
+        LOGGER.info('[GetTopology] request={:s}'.format(grpc_message_to_json_string(request)))
+        container_name = 'topology[{:s}]'.format(str(request.context_id.context_uuid.uuid))
+        return get_entry(context, self.database, container_name, request.topology_uuid.uuid)
+
+    def SetTopology(self, request: Topology, context : grpc.ServicerContext) -> TopologyId:
+        LOGGER.info('[SetTopology] request={:s}'.format(grpc_message_to_json_string(request)))
+        container_name = 'topology[{:s}]'.format(str(request.topology_id.context_id.context_uuid.uuid))
+        return set_entry(self.database, container_name, request.topology_id.topology_uuid.uuid, request).topology_id
+
+    def RemoveTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[RemoveTopology] request={:s}'.format(grpc_message_to_json_string(request)))
+        container_name = 'topology[{:s}]'.format(str(request.context_id.context_uuid.uuid))
+        return del_entry(context, self.database, container_name, request.topology_uuid.uuid)
+
+    def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]:
+        LOGGER.info('[GetTopologyEvents] request={:s}'.format(grpc_message_to_json_string(request)))
+
+
+    # ----- Device -----------------------------------------------------------------------------------------------------
+
+    def ListDeviceIds(self, request: Empty, context : grpc.ServicerContext) -> DeviceIdList:
+        LOGGER.info('[ListDeviceIds] request={:s}'.format(grpc_message_to_json_string(request)))
+        return DeviceIdList(device_ids=[device.device_id for device in get_entries(self.database, 'device')])
+
+    def ListDevices(self, request: Empty, context : grpc.ServicerContext) -> DeviceList:
+        LOGGER.info('[ListDevices] request={:s}'.format(grpc_message_to_json_string(request)))
+        return DeviceList(devices=get_entries(self.database, 'device'))
+
+    def GetDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Device:
+        LOGGER.info('[GetDevice] request={:s}'.format(grpc_message_to_json_string(request)))
+        return get_entry(context, self.database, 'device', request.device_uuid.uuid)
+
+    def SetDevice(self, request: Context, context : grpc.ServicerContext) -> DeviceId:
+        LOGGER.info('[SetDevice] request={:s}'.format(grpc_message_to_json_string(request)))
+        return set_entry(self.database, 'device', request.device_uuid.uuid, request).device_id
+
+    def RemoveDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[RemoveDevice] request={:s}'.format(grpc_message_to_json_string(request)))
+        return del_entry(context, self.database, 'device', request.device_uuid.uuid)
+
+    def GetDeviceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[DeviceEvent]:
+        LOGGER.info('[GetDeviceEvents] request={:s}'.format(grpc_message_to_json_string(request)))
+
+
+    # ----- Link -------------------------------------------------------------------------------------------------------
+
+    def ListLinkIds(self, request: Empty, context : grpc.ServicerContext) -> LinkIdList:
+        LOGGER.info('[ListLinkIds] request={:s}'.format(grpc_message_to_json_string(request)))
+        return LinkIdList(link_ids=[link.link_id for link in get_entries(self.database, 'link')])
+
+    def ListLinks(self, request: Empty, context : grpc.ServicerContext) -> LinkList:
+        LOGGER.info('[ListLinks] request={:s}'.format(grpc_message_to_json_string(request)))
+        return LinkList(links=get_entries(self.database, 'link'))
+
+    def GetLink(self, request: LinkId, context : grpc.ServicerContext) -> Link:
+        LOGGER.info('[GetLink] request={:s}'.format(grpc_message_to_json_string(request)))
+        return get_entry(context, self.database, 'link', request.link_uuid.uuid)
+
+    def SetLink(self, request: Context, context : grpc.ServicerContext) -> LinkId:
+        LOGGER.info('[SetLink] request={:s}'.format(grpc_message_to_json_string(request)))
+        return set_entry(self.database, 'link', request.link_uuid.uuid, request).link_id
+
+    def RemoveLink(self, request: LinkId, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[RemoveLink] request={:s}'.format(grpc_message_to_json_string(request)))
+        return del_entry(context, self.database, 'link', request.link_uuid.uuid)
+
+    def GetLinkEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[LinkEvent]:
+        LOGGER.info('[GetLinkEvents] request={:s}'.format(grpc_message_to_json_string(request)))
+
+
+    # ----- Service ----------------------------------------------------------------------------------------------------
+
+    def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList:
+        LOGGER.info('[ListServiceIds] request={:s}'.format(grpc_message_to_json_string(request)))
+        services = get_entries(self.database, 'service[{:s}]'.format(str(request.context_id.context_uuid.uuid)))
+        return ServiceIdList(service_ids=[service.service_id for service in services])
+
+    def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList:
+        LOGGER.info('[ListServices] request={:s}'.format(grpc_message_to_json_string(request)))
+        services = get_entries(self.database, 'service[{:s}]'.format(str(request.context_id.context_uuid.uuid)))
+        return ServiceList(services=[service for service in services])
+
+    def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service:
+        LOGGER.info('[GetService] request={:s}'.format(grpc_message_to_json_string(request)))
+        container_name = 'service[{:s}]'.format(str(request.context_id.context_uuid.uuid))
+        return get_entry(context, self.database, container_name, request.service_uuid.uuid)
+
+    def SetService(self, request: Service, context : grpc.ServicerContext) -> ServiceId:
+        LOGGER.info('[SetService] request={:s}'.format(grpc_message_to_json_string(request)))
+        return set_entry(
+            self.database, 'service[{:s}]'.format(str(request.service_id.context_id.context_uuid.uuid)),
+            request.service_id.service_uuid.uuid, request).service_id
+
+    def RemoveService(self, request: ServiceId, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[RemoveService] request={:s}'.format(grpc_message_to_json_string(request)))
+        container_name = 'service[{:s}]'.format(str(request.context_id.context_uuid.uuid))
+        return del_entry(context, self.database, container_name, request.service_uuid.uuid)
+
+    def GetServiceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ServiceEvent]:
+        LOGGER.info('[GetServiceEvents] request={:s}'.format(grpc_message_to_json_string(request)))
diff --git a/src/compute/tests/MockServicerImpl_Service.py b/src/compute/tests/MockServicerImpl_Service.py
new file mode 100644
index 0000000000000000000000000000000000000000..75fdc3073dac1b942c7701f3a0be9feacb60109b
--- /dev/null
+++ b/src/compute/tests/MockServicerImpl_Service.py
@@ -0,0 +1,32 @@
+import grpc, logging
+from common.Settings import get_setting
+from context.client.ContextClient import ContextClient
+from service.proto.context_pb2 import ConnectionList, Empty, Service, ServiceId
+from service.proto.service_pb2_grpc import ServiceServiceServicer
+from .Tools import grpc_message_to_json_string
+
+LOGGER = logging.getLogger(__name__)
+
+class MockServicerImpl_Service(ServiceServiceServicer):
+    def __init__(self):
+        LOGGER.info('[__init__] Creating Servicer...')
+        self.context_client = ContextClient(
+            get_setting('CONTEXTSERVICE_SERVICE_HOST'),
+            get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC'))
+        LOGGER.info('[__init__] Servicer Created')
+
+    def CreateService(self, request : Service, context : grpc.ServicerContext) -> ServiceId:
+        LOGGER.info('[CreateService] request={:s}'.format(grpc_message_to_json_string(request)))
+        return self.context_client.SetService(request)
+
+    def UpdateService(self, request : Service, context : grpc.ServicerContext) -> ServiceId:
+        LOGGER.info('[UpdateService] request={:s}'.format(grpc_message_to_json_string(request)))
+        return self.context_client.SetService(request)
+
+    def DeleteService(self, request : ServiceId, context : grpc.ServicerContext) -> Empty:
+        LOGGER.info('[DeleteService] request={:s}'.format(grpc_message_to_json_string(request)))
+        return self.context_client.RemoveService(request)
+
+    def GetConnectionList(self, request : ServiceId, context : grpc.ServicerContext) -> ConnectionList:
+        LOGGER.info('[GetConnectionList] request={:s}'.format(grpc_message_to_json_string(request)))
+        return ConnectionList()
diff --git a/src/compute/tests/Tools.py b/src/compute/tests/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..a96c38ce546d4062df8229f5506f9dd49af6fc81
--- /dev/null
+++ b/src/compute/tests/Tools.py
@@ -0,0 +1,7 @@
+import json
+from google.protobuf.json_format import MessageToDict
+
+def grpc_message_to_json_string(message):
+    return json.dumps(MessageToDict(
+        message, including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False),
+        sort_keys=True)
diff --git a/src/compute/tests/mock_osm/MockOSM.py b/src/compute/tests/mock_osm/MockOSM.py
new file mode 100644
index 0000000000000000000000000000000000000000..c50ee6c88e75a62a743bba065830ae82827fa7d7
--- /dev/null
+++ b/src/compute/tests/mock_osm/MockOSM.py
@@ -0,0 +1,94 @@
+import logging
+from .WimconnectorIETFL2VPN import WimconnectorIETFL2VPN
+
+LOGGER = logging.getLogger(__name__)
+
+WIM_USERNAME = 'admin'
+WIM_PASSWORD = 'admin'
+
+# Ref: https://osm.etsi.org/wikipub/index.php/WIM
+WIM_MAPPING  = [
+    {
+        'device-id'           : 'dev-1',            # pop_switch_dpid
+        #'device_interface_id' : ??,                # pop_switch_port
+        'service_endpoint_id' : 'ep-1',             # wan_service_endpoint_id
+        'service_mapping_info': {                   # wan_service_mapping_info, other extra info
+            'bearer': {'bearer-reference': 'dev-1:ep-1:10.0.0.1'},
+            'site-id': '1',
+        },
+        #'switch_dpid'         : ??,                # wan_switch_dpid
+        #'switch_port'         : ??,                # wan_switch_port
+        #'datacenter_id'       : ??,                # vim_account
+    },
+    {
+        'device-id'           : 'dev-2',            # pop_switch_dpid
+        #'device_interface_id' : ??,                # pop_switch_port
+        'service_endpoint_id' : 'ep-2',             # wan_service_endpoint_id
+        'service_mapping_info': {                   # wan_service_mapping_info, other extra info
+            'bearer': {'bearer-reference': 'dev-2:ep-2:10.0.0.2'},
+            'site-id': '2',
+        },
+        #'switch_dpid'         : ??,                # wan_switch_dpid
+        #'switch_port'         : ??,                # wan_switch_port
+        #'datacenter_id'       : ??,                # vim_account
+    },
+    {
+        'device-id'           : 'dev-3',            # pop_switch_dpid
+        #'device_interface_id' : ??,                # pop_switch_port
+        'service_endpoint_id' : 'ep-3',             # wan_service_endpoint_id
+        'service_mapping_info': {                   # wan_service_mapping_info, other extra info
+            'bearer': {'bearer-reference': 'dev-3:ep-3:10.0.0.3'},
+            'site-id': '3',
+        },
+        #'switch_dpid'         : ??,                # wan_switch_dpid
+        #'switch_port'         : ??,                # wan_switch_port
+        #'datacenter_id'       : ??,                # vim_account
+    },
+]
+
+SERVICE_TYPE = 'ELINE'
+SERVICE_CONNECTION_POINTS_1 = [
+    {'service_endpoint_id': 'ep-1',
+        'service_endpoint_encapsulation_type': 'dot1q',
+        'service_endpoint_encapsulation_info': {'vlan': 1234}},
+    {'service_endpoint_id': 'ep-2',
+        'service_endpoint_encapsulation_type': 'dot1q',
+        'service_endpoint_encapsulation_info': {'vlan': 1234}},
+]
+
+SERVICE_CONNECTION_POINTS_2 = [
+    {'service_endpoint_id': 'ep-3',
+        'service_endpoint_encapsulation_type': 'dot1q',
+        'service_endpoint_encapsulation_info': {'vlan': 1234}},
+]
+
+class MockOSM:
+    def __init__(self, wim_url):
+        wim = {'wim_url': wim_url}
+        wim_account = {'user': WIM_USERNAME, 'password': WIM_PASSWORD}
+        config = {'mapping_not_needed': False, 'service_endpoint_mapping': WIM_MAPPING}
+        self.wim = WimconnectorIETFL2VPN(wim, wim_account, config=config)
+        self.service_uuid = None
+        self.conn_info = None
+
+    def create_connectivity_service(self):
+        self.wim.check_credentials()
+        LOGGER.info('[create_connectivity_service] connection_points={:s}'.format(str(SERVICE_CONNECTION_POINTS_1)))
+        result = self.wim.create_connectivity_service(SERVICE_TYPE, SERVICE_CONNECTION_POINTS_1)
+        LOGGER.info('[create_connectivity_service] result={:s}'.format(str(result)))
+        self.service_uuid, self.conn_info = result
+
+    def get_connectivity_service_status(self):
+        self.wim.check_credentials()
+        result = self.wim.get_connectivity_service_status(self.service_uuid, conn_info=self.conn_info)
+        LOGGER.info('[get_connectivity_service] result={:s}'.format(str(result)))
+
+    def edit_connectivity_service(self):
+        self.wim.check_credentials()
+        LOGGER.info('[edit_connectivity_service] connection_points={:s}'.format(str(SERVICE_CONNECTION_POINTS_2)))
+        self.wim.edit_connectivity_service(
+            self.service_uuid, conn_info=self.conn_info, connection_points=SERVICE_CONNECTION_POINTS_2)
+
+    def delete_connectivity_service(self):
+        self.wim.check_credentials()
+        self.wim.delete_connectivity_service(self.service_uuid, conn_info=self.conn_info)
diff --git a/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py b/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py
new file mode 100644
index 0000000000000000000000000000000000000000..182115bad67a4fbe1eb04a83ed8d54be964568c8
--- /dev/null
+++ b/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py
@@ -0,0 +1,499 @@
+# -*- 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
+#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)
+
+            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.
+        """
+        if service_type == "ELINE":
+            if len(connection_points) > 2:
+                raise SdnConnectorError(
+                    "Connections between more than 2 endpoints are not supported"
+                )
+
+            if len(connection_points) < 2:
+                raise SdnConnectorError("Connections must be of at least 2 endpoints")
+
+            """First step, create the vpn service"""
+            uuid_l2vpn = str(uuid.uuid4())
+            vpn_service = {}
+            vpn_service["vpn-id"] = uuid_l2vpn
+            vpn_service["vpn-svc-type"] = "vpws"            # Rename "vpn-scv-type" -> "vpn-svc-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,
+                )
+
+            """Second step, create the connections and vpn attachments"""
+            for connection_point in 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"]
+                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 no 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
+        else:
+            raise NotImplementedError
+
+    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/compute/tests/mock_osm/__init__.py b/src/compute/tests/mock_osm/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/compute/tests/mock_osm/acknowledgements.txt b/src/compute/tests/mock_osm/acknowledgements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b7ce926dd006d9bc8afaffbed212d90fb05adbef
--- /dev/null
+++ b/src/compute/tests/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/src/compute/tests/mock_osm/sdnconn.py b/src/compute/tests/mock_osm/sdnconn.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1849c9ef3e1a1260ff42bbadabc99f91a6435d7
--- /dev/null
+++ b/src/compute/tests/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
diff --git a/src/compute/tests/test_unitary.py b/src/compute/tests/test_unitary.py
index 689e6038251d0a371833b34cd9e5158af2fcc0e6..001999f1b9607f03cd393f5582cc08a504c0e9d2 100644
--- a/src/compute/tests/test_unitary.py
+++ b/src/compute/tests/test_unitary.py
@@ -1,48 +1,54 @@
-import logging, os, pytest, requests, time
-from google.protobuf.json_format import MessageToDict
-from common.type_checkers.Assertions import validate_service_id
-from compute.client.ComputeClient import ComputeClient
-from compute.proto.context_pb2 import Service
-from compute.service.ComputeService import ComputeService
-from compute.Config import (
-    GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, RESTAPI_SERVICE_PORT, RESTAPI_BASE_URL)
-from compute.service.rest_server.Server import Server
-from compute.service.rest_server.resources.Compute import Compute
-from service.service.ServiceService import ServiceService
-from service.Config import (
-    GRPC_SERVICE_PORT as SERVICE_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as SERVICE_GRPC_MAX_WORKERS,
-    GRPC_GRACE_PERIOD as SERVICE_GRPC_GRACE_PERIOD)
-
-compute_grpc_port = 10000 + GRPC_SERVICE_PORT # avoid privileged ports
-compute_restapi_port = 10000 + RESTAPI_SERVICE_PORT # avoid privileged ports
-service_grpc_port = 10000 + SERVICE_GRPC_SERVICE_PORT # avoid privileged ports
-
-os.environ['SERVICESERVICE_SERVICE_HOST'] = '127.0.0.1'
-os.environ['SERVICESERVICE_SERVICE_PORT_GRPC'] = str(service_grpc_port)
+import logging, os, pytest, time
+from compute.Config import RESTAPI_SERVICE_PORT, RESTAPI_BASE_URL
+from compute.service.rest_server.RestServer import RestServer
+from context.proto.context_pb2_grpc import add_ContextServiceServicer_to_server
+from service.proto.service_pb2_grpc import add_ServiceServiceServicer_to_server
+from .mock_osm.MockOSM import MockOSM
+from .MockService import MockService
+from .MockServicerImpl_Context import MockServicerImpl_Context
+from .MockServicerImpl_Service import MockServicerImpl_Service
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.DEBUG)
 
-@pytest.fixture(scope='session')
-def service_service():
-    _service = ServiceService(
-        port=service_grpc_port, max_workers=SERVICE_GRPC_MAX_WORKERS, grace_period=SERVICE_GRPC_GRACE_PERIOD)
-    _service.start()
-    yield _service
-    _service.stop()
+LOCALHOST = '127.0.0.1'
+MOCKSERVER_GRPC_PORT = 10000
+COMPUTE_RESTAPI_PORT = 10000 + RESTAPI_SERVICE_PORT # avoid privileged ports
+
+class MockService_ContextService(MockService):
+    # Mock Server implementing Context and Service to simplify unitary tests of Compute
+
+    def __init__(self, cls_name='MockService_Service'):
+        super().__init__(LOCALHOST, MOCKSERVER_GRPC_PORT, cls_name=cls_name)
+
+    # pylint: disable=attribute-defined-outside-init
+    def install_servicers(self):
+        self.context_servicer = MockServicerImpl_Context()
+        add_ContextServiceServicer_to_server(self.context_servicer, self.server)
+        self.service_servicer = MockServicerImpl_Service()
+        add_ServiceServiceServicer_to_server(self.service_servicer, self.server)
+
+os.environ['CONTEXTSERVICE_SERVICE_HOST'] = LOCALHOST
+os.environ['CONTEXTSERVICE_SERVICE_PORT_GRPC'] = str(MOCKSERVER_GRPC_PORT)
+os.environ['SERVICESERVICE_SERVICE_HOST'] = LOCALHOST
+os.environ['SERVICESERVICE_SERVICE_PORT_GRPC'] = str(MOCKSERVER_GRPC_PORT)
+
+# NBI Plugin IETF L2VPN requires environment variables CONTEXTSERVICE_SERVICE_HOST, CONTEXTSERVICE_SERVICE_PORT_GRPC,
+# SERVICESERVICE_SERVICE_HOST, and SERVICESERVICE_SERVICE_PORT_GRPC to work properly.
+# pylint: disable=wrong-import-position,ungrouped-imports
+from compute.service.rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn
 
 @pytest.fixture(scope='session')
-def compute_service(service_service : ServiceService):
-    _service = ComputeService(port=compute_grpc_port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD)
+def mockservice():
+    _service = MockService_ContextService()
     _service.start()
     yield _service
     _service.stop()
 
 @pytest.fixture(scope='session')
-def compute_service_rest():
-    _rest_server = Server(port=compute_restapi_port, base_url=RESTAPI_BASE_URL)
-    _rest_server.add_resource(
-        Compute, '/restconf/config/compute', endpoint='api.compute')
+def compute_service_rest(mockservice):  # pylint: disable=redefined-outer-name
+    _rest_server = RestServer(port=COMPUTE_RESTAPI_PORT, base_url=RESTAPI_BASE_URL)
+    register_ietf_l2vpn(_rest_server)
     _rest_server.start()
     time.sleep(1) # bring time for the server to start
     yield _rest_server
@@ -50,27 +56,18 @@ def compute_service_rest():
     _rest_server.join()
 
 @pytest.fixture(scope='session')
-def compute_client(compute_service):
-    _client = ComputeClient(address='127.0.0.1', port=compute_grpc_port)
-    yield _client
-    _client.close()
+def osm_wim(compute_service_rest): # pylint: disable=redefined-outer-name
+    wim_url = 'http://{:s}:{:d}'.format(LOCALHOST, COMPUTE_RESTAPI_PORT)
+    return MockOSM(wim_url)
+
+def test_compute_create_connectivity_service_rest_api(osm_wim : MockOSM): # pylint: disable=redefined-outer-name
+    osm_wim.create_connectivity_service()
+
+def test_compute_get_connectivity_service_status_rest_api(osm_wim : MockOSM): # pylint: disable=redefined-outer-name
+    osm_wim.get_connectivity_service_status()
 
-def test_dummy_create_connectivity_service(compute_client : ComputeClient):
-    # dummy test: should fail with assertion error
-    with pytest.raises(AssertionError):
-        validate_service_id(MessageToDict(
-            compute_client.CreateConnectivityService(Service()),
-            including_default_value_fields=True, preserving_proto_field_name=True,
-            use_integers_for_enums=False))
+def test_compute_edit_connectivity_service_rest_api(osm_wim : MockOSM): # pylint: disable=redefined-outer-name
+    osm_wim.edit_connectivity_service()
 
-def test_dummy_create_connectivity_service_rest_api(compute_service_rest : Server):
-    # should work
-    request_url = 'http://127.0.0.1:{:s}{:s}/restconf/config/compute'
-    request_url = request_url.format(str(compute_restapi_port), RESTAPI_BASE_URL)
-    reply = requests.post(request_url, json={
-        # here add context of POST request body as JSON
-    })
-    json_reply = reply.json()
-    LOGGER.info('json_reply = {:s}'.format(str(json_reply)))
-    assert 'succeeded' in json_reply
-    assert json_reply['succeeded']
+def test_compute_delete_connectivity_service_rest_api(osm_wim : MockOSM): # pylint: disable=redefined-outer-name
+    osm_wim.delete_connectivity_service()
diff --git a/src/context/.gitlab-ci.yml b/src/context/.gitlab-ci.yml
index 23949213fed4941813b38ab39255648b8ad9364b..bae78b86d55c0437a468bff03c30afa28598d5c7 100644
--- a/src/context/.gitlab-ci.yml
+++ b/src/context/.gitlab-ci.yml
@@ -10,8 +10,8 @@ build context:
     - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile ./src/
     - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
     - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-#  after_script:
-#    - docker rmi $(docker images --quiet --filter=dangling=true)
+  after_script:
+    - docker images --filter="dangling=true" --quiet | xargs -r docker rmi
   rules:
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
     - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' 
@@ -38,10 +38,11 @@ unit test context:
   script:
     - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
     - docker pull "redis:6.2"
-    - docker run --name redis -d --network=teraflowbridge --rm redis:6.2
-    - docker run --name $IMAGE_NAME -d -p 1010:1010 --env "DB_BACKEND=redis" --env "REDIS_SERVICE_HOST=redis" --env "REDIS_SERVICE_PORT=6379" --env "REDIS_DATABASE_ID=0" --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name redis -d --network=teraflowbridge redis:6.2
+    - docker run --name $IMAGE_NAME -d -p 1010:1010 --env "DB_BACKEND=redis" --env "REDIS_SERVICE_HOST=redis" --env "REDIS_SERVICE_PORT=6379" --env "REDIS_DATABASE_ID=0" --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 10
     - docker ps -a
+    - docker logs $IMAGE_NAME
     - docker exec -i $IMAGE_NAME bash -c "pytest --log-level=DEBUG --verbose $IMAGE_NAME/tests/test_unitary.py"
   after_script:
     - docker rm -f $IMAGE_NAME
diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py
index 750fa2a311bd35d0f47e04da3e424ca9d10345f7..5836511e1fe6057a55ee714d1b0e9a905f638660 100644
--- a/src/context/tests/test_unitary.py
+++ b/src/context/tests/test_unitary.py
@@ -318,16 +318,22 @@ def test_grpc_topology(
     assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID
 
     # ----- Check create event -----------------------------------------------------------------------------------------
-    event = events_collector.get_event(block=True)
-    assert isinstance(event, TopologyEvent)
-    assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE
-    assert event.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
-    assert event.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
-
-    event = events_collector.get_event(block=True)
-    assert isinstance(event, ContextEvent)
-    assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
-    assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
+    context_event = None
+    topology_event = None
+    for _ in range(2):
+        event = events_collector.get_event(block=True)
+        assert isinstance(event, (ContextEvent, TopologyEvent))
+        if isinstance(event, ContextEvent): context_event = event
+        if isinstance(event, TopologyEvent): topology_event = event
+
+    assert context_event is not None
+    assert context_event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE
+    assert context_event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
+
+    assert topology_event is not None
+    assert topology_event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE
+    assert topology_event.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID
+    assert topology_event.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID
 
     # ----- Update the object ------------------------------------------------------------------------------------------
     response = context_client_grpc.SetTopology(Topology(**TOPOLOGY))
diff --git a/src/device/.gitlab-ci.yml b/src/device/.gitlab-ci.yml
index 8656e01fede917d6a6ef13d0c0cb5fac618c444c..2c93618afd4d6617370f0567bb33b4b4d382a1cf 100644
--- a/src/device/.gitlab-ci.yml
+++ b/src/device/.gitlab-ci.yml
@@ -10,8 +10,8 @@ build device:
     - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile ./src/
     - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
     - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-#  after_script:
-#    - docker rmi $(docker images --quiet --filter=dangling=true)
+  after_script:
+    - docker images --filter="dangling=true" --quiet | xargs -r docker rmi
   rules:
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
     - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' 
@@ -36,9 +36,10 @@ unit test device:
     - if docker container ls | grep $IMAGE_NAME; then docker rm -f $IMAGE_NAME; else echo "$IMAGE_NAME image is not in the system"; fi
   script:
     - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-    - docker run --name $IMAGE_NAME -d -p 2020:2020 --network=teraflowbridge --rm $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
+    - docker run --name $IMAGE_NAME -d -p 2020:2020 --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
     - sleep 5
     - docker ps -a
+    - docker logs $IMAGE_NAME
     - docker exec -i $IMAGE_NAME bash -c "pytest --log-level=DEBUG --verbose $IMAGE_NAME/tests/test_unitary.py"
   after_script:
     - docker rm -f $IMAGE_NAME
diff --git a/src/service/.gitlab-ci.yml b/src/service/.gitlab-ci.yml
index d47177171a768a776368561c20aae1658bdc614d..3c943d5438f64dd1e6f6b12749b839539a22ca46 100644
--- a/src/service/.gitlab-ci.yml
+++ b/src/service/.gitlab-ci.yml
@@ -10,8 +10,8 @@ build service:
     - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile ./src/
     - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
     - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
-#  after_script:
-#    - docker rmi $(docker images --quiet --filter=dangling=true)
+  after_script:
+    - docker images --filter="dangling=true" --quiet | xargs -r docker rmi
   rules:
     - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
     - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'